iced_tiny_skia/
raster.rs

1use crate::core::image as raster;
2use crate::core::{Rectangle, Size};
3use crate::graphics;
4
5use rustc_hash::{FxHashMap, FxHashSet};
6use std::cell::RefCell;
7use std::collections::hash_map;
8
9#[derive(Debug)]
10pub struct Pipeline {
11    cache: RefCell<Cache>,
12}
13
14impl Pipeline {
15    pub fn new() -> Self {
16        Self {
17            cache: RefCell::new(Cache::default()),
18        }
19    }
20
21    pub fn dimensions(&self, handle: &raster::Handle) -> Size<u32> {
22        if let Some(image) = self.cache.borrow_mut().allocate(handle) {
23            Size::new(image.width(), image.height())
24        } else {
25            Size::new(0, 0)
26        }
27    }
28
29    pub fn draw(
30        &mut self,
31        handle: &raster::Handle,
32        filter_method: raster::FilterMethod,
33        bounds: Rectangle,
34        opacity: f32,
35        pixels: &mut tiny_skia::PixmapMut<'_>,
36        transform: tiny_skia::Transform,
37        clip_mask: Option<&tiny_skia::Mask>,
38    ) {
39        if let Some(image) = self.cache.borrow_mut().allocate(handle) {
40            let width_scale = bounds.width / image.width() as f32;
41            let height_scale = bounds.height / image.height() as f32;
42
43            let transform = transform.pre_scale(width_scale, height_scale);
44
45            let quality = match filter_method {
46                raster::FilterMethod::Linear => {
47                    tiny_skia::FilterQuality::Bilinear
48                }
49                raster::FilterMethod::Nearest => {
50                    tiny_skia::FilterQuality::Nearest
51                }
52            };
53
54            pixels.draw_pixmap(
55                (bounds.x / width_scale) as i32,
56                (bounds.y / height_scale) as i32,
57                image,
58                &tiny_skia::PixmapPaint {
59                    quality,
60                    opacity,
61                    ..Default::default()
62                },
63                transform,
64                clip_mask,
65            );
66        }
67    }
68
69    pub fn trim_cache(&mut self) {
70        self.cache.borrow_mut().trim();
71    }
72}
73
74#[derive(Debug, Default)]
75struct Cache {
76    entries: FxHashMap<raster::Id, Option<Entry>>,
77    hits: FxHashSet<raster::Id>,
78}
79
80impl Cache {
81    pub fn allocate(
82        &mut self,
83        handle: &raster::Handle,
84    ) -> Option<tiny_skia::PixmapRef<'_>> {
85        let id = handle.id();
86
87        if let hash_map::Entry::Vacant(entry) = self.entries.entry(id) {
88            let image = graphics::image::load(handle).ok()?;
89
90            let mut buffer =
91                vec![0u32; image.width() as usize * image.height() as usize];
92
93            for (i, pixel) in image.pixels().enumerate() {
94                let [r, g, b, a] = pixel.0;
95
96                buffer[i] = bytemuck::cast(
97                    tiny_skia::ColorU8::from_rgba(b, g, r, a).premultiply(),
98                );
99            }
100
101            let _ = entry.insert(Some(Entry {
102                width: image.width(),
103                height: image.height(),
104                pixels: buffer,
105            }));
106        }
107
108        let _ = self.hits.insert(id);
109        self.entries.get(&id).unwrap().as_ref().map(|entry| {
110            tiny_skia::PixmapRef::from_bytes(
111                bytemuck::cast_slice(&entry.pixels),
112                entry.width,
113                entry.height,
114            )
115            .expect("Build pixmap from image bytes")
116        })
117    }
118
119    fn trim(&mut self) {
120        self.entries.retain(|key, _| self.hits.contains(key));
121        self.hits.clear();
122    }
123}
124
125#[derive(Debug)]
126struct Entry {
127    width: u32,
128    height: u32,
129    pixels: Vec<u32>,
130}