From 5cd98f069dea8720bca7748d6c12fa410cbe79b5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 7 Apr 2024 12:42:12 +0200 Subject: Use built-in `[lints]` table in `Cargo.toml` --- tiny_skia/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tiny_skia/src/lib.rs') diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index e7294f9b..d1f68daa 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -1,5 +1,4 @@ -#![forbid(rust_2018_idioms)] -#![deny(unsafe_code, unused_results, rustdoc::broken_intra_doc_links)] +#![allow(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] pub mod window; -- cgit From 6ad5bb3597f640ac329801adf735d633bf0a512f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 9 Apr 2024 22:25:16 +0200 Subject: Port `iced_tiny_skia` to new layering architecture --- tiny_skia/src/lib.rs | 383 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 380 insertions(+), 3 deletions(-) (limited to 'tiny_skia/src/lib.rs') diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index d1f68daa..fed2f32c 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -2,7 +2,8 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] pub mod window; -mod backend; +mod engine; +mod layer; mod primitive; mod settings; mod text; @@ -19,12 +20,388 @@ pub mod geometry; pub use iced_graphics as graphics; pub use iced_graphics::core; -pub use backend::Backend; +pub use layer::Layer; pub use primitive::Primitive; pub use settings::Settings; +#[cfg(feature = "geometry")] +pub use geometry::Geometry; + +use crate::core::renderer; +use crate::core::{ + Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, +}; +use crate::engine::Engine; +use crate::graphics::compositor; +use crate::graphics::text::{Editor, Paragraph}; +use crate::graphics::Viewport; + /// A [`tiny-skia`] graphics renderer for [`iced`]. /// /// [`tiny-skia`]: https://github.com/RazrFalcon/tiny-skia /// [`iced`]: https://github.com/iced-rs/iced -pub type Renderer = iced_graphics::Renderer; +#[derive(Debug)] +pub struct Renderer { + default_font: Font, + default_text_size: Pixels, + layers: layer::Stack, + engine: Engine, // TODO: Shared engine +} + +impl Renderer { + pub fn new(default_font: Font, default_text_size: Pixels) -> Self { + Self { + default_font, + default_text_size, + layers: layer::Stack::new(), + engine: Engine::new(), + } + } + + pub fn layers(&mut self) -> impl Iterator { + self.layers.flush(); + self.layers.iter() + } + + pub fn draw>( + &mut self, + pixels: &mut tiny_skia::PixmapMut<'_>, + clip_mask: &mut tiny_skia::Mask, + viewport: &Viewport, + damage: &[Rectangle], + background_color: Color, + overlay: &[T], + ) { + let physical_size = viewport.physical_size(); + let scale_factor = viewport.scale_factor() as f32; + + if !overlay.is_empty() { + let path = tiny_skia::PathBuilder::from_rect( + tiny_skia::Rect::from_xywh( + 0.0, + 0.0, + physical_size.width as f32, + physical_size.height as f32, + ) + .expect("Create damage rectangle"), + ); + + pixels.fill_path( + &path, + &tiny_skia::Paint { + shader: tiny_skia::Shader::SolidColor(engine::into_color( + Color { + a: 0.1, + ..background_color + }, + )), + anti_alias: false, + ..Default::default() + }, + tiny_skia::FillRule::default(), + tiny_skia::Transform::identity(), + None, + ); + } + + self.layers.flush(); + + for ®ion in damage { + let region = region * scale_factor; + + let path = tiny_skia::PathBuilder::from_rect( + tiny_skia::Rect::from_xywh( + region.x, + region.y, + region.width, + region.height, + ) + .expect("Create damage rectangle"), + ); + + pixels.fill_path( + &path, + &tiny_skia::Paint { + shader: tiny_skia::Shader::SolidColor(engine::into_color( + background_color, + )), + anti_alias: false, + blend_mode: tiny_skia::BlendMode::Source, + ..Default::default() + }, + tiny_skia::FillRule::default(), + tiny_skia::Transform::identity(), + None, + ); + + for layer in self.layers.iter() { + let Some(clip_bounds) = region.intersection(&layer.bounds) + else { + continue; + }; + + let clip_bounds = clip_bounds * scale_factor; + engine::adjust_clip_mask(clip_mask, clip_bounds); + + for (quad, background) in &layer.quads { + self.engine.draw_quad( + quad, + background, + Transformation::scale(scale_factor), + pixels, + clip_mask, + clip_bounds, + ); + } + + for group in &layer.text { + for text in group.as_slice() { + self.engine.draw_text( + text, + group.transformation() + * Transformation::scale(scale_factor), + pixels, + clip_mask, + clip_bounds, + ); + } + } + + for group in &layer.primitives { + let Some(new_clip_bounds) = + group.clip_bounds().intersection(&layer.bounds) + else { + continue; + }; + + engine::adjust_clip_mask( + clip_mask, + new_clip_bounds * scale_factor, + ); + + for primitive in group.as_slice() { + self.engine.draw_primitive( + primitive, + group.transformation() + * Transformation::scale(scale_factor), + pixels, + clip_mask, + clip_bounds, + ); + } + + engine::adjust_clip_mask(clip_mask, clip_bounds); + } + + for image in &layer.images { + self.engine.draw_image( + image, + Transformation::scale(scale_factor), + pixels, + clip_mask, + clip_bounds, + ); + } + } + + if !overlay.is_empty() { + pixels.stroke_path( + &path, + &tiny_skia::Paint { + shader: tiny_skia::Shader::SolidColor( + engine::into_color(Color::from_rgb(1.0, 0.0, 0.0)), + ), + anti_alias: false, + ..tiny_skia::Paint::default() + }, + &tiny_skia::Stroke { + width: 1.0, + ..tiny_skia::Stroke::default() + }, + tiny_skia::Transform::identity(), + None, + ); + } + } + + self.engine.trim(); + } +} + +impl core::Renderer for Renderer { + fn start_layer(&mut self, bounds: Rectangle) { + self.layers.push_clip(bounds); + } + + fn end_layer(&mut self) { + self.layers.pop_clip(); + } + + fn start_transformation(&mut self, transformation: Transformation) { + self.layers.push_transformation(transformation); + } + + fn end_transformation(&mut self) { + self.layers.pop_transformation(); + } + + fn fill_quad( + &mut self, + quad: renderer::Quad, + background: impl Into, + ) { + let (layer, transformation) = self.layers.current_mut(); + layer.draw_quad(quad, background.into(), transformation); + } + + fn clear(&mut self) { + self.layers.clear(); + } +} + +impl core::text::Renderer for Renderer { + type Font = Font; + type Paragraph = Paragraph; + type Editor = Editor; + + const ICON_FONT: Font = Font::with_name("Iced-Icons"); + const CHECKMARK_ICON: char = '\u{f00c}'; + const ARROW_DOWN_ICON: char = '\u{e800}'; + + fn default_font(&self) -> Self::Font { + self.default_font + } + + fn default_size(&self) -> Pixels { + self.default_text_size + } + + fn fill_paragraph( + &mut self, + text: &Self::Paragraph, + position: Point, + color: Color, + clip_bounds: Rectangle, + ) { + let (layer, transformation) = self.layers.current_mut(); + + layer.draw_paragraph( + text, + position, + color, + clip_bounds, + transformation, + ); + } + + fn fill_editor( + &mut self, + editor: &Self::Editor, + position: Point, + color: Color, + clip_bounds: Rectangle, + ) { + let (layer, transformation) = self.layers.current_mut(); + layer.draw_editor(editor, position, color, clip_bounds, transformation); + } + + fn fill_text( + &mut self, + text: core::Text, + position: Point, + color: Color, + clip_bounds: Rectangle, + ) { + let (layer, transformation) = self.layers.current_mut(); + layer.draw_text(text, position, color, clip_bounds, transformation); + } +} + +#[cfg(feature = "geometry")] +impl graphics::geometry::Renderer for Renderer { + type Geometry = Geometry; + type Frame = geometry::Frame; + + fn new_frame(&self, size: core::Size) -> Self::Frame { + geometry::Frame::new(size) + } + + fn draw_geometry(&mut self, geometry: Self::Geometry) { + let (layer, transformation) = self.layers.current_mut(); + + match geometry { + Geometry::Live { + primitives, + text, + clip_bounds, + } => { + layer.draw_primitive_group( + primitives, + clip_bounds, + transformation, + ); + + layer.draw_text_group(text, clip_bounds, transformation); + } + Geometry::Cache(cache) => { + layer.draw_primitive_cache( + cache.primitives, + cache.clip_bounds, + transformation, + ); + + layer.draw_text_cache( + cache.text, + cache.clip_bounds, + transformation, + ); + } + } + } +} + +impl graphics::mesh::Renderer for Renderer { + fn draw_mesh(&mut self, _mesh: graphics::Mesh) { + log::warn!("iced_tiny_skia does not support drawing meshes"); + } +} + +#[cfg(feature = "image")] +impl core::image::Renderer for Renderer { + type Handle = core::image::Handle; + + fn measure_image(&self, handle: &Self::Handle) -> Size { + self.engine.raster_pipeline.dimensions(handle) + } + + fn draw_image( + &mut self, + handle: Self::Handle, + filter_method: core::image::FilterMethod, + bounds: Rectangle, + ) { + let (layer, transformation) = self.layers.current_mut(); + layer.draw_image(handle, filter_method, bounds, transformation); + } +} + +#[cfg(feature = "svg")] +impl core::svg::Renderer for Renderer { + fn measure_svg(&self, handle: &core::svg::Handle) -> Size { + self.engine.vector_pipeline.viewport_dimensions(handle) + } + + fn draw_svg( + &mut self, + handle: core::svg::Handle, + color: Option, + bounds: Rectangle, + ) { + let (layer, transformation) = self.layers.current_mut(); + layer.draw_svg(handle, color, bounds, transformation); + } +} + +impl compositor::Default for Renderer { + type Compositor = window::Compositor; +} -- cgit From 1e802e776cb591f3860d1bfbaa1423d356fc8456 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 10 Apr 2024 15:21:42 +0200 Subject: Reintroduce damage tracking for `iced_tiny_skia` --- tiny_skia/src/lib.rs | 53 ++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'tiny_skia/src/lib.rs') diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index fed2f32c..4c2c9430 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -29,7 +29,7 @@ pub use geometry::Geometry; use crate::core::renderer; use crate::core::{ - Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, + Background, Color, Font, Pixels, Point, Rectangle, Transformation, }; use crate::engine::Engine; use crate::graphics::compositor; @@ -58,9 +58,9 @@ impl Renderer { } } - pub fn layers(&mut self) -> impl Iterator { + pub fn layers(&mut self) -> &[Layer] { self.layers.flush(); - self.layers.iter() + self.layers.as_slice() } pub fn draw>( @@ -135,12 +135,12 @@ impl Renderer { ); for layer in self.layers.iter() { - let Some(clip_bounds) = region.intersection(&layer.bounds) + let Some(clip_bounds) = + region.intersection(&(layer.bounds * scale_factor)) else { continue; }; - let clip_bounds = clip_bounds * scale_factor; engine::adjust_clip_mask(clip_mask, clip_bounds); for (quad, background) in &layer.quads { @@ -154,30 +154,15 @@ impl Renderer { ); } - for group in &layer.text { - for text in group.as_slice() { - self.engine.draw_text( - text, - group.transformation() - * Transformation::scale(scale_factor), - pixels, - clip_mask, - clip_bounds, - ); - } - } - for group in &layer.primitives { - let Some(new_clip_bounds) = - group.clip_bounds().intersection(&layer.bounds) + let Some(new_clip_bounds) = (group.clip_bounds() + * scale_factor) + .intersection(&clip_bounds) else { continue; }; - engine::adjust_clip_mask( - clip_mask, - new_clip_bounds * scale_factor, - ); + engine::adjust_clip_mask(clip_mask, new_clip_bounds); for primitive in group.as_slice() { self.engine.draw_primitive( @@ -193,6 +178,19 @@ impl Renderer { engine::adjust_clip_mask(clip_mask, clip_bounds); } + for group in &layer.text { + for text in group.as_slice() { + self.engine.draw_text( + text, + group.transformation() + * Transformation::scale(scale_factor), + pixels, + clip_mask, + clip_bounds, + ); + } + } + for image in &layer.images { self.engine.draw_image( image, @@ -370,7 +368,7 @@ impl graphics::mesh::Renderer for Renderer { impl core::image::Renderer for Renderer { type Handle = core::image::Handle; - fn measure_image(&self, handle: &Self::Handle) -> Size { + fn measure_image(&self, handle: &Self::Handle) -> crate::core::Size { self.engine.raster_pipeline.dimensions(handle) } @@ -387,7 +385,10 @@ impl core::image::Renderer for Renderer { #[cfg(feature = "svg")] impl core::svg::Renderer for Renderer { - fn measure_svg(&self, handle: &core::svg::Handle) -> Size { + fn measure_svg( + &self, + handle: &core::svg::Handle, + ) -> crate::core::Size { self.engine.vector_pipeline.viewport_dimensions(handle) } -- cgit From 09a6bcfffc24f5abdc8709403bab7ae1e01563f1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 13:15:17 +0200 Subject: Add `Image` rotation support Co-authored-by: DKolter <68352124+DKolter@users.noreply.github.com> --- tiny_skia/src/lib.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'tiny_skia/src/lib.rs') diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index 4c2c9430..4e3ebad3 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -29,7 +29,7 @@ pub use geometry::Geometry; use crate::core::renderer; use crate::core::{ - Background, Color, Font, Pixels, Point, Rectangle, Transformation, + Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, }; use crate::engine::Engine; use crate::graphics::compositor; @@ -377,9 +377,18 @@ impl core::image::Renderer for Renderer { handle: Self::Handle, filter_method: core::image::FilterMethod, bounds: Rectangle, + rotation: f32, + scale: Size, ) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_image(handle, filter_method, bounds, transformation); + layer.draw_image( + handle, + filter_method, + bounds, + transformation, + rotation, + scale, + ); } } @@ -397,9 +406,11 @@ impl core::svg::Renderer for Renderer { handle: core::svg::Handle, color: Option, bounds: Rectangle, + rotation: f32, + scale: Size, ) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_svg(handle, color, bounds, transformation); + layer.draw_svg(handle, color, bounds, transformation, rotation, scale); } } -- cgit From a57313b23ecb9843856ca0ea08635b6121fcb2cb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 15:21:22 +0200 Subject: Simplify image rotation API and its internals --- tiny_skia/src/lib.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'tiny_skia/src/lib.rs') diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index 4e3ebad3..75aaaf92 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -29,7 +29,7 @@ pub use geometry::Geometry; use crate::core::renderer; use crate::core::{ - Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, + Background, Color, Font, Pixels, Point, Radians, Rectangle, Transformation, }; use crate::engine::Engine; use crate::graphics::compositor; @@ -377,8 +377,7 @@ impl core::image::Renderer for Renderer { handle: Self::Handle, filter_method: core::image::FilterMethod, bounds: Rectangle, - rotation: f32, - scale: Size, + rotation: Radians, ) { let (layer, transformation) = self.layers.current_mut(); layer.draw_image( @@ -387,7 +386,6 @@ impl core::image::Renderer for Renderer { bounds, transformation, rotation, - scale, ); } } @@ -406,11 +404,10 @@ impl core::svg::Renderer for Renderer { handle: core::svg::Handle, color: Option, bounds: Rectangle, - rotation: f32, - scale: Size, + rotation: Radians, ) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_svg(handle, color, bounds, transformation, rotation, scale); + layer.draw_svg(handle, color, bounds, transformation, rotation); } } -- cgit From 15057a05c118dafcb8cf90d4119e66caaa6026c5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 3 May 2024 09:11:46 +0200 Subject: Introduce `center` widget helper ... and also make `center_x` and `center_y` set `width` and `height` to `Length::Fill`, respectively. This targets the most common use case when centering things and removes a bunch of boilerplate as a result. --- tiny_skia/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tiny_skia/src/lib.rs') diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index 75aaaf92..e0cbfa0d 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -29,7 +29,7 @@ pub use geometry::Geometry; use crate::core::renderer; use crate::core::{ - Background, Color, Font, Pixels, Point, Radians, Rectangle, Transformation, + Background, Color, Font, Pixels, Point, Rectangle, Transformation, }; use crate::engine::Engine; use crate::graphics::compositor; @@ -377,7 +377,7 @@ impl core::image::Renderer for Renderer { handle: Self::Handle, filter_method: core::image::FilterMethod, bounds: Rectangle, - rotation: Radians, + rotation: core::Radians, ) { let (layer, transformation) = self.layers.current_mut(); layer.draw_image( @@ -404,7 +404,7 @@ impl core::svg::Renderer for Renderer { handle: core::svg::Handle, color: Option, bounds: Rectangle, - rotation: Radians, + rotation: core::Radians, ) { let (layer, transformation) = self.layers.current_mut(); layer.draw_svg(handle, color, bounds, transformation, rotation); -- cgit From fa9e1d96ea1924b51749b775ea0e67e69bc8a305 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 3 May 2024 13:25:58 +0200 Subject: Introduce dynamic `opacity` support for `Image` and `Svg` --- tiny_skia/src/lib.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'tiny_skia/src/lib.rs') diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index e0cbfa0d..1aabff00 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -378,6 +378,7 @@ impl core::image::Renderer for Renderer { filter_method: core::image::FilterMethod, bounds: Rectangle, rotation: core::Radians, + opacity: f32, ) { let (layer, transformation) = self.layers.current_mut(); layer.draw_image( @@ -386,6 +387,7 @@ impl core::image::Renderer for Renderer { bounds, transformation, rotation, + opacity, ); } } @@ -405,9 +407,17 @@ impl core::svg::Renderer for Renderer { color: Option, bounds: Rectangle, rotation: core::Radians, + opacity: f32, ) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_svg(handle, color, bounds, transformation, rotation); + layer.draw_svg( + handle, + color, + bounds, + transformation, + rotation, + opacity, + ); } } -- cgit