summaryrefslogtreecommitdiffstats
path: root/tiny_skia/src/raster.rs
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/src/raster.rs
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/src/raster.rs')
-rw-r--r--tiny_skia/src/raster.rs107
1 files changed, 107 insertions, 0 deletions
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>,
+}