summaryrefslogtreecommitdiffstats
path: root/tiny_skia
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-03-07 05:06:26 +0100
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-03-07 05:06:26 +0100
commitbb49e17cabd45f3a21af98b4c5ecdddd507fd427 (patch)
tree42d3255e869ca9a9fe6270e0658856830c2e7d31 /tiny_skia
parent3a26baa564524b0f25c5cb180b592c8b004b68a9 (diff)
downloadiced-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.toml2
-rw-r--r--tiny_skia/src/backend.rs28
-rw-r--r--tiny_skia/src/lib.rs3
-rw-r--r--tiny_skia/src/raster.rs107
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>,
+}