summaryrefslogtreecommitdiffstats
path: root/tiny_skia
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-03-07 06:09:51 +0100
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-03-07 06:09:51 +0100
commit5b3977daf6df624ca5d5e1a21ce282161234b22d (patch)
treec0efb1cda1dde399b60b89a2d57dc39feecca2cd /tiny_skia
parentbb49e17cabd45f3a21af98b4c5ecdddd507fd427 (diff)
downloadiced-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.toml6
-rw-r--r--tiny_skia/src/backend.rs30
-rw-r--r--tiny_skia/src/lib.rs3
-rw-r--r--tiny_skia/src/text.rs2
-rw-r--r--tiny_skia/src/vector.rs149
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();
+ }
+}