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}