diff options
Diffstat (limited to 'tiny_skia/src/lib.rs')
-rw-r--r-- | tiny_skia/src/lib.rs | 405 |
1 files changed, 400 insertions, 5 deletions
diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index e7294f9b..1aabff00 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -1,9 +1,9 @@ -#![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; -mod backend; +mod engine; +mod layer; mod primitive; mod settings; mod text; @@ -20,12 +20,407 @@ 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, 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<Backend>; +#[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) -> &[Layer] { + self.layers.flush(); + self.layers.as_slice() + } + + pub fn draw<T: AsRef<str>>( + &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 * scale_factor)) + else { + continue; + }; + + 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.primitives { + let Some(new_clip_bounds) = (group.clip_bounds() + * scale_factor) + .intersection(&clip_bounds) + else { + continue; + }; + + engine::adjust_clip_mask(clip_mask, new_clip_bounds); + + 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 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, + 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<Background>, + ) { + 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) -> crate::core::Size<u32> { + self.engine.raster_pipeline.dimensions(handle) + } + + fn draw_image( + &mut self, + handle: Self::Handle, + filter_method: core::image::FilterMethod, + bounds: Rectangle, + rotation: core::Radians, + opacity: f32, + ) { + let (layer, transformation) = self.layers.current_mut(); + layer.draw_image( + handle, + filter_method, + bounds, + transformation, + rotation, + opacity, + ); + } +} + +#[cfg(feature = "svg")] +impl core::svg::Renderer for Renderer { + fn measure_svg( + &self, + handle: &core::svg::Handle, + ) -> crate::core::Size<u32> { + self.engine.vector_pipeline.viewport_dimensions(handle) + } + + fn draw_svg( + &mut self, + handle: core::svg::Handle, + color: Option<Color>, + bounds: Rectangle, + rotation: core::Radians, + opacity: f32, + ) { + let (layer, transformation) = self.layers.current_mut(); + layer.draw_svg( + handle, + color, + bounds, + transformation, + rotation, + opacity, + ); + } +} + +impl compositor::Default for Renderer { + type Compositor = window::Compositor; +} |