Implement raster pipeline in iced_tiny_skia

This commit is contained in:
Héctor Ramón Jiménez 2023-03-07 05:06:26 +01:00
parent 3a26baa564
commit bb49e17cab
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
4 changed files with 134 additions and 6 deletions

View file

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"
[features]
image = []
image = ["iced_graphics/image"]
svg = []
geometry = ["iced_graphics/geometry"]

View file

@ -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)
}
}

View file

@ -4,6 +4,9 @@ mod backend;
mod settings;
mod text;
#[cfg(feature = "image")]
mod raster;
#[cfg(feature = "geometry")]
pub mod geometry;

107
tiny_skia/src/raster.rs Normal file
View file

@ -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>,
}