summaryrefslogtreecommitdiffstats
path: root/tiny_skia/src/vector.rs
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/src/vector.rs
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/src/vector.rs')
-rw-r--r--tiny_skia/src/vector.rs149
1 files changed, 149 insertions, 0 deletions
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();
+ }
+}