diff options
author | 2023-03-07 05:06:26 +0100 | |
---|---|---|
committer | 2023-03-07 05:06:26 +0100 | |
commit | bb49e17cabd45f3a21af98b4c5ecdddd507fd427 (patch) | |
tree | 42d3255e869ca9a9fe6270e0658856830c2e7d31 /tiny_skia/src/raster.rs | |
parent | 3a26baa564524b0f25c5cb180b592c8b004b68a9 (diff) | |
download | iced-bb49e17cabd45f3a21af98b4c5ecdddd507fd427.tar.gz iced-bb49e17cabd45f3a21af98b4c5ecdddd507fd427.tar.bz2 iced-bb49e17cabd45f3a21af98b4c5ecdddd507fd427.zip |
Implement `raster` pipeline in `iced_tiny_skia`
Diffstat (limited to 'tiny_skia/src/raster.rs')
-rw-r--r-- | tiny_skia/src/raster.rs | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/tiny_skia/src/raster.rs b/tiny_skia/src/raster.rs new file mode 100644 index 00000000..e57f0e50 --- /dev/null +++ b/tiny_skia/src/raster.rs @@ -0,0 +1,107 @@ +use crate::core::image as raster; +use crate::core::{Rectangle, Size}; +use crate::graphics; + +use rustc_hash::{FxHashMap, FxHashSet}; +use std::cell::RefCell; +use std::collections::hash_map; + +pub struct Pipeline { + cache: RefCell<Cache>, +} + +impl Pipeline { + pub fn new() -> Self { + Self { + cache: RefCell::new(Cache::default()), + } + } + + pub fn dimensions(&self, handle: &raster::Handle) -> Size<u32> { + if let Some(image) = self.cache.borrow_mut().allocate(handle) { + Size::new(image.width(), image.height()) + } else { + Size::new(0, 0) + } + } + + pub fn draw( + &mut self, + handle: &raster::Handle, + bounds: Rectangle, + pixels: &mut tiny_skia::PixmapMut<'_>, + transform: tiny_skia::Transform, + clip_mask: Option<&tiny_skia::ClipMask>, + ) { + if let Some(image) = self.cache.borrow_mut().allocate(handle) { + let width_scale = bounds.width / image.width() as f32; + let height_scale = bounds.height / image.height() as f32; + + let transform = transform.pre_scale(width_scale, height_scale); + + pixels.draw_pixmap( + (bounds.x / width_scale) as i32, + (bounds.y / height_scale) as i32, + image, + &tiny_skia::PixmapPaint { + quality: tiny_skia::FilterQuality::Bilinear, + ..Default::default() + }, + transform, + clip_mask, + ); + } + } +} + +#[derive(Default)] +struct Cache { + entries: FxHashMap<u64, Option<Entry>>, + hits: FxHashSet<u64>, +} + +impl Cache { + pub fn allocate( + &mut self, + handle: &raster::Handle, + ) -> Option<tiny_skia::PixmapRef<'_>> { + let id = handle.id(); + + if let hash_map::Entry::Vacant(entry) = self.entries.entry(id) { + let image = graphics::image::load(handle).ok()?.into_rgba8(); + + let mut buffer = + vec![0u32; image.width() as usize * image.height() as usize]; + + for (i, pixel) in image.pixels().enumerate() { + let [r, g, b, a] = pixel.0; + + buffer[i] = tiny_skia::ColorU8::from_rgba(b, g, r, a) + .premultiply() + .get(); + } + + entry.insert(Some(Entry { + width: image.width(), + height: image.height(), + pixels: buffer, + })); + } + + self.hits.insert(id); + self.entries.get(&id).unwrap().as_ref().map(|entry| { + tiny_skia::PixmapRef::from_bytes( + bytemuck::cast_slice(&entry.pixels), + entry.width, + entry.height, + ) + .expect("Build pixmap from image bytes") + }) + } +} + +struct Entry { + width: u32, + height: u32, + pixels: Vec<u32>, +} |