diff options
Diffstat (limited to '')
28 files changed, 618 insertions, 263 deletions
diff --git a/examples/geometry/Cargo.toml b/examples/geometry/Cargo.toml index 79fe52d5..6068d651 100644 --- a/examples/geometry/Cargo.toml +++ b/examples/geometry/Cargo.toml @@ -7,4 +7,3 @@ publish = false [dependencies] iced = { path = "../..", features = ["advanced"] } -iced_graphics = { path = "../../graphics" } diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 29f78ea1..e146c6bd 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -1,9 +1,8 @@ //! This example showcases a simple native custom widget that renders using //! arbitrary low-level geometry. mod rainbow { - use iced_graphics::primitive::{ColoredVertex2D, Primitive}; - use iced::advanced::graphics::color; + use iced::advanced::graphics::primitive::{ColoredVertex2D, Primitive}; use iced::advanced::layout::{self, Layout}; use iced::advanced::renderer; use iced::advanced::widget::{self, Widget}; @@ -46,8 +45,8 @@ mod rainbow { cursor: mouse::Cursor, _viewport: &Rectangle, ) { + use iced::advanced::graphics::primitive::Mesh2D; use iced::advanced::Renderer as _; - use iced_graphics::primitive::Mesh2D; let bounds = layout.bounds(); @@ -135,7 +134,7 @@ mod rainbow { renderer.with_translation( Vector::new(bounds.x, bounds.y), |renderer| { - renderer.draw_primitive(mesh); + renderer.draw_with_wgpu(mesh); }, ); } diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs index 4cef081e..3a35e029 100644 --- a/examples/loading_spinners/src/circular.rs +++ b/examples/loading_spinners/src/circular.rs @@ -358,7 +358,9 @@ where renderer.with_translation( Vector::new(bounds.x, bounds.y), |renderer| { - renderer.draw_primitive(geometry.0); + use iced::advanced::graphics::geometry::Renderer as _; + + renderer.draw(vec![geometry]); }, ); } diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index 02621695..7a9e6aee 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -32,10 +32,6 @@ features = ["derive"] version = "0.9" path = "../core" -[dependencies.tiny-skia] -version = "0.9" -optional = true - [dependencies.image] version = "0.24" optional = true diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index 70ccc664..3ff24335 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -6,6 +6,13 @@ use iced_core::{Font, Point, Size}; use std::borrow::Cow; +/// The graphics backend of a [`Renderer`]. +/// +/// [`Renderer`]: crate::Renderer +pub trait Backend { + type Primitive; +} + /// A graphics backend that supports text rendering. pub trait Text { /// The icon font of the backend. diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs index c6b0f759..1add6707 100644 --- a/graphics/src/damage.rs +++ b/graphics/src/damage.rs @@ -1,11 +1,68 @@ //! Track and compute the damage of graphical primitives. +use crate::core::alignment; use crate::core::{Rectangle, Size}; use crate::Primitive; use std::sync::Arc; -/// Computes the damage regions between the two given primitives. -pub fn regions(a: &Primitive, b: &Primitive) -> Vec<Rectangle> { +pub trait Damage: PartialEq { + /// Returns the bounds of the [`Damage`]. + fn bounds(&self) -> Rectangle; +} + +impl<T: Damage> Damage for Primitive<T> { + fn bounds(&self) -> Rectangle { + match self { + Self::Text { + bounds, + horizontal_alignment, + vertical_alignment, + .. + } => { + let mut bounds = *bounds; + + bounds.x = match horizontal_alignment { + alignment::Horizontal::Left => bounds.x, + alignment::Horizontal::Center => { + bounds.x - bounds.width / 2.0 + } + alignment::Horizontal::Right => bounds.x - bounds.width, + }; + + bounds.y = match vertical_alignment { + alignment::Vertical::Top => bounds.y, + alignment::Vertical::Center => { + bounds.y - bounds.height / 2.0 + } + alignment::Vertical::Bottom => bounds.y - bounds.height, + }; + + bounds.expand(1.5) + } + Self::Quad { bounds, .. } + | Self::Image { bounds, .. } + | Self::Svg { bounds, .. } => bounds.expand(1.0), + Self::Clip { bounds, .. } => bounds.expand(1.0), + Self::SolidMesh { size, .. } | Self::GradientMesh { size, .. } => { + Rectangle::with_size(*size) + } + Self::Group { primitives } => primitives + .iter() + .map(Self::bounds) + .fold(Rectangle::with_size(Size::ZERO), |a, b| { + Rectangle::union(&a, &b) + }), + Self::Translate { + translation, + content, + } => content.bounds() + *translation, + Self::Cache { content } => content.bounds(), + Self::Custom(custom) => custom.bounds(), + } + } +} + +fn regions<T: Damage>(a: &Primitive<T>, b: &Primitive<T>) -> Vec<Rectangle> { match (a, b) { ( Primitive::Group { @@ -76,7 +133,10 @@ pub fn regions(a: &Primitive, b: &Primitive) -> Vec<Rectangle> { } /// Computes the damage regions between the two given lists of primitives. -pub fn list(previous: &[Primitive], current: &[Primitive]) -> Vec<Rectangle> { +pub fn list<T: Damage>( + previous: &[Primitive<T>], + current: &[Primitive<T>], +) -> Vec<Rectangle> { let damage = previous .iter() .zip(current) @@ -93,7 +153,7 @@ pub fn list(previous: &[Primitive], current: &[Primitive]) -> Vec<Rectangle> { // Extend damage by the added/removed primitives damage - .chain(bigger[smaller.len()..].iter().map(Primitive::bounds)) + .chain(bigger[smaller.len()..].iter().map(Damage::bounds)) .collect() } } diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index 729c3d44..5e547bae 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -14,20 +14,10 @@ pub use text::Text; pub use crate::gradient::{self, Gradient}; -use crate::Primitive; - -/// A bunch of shapes that can be drawn. -#[derive(Debug, Clone)] -pub struct Geometry(pub Primitive); - -impl From<Geometry> for Primitive { - fn from(geometry: Geometry) -> Self { - geometry.0 - } -} - /// A renderer capable of drawing some [`Geometry`]. pub trait Renderer: crate::core::Renderer { + type Geometry; + /// Draws the given layers of [`Geometry`]. - fn draw(&mut self, layers: Vec<Geometry>); + fn draw(&mut self, layers: Vec<Self::Geometry>); } diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 226b245b..055a9216 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -8,8 +8,8 @@ html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" )] #![deny( - missing_debug_implementations, - missing_docs, + //missing_debug_implementations, + //missing_docs, unsafe_code, unused_results, clippy::extra_unused_lifetimes, @@ -41,7 +41,9 @@ pub mod geometry; pub mod image; pub use antialiasing::Antialiasing; +pub use backend::Backend; pub use compositor::Compositor; +pub use damage::Damage; pub use error::Error; pub use gradient::Gradient; pub use primitive::Primitive; @@ -49,7 +51,4 @@ pub use renderer::Renderer; pub use transformation::Transformation; pub use viewport::Viewport; -#[cfg(feature = "geometry")] -pub use geometry::Geometry; - pub use iced_core as core; diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 187c6c6c..f8b005ad 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -12,8 +12,7 @@ use std::sync::Arc; /// A rendering primitive. #[derive(Debug, Clone, PartialEq)] -#[non_exhaustive] -pub enum Primitive { +pub enum Primitive<T> { /// A text primitive Text { /// The contents of the text @@ -90,41 +89,17 @@ pub enum Primitive { /// Any geometry that falls out of this region will be clipped. size: Size, }, - /// A [`tiny_skia`] path filled with some paint. - #[cfg(feature = "tiny-skia")] - Fill { - /// The path to fill. - path: tiny_skia::Path, - /// The paint to use. - paint: tiny_skia::Paint<'static>, - /// The fill rule to follow. - rule: tiny_skia::FillRule, - /// The transform to apply to the path. - transform: tiny_skia::Transform, - }, - /// A [`tiny_skia`] path stroked with some paint. - #[cfg(feature = "tiny-skia")] - Stroke { - /// The path to stroke. - path: tiny_skia::Path, - /// The paint to use. - paint: tiny_skia::Paint<'static>, - /// The stroke settings. - stroke: tiny_skia::Stroke, - /// The transform to apply to the path. - transform: tiny_skia::Transform, - }, /// A group of primitives Group { /// The primitives of the group - primitives: Vec<Primitive>, + primitives: Vec<Primitive<T>>, }, /// A clip primitive Clip { /// The bounds of the clip bounds: Rectangle, /// The content of the clip - content: Box<Primitive>, + content: Box<Primitive<T>>, }, /// A primitive that applies a translation Translate { @@ -132,7 +107,7 @@ pub enum Primitive { translation: Vector, /// The primitive to translate - content: Box<Primitive>, + content: Box<Primitive<T>>, }, /// A cached primitive. /// @@ -140,11 +115,13 @@ pub enum Primitive { /// generation is expensive. Cache { /// The cached primitive - content: Arc<Primitive>, + content: Arc<Primitive<T>>, }, + /// A backend-specific primitive. + Custom(T), } -impl Primitive { +impl<T> Primitive<T> { /// Creates a [`Primitive::Group`]. pub fn group(primitives: Vec<Self>) -> Self { Self::Group { primitives } @@ -165,68 +142,6 @@ impl Primitive { content: Box::new(self), } } - - /// Returns the bounds of the [`Primitive`]. - pub fn bounds(&self) -> Rectangle { - match self { - Self::Text { - bounds, - horizontal_alignment, - vertical_alignment, - .. - } => { - let mut bounds = *bounds; - - bounds.x = match horizontal_alignment { - alignment::Horizontal::Left => bounds.x, - alignment::Horizontal::Center => { - bounds.x - bounds.width / 2.0 - } - alignment::Horizontal::Right => bounds.x - bounds.width, - }; - - bounds.y = match vertical_alignment { - alignment::Vertical::Top => bounds.y, - alignment::Vertical::Center => { - bounds.y - bounds.height / 2.0 - } - alignment::Vertical::Bottom => bounds.y - bounds.height, - }; - - bounds.expand(1.5) - } - Self::Quad { bounds, .. } - | Self::Image { bounds, .. } - | Self::Svg { bounds, .. } => bounds.expand(1.0), - Self::Clip { bounds, .. } => bounds.expand(1.0), - Self::SolidMesh { size, .. } | Self::GradientMesh { size, .. } => { - Rectangle::with_size(*size) - } - #[cfg(feature = "tiny-skia")] - Self::Fill { path, .. } | Self::Stroke { path, .. } => { - let bounds = path.bounds(); - - Rectangle { - x: bounds.x(), - y: bounds.y(), - width: bounds.width(), - height: bounds.height(), - } - .expand(1.0) - } - Self::Group { primitives } => primitives - .iter() - .map(Self::bounds) - .fold(Rectangle::with_size(Size::ZERO), |a, b| { - Rectangle::union(&a, &b) - }), - Self::Translate { - translation, - content, - } => content.bounds() + *translation, - Self::Cache { content } => content.bounds(), - } - } } /// A set of [`Vertex2D`] and indices representing a list of triangles. @@ -268,9 +183,3 @@ unsafe impl Zeroable for GradientVertex2D {} #[allow(unsafe_code)] unsafe impl Pod for GradientVertex2D {} - -impl From<()> for Primitive { - fn from(_: ()) -> Self { - Self::Group { primitives: vec![] } - } -} diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 7e70a766..9acfc2b7 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -1,5 +1,5 @@ //! Create a renderer from a [`Backend`]. -use crate::backend; +use crate::backend::{self, Backend}; use crate::Primitive; use iced_core::image; @@ -16,13 +16,13 @@ use std::marker::PhantomData; /// A backend-agnostic renderer that supports all the built-in widgets. #[derive(Debug)] -pub struct Renderer<B, Theme> { +pub struct Renderer<B: Backend, Theme> { backend: B, - primitives: Vec<Primitive>, + primitives: Vec<Primitive<B::Primitive>>, theme: PhantomData<Theme>, } -impl<B, T> Renderer<B, T> { +impl<B: Backend, T> Renderer<B, T> { /// Creates a new [`Renderer`] from the given [`Backend`]. pub fn new(backend: B) -> Self { Self { @@ -38,7 +38,7 @@ impl<B, T> Renderer<B, T> { } /// Enqueues the given [`Primitive`] in the [`Renderer`] for drawing. - pub fn draw_primitive(&mut self, primitive: Primitive) { + pub fn draw_primitive(&mut self, primitive: Primitive<B::Primitive>) { self.primitives.push(primitive); } @@ -46,13 +46,42 @@ impl<B, T> Renderer<B, T> { /// of the [`Renderer`]. pub fn with_primitives<O>( &mut self, - f: impl FnOnce(&mut B, &[Primitive]) -> O, + f: impl FnOnce(&mut B, &[Primitive<B::Primitive>]) -> O, ) -> O { f(&mut self.backend, &self.primitives) } + + pub fn start_layer(&mut self) -> Vec<Primitive<B::Primitive>> { + std::mem::take(&mut self.primitives) + } + + pub fn end_layer( + &mut self, + primitives: Vec<Primitive<B::Primitive>>, + bounds: Rectangle, + ) { + let layer = std::mem::replace(&mut self.primitives, primitives); + + self.primitives.push(Primitive::group(layer).clip(bounds)); + } + + pub fn start_translation(&mut self) -> Vec<Primitive<B::Primitive>> { + std::mem::take(&mut self.primitives) + } + + pub fn end_translation( + &mut self, + primitives: Vec<Primitive<B::Primitive>>, + translation: Vector, + ) { + let layer = std::mem::replace(&mut self.primitives, primitives); + + self.primitives + .push(Primitive::group(layer).translate(translation)); + } } -impl<B, T> iced_core::Renderer for Renderer<B, T> { +impl<B: Backend, T> iced_core::Renderer for Renderer<B, T> { type Theme = T; fn layout<Message>( @@ -64,13 +93,11 @@ impl<B, T> iced_core::Renderer for Renderer<B, T> { } fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { - let current = std::mem::take(&mut self.primitives); + let current = self.start_layer(); f(self); - let layer = std::mem::replace(&mut self.primitives, current); - - self.primitives.push(Primitive::group(layer).clip(bounds)); + self.end_layer(current, bounds); } fn with_translation( @@ -78,14 +105,11 @@ impl<B, T> iced_core::Renderer for Renderer<B, T> { translation: Vector, f: impl FnOnce(&mut Self), ) { - let current = std::mem::take(&mut self.primitives); + let current = self.start_translation(); f(self); - let layer = std::mem::replace(&mut self.primitives, current); - - self.primitives - .push(Primitive::group(layer).translate(translation)); + self.end_translation(current, translation); } fn fill_quad( @@ -109,7 +133,7 @@ impl<B, T> iced_core::Renderer for Renderer<B, T> { impl<B, T> text::Renderer for Renderer<B, T> where - B: backend::Text, + B: Backend + backend::Text, { type Font = Font; @@ -188,7 +212,7 @@ where impl<B, T> image::Renderer for Renderer<B, T> where - B: backend::Image, + B: Backend + backend::Image, { type Handle = image::Handle; @@ -203,7 +227,7 @@ where impl<B, T> svg::Renderer for Renderer<B, T> where - B: backend::Svg, + B: Backend + backend::Svg, { fn dimensions(&self, handle: &svg::Handle) -> Size<u32> { self.backend().viewport_dimensions(handle) @@ -222,11 +246,3 @@ where }) } } - -#[cfg(feature = "geometry")] -impl<B, T> crate::geometry::Renderer for Renderer<B, T> { - fn draw(&mut self, layers: Vec<crate::Geometry>) { - self.primitives - .extend(layers.into_iter().map(crate::Geometry::into)); - } -} diff --git a/renderer/src/compositor.rs b/renderer/src/compositor.rs index 57317b28..8b17a4b0 100644 --- a/renderer/src/compositor.rs +++ b/renderer/src/compositor.rs @@ -100,26 +100,28 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> { background_color: Color, overlay: &[T], ) -> Result<(), SurfaceError> { - renderer.with_primitives(|backend, primitives| { - match (self, backend, surface) { - ( - Self::TinySkia(_compositor), - crate::Backend::TinySkia(backend), - Surface::TinySkia(surface), - ) => iced_tiny_skia::window::compositor::present( + match (self, renderer, surface) { + ( + Self::TinySkia(_compositor), + crate::Renderer::TinySkia(renderer), + Surface::TinySkia(surface), + ) => renderer.with_primitives(|backend, primitives| { + iced_tiny_skia::window::compositor::present( backend, surface, primitives, viewport, background_color, overlay, - ), - #[cfg(feature = "wgpu")] - ( - Self::Wgpu(compositor), - crate::Backend::Wgpu(backend), - Surface::Wgpu(surface), - ) => iced_wgpu::window::compositor::present( + ) + }), + #[cfg(feature = "wgpu")] + ( + Self::Wgpu(compositor), + crate::Renderer::Wgpu(renderer), + Surface::Wgpu(surface), + ) => renderer.with_primitives(|backend, primitives| { + iced_wgpu::window::compositor::present( compositor, backend, surface, @@ -127,14 +129,14 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> { viewport, background_color, overlay, - ), - #[allow(unreachable_patterns)] - _ => panic!( - "The provided renderer or surface are not compatible \ + ) + }), + #[allow(unreachable_patterns)] + _ => panic!( + "The provided renderer or surface are not compatible \ with the compositor." - ), - } - }) + ), + } } fn screenshot<T: AsRef<str>>( @@ -145,12 +147,27 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> { background_color: Color, overlay: &[T], ) -> Vec<u8> { - renderer.with_primitives(|backend, primitives| match (self, backend, surface) { - (Self::TinySkia(_compositor), crate::Backend::TinySkia(backend), Surface::TinySkia(surface)) => { - iced_tiny_skia::window::compositor::screenshot(surface, backend, primitives, viewport, background_color, overlay) - }, + match (self, renderer, surface) { + ( + Self::TinySkia(_compositor), + Renderer::TinySkia(renderer), + Surface::TinySkia(surface), + ) => renderer.with_primitives(|backend, primitives| { + iced_tiny_skia::window::compositor::screenshot( + surface, + backend, + primitives, + viewport, + background_color, + overlay, + ) + }), #[cfg(feature = "wgpu")] - (Self::Wgpu(compositor), crate::Backend::Wgpu(backend), Surface::Wgpu(_)) => { + ( + Self::Wgpu(compositor), + Renderer::Wgpu(renderer), + Surface::Wgpu(_), + ) => renderer.with_primitives(|backend, primitives| { iced_wgpu::window::compositor::screenshot( compositor, backend, @@ -159,12 +176,13 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> { background_color, overlay, ) - }, + }), #[allow(unreachable_patterns)] _ => panic!( - "The provided renderer or backend are not compatible with the compositor." + "The provided renderer or backend are not compatible \ + with the compositor." ), - }) + } } } @@ -215,7 +233,7 @@ impl Candidate { Ok(( Compositor::TinySkia(compositor), - Renderer::new(crate::Backend::TinySkia(backend)), + Renderer::TinySkia(iced_tiny_skia::Renderer::new(backend)), )) } #[cfg(feature = "wgpu")] @@ -232,7 +250,7 @@ impl Candidate { Ok(( Compositor::Wgpu(compositor), - Renderer::new(crate::Backend::Wgpu(backend)), + Renderer::Wgpu(iced_wgpu::Renderer::new(backend)), )) } #[cfg(not(feature = "wgpu"))] diff --git a/renderer/src/geometry.rs b/renderer/src/geometry.rs index 26e2fed0..04b5d9e6 100644 --- a/renderer/src/geometry.rs +++ b/renderer/src/geometry.rs @@ -3,8 +3,8 @@ mod cache; pub use cache::Cache; use crate::core::{Point, Rectangle, Size, Vector}; -use crate::graphics::geometry::{Fill, Geometry, Path, Stroke, Text}; -use crate::Backend; +use crate::graphics::geometry::{Fill, Path, Stroke, Text}; +use crate::Renderer; pub enum Frame { TinySkia(iced_tiny_skia::geometry::Frame), @@ -12,6 +12,12 @@ pub enum Frame { Wgpu(iced_wgpu::geometry::Frame), } +pub enum Geometry { + TinySkia(iced_tiny_skia::Primitive), + #[cfg(feature = "wgpu")] + Wgpu(iced_wgpu::Primitive), +} + macro_rules! delegate { ($frame:expr, $name:ident, $body:expr) => { match $frame { @@ -23,13 +29,13 @@ macro_rules! delegate { } impl Frame { - pub fn new<Theme>(renderer: &crate::Renderer<Theme>, size: Size) -> Self { - match renderer.backend() { - Backend::TinySkia(_) => { + pub fn new<Theme>(renderer: &Renderer<Theme>, size: Size) -> Self { + match renderer { + Renderer::TinySkia(_) => { Frame::TinySkia(iced_tiny_skia::geometry::Frame::new(size)) } #[cfg(feature = "wgpu")] - Backend::Wgpu(_) => { + Renderer::Wgpu(_) => { Frame::Wgpu(iced_wgpu::geometry::Frame::new(size)) } } @@ -169,6 +175,10 @@ impl Frame { } pub fn into_geometry(self) -> Geometry { - Geometry(delegate!(self, frame, frame.into_primitive())) + match self { + Self::TinySkia(frame) => Geometry::TinySkia(frame.into_primitive()), + #[cfg(feature = "wgpu")] + Self::Wgpu(frame) => Geometry::Wgpu(frame.into_primitive()), + } } } diff --git a/renderer/src/geometry/cache.rs b/renderer/src/geometry/cache.rs index 2a3534d0..d82e7f69 100644 --- a/renderer/src/geometry/cache.rs +++ b/renderer/src/geometry/cache.rs @@ -1,6 +1,5 @@ use crate::core::Size; use crate::geometry::{Frame, Geometry}; -use crate::graphics::Primitive; use crate::Renderer; use std::cell::RefCell; @@ -21,10 +20,17 @@ enum State { Empty, Filled { bounds: Size, - primitive: Arc<Primitive>, + primitive: Internal, }, } +#[derive(Debug, Clone)] +enum Internal { + TinySkia(Arc<iced_tiny_skia::Primitive>), + #[cfg(feature = "wgpu")] + Wgpu(Arc<iced_wgpu::Primitive>), +} + impl Cache { /// Creates a new empty [`Cache`]. pub fn new() -> Self { @@ -62,9 +68,21 @@ impl Cache { } = self.state.borrow().deref() { if *cached_bounds == bounds { - return Geometry(Primitive::Cache { - content: primitive.clone(), - }); + match primitive { + Internal::TinySkia(primitive) => { + return Geometry::TinySkia( + iced_tiny_skia::Primitive::Cache { + content: primitive.clone(), + }, + ); + } + #[cfg(feature = "wgpu")] + Internal::Wgpu(primitive) => { + return Geometry::Wgpu(iced_wgpu::Primitive::Cache { + content: primitive.clone(), + }); + } + } } } @@ -74,7 +92,15 @@ impl Cache { let primitive = { let geometry = frame.into_geometry(); - Arc::new(geometry.0) + match geometry { + Geometry::TinySkia(primitive) => { + Internal::TinySkia(Arc::new(primitive)) + } + #[cfg(feature = "wgpu")] + Geometry::Wgpu(primitive) => { + Internal::Wgpu(Arc::new(primitive)) + } + } }; *self.state.borrow_mut() = State::Filled { @@ -82,6 +108,18 @@ impl Cache { primitive: primitive.clone(), }; - Geometry(Primitive::Cache { content: primitive }) + match primitive { + Internal::TinySkia(primitive) => { + Geometry::TinySkia(iced_tiny_skia::Primitive::Cache { + content: primitive, + }) + } + #[cfg(feature = "wgpu")] + Internal::Wgpu(primitive) => { + Geometry::Wgpu(iced_wgpu::Primitive::Cache { + content: primitive, + }) + } + } } } diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs index 22ec7bd1..89b8f4c6 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -3,17 +3,259 @@ pub mod compositor; #[cfg(feature = "geometry")] pub mod geometry; -mod backend; mod settings; pub use iced_graphics as graphics; pub use iced_graphics::core; -pub use backend::Backend; pub use compositor::Compositor; pub use settings::Settings; +#[cfg(feature = "geometry")] +pub use geometry::Geometry; + +use crate::core::renderer; +use crate::core::text::{self, Text}; +use crate::core::{Background, Font, Point, Rectangle, Size, Vector}; + +use std::borrow::Cow; + /// The default graphics renderer for [`iced`]. /// /// [`iced`]: https://github.com/iced-rs/iced -pub type Renderer<Theme> = iced_graphics::Renderer<Backend, Theme>; +pub enum Renderer<Theme> { + TinySkia(iced_tiny_skia::Renderer<Theme>), + #[cfg(feature = "wgpu")] + Wgpu(iced_wgpu::Renderer<Theme>), +} + +macro_rules! delegate { + ($renderer:expr, $name:ident, $body:expr) => { + match $renderer { + Self::TinySkia($name) => $body, + #[cfg(feature = "wgpu")] + Self::Wgpu($name) => $body, + } + }; +} + +impl<T> Renderer<T> { + #[cfg(feature = "wgpu")] + pub fn draw_with_wgpu(&mut self, primitive: iced_wgpu::Primitive) { + if let Self::Wgpu(renderer) = self { + renderer.draw_primitive(primitive); + } + } +} + +impl<T> core::Renderer for Renderer<T> { + type Theme = T; + + fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { + match self { + Self::TinySkia(renderer) => { + let primitives = renderer.start_layer(); + + f(self); + + match self { + Self::TinySkia(renderer) => { + renderer.end_layer(primitives, bounds); + } + #[cfg(feature = "wgpu")] + _ => unreachable!(), + } + } + #[cfg(feature = "wgpu")] + Self::Wgpu(renderer) => { + let primitives = renderer.start_layer(); + + f(self); + + match self { + #[cfg(feature = "wgpu")] + Self::Wgpu(renderer) => { + renderer.end_layer(primitives, bounds); + } + _ => unreachable!(), + } + } + } + } + + fn with_translation( + &mut self, + translation: Vector, + f: impl FnOnce(&mut Self), + ) { + match self { + Self::TinySkia(renderer) => { + let primitives = renderer.start_translation(); + + f(self); + + match self { + Self::TinySkia(renderer) => { + renderer.end_translation(primitives, translation); + } + #[cfg(feature = "wgpu")] + _ => unreachable!(), + } + } + #[cfg(feature = "wgpu")] + Self::Wgpu(renderer) => { + let primitives = renderer.start_translation(); + + f(self); + + match self { + #[cfg(feature = "wgpu")] + Self::Wgpu(renderer) => { + renderer.end_translation(primitives, translation); + } + _ => unreachable!(), + } + } + } + } + + fn fill_quad( + &mut self, + quad: renderer::Quad, + background: impl Into<Background>, + ) { + delegate!(self, renderer, renderer.fill_quad(quad, background)); + } + + fn clear(&mut self) { + delegate!(self, renderer, renderer.clear()); + } +} + +impl<T> text::Renderer for Renderer<T> { + type Font = Font; + + const ICON_FONT: Font = iced_tiny_skia::Renderer::<T>::ICON_FONT; + const CHECKMARK_ICON: char = iced_tiny_skia::Renderer::<T>::CHECKMARK_ICON; + const ARROW_DOWN_ICON: char = + iced_tiny_skia::Renderer::<T>::ARROW_DOWN_ICON; + + fn default_font(&self) -> Self::Font { + delegate!(self, renderer, renderer.default_font()) + } + + fn default_size(&self) -> f32 { + delegate!(self, renderer, renderer.default_size()) + } + + fn measure( + &self, + content: &str, + size: f32, + line_height: text::LineHeight, + font: Font, + bounds: Size, + shaping: text::Shaping, + ) -> Size { + delegate!( + self, + renderer, + renderer.measure(content, size, line_height, font, bounds, shaping) + ) + } + + fn hit_test( + &self, + content: &str, + size: f32, + line_height: text::LineHeight, + font: Font, + bounds: Size, + shaping: text::Shaping, + point: Point, + nearest_only: bool, + ) -> Option<text::Hit> { + delegate!( + self, + renderer, + renderer.hit_test( + content, + size, + line_height, + font, + bounds, + shaping, + point, + nearest_only + ) + ) + } + + fn load_font(&mut self, bytes: Cow<'static, [u8]>) { + delegate!(self, renderer, renderer.load_font(bytes)); + } + + fn fill_text(&mut self, text: Text<'_, Self::Font>) { + delegate!(self, renderer, renderer.fill_text(text)); + } +} + +#[cfg(feature = "image")] +impl<T> crate::core::image::Renderer for Renderer<T> { + type Handle = crate::core::image::Handle; + + fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> { + delegate!(self, renderer, renderer.dimensions(handle)) + } + + fn draw(&mut self, handle: crate::core::image::Handle, bounds: Rectangle) { + delegate!(self, renderer, renderer.draw(handle, bounds)); + } +} + +#[cfg(feature = "svg")] +impl<T> crate::core::svg::Renderer for Renderer<T> { + fn dimensions(&self, handle: &crate::core::svg::Handle) -> Size<u32> { + delegate!(self, renderer, renderer.dimensions(handle)) + } + + fn draw( + &mut self, + handle: crate::core::svg::Handle, + color: Option<crate::core::Color>, + bounds: Rectangle, + ) { + delegate!(self, renderer, renderer.draw(handle, color, bounds)) + } +} + +#[cfg(feature = "geometry")] +impl<T> crate::graphics::geometry::Renderer for Renderer<T> { + type Geometry = crate::Geometry; + + fn draw(&mut self, layers: Vec<Self::Geometry>) { + match self { + Self::TinySkia(renderer) => { + for layer in layers { + match layer { + crate::Geometry::TinySkia(primitive) => { + renderer.draw_primitive(primitive); + } + _ => unreachable!(), + } + } + } + #[cfg(feature = "wgpu")] + Self::Wgpu(renderer) => { + for layer in layers { + match layer { + crate::Geometry::Wgpu(primitive) => { + renderer.draw_primitive(primitive); + } + _ => unreachable!(), + } + } + } + } + } +} diff --git a/tiny_skia/Cargo.toml b/tiny_skia/Cargo.toml index ef993fb9..431f324b 100644 --- a/tiny_skia/Cargo.toml +++ b/tiny_skia/Cargo.toml @@ -20,7 +20,6 @@ log = "0.4" [dependencies.iced_graphics] version = "0.8" path = "../graphics" -features = ["tiny-skia"] [dependencies.cosmic-text] git = "https://github.com/hecrj/cosmic-text.git" diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index c8999561..0d06ef70 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -2,7 +2,8 @@ use crate::core::text; use crate::core::Gradient; use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector}; use crate::graphics::backend; -use crate::graphics::{Primitive, Viewport}; +use crate::graphics::{Damage, Viewport}; +use crate::primitive::{self, Primitive}; use crate::Settings; use std::borrow::Cow; @@ -419,6 +420,13 @@ impl Backend { self.raster_pipeline .draw(handle, *bounds, pixels, transform, clip_mask); } + #[cfg(not(feature = "image"))] + Primitive::Image { .. } => { + log::warn!( + "Unsupported primitive in `iced_tiny_skia`: {:?}", + primitive + ); + } #[cfg(feature = "svg")] Primitive::Svg { handle, @@ -442,12 +450,19 @@ impl Backend { clip_mask, ); } - Primitive::Fill { + #[cfg(not(feature = "svg"))] + Primitive::Svg { .. } => { + log::warn!( + "Unsupported primitive in `iced_tiny_skia`: {:?}", + primitive + ); + } + Primitive::Custom(primitive::Custom::Fill { path, paint, rule, transform, - } => { + }) => { let bounds = path.bounds(); let physical_bounds = (Rectangle { @@ -475,12 +490,12 @@ impl Backend { clip_mask, ); } - Primitive::Stroke { + Primitive::Custom(primitive::Custom::Stroke { path, paint, stroke, transform, - } => { + }) => { let bounds = path.bounds(); let physical_bounds = (Rectangle { @@ -588,13 +603,6 @@ impl Backend { primitive ); } - _ => { - // Not supported! - log::warn!( - "Unsupported primitive in `iced_tiny_skia`: {:?}", - primitive - ); - } } } } @@ -766,6 +774,10 @@ fn adjust_clip_mask(clip_mask: &mut tiny_skia::Mask, bounds: Rectangle) { ); } +impl iced_graphics::Backend for Backend { + type Primitive = primitive::Custom; +} + impl backend::Text for Backend { const ICON_FONT: Font = Font::with_name("Iced-Icons"); const CHECKMARK_ICON: char = '\u{f00c}'; diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index ee347c73..9bd47556 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -3,7 +3,7 @@ use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::stroke::{self, Stroke}; use crate::graphics::geometry::{Path, Style, Text}; use crate::graphics::Gradient; -use crate::graphics::Primitive; +use crate::primitive::{self, Primitive}; pub struct Frame { size: Size, @@ -42,12 +42,13 @@ impl Frame { let Some(path) = convert_path(path) else { return }; let fill = fill.into(); - self.primitives.push(Primitive::Fill { - path, - paint: into_paint(fill.style), - rule: into_fill_rule(fill.rule), - transform: self.transform, - }); + self.primitives + .push(Primitive::Custom(primitive::Custom::Fill { + path, + paint: into_paint(fill.style), + rule: into_fill_rule(fill.rule), + transform: self.transform, + })); } pub fn fill_rectangle( @@ -59,15 +60,16 @@ impl Frame { let Some(path) = convert_path(&Path::rectangle(top_left, size)) else { return }; let fill = fill.into(); - self.primitives.push(Primitive::Fill { - path, - paint: tiny_skia::Paint { - anti_alias: false, - ..into_paint(fill.style) - }, - rule: into_fill_rule(fill.rule), - transform: self.transform, - }); + self.primitives + .push(Primitive::Custom(primitive::Custom::Fill { + path, + paint: tiny_skia::Paint { + anti_alias: false, + ..into_paint(fill.style) + }, + rule: into_fill_rule(fill.rule), + transform: self.transform, + })); } pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) { @@ -76,12 +78,13 @@ impl Frame { let stroke = stroke.into(); let skia_stroke = into_stroke(&stroke); - self.primitives.push(Primitive::Stroke { - path, - paint: into_paint(stroke.style), - stroke: skia_stroke, - transform: self.transform, - }); + self.primitives + .push(Primitive::Custom(primitive::Custom::Stroke { + path, + paint: into_paint(stroke.style), + stroke: skia_stroke, + transform: self.transform, + })); } pub fn fill_text(&mut self, text: impl Into<Text>) { diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index 83baef1c..15de6ce2 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -1,6 +1,7 @@ pub mod window; mod backend; +mod primitive; mod settings; mod text; @@ -17,6 +18,7 @@ pub use iced_graphics as graphics; pub use iced_graphics::core; pub use backend::Backend; +pub use primitive::Primitive; pub use settings::Settings; /// A [`tiny-skia`] graphics renderer for [`iced`]. diff --git a/tiny_skia/src/primitive.rs b/tiny_skia/src/primitive.rs new file mode 100644 index 00000000..0ed24969 --- /dev/null +++ b/tiny_skia/src/primitive.rs @@ -0,0 +1,48 @@ +use crate::core::Rectangle; +use crate::graphics::Damage; + +pub type Primitive = crate::graphics::Primitive<Custom>; + +#[derive(Debug, Clone, PartialEq)] +pub enum Custom { + /// A path filled with some paint. + Fill { + /// The path to fill. + path: tiny_skia::Path, + /// The paint to use. + paint: tiny_skia::Paint<'static>, + /// The fill rule to follow. + rule: tiny_skia::FillRule, + /// The transform to apply to the path. + transform: tiny_skia::Transform, + }, + /// A path stroked with some paint. + Stroke { + /// The path to stroke. + path: tiny_skia::Path, + /// The paint to use. + paint: tiny_skia::Paint<'static>, + /// The stroke settings. + stroke: tiny_skia::Stroke, + /// The transform to apply to the path. + transform: tiny_skia::Transform, + }, +} + +impl Damage for Custom { + fn bounds(&self) -> Rectangle { + match self { + Self::Fill { path, .. } | Self::Stroke { path, .. } => { + let bounds = path.bounds(); + + Rectangle { + x: bounds.x(), + y: bounds.y(), + width: bounds.width(), + height: bounds.height(), + } + .expand(1.0) + } + } + } +} diff --git a/tiny_skia/src/window/compositor.rs b/tiny_skia/src/window/compositor.rs index f3be3f16..775cf9e5 100644 --- a/tiny_skia/src/window/compositor.rs +++ b/tiny_skia/src/window/compositor.rs @@ -1,8 +1,8 @@ use crate::core::{Color, Rectangle, Size}; use crate::graphics::compositor::{self, Information}; use crate::graphics::damage; -use crate::graphics::{Error, Primitive, Viewport}; -use crate::{Backend, Renderer, Settings}; +use crate::graphics::{Error, Viewport}; +use crate::{Backend, Primitive, Renderer, Settings}; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; use std::marker::PhantomData; diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index f8829e47..596d43c5 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -2,7 +2,8 @@ use crate::core; use crate::core::{Color, Font, Point, Size}; use crate::graphics::backend; use crate::graphics::color; -use crate::graphics::{Primitive, Transformation, Viewport}; +use crate::graphics::{Transformation, Viewport}; +use crate::primitive::{self, Primitive}; use crate::quad; use crate::text; use crate::triangle; @@ -334,6 +335,10 @@ impl Backend { } } +impl crate::graphics::Backend for Backend { + type Primitive = primitive::Custom; +} + impl backend::Text for Backend { const ICON_FONT: Font = Font::with_name("Iced-Icons"); const CHECKMARK_ICON: char = '\u{f00c}'; diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index f81b5b2f..d43f1065 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -5,10 +5,10 @@ use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::{ LineCap, LineDash, LineJoin, Path, Stroke, Style, Text, }; -use crate::graphics::primitive::{self, Primitive}; -use crate::graphics::Gradient; +use crate::graphics::gradient::{self, Gradient}; +use crate::graphics::primitive; +use crate::Primitive; -use iced_graphics::gradient; use lyon::geom::euclid; use lyon::tessellation; use std::borrow::Cow; diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 71570e3d..ef850cd9 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -12,8 +12,9 @@ use crate::core; use crate::core::alignment; use crate::core::{Color, Font, Point, Rectangle, Size, Vector}; use crate::graphics::color; -use crate::graphics::{Primitive, Viewport}; +use crate::graphics::Viewport; use crate::quad::{self, Quad}; +use crate::Primitive; /// A group of primitives that should be clipped together. #[derive(Debug)] @@ -262,13 +263,7 @@ impl<'a> Layer<'a> { current_layer, ); } - _ => { - // Not supported! - log::warn!( - "Unsupported primitive in `iced_wgpu`: {:?}", - primitive - ); - } + Primitive::Custom(()) => {} } } } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 86a962a5..79dfdd24 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -24,8 +24,8 @@ html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" )] #![deny( - missing_debug_implementations, - missing_docs, + //missing_debug_implementations, + //missing_docs, unsafe_code, unused_results, clippy::extra_unused_lifetimes, @@ -47,6 +47,7 @@ pub mod geometry; mod backend; mod buffer; mod color; +mod primitive; mod quad; mod text; mod triangle; @@ -60,6 +61,7 @@ pub use wgpu; pub use backend::Backend; pub use layer::Layer; +pub use primitive::Primitive; pub use settings::Settings; #[cfg(any(feature = "image", feature = "svg"))] diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs new file mode 100644 index 00000000..2e31cd53 --- /dev/null +++ b/wgpu/src/primitive.rs @@ -0,0 +1,3 @@ +pub type Primitive = crate::graphics::Primitive<Custom>; + +pub type Custom = (); diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index 1cfd7b67..cd5b20cc 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -3,8 +3,8 @@ use crate::core::{Color, Size}; use crate::graphics; use crate::graphics::color; use crate::graphics::compositor; -use crate::graphics::{Error, Primitive, Viewport}; -use crate::{Backend, Renderer, Settings}; +use crate::graphics::{Error, Viewport}; +use crate::{Backend, Primitive, Renderer, Settings}; use futures::stream::{self, StreamExt}; diff --git a/widget/src/canvas/program.rs b/widget/src/canvas/program.rs index 929ee285..b3f6175e 100644 --- a/widget/src/canvas/program.rs +++ b/widget/src/canvas/program.rs @@ -1,7 +1,7 @@ use crate::canvas::event::{self, Event}; use crate::canvas::mouse; use crate::core::Rectangle; -use crate::graphics::geometry::{self, Geometry}; +use crate::graphics::geometry; /// The state and logic of a [`Canvas`]. /// @@ -51,7 +51,7 @@ where theme: &Renderer::Theme, bounds: Rectangle, cursor: mouse::Cursor, - ) -> Vec<Geometry>; + ) -> Vec<Renderer::Geometry>; /// Returns the current mouse interaction of the [`Program`]. /// @@ -93,7 +93,7 @@ where theme: &Renderer::Theme, bounds: Rectangle, cursor: mouse::Cursor, - ) -> Vec<Geometry> { + ) -> Vec<Renderer::Geometry> { T::draw(self, state, renderer, theme, bounds, cursor) } diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs index 06be93c0..51a541fd 100644 --- a/widget/src/qr_code.rs +++ b/widget/src/qr_code.rs @@ -7,6 +7,7 @@ use crate::core::widget::Tree; use crate::core::{ Color, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget, }; +use crate::graphics::geometry::Renderer as _; use crate::Renderer; use thiserror::Error; @@ -121,7 +122,7 @@ impl<'a, Message, Theme> Widget<Message, Renderer<Theme>> for QRCode<'a> { let translation = Vector::new(bounds.x, bounds.y); renderer.with_translation(translation, |renderer| { - renderer.draw_primitive(geometry.0); + renderer.draw(vec![geometry]); }); } } |