Implement raster pipeline in iced_tiny_skia
This commit is contained in:
parent
3a26baa564
commit
bb49e17cab
4 changed files with 134 additions and 6 deletions
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[features]
|
||||
image = []
|
||||
image = ["iced_graphics/image"]
|
||||
svg = []
|
||||
geometry = ["iced_graphics/geometry"]
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
107
tiny_skia/src/raster.rs
Normal 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>,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue