diff options
author | 2023-03-07 06:09:51 +0100 | |
---|---|---|
committer | 2023-03-07 06:09:51 +0100 | |
commit | 5b3977daf6df624ca5d5e1a21ce282161234b22d (patch) | |
tree | c0efb1cda1dde399b60b89a2d57dc39feecca2cd /tiny_skia | |
parent | bb49e17cabd45f3a21af98b4c5ecdddd507fd427 (diff) | |
download | iced-5b3977daf6df624ca5d5e1a21ce282161234b22d.tar.gz iced-5b3977daf6df624ca5d5e1a21ce282161234b22d.tar.bz2 iced-5b3977daf6df624ca5d5e1a21ce282161234b22d.zip |
Implement `vector` pipeline in `iced_tiny_skia`
Diffstat (limited to 'tiny_skia')
-rw-r--r-- | tiny_skia/Cargo.toml | 6 | ||||
-rw-r--r-- | tiny_skia/src/backend.rs | 30 | ||||
-rw-r--r-- | tiny_skia/src/lib.rs | 3 | ||||
-rw-r--r-- | tiny_skia/src/text.rs | 2 | ||||
-rw-r--r-- | tiny_skia/src/vector.rs | 149 |
5 files changed, 182 insertions, 8 deletions
diff --git a/tiny_skia/Cargo.toml b/tiny_skia/Cargo.toml index 647ec12a..69197589 100644 --- a/tiny_skia/Cargo.toml +++ b/tiny_skia/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [features] image = ["iced_graphics/image"] -svg = [] +svg = ["resvg"] geometry = ["iced_graphics/geometry"] [dependencies] @@ -34,3 +34,7 @@ default-features = false [target.'cfg(not(target_arch = "wasm32"))'.dependencies.twox-hash] version = "1.6.1" features = ["std"] + +[dependencies.resvg] +version = "0.29" +optional = true diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index d894ab95..3c2a97b9 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -14,6 +14,9 @@ pub struct Backend { #[cfg(feature = "image")] raster_pipeline: crate::raster::Pipeline, + + #[cfg(feature = "svg")] + vector_pipeline: crate::vector::Pipeline, } impl Backend { @@ -25,6 +28,9 @@ impl Backend { #[cfg(feature = "image")] raster_pipeline: crate::raster::Pipeline::new(), + + #[cfg(feature = "svg")] + vector_pipeline: crate::vector::Pipeline::new(), } } @@ -78,7 +84,10 @@ impl Backend { ); } - self.text_pipeline.end_frame(); + self.text_pipeline.trim_cache(); + + #[cfg(feature = "svg")] + self.vector_pipeline.trim_cache(); } fn draw_primitive( @@ -181,8 +190,18 @@ impl Backend { clip_bounds.map(|_| clip_mask as &_), ); } - Primitive::Svg { .. } => { - // TODO + #[cfg(feature = "svg")] + Primitive::Svg { + handle, + bounds, + color: _, // TODO: Implement color filter + } => { + self.vector_pipeline.draw( + handle, + (*bounds + translation) * scale_factor, + pixels, + clip_bounds.map(|_| clip_mask as &_), + ); } Primitive::Fill { path, @@ -518,9 +537,8 @@ impl backend::Image for Backend { impl backend::Svg for Backend { fn viewport_dimensions( &self, - _handle: &crate::core::svg::Handle, + handle: &crate::core::svg::Handle, ) -> Size<u32> { - // TODO - Size::new(0, 0) + self.vector_pipeline.viewport_dimensions(handle) } } diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index d03bdcc2..83baef1c 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -7,6 +7,9 @@ mod text; #[cfg(feature = "image")] mod raster; +#[cfg(feature = "svg")] +mod vector; + #[cfg(feature = "geometry")] pub mod geometry; diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index c4edadb3..bfe5da9d 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -143,7 +143,7 @@ impl Pipeline { }); } - pub fn end_frame(&mut self) { + pub fn trim_cache(&mut self) { self.system .as_mut() .unwrap() diff --git a/tiny_skia/src/vector.rs b/tiny_skia/src/vector.rs new file mode 100644 index 00000000..fd9cfdc5 --- /dev/null +++ b/tiny_skia/src/vector.rs @@ -0,0 +1,149 @@ +use crate::core::svg::{Data, Handle}; +use crate::core::{Rectangle, Size}; + +use resvg::usvg; +use rustc_hash::{FxHashMap, FxHashSet}; + +use std::cell::RefCell; +use std::collections::hash_map; +use std::fs; + +pub struct Pipeline { + cache: RefCell<Cache>, +} + +impl Pipeline { + pub fn new() -> Self { + Self { + cache: RefCell::new(Cache::default()), + } + } + + pub fn viewport_dimensions(&self, handle: &Handle) -> Size<u32> { + self.cache + .borrow_mut() + .viewport_dimensions(handle) + .unwrap_or(Size::new(0, 0)) + } + + pub fn draw( + &mut self, + handle: &Handle, + bounds: Rectangle, + pixels: &mut tiny_skia::PixmapMut<'_>, + clip_mask: Option<&tiny_skia::ClipMask>, + ) { + if let Some(image) = self + .cache + .borrow_mut() + .draw(handle, Size::new(bounds.width as u32, bounds.height as u32)) + { + pixels.draw_pixmap( + bounds.x as i32, + bounds.y as i32, + image, + &tiny_skia::PixmapPaint::default(), + tiny_skia::Transform::identity(), + clip_mask, + ); + } + } + + pub fn trim_cache(&mut self) { + self.cache.borrow_mut().trim(); + } +} + +#[derive(Default)] +struct Cache { + trees: FxHashMap<u64, Option<resvg::usvg::Tree>>, + tree_hits: FxHashSet<u64>, + rasters: FxHashMap<(u64, Size<u32>), tiny_skia::Pixmap>, + raster_hits: FxHashSet<(u64, Size<u32>)>, +} + +impl Cache { + fn load(&mut self, handle: &Handle) -> Option<&usvg::Tree> { + let id = handle.id(); + + if let hash_map::Entry::Vacant(entry) = self.trees.entry(id) { + let svg = match handle.data() { + Data::Path(path) => { + fs::read_to_string(path).ok().and_then(|contents| { + usvg::Tree::from_str( + &contents, + &usvg::Options::default(), + ) + .ok() + }) + } + Data::Bytes(bytes) => { + usvg::Tree::from_data(bytes, &usvg::Options::default()).ok() + } + }; + + entry.insert(svg); + } + + self.tree_hits.insert(id); + self.trees.get(&id).unwrap().as_ref() + } + + fn viewport_dimensions(&mut self, handle: &Handle) -> Option<Size<u32>> { + let tree = self.load(handle)?; + + Some(Size::new( + tree.size.width() as u32, + tree.size.height() as u32, + )) + } + + fn draw( + &mut self, + handle: &Handle, + size: Size<u32>, + ) -> Option<tiny_skia::PixmapRef<'_>> { + if size.width == 0 || size.height == 0 { + return None; + } + + let id = handle.id(); + + if !self.rasters.contains_key(&(id, size)) { + let tree = self.load(handle)?; + + let mut image = tiny_skia::Pixmap::new(size.width, size.height)?; + + resvg::render( + tree, + if size.width > size.height { + usvg::FitTo::Width(size.width) + } else { + usvg::FitTo::Height(size.height) + }, + tiny_skia::Transform::default(), + image.as_mut(), + )?; + + // Swap R and B channels for `softbuffer` presentation + for pixel in bytemuck::cast_slice_mut::<u8, u32>(image.data_mut()) { + *pixel = *pixel & 0xFF00FF00 + | ((0x000000FF & *pixel) << 16) + | ((0x00FF0000 & *pixel) >> 16); + } + + self.rasters.insert((id, size), image); + } + + self.raster_hits.insert((id, size)); + self.rasters.get(&(id, size)).map(tiny_skia::Pixmap::as_ref) + } + + fn trim(&mut self) { + self.trees.retain(|key, _| self.tree_hits.contains(key)); + self.rasters.retain(|key, _| self.raster_hits.contains(key)); + + self.tree_hits.clear(); + self.raster_hits.clear(); + } +} |