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 | |
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')
-rw-r--r-- | tiny_skia/Cargo.toml | 2 | ||||
-rw-r--r-- | tiny_skia/src/backend.rs | 28 | ||||
-rw-r--r-- | tiny_skia/src/lib.rs | 3 | ||||
-rw-r--r-- | tiny_skia/src/raster.rs | 107 |
4 files changed, 134 insertions, 6 deletions
diff --git a/tiny_skia/Cargo.toml b/tiny_skia/Cargo.toml index 08e79bb8..647ec12a 100644 --- a/tiny_skia/Cargo.toml +++ b/tiny_skia/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [features] -image = [] +image = ["iced_graphics/image"] svg = [] geometry = ["iced_graphics/geometry"] diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index d364e36a..d894ab95 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -11,6 +11,9 @@ pub struct Backend { default_font: Font, default_text_size: f32, text_pipeline: crate::text::Pipeline, + + #[cfg(feature = "image")] + raster_pipeline: crate::raster::Pipeline, } impl Backend { @@ -19,6 +22,9 @@ impl Backend { default_font: settings.default_font, default_text_size: settings.default_text_size, text_pipeline: crate::text::Pipeline::new(), + + #[cfg(feature = "image")] + raster_pipeline: crate::raster::Pipeline::new(), } } @@ -159,8 +165,21 @@ impl Backend { clip_bounds.map(|_| clip_mask as &_), ); } - Primitive::Image { .. } => { - // TODO + #[cfg(feature = "image")] + Primitive::Image { handle, bounds } => { + let transform = tiny_skia::Transform::from_translate( + translation.x, + translation.y, + ) + .post_scale(scale_factor, scale_factor); + + self.raster_pipeline.draw( + handle, + *bounds, + pixels, + transform, + clip_bounds.map(|_| clip_mask as &_), + ); } Primitive::Svg { .. } => { // TODO @@ -490,9 +509,8 @@ impl backend::Text for Backend { #[cfg(feature = "image")] impl backend::Image for Backend { - fn dimensions(&self, _handle: &crate::core::image::Handle) -> Size<u32> { - // TODO - Size::new(0, 0) + fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> { + self.raster_pipeline.dimensions(handle) } } diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index bf83e400..d03bdcc2 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -4,6 +4,9 @@ mod backend; mod settings; mod text; +#[cfg(feature = "image")] +mod raster; + #[cfg(feature = "geometry")] pub mod geometry; 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>, +} |