From 188db4da48954b95a3fe79bcd22689ffc3a661e0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 Mar 2024 05:52:48 +0100 Subject: Draft support for dynamic custom renderer injection --- graphics/src/renderer.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'graphics') diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 143f348b..e7154385 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -211,11 +211,11 @@ where { type Handle = image::Handle; - fn dimensions(&self, handle: &image::Handle) -> Size { + fn measure_image(&self, handle: &image::Handle) -> Size { self.backend().dimensions(handle) } - fn draw( + fn draw_image( &mut self, handle: image::Handle, filter_method: image::FilterMethod, @@ -233,11 +233,11 @@ impl svg::Renderer for Renderer where B: Backend + backend::Svg, { - fn dimensions(&self, handle: &svg::Handle) -> Size { + fn measure_svg(&self, handle: &svg::Handle) -> Size { self.backend().viewport_dimensions(handle) } - fn draw( + fn draw_svg( &mut self, handle: svg::Handle, color: Option, -- cgit From 3645d34d6a1ba1247238e830e9eefd52d9e5b986 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 Mar 2024 22:27:17 +0100 Subject: Implement composable, type-safe renderer fallback --- graphics/src/backend.rs | 3 +- graphics/src/geometry.rs | 272 ++++++++++++++++++++++++++++++++++++++++++++++- graphics/src/lib.rs | 4 +- graphics/src/mesh.rs | 6 +- graphics/src/renderer.rs | 106 ++++++++++-------- 5 files changed, 341 insertions(+), 50 deletions(-) (limited to 'graphics') diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index 10eb337f..e394c956 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -2,6 +2,7 @@ use crate::core::image; use crate::core::svg; use crate::core::Size; +use crate::Mesh; use std::borrow::Cow; @@ -10,7 +11,7 @@ use std::borrow::Cow; /// [`Renderer`]: crate::Renderer pub trait Backend { /// The custom kind of primitives this [`Backend`] supports. - type Primitive; + type Primitive: TryFrom; } /// A graphics backend that supports text rendering. diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index d7d6a0aa..cd4c9267 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -14,11 +14,277 @@ pub use text::Text; pub use crate::gradient::{self, Gradient}; +use crate::core::{Point, Radians, Rectangle, Size, Vector}; +use crate::Primitive; + +use std::cell::RefCell; +use std::sync::Arc; + +pub fn frame(renderer: &Renderer, size: Size) -> Renderer::Frame +where + Renderer: self::Renderer, +{ + renderer.new_frame(size) +} + /// A renderer capable of drawing some [`Self::Geometry`]. pub trait Renderer: crate::core::Renderer { /// The kind of geometry this renderer can draw. - type Geometry; + type Geometry: Geometry; + + /// The kind of [`Frame`] this renderer supports. + type Frame: Frame; + + fn new_frame(&self, size: Size) -> Self::Frame; + + /// Draws the given [`Self::Geometry`]. + fn draw_geometry(&mut self, geometry: Self::Geometry); +} + +pub trait Backend { + /// The kind of [`Frame`] this backend supports. + type Frame: Frame; + + fn new_frame(&self, size: Size) -> Self::Frame; +} + +pub trait Frame: Sized + Into { + /// The kind of geometry this frame can draw. + type Geometry: Geometry; + + /// Returns the width of the [`Frame`]. + fn width(&self) -> f32; + + /// Returns the height of the [`Frame`]. + fn height(&self) -> f32; + + /// Returns the dimensions of the [`Frame`]. + fn size(&self) -> Size; + + /// Returns the coordinate of the center of the [`Frame`]. + fn center(&self) -> Point; + + /// Draws the given [`Path`] on the [`Frame`] by filling it with the + /// provided style. + fn fill(&mut self, path: &Path, fill: impl Into); + + /// Draws an axis-aligned rectangle given its top-left corner coordinate and + /// its `Size` on the [`Frame`] by filling it with the provided style. + fn fill_rectangle( + &mut self, + top_left: Point, + size: Size, + fill: impl Into, + ); + + /// Draws the stroke of the given [`Path`] on the [`Frame`] with the + /// provided style. + fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>); + + /// Draws the characters of the given [`Text`] on the [`Frame`], filling + /// them with the given color. + /// + /// __Warning:__ Text currently does not work well with rotations and scale + /// transforms! The position will be correctly transformed, but the + /// resulting glyphs will not be rotated or scaled properly. + /// + /// Additionally, all text will be rendered on top of all the layers of + /// a `Canvas`. Therefore, it is currently only meant to be used for + /// overlays, which is the most common use case. + /// + /// Support for vectorial text is planned, and should address all these + /// limitations. + fn fill_text(&mut self, text: impl Into); + + /// Stores the current transform of the [`Frame`] and executes the given + /// drawing operations, restoring the transform afterwards. + /// + /// This method is useful to compose transforms and perform drawing + /// operations in different coordinate systems. + #[inline] + fn with_save(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + self.push_transform(); + + let result = f(self); + + self.pop_transform(); + + result + } + + /// Pushes the current transform in the transform stack. + fn push_transform(&mut self); + + /// Pops a transform from the transform stack and sets it as the current transform. + fn pop_transform(&mut self); + + /// Executes the given drawing operations within a [`Rectangle`] region, + /// clipping any geometry that overflows its bounds. Any transformations + /// performed are local to the provided closure. + /// + /// This method is useful to perform drawing operations that need to be + /// clipped. + #[inline] + fn with_clip( + &mut self, + region: Rectangle, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + let mut frame = self.draft(region.size()); + + let result = f(&mut frame); + + let origin = Point::new(region.x, region.y); + + self.paste(frame, origin); + + result + } + + /// Creates a new [`Frame`] with the given [`Size`]. + /// + /// Draw its contents back to this [`Frame`] with [`paste`]. + /// + /// [`paste`]: Self::paste + fn draft(&mut self, size: Size) -> Self; + + /// Draws the contents of the given [`Frame`] with origin at the given [`Point`]. + fn paste(&mut self, frame: Self, at: Point); + + /// Applies a translation to the current transform of the [`Frame`]. + fn translate(&mut self, translation: Vector); + + /// Applies a rotation in radians to the current transform of the [`Frame`]. + fn rotate(&mut self, angle: impl Into); + + /// Applies a uniform scaling to the current transform of the [`Frame`]. + fn scale(&mut self, scale: impl Into); + + /// Applies a non-uniform scaling to the current transform of the [`Frame`]. + fn scale_nonuniform(&mut self, scale: impl Into); +} + +pub trait Geometry: Sized { + type Cache; + + fn load(cache: &Self::Cache) -> Self; + + fn cache(self) -> Self::Cache; +} + +/// A simple cache that stores generated [`Geometry`] to avoid recomputation. +/// +/// A [`Cache`] will not redraw its geometry unless the dimensions of its layer +/// change or it is explicitly cleared. +pub struct Cache +where + Renderer: self::Renderer, +{ + state: RefCell>, +} + +impl Cache +where + Renderer: self::Renderer, +{ + /// Creates a new empty [`Cache`]. + pub fn new() -> Self { + Cache { + state: RefCell::new(State::Empty), + } + } + + /// Clears the [`Cache`], forcing a redraw the next time it is used. + pub fn clear(&self) { + *self.state.borrow_mut() = State::Empty; + } + + /// Draws [`Geometry`] using the provided closure and stores it in the + /// [`Cache`]. + /// + /// The closure will only be called when + /// - the bounds have changed since the previous draw call. + /// - the [`Cache`] is empty or has been explicitly cleared. + /// + /// Otherwise, the previously stored [`Geometry`] will be returned. The + /// [`Cache`] is not cleared in this case. In other words, it will keep + /// returning the stored [`Geometry`] if needed. + pub fn draw( + &self, + renderer: &Renderer, + bounds: Size, + draw_fn: impl FnOnce(&mut Renderer::Frame), + ) -> Renderer::Geometry { + use std::ops::Deref; + + if let State::Filled { + bounds: cached_bounds, + geometry, + } = self.state.borrow().deref() + { + if *cached_bounds == bounds { + return Geometry::load(geometry); + } + } + + let mut frame = frame(renderer, bounds); + draw_fn(&mut frame); + + let geometry = frame.into().cache(); + let result = Geometry::load(&geometry); + + *self.state.borrow_mut() = State::Filled { bounds, geometry }; + + result + } +} + +impl std::fmt::Debug for Cache +where + Renderer: self::Renderer, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let state = self.state.borrow(); + + match *state { + State::Empty => write!(f, "Cache::Empty"), + State::Filled { bounds, .. } => { + write!(f, "Cache::Filled {{ bounds: {bounds:?} }}") + } + } + } +} + +impl Default for Cache +where + Renderer: self::Renderer, +{ + fn default() -> Self { + Self::new() + } +} + +enum State +where + Geometry: self::Geometry, +{ + Empty, + Filled { + bounds: Size, + geometry: Geometry::Cache, + }, +} + +impl Geometry for Primitive { + type Cache = Arc; + + fn load(cache: &Arc) -> Self { + Self::Cache { + content: cache.clone(), + } + } - /// Draws the given layers of [`Self::Geometry`]. - fn draw(&mut self, layers: Vec); + fn cache(self) -> Arc { + Arc::new(self) + } } diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index aa9d00e8..6d0862ad 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -9,8 +9,8 @@ )] #![forbid(rust_2018_idioms)] #![deny( - missing_debug_implementations, - missing_docs, + // missing_debug_implementations, + // missing_docs, unsafe_code, unused_results, rustdoc::broken_intra_doc_links diff --git a/graphics/src/mesh.rs b/graphics/src/mesh.rs index 041986cf..5be3ee5b 100644 --- a/graphics/src/mesh.rs +++ b/graphics/src/mesh.rs @@ -1,6 +1,6 @@ //! Draw triangles! use crate::color; -use crate::core::{Rectangle, Size}; +use crate::core::{self, Rectangle, Size}; use crate::gradient; use crate::Damage; @@ -74,3 +74,7 @@ pub struct GradientVertex2D { /// The packed vertex data of the gradient. pub gradient: gradient::Packed, } + +pub trait Renderer: core::Renderer { + fn draw_mesh(&mut self, mesh: Mesh); +} diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index e7154385..3b21aa11 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -8,8 +8,9 @@ use crate::core::text::Text; use crate::core::{ Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, }; +use crate::mesh; use crate::text; -use crate::Primitive; +use crate::{Mesh, Primitive}; use std::borrow::Cow; @@ -20,6 +21,7 @@ pub struct Renderer { default_font: Font, default_text_size: Pixels, primitives: Vec>, + stack: Vec>>, } impl Renderer { @@ -34,6 +36,7 @@ impl Renderer { default_font, default_text_size, primitives: Vec::new(), + stack: Vec::new(), } } @@ -56,59 +59,45 @@ impl Renderer { f(&mut self.backend, &self.primitives) } - /// Starts recording a new layer. - pub fn start_layer(&mut self) -> Vec> { - std::mem::take(&mut self.primitives) - } - - /// Ends the recording of a layer. - pub fn end_layer( - &mut self, - primitives: Vec>, - bounds: Rectangle, - ) { - let layer = std::mem::replace(&mut self.primitives, primitives); - - self.primitives.push(Primitive::group(layer).clip(bounds)); - } - - /// Starts recording a translation. - pub fn start_transformation(&mut self) -> Vec> { - std::mem::take(&mut self.primitives) - } - - /// Ends the recording of a translation. - pub fn end_transformation( + #[cfg(feature = "geometry")] + pub fn draw_geometry( &mut self, - primitives: Vec>, - transformation: Transformation, - ) { - let layer = std::mem::replace(&mut self.primitives, primitives); - - self.primitives - .push(Primitive::group(layer).transform(transformation)); + layers: impl IntoIterator, + ) where + Geometry: Into>, + { + for layer in layers { + self.draw_primitive(layer.into()); + } } } impl iced_core::Renderer for Renderer { - fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { - let current = self.start_layer(); + fn start_layer(&mut self) { + self.stack.push(std::mem::take(&mut self.primitives)); + } - f(self); + fn end_layer(&mut self, bounds: Rectangle) { + let layer = std::mem::replace( + &mut self.primitives, + self.stack.pop().expect("a layer should be recording"), + ); - self.end_layer(current, bounds); + self.primitives.push(Primitive::group(layer).clip(bounds)); } - fn with_transformation( - &mut self, - transformation: Transformation, - f: impl FnOnce(&mut Self), - ) { - let current = self.start_transformation(); + fn start_transformation(&mut self) { + self.stack.push(std::mem::take(&mut self.primitives)); + } - f(self); + fn end_transformation(&mut self, transformation: Transformation) { + let layer = std::mem::replace( + &mut self.primitives, + self.stack.pop().expect("a layer should be recording"), + ); - self.end_transformation(current, transformation); + self.primitives + .push(Primitive::group(layer).transform(transformation)); } fn fill_quad( @@ -250,3 +239,34 @@ where }); } } + +impl mesh::Renderer for Renderer { + fn draw_mesh(&mut self, mesh: Mesh) { + match B::Primitive::try_from(mesh) { + Ok(primitive) => { + self.draw_primitive(Primitive::Custom(primitive)); + } + Err(error) => { + log::warn!("mesh primitive could not be drawn: {error:?}"); + } + } + } +} + +#[cfg(feature = "geometry")] +impl crate::geometry::Renderer for Renderer +where + B: Backend + crate::geometry::Backend, + B::Frame: crate::geometry::Frame>, +{ + type Frame = B::Frame; + type Geometry = Primitive; + + fn new_frame(&self, size: Size) -> Self::Frame { + self.backend.new_frame(size) + } + + fn draw_geometry(&mut self, geometry: Self::Geometry) { + self.draw_primitive(geometry); + } +} -- cgit From 53a183fe0d6aed460fbb8155ac9541757277aab3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Mar 2024 01:35:14 +0100 Subject: Restore `canvas::Frame` API --- graphics/src/geometry.rs | 254 ++--------------------------------------- graphics/src/geometry/cache.rs | 123 ++++++++++++++++++++ graphics/src/geometry/frame.rs | 208 +++++++++++++++++++++++++++++++++ graphics/src/renderer.rs | 3 +- 4 files changed, 340 insertions(+), 248 deletions(-) create mode 100644 graphics/src/geometry/cache.rs create mode 100644 graphics/src/geometry/frame.rs (limited to 'graphics') diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index cd4c9267..2b18243e 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -1,12 +1,16 @@ //! Build and draw geometry. pub mod fill; +pub mod frame; pub mod path; pub mod stroke; +mod cache; mod style; mod text; +pub use cache::Cache; pub use fill::Fill; +pub use frame::Frame; pub use path::Path; pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; pub use style::Style; @@ -14,18 +18,7 @@ pub use text::Text; pub use crate::gradient::{self, Gradient}; -use crate::core::{Point, Radians, Rectangle, Size, Vector}; -use crate::Primitive; - -use std::cell::RefCell; -use std::sync::Arc; - -pub fn frame(renderer: &Renderer, size: Size) -> Renderer::Frame -where - Renderer: self::Renderer, -{ - renderer.new_frame(size) -} +use crate::core::Size; /// A renderer capable of drawing some [`Self::Geometry`]. pub trait Renderer: crate::core::Renderer { @@ -33,7 +26,7 @@ pub trait Renderer: crate::core::Renderer { type Geometry: Geometry; /// The kind of [`Frame`] this renderer supports. - type Frame: Frame; + type Frame: frame::Backend; fn new_frame(&self, size: Size) -> Self::Frame; @@ -43,127 +36,11 @@ pub trait Renderer: crate::core::Renderer { pub trait Backend { /// The kind of [`Frame`] this backend supports. - type Frame: Frame; + type Frame: frame::Backend; fn new_frame(&self, size: Size) -> Self::Frame; } -pub trait Frame: Sized + Into { - /// The kind of geometry this frame can draw. - type Geometry: Geometry; - - /// Returns the width of the [`Frame`]. - fn width(&self) -> f32; - - /// Returns the height of the [`Frame`]. - fn height(&self) -> f32; - - /// Returns the dimensions of the [`Frame`]. - fn size(&self) -> Size; - - /// Returns the coordinate of the center of the [`Frame`]. - fn center(&self) -> Point; - - /// Draws the given [`Path`] on the [`Frame`] by filling it with the - /// provided style. - fn fill(&mut self, path: &Path, fill: impl Into); - - /// Draws an axis-aligned rectangle given its top-left corner coordinate and - /// its `Size` on the [`Frame`] by filling it with the provided style. - fn fill_rectangle( - &mut self, - top_left: Point, - size: Size, - fill: impl Into, - ); - - /// Draws the stroke of the given [`Path`] on the [`Frame`] with the - /// provided style. - fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>); - - /// Draws the characters of the given [`Text`] on the [`Frame`], filling - /// them with the given color. - /// - /// __Warning:__ Text currently does not work well with rotations and scale - /// transforms! The position will be correctly transformed, but the - /// resulting glyphs will not be rotated or scaled properly. - /// - /// Additionally, all text will be rendered on top of all the layers of - /// a `Canvas`. Therefore, it is currently only meant to be used for - /// overlays, which is the most common use case. - /// - /// Support for vectorial text is planned, and should address all these - /// limitations. - fn fill_text(&mut self, text: impl Into); - - /// Stores the current transform of the [`Frame`] and executes the given - /// drawing operations, restoring the transform afterwards. - /// - /// This method is useful to compose transforms and perform drawing - /// operations in different coordinate systems. - #[inline] - fn with_save(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - self.push_transform(); - - let result = f(self); - - self.pop_transform(); - - result - } - - /// Pushes the current transform in the transform stack. - fn push_transform(&mut self); - - /// Pops a transform from the transform stack and sets it as the current transform. - fn pop_transform(&mut self); - - /// Executes the given drawing operations within a [`Rectangle`] region, - /// clipping any geometry that overflows its bounds. Any transformations - /// performed are local to the provided closure. - /// - /// This method is useful to perform drawing operations that need to be - /// clipped. - #[inline] - fn with_clip( - &mut self, - region: Rectangle, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - let mut frame = self.draft(region.size()); - - let result = f(&mut frame); - - let origin = Point::new(region.x, region.y); - - self.paste(frame, origin); - - result - } - - /// Creates a new [`Frame`] with the given [`Size`]. - /// - /// Draw its contents back to this [`Frame`] with [`paste`]. - /// - /// [`paste`]: Self::paste - fn draft(&mut self, size: Size) -> Self; - - /// Draws the contents of the given [`Frame`] with origin at the given [`Point`]. - fn paste(&mut self, frame: Self, at: Point); - - /// Applies a translation to the current transform of the [`Frame`]. - fn translate(&mut self, translation: Vector); - - /// Applies a rotation in radians to the current transform of the [`Frame`]. - fn rotate(&mut self, angle: impl Into); - - /// Applies a uniform scaling to the current transform of the [`Frame`]. - fn scale(&mut self, scale: impl Into); - - /// Applies a non-uniform scaling to the current transform of the [`Frame`]. - fn scale_nonuniform(&mut self, scale: impl Into); -} - pub trait Geometry: Sized { type Cache; @@ -171,120 +48,3 @@ pub trait Geometry: Sized { fn cache(self) -> Self::Cache; } - -/// A simple cache that stores generated [`Geometry`] to avoid recomputation. -/// -/// A [`Cache`] will not redraw its geometry unless the dimensions of its layer -/// change or it is explicitly cleared. -pub struct Cache -where - Renderer: self::Renderer, -{ - state: RefCell>, -} - -impl Cache -where - Renderer: self::Renderer, -{ - /// Creates a new empty [`Cache`]. - pub fn new() -> Self { - Cache { - state: RefCell::new(State::Empty), - } - } - - /// Clears the [`Cache`], forcing a redraw the next time it is used. - pub fn clear(&self) { - *self.state.borrow_mut() = State::Empty; - } - - /// Draws [`Geometry`] using the provided closure and stores it in the - /// [`Cache`]. - /// - /// The closure will only be called when - /// - the bounds have changed since the previous draw call. - /// - the [`Cache`] is empty or has been explicitly cleared. - /// - /// Otherwise, the previously stored [`Geometry`] will be returned. The - /// [`Cache`] is not cleared in this case. In other words, it will keep - /// returning the stored [`Geometry`] if needed. - pub fn draw( - &self, - renderer: &Renderer, - bounds: Size, - draw_fn: impl FnOnce(&mut Renderer::Frame), - ) -> Renderer::Geometry { - use std::ops::Deref; - - if let State::Filled { - bounds: cached_bounds, - geometry, - } = self.state.borrow().deref() - { - if *cached_bounds == bounds { - return Geometry::load(geometry); - } - } - - let mut frame = frame(renderer, bounds); - draw_fn(&mut frame); - - let geometry = frame.into().cache(); - let result = Geometry::load(&geometry); - - *self.state.borrow_mut() = State::Filled { bounds, geometry }; - - result - } -} - -impl std::fmt::Debug for Cache -where - Renderer: self::Renderer, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let state = self.state.borrow(); - - match *state { - State::Empty => write!(f, "Cache::Empty"), - State::Filled { bounds, .. } => { - write!(f, "Cache::Filled {{ bounds: {bounds:?} }}") - } - } - } -} - -impl Default for Cache -where - Renderer: self::Renderer, -{ - fn default() -> Self { - Self::new() - } -} - -enum State -where - Geometry: self::Geometry, -{ - Empty, - Filled { - bounds: Size, - geometry: Geometry::Cache, - }, -} - -impl Geometry for Primitive { - type Cache = Arc; - - fn load(cache: &Arc) -> Self { - Self::Cache { - content: cache.clone(), - } - } - - fn cache(self) -> Arc { - Arc::new(self) - } -} diff --git a/graphics/src/geometry/cache.rs b/graphics/src/geometry/cache.rs new file mode 100644 index 00000000..490e69e2 --- /dev/null +++ b/graphics/src/geometry/cache.rs @@ -0,0 +1,123 @@ +use crate::core::Size; +use crate::geometry::{self, Frame, Geometry}; +use crate::Primitive; + +use std::cell::RefCell; +use std::sync::Arc; + +/// A simple cache that stores generated [`Geometry`] to avoid recomputation. +/// +/// A [`Cache`] will not redraw its geometry unless the dimensions of its layer +/// change or it is explicitly cleared. +pub struct Cache +where + Renderer: geometry::Renderer, +{ + state: RefCell>, +} + +impl Cache +where + Renderer: geometry::Renderer, +{ + /// Creates a new empty [`Cache`]. + pub fn new() -> Self { + Cache { + state: RefCell::new(State::Empty), + } + } + + /// Clears the [`Cache`], forcing a redraw the next time it is used. + pub fn clear(&self) { + *self.state.borrow_mut() = State::Empty; + } + + /// Draws [`Geometry`] using the provided closure and stores it in the + /// [`Cache`]. + /// + /// The closure will only be called when + /// - the bounds have changed since the previous draw call. + /// - the [`Cache`] is empty or has been explicitly cleared. + /// + /// Otherwise, the previously stored [`Geometry`] will be returned. The + /// [`Cache`] is not cleared in this case. In other words, it will keep + /// returning the stored [`Geometry`] if needed. + pub fn draw( + &self, + renderer: &Renderer, + bounds: Size, + draw_fn: impl FnOnce(&mut Frame), + ) -> Renderer::Geometry { + use std::ops::Deref; + + if let State::Filled { + bounds: cached_bounds, + geometry, + } = self.state.borrow().deref() + { + if *cached_bounds == bounds { + return Geometry::load(geometry); + } + } + + let mut frame = Frame::new(renderer, bounds); + draw_fn(&mut frame); + + let geometry = frame.into_geometry().cache(); + let result = Geometry::load(&geometry); + + *self.state.borrow_mut() = State::Filled { bounds, geometry }; + + result + } +} + +impl std::fmt::Debug for Cache +where + Renderer: geometry::Renderer, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let state = self.state.borrow(); + + match *state { + State::Empty => write!(f, "Cache::Empty"), + State::Filled { bounds, .. } => { + write!(f, "Cache::Filled {{ bounds: {bounds:?} }}") + } + } + } +} + +impl Default for Cache +where + Renderer: geometry::Renderer, +{ + fn default() -> Self { + Self::new() + } +} + +enum State +where + Geometry: self::Geometry, +{ + Empty, + Filled { + bounds: Size, + geometry: Geometry::Cache, + }, +} + +impl Geometry for Primitive { + type Cache = Arc; + + fn load(cache: &Arc) -> Self { + Self::Cache { + content: cache.clone(), + } + } + + fn cache(self) -> Arc { + Arc::new(self) + } +} diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs new file mode 100644 index 00000000..e88c43b0 --- /dev/null +++ b/graphics/src/geometry/frame.rs @@ -0,0 +1,208 @@ +use crate::core::{Point, Radians, Rectangle, Size, Vector}; +use crate::geometry::{self, Geometry}; +use crate::geometry::{Fill, Path, Stroke, Text}; + +pub struct Frame +where + Renderer: geometry::Renderer, +{ + raw: Renderer::Frame, +} + +impl Frame +where + Renderer: geometry::Renderer, +{ + pub fn new(renderer: &Renderer, size: Size) -> Self { + Self { + raw: renderer.new_frame(size), + } + } + + /// Returns the width of the [`Frame`]. + pub fn width(&self) -> f32 { + self.raw.width() + } + + /// Returns the height of the [`Frame`]. + pub fn height(&self) -> f32 { + self.raw.height() + } + + /// Returns the dimensions of the [`Frame`]. + pub fn size(&self) -> Size { + self.raw.size() + } + + /// Returns the coordinate of the center of the [`Frame`]. + pub fn center(&self) -> Point { + self.raw.center() + } + + /// Draws the given [`Path`] on the [`Frame`] by filling it with the + /// provided style. + pub fn fill(&mut self, path: &Path, fill: impl Into) { + self.raw.fill(path, fill); + } + + /// Draws an axis-aligned rectangle given its top-left corner coordinate and + /// its `Size` on the [`Frame`] by filling it with the provided style. + pub fn fill_rectangle( + &mut self, + top_left: Point, + size: Size, + fill: impl Into, + ) { + self.raw.fill_rectangle(top_left, size, fill); + } + + /// Draws the stroke of the given [`Path`] on the [`Frame`] with the + /// provided style. + pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>) { + self.raw.stroke(path, stroke); + } + + /// Draws the characters of the given [`Text`] on the [`Frame`], filling + /// them with the given color. + /// + /// __Warning:__ Text currently does not work well with rotations and scale + /// transforms! The position will be correctly transformed, but the + /// resulting glyphs will not be rotated or scaled properly. + /// + /// Additionally, all text will be rendered on top of all the layers of + /// a `Canvas`. Therefore, it is currently only meant to be used for + /// overlays, which is the most common use case. + /// + /// Support for vectorial text is planned, and should address all these + /// limitations. + pub fn fill_text(&mut self, text: impl Into) { + self.raw.fill_text(text); + } + + /// Stores the current transform of the [`Frame`] and executes the given + /// drawing operations, restoring the transform afterwards. + /// + /// This method is useful to compose transforms and perform drawing + /// operations in different coordinate systems. + #[inline] + pub fn with_save(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + self.push_transform(); + + let result = f(self); + + self.pop_transform(); + + result + } + + /// Pushes the current transform in the transform stack. + pub fn push_transform(&mut self) { + self.raw.push_transform(); + } + + /// Pops a transform from the transform stack and sets it as the current transform. + pub fn pop_transform(&mut self) { + self.raw.pop_transform(); + } + + /// Executes the given drawing operations within a [`Rectangle`] region, + /// clipping any geometry that overflows its bounds. Any transformations + /// performed are local to the provided closure. + /// + /// This method is useful to perform drawing operations that need to be + /// clipped. + #[inline] + pub fn with_clip( + &mut self, + region: Rectangle, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + let mut frame = self.draft(region.size()); + + let result = f(&mut frame); + + let origin = Point::new(region.x, region.y); + + self.paste(frame, origin); + + result + } + + /// Creates a new [`Frame`] with the given [`Size`]. + /// + /// Draw its contents back to this [`Frame`] with [`paste`]. + /// + /// [`paste`]: Self::paste + pub fn draft(&mut self, size: Size) -> Self { + Self { + raw: self.raw.draft(size), + } + } + + /// Draws the contents of the given [`Frame`] with origin at the given [`Point`]. + pub fn paste(&mut self, frame: Self, at: Point) { + self.raw.paste(frame.raw, at); + } + + /// Applies a translation to the current transform of the [`Frame`]. + pub fn translate(&mut self, translation: Vector) { + self.raw.translate(translation); + } + + /// Applies a rotation in radians to the current transform of the [`Frame`]. + pub fn rotate(&mut self, angle: impl Into) { + self.raw.rotate(angle); + } + + /// Applies a uniform scaling to the current transform of the [`Frame`]. + pub fn scale(&mut self, scale: impl Into) { + self.raw.scale(scale); + } + + /// Applies a non-uniform scaling to the current transform of the [`Frame`]. + pub fn scale_nonuniform(&mut self, scale: impl Into) { + self.raw.scale_nonuniform(scale); + } + + pub fn into_geometry(self) -> Renderer::Geometry { + self.raw.into_geometry() + } +} + +/// The internal implementation of a [`Frame`]. +/// +/// Analogous to [`Frame`]. See [`Frame`] for the documentation +/// of each method. +#[allow(missing_docs)] +pub trait Backend: Sized { + type Geometry: Geometry; + + fn width(&self) -> f32; + fn height(&self) -> f32; + fn size(&self) -> Size; + fn center(&self) -> Point; + + fn push_transform(&mut self); + fn pop_transform(&mut self); + + fn translate(&mut self, translation: Vector); + fn rotate(&mut self, angle: impl Into); + fn scale(&mut self, scale: impl Into); + fn scale_nonuniform(&mut self, scale: impl Into); + + fn draft(&mut self, size: Size) -> Self; + fn paste(&mut self, frame: Self, at: Point); + + fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>); + + fn fill(&mut self, path: &Path, fill: impl Into); + fn fill_text(&mut self, text: impl Into); + fn fill_rectangle( + &mut self, + top_left: Point, + size: Size, + fill: impl Into, + ); + + fn into_geometry(self) -> Self::Geometry; +} diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 3b21aa11..2fcb55aa 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -257,7 +257,8 @@ impl mesh::Renderer for Renderer { impl crate::geometry::Renderer for Renderer where B: Backend + crate::geometry::Backend, - B::Frame: crate::geometry::Frame>, + B::Frame: + crate::geometry::frame::Backend>, { type Frame = B::Frame; type Geometry = Primitive; -- cgit From 85800c99ab285efd244c0addfdcf3c732a98de1d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Mar 2024 01:53:48 +0100 Subject: Fix broken links in documentation --- graphics/src/cached.rs | 33 +++++++++++++++++++++++++++++++++ graphics/src/geometry.rs | 14 +++++--------- graphics/src/geometry/cache.rs | 33 +++++++++------------------------ graphics/src/geometry/frame.rs | 11 ++++++++--- graphics/src/lib.rs | 6 ++++-- graphics/src/mesh.rs | 2 ++ graphics/src/renderer.rs | 12 ------------ 7 files changed, 61 insertions(+), 50 deletions(-) create mode 100644 graphics/src/cached.rs (limited to 'graphics') diff --git a/graphics/src/cached.rs b/graphics/src/cached.rs new file mode 100644 index 00000000..f7968c9f --- /dev/null +++ b/graphics/src/cached.rs @@ -0,0 +1,33 @@ +use crate::Primitive; + +use std::sync::Arc; + +/// A piece of data that can be cached. +pub trait Cached: Sized { + /// The type of cache produced. + type Cache; + + /// Loads the [`Cache`] into a proper instance. + /// + /// [`Cache`]: Self::Cache + fn load(cache: &Self::Cache) -> Self; + + /// Caches this value, producing its corresponding [`Cache`]. + /// + /// [`Cache`]: Self::Cache + fn cache(self) -> Self::Cache; +} + +impl Cached for Primitive { + type Cache = Arc; + + fn load(cache: &Arc) -> Self { + Self::Cache { + content: cache.clone(), + } + } + + fn cache(self) -> Arc { + Arc::new(self) + } +} diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index 2b18243e..cc2359b6 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -19,32 +19,28 @@ pub use text::Text; pub use crate::gradient::{self, Gradient}; use crate::core::Size; +use crate::Cached; /// A renderer capable of drawing some [`Self::Geometry`]. pub trait Renderer: crate::core::Renderer { /// The kind of geometry this renderer can draw. - type Geometry: Geometry; + type Geometry: Cached; /// The kind of [`Frame`] this renderer supports. type Frame: frame::Backend; + /// Creates a new [`Self::Frame`]. fn new_frame(&self, size: Size) -> Self::Frame; /// Draws the given [`Self::Geometry`]. fn draw_geometry(&mut self, geometry: Self::Geometry); } +/// The graphics backend of a geometry renderer. pub trait Backend { /// The kind of [`Frame`] this backend supports. type Frame: frame::Backend; + /// Creates a new [`Self::Frame`]. fn new_frame(&self, size: Size) -> Self::Frame; } - -pub trait Geometry: Sized { - type Cache; - - fn load(cache: &Self::Cache) -> Self; - - fn cache(self) -> Self::Cache; -} diff --git a/graphics/src/geometry/cache.rs b/graphics/src/geometry/cache.rs index 490e69e2..37d433c2 100644 --- a/graphics/src/geometry/cache.rs +++ b/graphics/src/geometry/cache.rs @@ -1,11 +1,10 @@ use crate::core::Size; -use crate::geometry::{self, Frame, Geometry}; -use crate::Primitive; +use crate::geometry::{self, Frame}; +use crate::Cached; use std::cell::RefCell; -use std::sync::Arc; -/// A simple cache that stores generated [`Geometry`] to avoid recomputation. +/// A simple cache that stores generated geometry to avoid recomputation. /// /// A [`Cache`] will not redraw its geometry unless the dimensions of its layer /// change or it is explicitly cleared. @@ -32,16 +31,16 @@ where *self.state.borrow_mut() = State::Empty; } - /// Draws [`Geometry`] using the provided closure and stores it in the + /// Draws geometry using the provided closure and stores it in the /// [`Cache`]. /// /// The closure will only be called when /// - the bounds have changed since the previous draw call. /// - the [`Cache`] is empty or has been explicitly cleared. /// - /// Otherwise, the previously stored [`Geometry`] will be returned. The + /// Otherwise, the previously stored geometry will be returned. The /// [`Cache`] is not cleared in this case. In other words, it will keep - /// returning the stored [`Geometry`] if needed. + /// returning the stored geometry if needed. pub fn draw( &self, renderer: &Renderer, @@ -56,7 +55,7 @@ where } = self.state.borrow().deref() { if *cached_bounds == bounds { - return Geometry::load(geometry); + return Cached::load(geometry); } } @@ -64,7 +63,7 @@ where draw_fn(&mut frame); let geometry = frame.into_geometry().cache(); - let result = Geometry::load(&geometry); + let result = Cached::load(&geometry); *self.state.borrow_mut() = State::Filled { bounds, geometry }; @@ -99,7 +98,7 @@ where enum State where - Geometry: self::Geometry, + Geometry: Cached, { Empty, Filled { @@ -107,17 +106,3 @@ where geometry: Geometry::Cache, }, } - -impl Geometry for Primitive { - type Cache = Arc; - - fn load(cache: &Arc) -> Self { - Self::Cache { - content: cache.clone(), - } - } - - fn cache(self) -> Arc { - Arc::new(self) - } -} diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index e88c43b0..37e0df38 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -1,7 +1,10 @@ +//! Draw and generate geometry. use crate::core::{Point, Radians, Rectangle, Size, Vector}; -use crate::geometry::{self, Geometry}; -use crate::geometry::{Fill, Path, Stroke, Text}; +use crate::geometry::{self, Fill, Path, Stroke, Text}; +use crate::Cached; +/// The region of a surface that can be used to draw geometry. +#[allow(missing_debug_implementations)] pub struct Frame where Renderer: geometry::Renderer, @@ -13,6 +16,7 @@ impl Frame where Renderer: geometry::Renderer, { + /// Creates a new [`Frame`] with the given dimensions. pub fn new(renderer: &Renderer, size: Size) -> Self { Self { raw: renderer.new_frame(size), @@ -164,6 +168,7 @@ where self.raw.scale_nonuniform(scale); } + /// Turns the [`Frame`] into its underlying geometry. pub fn into_geometry(self) -> Renderer::Geometry { self.raw.into_geometry() } @@ -175,7 +180,7 @@ where /// of each method. #[allow(missing_docs)] pub trait Backend: Sized { - type Geometry: Geometry; + type Geometry: Cached; fn width(&self) -> f32; fn height(&self) -> f32; diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 6d0862ad..a682b89b 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -9,14 +9,15 @@ )] #![forbid(rust_2018_idioms)] #![deny( - // missing_debug_implementations, - // missing_docs, + missing_debug_implementations, + missing_docs, unsafe_code, unused_results, rustdoc::broken_intra_doc_links )] #![cfg_attr(docsrs, feature(doc_auto_cfg))] mod antialiasing; +mod cached; mod error; mod primitive; mod viewport; @@ -38,6 +39,7 @@ pub mod image; pub use antialiasing::Antialiasing; pub use backend::Backend; +pub use cached::Cached; pub use compositor::Compositor; pub use damage::Damage; pub use error::Error; diff --git a/graphics/src/mesh.rs b/graphics/src/mesh.rs index 5be3ee5b..d671f494 100644 --- a/graphics/src/mesh.rs +++ b/graphics/src/mesh.rs @@ -75,6 +75,8 @@ pub struct GradientVertex2D { pub gradient: gradient::Packed, } +/// A renderer capable of drawing a [`Mesh`]. pub trait Renderer: core::Renderer { + /// Draws the given [`Mesh`]. fn draw_mesh(&mut self, mesh: Mesh); } diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 2fcb55aa..eb720495 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -58,18 +58,6 @@ impl Renderer { ) -> O { f(&mut self.backend, &self.primitives) } - - #[cfg(feature = "geometry")] - pub fn draw_geometry( - &mut self, - layers: impl IntoIterator, - ) where - Geometry: Into>, - { - for layer in layers { - self.draw_primitive(layer.into()); - } - } } impl iced_core::Renderer for Renderer { -- cgit From bbafeed13d20f2cbd6fc18b949b34596aa0c6c2e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Mar 2024 01:55:28 +0100 Subject: Fix outdated warning in docs of `Frame::fill_text` --- graphics/src/geometry/frame.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'graphics') diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index 37e0df38..b54fca5d 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -69,16 +69,9 @@ where /// Draws the characters of the given [`Text`] on the [`Frame`], filling /// them with the given color. /// - /// __Warning:__ Text currently does not work well with rotations and scale - /// transforms! The position will be correctly transformed, but the - /// resulting glyphs will not be rotated or scaled properly. - /// - /// Additionally, all text will be rendered on top of all the layers of + /// __Warning:__ All text will be rendered on top of all the layers of /// a `Canvas`. Therefore, it is currently only meant to be used for /// overlays, which is the most common use case. - /// - /// Support for vectorial text is planned, and should address all these - /// limitations. pub fn fill_text(&mut self, text: impl Into) { self.raw.fill_text(text); } -- cgit From 1f13a91361258a1607c71f4840a26a6437f88612 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Mar 2024 05:27:31 +0100 Subject: Make `iced_tiny_skia` optional with a `tiny-skia` feature --- graphics/src/backend.rs | 3 ++ graphics/src/compositor.rs | 62 +++++++++++++++++++++++++++++++++++++++++- graphics/src/geometry.rs | 4 +-- graphics/src/geometry/frame.rs | 3 +- graphics/src/mesh.rs | 4 +-- 5 files changed, 69 insertions(+), 7 deletions(-) (limited to 'graphics') diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index e394c956..e982b54a 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -10,6 +10,9 @@ use std::borrow::Cow; /// /// [`Renderer`]: crate::Renderer pub trait Backend { + /// The compositor of this [`Backend`]. + type Compositor; + /// The custom kind of primitives this [`Backend`] supports. type Primitive: TryFrom; } diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs index 91951a8e..32cea46a 100644 --- a/graphics/src/compositor.rs +++ b/graphics/src/compositor.rs @@ -15,7 +15,7 @@ pub trait Compositor: Sized { type Settings: Default; /// The iced renderer of the backend. - type Renderer: iced_core::Renderer; + type Renderer; /// The surface of the backend. type Surface; @@ -122,3 +122,63 @@ pub struct Information { /// Contains the graphics backend. pub backend: String, } + +impl Compositor for () { + type Settings = (); + type Renderer = (); + type Surface = (); + + async fn new( + _settings: Self::Settings, + _compatible_window: W, + ) -> Result { + Ok(()) + } + + fn create_renderer(&self) -> Self::Renderer {} + + fn create_surface( + &mut self, + _window: W, + _width: u32, + _height: u32, + ) -> Self::Surface { + } + + fn configure_surface( + &mut self, + _surface: &mut Self::Surface, + _width: u32, + _height: u32, + ) { + } + + fn fetch_information(&self) -> Information { + Information { + adapter: String::from("Null Renderer"), + backend: String::from("Null"), + } + } + + fn present>( + &mut self, + _renderer: &mut Self::Renderer, + _surface: &mut Self::Surface, + _viewport: &Viewport, + _background_color: Color, + _overlay: &[T], + ) -> Result<(), SurfaceError> { + Ok(()) + } + + fn screenshot>( + &mut self, + _renderer: &mut Self::Renderer, + _surface: &mut Self::Surface, + _viewport: &Viewport, + _background_color: Color, + _overlay: &[T], + ) -> Vec { + vec![] + } +} diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index cc2359b6..194f37b2 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -18,11 +18,11 @@ pub use text::Text; pub use crate::gradient::{self, Gradient}; -use crate::core::Size; +use crate::core::{self, Size}; use crate::Cached; /// A renderer capable of drawing some [`Self::Geometry`]. -pub trait Renderer: crate::core::Renderer { +pub trait Renderer: core::Renderer { /// The kind of geometry this renderer can draw. type Geometry: Cached; diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index b54fca5d..635012d0 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -1,7 +1,6 @@ //! Draw and generate geometry. use crate::core::{Point, Radians, Rectangle, Size, Vector}; use crate::geometry::{self, Fill, Path, Stroke, Text}; -use crate::Cached; /// The region of a surface that can be used to draw geometry. #[allow(missing_debug_implementations)] @@ -173,7 +172,7 @@ where /// of each method. #[allow(missing_docs)] pub trait Backend: Sized { - type Geometry: Cached; + type Geometry; fn width(&self) -> f32; fn height(&self) -> f32; diff --git a/graphics/src/mesh.rs b/graphics/src/mesh.rs index d671f494..20692b07 100644 --- a/graphics/src/mesh.rs +++ b/graphics/src/mesh.rs @@ -1,6 +1,6 @@ //! Draw triangles! use crate::color; -use crate::core::{self, Rectangle, Size}; +use crate::core::{Rectangle, Size}; use crate::gradient; use crate::Damage; @@ -76,7 +76,7 @@ pub struct GradientVertex2D { } /// A renderer capable of drawing a [`Mesh`]. -pub trait Renderer: core::Renderer { +pub trait Renderer { /// Draws the given [`Mesh`]. fn draw_mesh(&mut self, mesh: Mesh); } -- cgit From 4f2f40c68b4647f281d34034beb159a41422aa06 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Mar 2024 05:41:15 +0100 Subject: Fix standalone compilation of `iced_widget` crate --- graphics/src/cached.rs | 9 +++++++++ graphics/src/geometry.rs | 10 +++++++++ graphics/src/geometry/frame.rs | 46 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) (limited to 'graphics') diff --git a/graphics/src/cached.rs b/graphics/src/cached.rs index f7968c9f..b52f9d9d 100644 --- a/graphics/src/cached.rs +++ b/graphics/src/cached.rs @@ -31,3 +31,12 @@ impl Cached for Primitive { Arc::new(self) } } + +#[cfg(debug_assertions)] +impl Cached for () { + type Cache = (); + + fn load(_cache: &Self::Cache) -> Self {} + + fn cache(self) -> Self::Cache {} +} diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index 194f37b2..d251efb8 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -44,3 +44,13 @@ pub trait Backend { /// Creates a new [`Self::Frame`]. fn new_frame(&self, size: Size) -> Self::Frame; } + +#[cfg(debug_assertions)] +impl Renderer for () { + type Geometry = (); + type Frame = (); + + fn new_frame(&self, _size: Size) -> Self::Frame {} + + fn draw_geometry(&mut self, _geometry: Self::Geometry) {} +} diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index 635012d0..ad35e8ec 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -203,3 +203,49 @@ pub trait Backend: Sized { fn into_geometry(self) -> Self::Geometry; } + +#[cfg(debug_assertions)] +impl Backend for () { + type Geometry = (); + + fn width(&self) -> f32 { + 0.0 + } + + fn height(&self) -> f32 { + 0.0 + } + + fn size(&self) -> Size { + Size::ZERO + } + + fn center(&self) -> Point { + Point::ORIGIN + } + + fn push_transform(&mut self) {} + fn pop_transform(&mut self) {} + + fn translate(&mut self, _translation: Vector) {} + fn rotate(&mut self, _angle: impl Into) {} + fn scale(&mut self, _scale: impl Into) {} + fn scale_nonuniform(&mut self, _scale: impl Into) {} + + fn draft(&mut self, _size: Size) -> Self {} + fn paste(&mut self, _frame: Self, _at: Point) {} + + fn stroke<'a>(&mut self, _path: &Path, _stroke: impl Into>) {} + + fn fill(&mut self, _path: &Path, _fill: impl Into) {} + fn fill_text(&mut self, _text: impl Into) {} + fn fill_rectangle( + &mut self, + _top_left: Point, + _size: Size, + _fill: impl Into, + ) { + } + + fn into_geometry(self) -> Self::Geometry {} +} -- cgit From 5137d655e6bbd29581fc1469d0385515113f2999 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Mar 2024 07:09:51 +0100 Subject: Allow custom renderers in `Program` and `Application` --- graphics/src/backend.rs | 6 +++--- graphics/src/compositor.rs | 24 ++++++++++++++++-------- graphics/src/lib.rs | 2 ++ graphics/src/renderer.rs | 8 ++++++++ graphics/src/settings.rs | 29 +++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 graphics/src/settings.rs (limited to 'graphics') diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index e982b54a..aa7bf4e8 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -2,16 +2,16 @@ use crate::core::image; use crate::core::svg; use crate::core::Size; -use crate::Mesh; +use crate::{Compositor, Mesh, Renderer}; use std::borrow::Cow; /// The graphics backend of a [`Renderer`]. /// /// [`Renderer`]: crate::Renderer -pub trait Backend { +pub trait Backend: Sized { /// The compositor of this [`Backend`]. - type Compositor; + type Compositor: Compositor>; /// The custom kind of primitives this [`Backend`] supports. type Primitive: TryFrom; diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs index 32cea46a..4d548f30 100644 --- a/graphics/src/compositor.rs +++ b/graphics/src/compositor.rs @@ -1,9 +1,9 @@ //! A compositor is responsible for initializing a renderer and managing window //! surfaces. -use crate::{Error, Viewport}; - +use crate::core; use crate::core::Color; use crate::futures::{MaybeSend, MaybeSync}; +use crate::{Error, Settings, Viewport}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use std::future::Future; @@ -11,9 +11,6 @@ use thiserror::Error; /// A graphics compositor that can draw to windows. pub trait Compositor: Sized { - /// The settings of the backend. - type Settings: Default; - /// The iced renderer of the backend. type Renderer; @@ -22,7 +19,7 @@ pub trait Compositor: Sized { /// Creates a new [`Compositor`]. fn new( - settings: Self::Settings, + settings: Settings, compatible_window: W, ) -> impl Future>; @@ -93,6 +90,12 @@ impl Window for T where { } +/// A renderer that supports composition. +pub trait Renderer: core::Renderer { + /// The compositor of the renderer. + type Compositor: Compositor; +} + /// Result of an unsuccessful call to [`Compositor::present`]. #[derive(Clone, PartialEq, Eq, Debug, Error)] pub enum SurfaceError { @@ -123,13 +126,13 @@ pub struct Information { pub backend: String, } +#[cfg(debug_assertions)] impl Compositor for () { - type Settings = (); type Renderer = (); type Surface = (); async fn new( - _settings: Self::Settings, + _settings: Settings, _compatible_window: W, ) -> Result { Ok(()) @@ -182,3 +185,8 @@ impl Compositor for () { vec![] } } + +#[cfg(debug_assertions)] +impl Renderer for () { + type Compositor = (); +} diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index a682b89b..2e476f8c 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -20,6 +20,7 @@ mod antialiasing; mod cached; mod error; mod primitive; +mod settings; mod viewport; pub mod backend; @@ -47,6 +48,7 @@ pub use gradient::Gradient; pub use mesh::Mesh; pub use primitive::Primitive; pub use renderer::Renderer; +pub use settings::Settings; pub use viewport::Viewport; pub use iced_core as core; diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index eb720495..5de7f97f 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -1,5 +1,6 @@ //! Create a renderer from a [`Backend`]. use crate::backend::{self, Backend}; +use crate::compositor; use crate::core; use crate::core::image; use crate::core::renderer; @@ -259,3 +260,10 @@ where self.draw_primitive(geometry); } } + +impl compositor::Renderer for Renderer +where + B: Backend, +{ + type Compositor = B::Compositor; +} diff --git a/graphics/src/settings.rs b/graphics/src/settings.rs new file mode 100644 index 00000000..68673536 --- /dev/null +++ b/graphics/src/settings.rs @@ -0,0 +1,29 @@ +use crate::core::{Font, Pixels}; +use crate::Antialiasing; + +/// The settings of a Backend. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Settings { + /// The default [`Font`] to use. + pub default_font: Font, + + /// The default size of text. + /// + /// By default, it will be set to `16.0`. + pub default_text_size: Pixels, + + /// The antialiasing strategy that will be used for triangle primitives. + /// + /// By default, it is `None`. + pub antialiasing: Option, +} + +impl Default for Settings { + fn default() -> Settings { + Settings { + default_font: Font::default(), + default_text_size: Pixels(16.0), + antialiasing: None, + } + } +} -- cgit From 441e9237cd1c9c9b61d9b144b5b4dafa236ace28 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Mar 2024 19:35:19 +0100 Subject: Rename `compositor::Renderer` to `Default` --- graphics/src/backend.rs | 6 +++--- graphics/src/compositor.rs | 7 +++---- graphics/src/renderer.rs | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) (limited to 'graphics') diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index aa7bf4e8..7abc42c5 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -10,11 +10,11 @@ use std::borrow::Cow; /// /// [`Renderer`]: crate::Renderer pub trait Backend: Sized { - /// The compositor of this [`Backend`]. - type Compositor: Compositor>; - /// The custom kind of primitives this [`Backend`] supports. type Primitive: TryFrom; + + /// The default compositor of this [`Backend`]. + type Compositor: Compositor>; } /// A graphics backend that supports text rendering. diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs index 4d548f30..8c67cd16 100644 --- a/graphics/src/compositor.rs +++ b/graphics/src/compositor.rs @@ -1,6 +1,5 @@ //! A compositor is responsible for initializing a renderer and managing window //! surfaces. -use crate::core; use crate::core::Color; use crate::futures::{MaybeSend, MaybeSync}; use crate::{Error, Settings, Viewport}; @@ -90,8 +89,8 @@ impl Window for T where { } -/// A renderer that supports composition. -pub trait Renderer: core::Renderer { +/// Defines the default compositor of a renderer. +pub trait Default { /// The compositor of the renderer. type Compositor: Compositor; } @@ -187,6 +186,6 @@ impl Compositor for () { } #[cfg(debug_assertions)] -impl Renderer for () { +impl Default for () { type Compositor = (); } diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 5de7f97f..f517ff3e 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -261,7 +261,7 @@ where } } -impl compositor::Renderer for Renderer +impl compositor::Default for Renderer where B: Backend, { -- cgit From 4f5b63f1f4cd7d3ab72289c697f4abc767114eca Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 24 Mar 2024 08:04:28 +0100 Subject: Reintroduce backend selection through `ICED_BACKEND` env var --- graphics/src/compositor.rs | 15 ++++++++++++++- graphics/src/error.rs | 27 +++++++++++++++++++++++++-- graphics/src/lib.rs | 2 +- 3 files changed, 40 insertions(+), 4 deletions(-) (limited to 'graphics') diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs index 8c67cd16..86472a58 100644 --- a/graphics/src/compositor.rs +++ b/graphics/src/compositor.rs @@ -20,6 +20,18 @@ pub trait Compositor: Sized { fn new( settings: Settings, compatible_window: W, + ) -> impl Future> { + Self::with_backend(settings, compatible_window, None) + } + + /// Creates a new [`Compositor`] with a backend preference. + /// + /// If the backend does not match the preference, it will return + /// [`Error::GraphicsAdapterNotFound`]. + fn with_backend( + _settings: Settings, + _compatible_window: W, + _backend: Option<&str>, ) -> impl Future>; /// Creates a [`Self::Renderer`] for the [`Compositor`]. @@ -130,9 +142,10 @@ impl Compositor for () { type Renderer = (); type Surface = (); - async fn new( + async fn with_backend( _settings: Settings, _compatible_window: W, + _preffered_backend: Option<&str>, ) -> Result { Ok(()) } diff --git a/graphics/src/error.rs b/graphics/src/error.rs index c6ea98a3..6ea1d3a4 100644 --- a/graphics/src/error.rs +++ b/graphics/src/error.rs @@ -1,5 +1,7 @@ +//! See what can go wrong when creating graphical backends. + /// An error that occurred while creating an application's graphical context. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] pub enum Error { /// The requested backend version is not supported. #[error("the requested backend version is not supported")] @@ -11,9 +13,30 @@ pub enum Error { /// A suitable graphics adapter or device could not be found. #[error("a suitable graphics adapter or device could not be found")] - GraphicsAdapterNotFound, + GraphicsAdapterNotFound { + /// The name of the backend where the error happened + backend: &'static str, + /// The reason why this backend could not be used + reason: Reason, + }, /// An error occurred in the context's internal backend #[error("an error occurred in the context's internal backend")] BackendError(String), + + /// Multiple errors occurred + #[error("multiple errors occurred: {0:?}")] + List(Vec), +} + +/// The reason why a graphics adapter could not be found +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Reason { + /// The backend did not match the preference + DidNotMatch { + /// The preferred backend + preferred_backend: String, + }, + /// The request to create the backend failed + RequestFailed(String), } diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 2e476f8c..d7f2f439 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -18,7 +18,6 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] mod antialiasing; mod cached; -mod error; mod primitive; mod settings; mod viewport; @@ -27,6 +26,7 @@ pub mod backend; pub mod color; pub mod compositor; pub mod damage; +pub mod error; pub mod gradient; pub mod mesh; pub mod renderer; -- cgit