summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-03-21 22:27:17 +0100
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-03-21 22:27:17 +0100
commit3645d34d6a1ba1247238e830e9eefd52d9e5b986 (patch)
tree2d38961161df0a85c1667474b2b696aab86b7160
parent7e4ae8450e1f28c15717ca5ca9748981af9c9541 (diff)
downloadiced-3645d34d6a1ba1247238e830e9eefd52d9e5b986.tar.gz
iced-3645d34d6a1ba1247238e830e9eefd52d9e5b986.tar.bz2
iced-3645d34d6a1ba1247238e830e9eefd52d9e5b986.zip
Implement composable, type-safe renderer fallback
-rw-r--r--core/src/renderer.rs28
-rw-r--r--core/src/renderer/null.rs13
-rw-r--r--examples/arc/src/main.rs10
-rw-r--r--examples/bezier_tool/src/main.rs33
-rw-r--r--examples/clock/src/main.rs8
-rw-r--r--examples/color_palette/src/main.rs10
-rw-r--r--examples/game_of_life/src/main.rs18
-rw-r--r--examples/geometry/src/main.rs4
-rw-r--r--examples/layout/src/main.rs10
-rw-r--r--examples/loading_spinners/src/circular.rs6
-rw-r--r--examples/multitouch/src/main.rs10
-rw-r--r--examples/sierpinski_triangle/src/main.rs8
-rw-r--r--examples/solar_system/src/main.rs7
-rw-r--r--examples/vectorial_text/src/main.rs8
-rw-r--r--graphics/src/backend.rs3
-rw-r--r--graphics/src/geometry.rs272
-rw-r--r--graphics/src/lib.rs4
-rw-r--r--graphics/src/mesh.rs6
-rw-r--r--graphics/src/renderer.rs106
-rw-r--r--renderer/src/compositor.rs299
-rw-r--r--renderer/src/custom.rs4
-rw-r--r--renderer/src/fallback.rs562
-rw-r--r--renderer/src/lib.rs368
-rw-r--r--renderer/src/settings.rs21
-rw-r--r--tiny_skia/src/backend.rs9
-rw-r--r--tiny_skia/src/geometry.rs60
-rw-r--r--tiny_skia/src/primitive.rs10
-rw-r--r--wgpu/src/backend.rs9
-rw-r--r--wgpu/src/geometry.rs493
-rw-r--r--wgpu/src/primitive.rs8
-rw-r--r--widget/src/canvas.rs19
-rw-r--r--widget/src/canvas/program.rs19
-rw-r--r--widget/src/qr_code.rs9
-rw-r--r--winit/src/application.rs4
-rw-r--r--winit/src/multi_window.rs8
35 files changed, 1365 insertions, 1101 deletions
diff --git a/core/src/renderer.rs b/core/src/renderer.rs
index 47b09d32..406b33f3 100644
--- a/core/src/renderer.rs
+++ b/core/src/renderer.rs
@@ -11,17 +11,41 @@ use crate::{
/// A component that can be used by widgets to draw themselves on a screen.
pub trait Renderer {
+ /// Starts recording a new layer.
+ fn start_layer(&mut self);
+
+ /// Ends recording a new layer.
+ ///
+ /// The new layer will clip its contents to the provided `bounds`.
+ fn end_layer(&mut self, bounds: Rectangle);
+
/// Draws the primitives recorded in the given closure in a new layer.
///
/// The layer will clip its contents to the provided `bounds`.
- fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self));
+ fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) {
+ self.start_layer();
+ f(self);
+ self.end_layer(bounds);
+ }
+
+ /// Starts recording with a new [`Transformation`].
+ fn start_transformation(&mut self);
+
+ /// Ends recording a new layer.
+ ///
+ /// The new layer will clip its contents to the provided `bounds`.
+ fn end_transformation(&mut self, transformation: Transformation);
/// Applies a [`Transformation`] to the primitives recorded in the given closure.
fn with_transformation(
&mut self,
transformation: Transformation,
f: impl FnOnce(&mut Self),
- );
+ ) {
+ self.start_transformation();
+ f(self);
+ self.end_transformation(transformation);
+ }
/// Applies a translation to the primitives recorded in the given closure.
fn with_translation(
diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs
index 83688ff7..0d7b7c14 100644
--- a/core/src/renderer/null.rs
+++ b/core/src/renderer/null.rs
@@ -21,14 +21,13 @@ impl Null {
}
impl Renderer for Null {
- fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {}
+ fn start_layer(&mut self) {}
- fn with_transformation(
- &mut self,
- _transformation: Transformation,
- _f: impl FnOnce(&mut Self),
- ) {
- }
+ fn end_layer(&mut self, _bounds: Rectangle) {}
+
+ fn start_transformation(&mut self) {}
+
+ fn end_transformation(&mut self, _transformation: Transformation) {}
fn clear(&mut self) {}
diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs
index 4576404f..a7893efa 100644
--- a/examples/arc/src/main.rs
+++ b/examples/arc/src/main.rs
@@ -1,9 +1,7 @@
use std::{f32::consts::PI, time::Instant};
use iced::mouse;
-use iced::widget::canvas::{
- self, stroke, Cache, Canvas, Geometry, Path, Stroke,
-};
+use iced::widget::canvas::{self, stroke, Cache, Canvas, Frame, Path, Stroke};
use iced::{Element, Length, Point, Rectangle, Renderer, Subscription, Theme};
pub fn main() -> iced::Result {
@@ -57,11 +55,11 @@ impl<Message> canvas::Program<Message> for Arc {
fn draw(
&self,
_state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
- ) -> Vec<Geometry> {
+ ) {
let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
let palette = theme.palette();
@@ -104,6 +102,6 @@ impl<Message> canvas::Program<Message> for Arc {
);
});
- vec![geometry]
+ renderer.draw_geometry([geometry]);
}
}
diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs
index cf70bd40..e51f2a31 100644
--- a/examples/bezier_tool/src/main.rs
+++ b/examples/bezier_tool/src/main.rs
@@ -52,7 +52,7 @@ impl Example {
mod bezier {
use iced::mouse;
use iced::widget::canvas::event::{self, Event};
- use iced::widget::canvas::{self, Canvas, Frame, Geometry, Path, Stroke};
+ use iced::widget::canvas::{self, frame, Canvas, Frame, Path, Stroke};
use iced::{Element, Length, Point, Rectangle, Renderer, Theme};
#[derive(Default)]
@@ -138,30 +138,25 @@ mod bezier {
fn draw(
&self,
state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
_theme: &Theme,
bounds: Rectangle,
cursor: mouse::Cursor,
- ) -> Vec<Geometry> {
- let content = self.state.cache.draw(
- renderer,
- bounds.size(),
- |frame: &mut Frame| {
+ ) {
+ let content =
+ self.state.cache.draw(renderer, bounds.size(), |frame| {
Curve::draw_all(self.curves, frame);
frame.stroke(
&Path::rectangle(Point::ORIGIN, frame.size()),
Stroke::default().with_width(2.0),
);
- },
- );
+ });
- if let Some(pending) = state {
- let pending_curve = pending.draw(renderer, bounds, cursor);
+ renderer.draw_geometry([content]);
- vec![content, pending_curve]
- } else {
- vec![content]
+ if let Some(pending) = state {
+ pending.draw(renderer, bounds, cursor);
}
}
@@ -187,7 +182,7 @@ mod bezier {
}
impl Curve {
- fn draw_all(curves: &[Curve], frame: &mut Frame) {
+ fn draw_all(curves: &[Curve], frame: &mut impl Frame) {
let curves = Path::new(|p| {
for curve in curves {
p.move_to(curve.from);
@@ -208,11 +203,11 @@ mod bezier {
impl Pending {
fn draw(
&self,
- renderer: &Renderer,
+ renderer: &mut Renderer,
bounds: Rectangle,
cursor: mouse::Cursor,
- ) -> Geometry {
- let mut frame = Frame::new(renderer, bounds.size());
+ ) {
+ let mut frame = frame(renderer, bounds.size());
if let Some(cursor_position) = cursor.position_in(bounds) {
match *self {
@@ -232,7 +227,7 @@ mod bezier {
};
}
- frame.into_geometry()
+ renderer.draw_geometry([frame]);
}
}
}
diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs
index 897f8f1b..9f78903c 100644
--- a/examples/clock/src/main.rs
+++ b/examples/clock/src/main.rs
@@ -1,6 +1,6 @@
use iced::alignment;
use iced::mouse;
-use iced::widget::canvas::{stroke, Cache, Geometry, LineCap, Path, Stroke};
+use iced::widget::canvas::{stroke, Cache, Frame, LineCap, Path, Stroke};
use iced::widget::{canvas, container};
use iced::{
Degrees, Element, Font, Length, Point, Rectangle, Renderer, Subscription,
@@ -82,11 +82,11 @@ impl<Message> canvas::Program<Message> for Clock {
fn draw(
&self,
_state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
- ) -> Vec<Geometry> {
+ ) {
let clock = self.clock.draw(renderer, bounds.size(), |frame| {
let palette = theme.extended_palette();
@@ -163,7 +163,7 @@ impl<Message> canvas::Program<Message> for Clock {
});
});
- vec![clock]
+ renderer.draw_geometry([clock]);
}
}
diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs
index d9325edb..400766ff 100644
--- a/examples/color_palette/src/main.rs
+++ b/examples/color_palette/src/main.rs
@@ -1,6 +1,6 @@
use iced::alignment::{self, Alignment};
use iced::mouse;
-use iced::widget::canvas::{self, Canvas, Frame, Geometry, Path};
+use iced::widget::canvas::{self, Canvas, Frame, Path};
use iced::widget::{column, row, text, Slider};
use iced::{
Color, Element, Font, Length, Pixels, Point, Rectangle, Renderer, Size,
@@ -156,7 +156,7 @@ impl Theme {
.into()
}
- fn draw(&self, frame: &mut Frame, text_color: Color) {
+ fn draw(&self, frame: &mut impl Frame, text_color: Color) {
let pad = 20.0;
let box_size = Size {
@@ -252,18 +252,18 @@ impl<Message> canvas::Program<Message> for Theme {
fn draw(
&self,
_state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
theme: &iced::Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
- ) -> Vec<Geometry> {
+ ) {
let theme = self.canvas_cache.draw(renderer, bounds.size(), |frame| {
let palette = theme.extended_palette();
self.draw(frame, palette.background.base.text);
});
- vec![theme]
+ renderer.draw_geometry([theme]);
}
}
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 2b0fae0b..f681b4cc 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -193,7 +193,7 @@ mod grid {
use iced::touch;
use iced::widget::canvas;
use iced::widget::canvas::event::{self, Event};
- use iced::widget::canvas::{Cache, Canvas, Frame, Geometry, Path, Text};
+ use iced::widget::canvas::{frame, Cache, Canvas, Frame, Path, Text};
use iced::{
Color, Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector,
};
@@ -516,11 +516,11 @@ mod grid {
fn draw(
&self,
_interaction: &Interaction,
- renderer: &Renderer,
+ renderer: &mut Renderer,
_theme: &Theme,
bounds: Rectangle,
cursor: mouse::Cursor,
- ) -> Vec<Geometry> {
+ ) {
let center = Vector::new(bounds.width / 2.0, bounds.height / 2.0);
let life = self.life_cache.draw(renderer, bounds.size(), |frame| {
@@ -546,7 +546,7 @@ mod grid {
});
let overlay = {
- let mut frame = Frame::new(renderer, bounds.size());
+ let mut frame = frame(renderer, bounds.size());
let hovered_cell = cursor.position_in(bounds).map(|position| {
Cell::at(self.project(position, frame.size()))
@@ -599,12 +599,10 @@ mod grid {
..text
});
- frame.into_geometry()
+ frame.into()
};
- if self.scaling < 0.2 || !self.show_lines {
- vec![life, overlay]
- } else {
+ if self.scaling >= 0.2 && self.show_lines {
let grid =
self.grid_cache.draw(renderer, bounds.size(), |frame| {
frame.translate(center);
@@ -640,7 +638,9 @@ mod grid {
}
});
- vec![life, grid, overlay]
+ renderer.draw_geometry([life, grid, overlay]);
+ } else {
+ renderer.draw_geometry([life, overlay]);
}
}
diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs
index 63efcbdd..16cdb86f 100644
--- a/examples/geometry/src/main.rs
+++ b/examples/geometry/src/main.rs
@@ -44,7 +44,9 @@ mod rainbow {
cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
- use iced::advanced::graphics::mesh::{self, Mesh, SolidVertex2D};
+ use iced::advanced::graphics::mesh::{
+ self, Mesh, Renderer as _, SolidVertex2D,
+ };
use iced::advanced::Renderer as _;
let bounds = layout.bounds();
diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs
index 713e2b70..198237f5 100644
--- a/examples/layout/src/main.rs
+++ b/examples/layout/src/main.rs
@@ -292,12 +292,14 @@ fn square<'a>(size: impl Into<Length> + Copy) -> Element<'a, Message> {
fn draw(
&self,
_state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
- ) -> Vec<canvas::Geometry> {
- let mut frame = canvas::Frame::new(renderer, bounds.size());
+ ) {
+ use canvas::Frame;
+
+ let mut frame = canvas::frame(renderer, bounds.size());
let palette = theme.extended_palette();
@@ -307,7 +309,7 @@ fn square<'a>(size: impl Into<Length> + Copy) -> Element<'a, Message> {
palette.background.strong.color,
);
- vec![frame.into_geometry()]
+ renderer.draw_geometry([frame]);
}
}
diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs
index 12670ed1..306988af 100644
--- a/examples/loading_spinners/src/circular.rs
+++ b/examples/loading_spinners/src/circular.rs
@@ -6,7 +6,7 @@ use iced::advanced::{self, Clipboard, Layout, Shell, Widget};
use iced::event;
use iced::mouse;
use iced::time::Instant;
-use iced::widget::canvas;
+use iced::widget::canvas::{self, Frame};
use iced::window::{self, RedrawRequest};
use iced::{
Background, Color, Element, Event, Length, Radians, Rectangle, Renderer,
@@ -356,9 +356,7 @@ where
renderer.with_translation(
Vector::new(bounds.x, bounds.y),
|renderer| {
- use iced::advanced::graphics::geometry::Renderer as _;
-
- renderer.draw(vec![geometry]);
+ renderer.draw_geometry([geometry]);
},
);
}
diff --git a/examples/multitouch/src/main.rs b/examples/multitouch/src/main.rs
index 2453c7f5..41bd0151 100644
--- a/examples/multitouch/src/main.rs
+++ b/examples/multitouch/src/main.rs
@@ -5,7 +5,7 @@ use iced::mouse;
use iced::touch;
use iced::widget::canvas::event;
use iced::widget::canvas::stroke::{self, Stroke};
-use iced::widget::canvas::{self, Canvas, Geometry};
+use iced::widget::canvas::{self, Canvas};
use iced::{Color, Element, Length, Point, Rectangle, Renderer, Theme};
use std::collections::HashMap;
@@ -83,11 +83,13 @@ impl canvas::Program<Message> for Multitouch {
fn draw(
&self,
_state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
- ) -> Vec<Geometry> {
+ ) {
+ use canvas::Frame;
+
let fingerweb = self.cache.draw(renderer, bounds.size(), |frame| {
if self.fingers.len() < 2 {
return;
@@ -154,6 +156,6 @@ impl canvas::Program<Message> for Multitouch {
}
});
- vec![fingerweb]
+ renderer.draw_geometry([fingerweb]);
}
}
diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs
index 07ae05d6..b440b8b4 100644
--- a/examples/sierpinski_triangle/src/main.rs
+++ b/examples/sierpinski_triangle/src/main.rs
@@ -107,11 +107,13 @@ impl canvas::Program<Message> for SierpinskiGraph {
fn draw(
&self,
_state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
- ) -> Vec<canvas::Geometry> {
+ ) {
+ use canvas::Frame;
+
let geom = self.cache.draw(renderer, bounds.size(), |frame| {
frame.stroke(
&canvas::Path::rectangle(Point::ORIGIN, frame.size()),
@@ -139,7 +141,7 @@ impl canvas::Program<Message> for SierpinskiGraph {
});
});
- vec![geom]
+ renderer.draw_geometry([geom]);
}
}
diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs
index b5228f09..dd36b711 100644
--- a/examples/solar_system/src/main.rs
+++ b/examples/solar_system/src/main.rs
@@ -126,11 +126,12 @@ impl<Message> canvas::Program<Message> for State {
fn draw(
&self,
_state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
- ) -> Vec<canvas::Geometry> {
+ ) {
+ use canvas::Frame;
use std::f32::consts::PI;
let background =
@@ -197,7 +198,7 @@ impl<Message> canvas::Program<Message> for State {
});
});
- vec![background, system]
+ renderer.draw_geometry([background, system]);
}
}
diff --git a/examples/vectorial_text/src/main.rs b/examples/vectorial_text/src/main.rs
index a7391e23..9f5baac8 100644
--- a/examples/vectorial_text/src/main.rs
+++ b/examples/vectorial_text/src/main.rs
@@ -124,11 +124,13 @@ impl<Message> canvas::Program<Message> for State {
fn draw(
&self,
_state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
- ) -> Vec<canvas::Geometry> {
+ ) {
+ use canvas::Frame;
+
let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
let palette = theme.palette();
let center = bounds.center();
@@ -153,7 +155,7 @@ impl<Message> canvas::Program<Message> for State {
});
});
- vec![geometry]
+ renderer.draw_geometry([geometry]);
}
}
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<Mesh, Error = &'static str>;
}
/// 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: &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<Geometry = Self::Geometry>;
+
+ 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<Self::Geometry> {
+ /// 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<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.
+ fn fill_rectangle(
+ &mut self,
+ top_left: Point,
+ size: Size,
+ fill: impl Into<Fill>,
+ );
+
+ /// Draws the stroke of the given [`Path`] on the [`Frame`] with the
+ /// provided style.
+ fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>);
+
+ /// 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<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]
+ fn with_save<R>(&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<R>(
+ &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<Radians>);
+
+ /// Applies a uniform scaling to the current transform of the [`Frame`].
+ fn scale(&mut self, scale: impl Into<f32>);
+
+ /// Applies a non-uniform scaling to the current transform of the [`Frame`].
+ fn scale_nonuniform(&mut self, scale: impl Into<Vector>);
+}
+
+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<Renderer>
+where
+ Renderer: self::Renderer,
+{
+ state: RefCell<State<Renderer::Geometry>>,
+}
+
+impl<Renderer> Cache<Renderer>
+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<Renderer> std::fmt::Debug for Cache<Renderer>
+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<Renderer> Default for Cache<Renderer>
+where
+ Renderer: self::Renderer,
+{
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+enum State<Geometry>
+where
+ Geometry: self::Geometry,
+{
+ Empty,
+ Filled {
+ bounds: Size,
+ geometry: Geometry::Cache,
+ },
+}
+
+impl<T> Geometry for Primitive<T> {
+ type Cache = Arc<Self>;
+
+ fn load(cache: &Arc<Self>) -> Self {
+ Self::Cache {
+ content: cache.clone(),
+ }
+ }
- /// Draws the given layers of [`Self::Geometry`].
- fn draw(&mut self, layers: Vec<Self::Geometry>);
+ fn cache(self) -> Arc<Self> {
+ 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<B: Backend> {
default_font: Font,
default_text_size: Pixels,
primitives: Vec<Primitive<B::Primitive>>,
+ stack: Vec<Vec<Primitive<B::Primitive>>>,
}
impl<B: Backend> Renderer<B> {
@@ -34,6 +36,7 @@ impl<B: Backend> Renderer<B> {
default_font,
default_text_size,
primitives: Vec::new(),
+ stack: Vec::new(),
}
}
@@ -56,59 +59,45 @@ impl<B: Backend> Renderer<B> {
f(&mut self.backend, &self.primitives)
}
- /// Starts recording a new layer.
- pub fn start_layer(&mut self) -> Vec<Primitive<B::Primitive>> {
- std::mem::take(&mut self.primitives)
- }
-
- /// Ends the recording of a layer.
- 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));
- }
-
- /// Starts recording a translation.
- pub fn start_transformation(&mut self) -> Vec<Primitive<B::Primitive>> {
- std::mem::take(&mut self.primitives)
- }
-
- /// Ends the recording of a translation.
- pub fn end_transformation(
+ #[cfg(feature = "geometry")]
+ pub fn draw_geometry<Geometry>(
&mut self,
- primitives: Vec<Primitive<B::Primitive>>,
- transformation: Transformation,
- ) {
- let layer = std::mem::replace(&mut self.primitives, primitives);
-
- self.primitives
- .push(Primitive::group(layer).transform(transformation));
+ layers: impl IntoIterator<Item = Geometry>,
+ ) where
+ Geometry: Into<Primitive<B::Primitive>>,
+ {
+ for layer in layers {
+ self.draw_primitive(layer.into());
+ }
}
}
impl<B: Backend> iced_core::Renderer for Renderer<B> {
- 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<B: Backend> mesh::Renderer for Renderer<B> {
+ 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<B> crate::geometry::Renderer for Renderer<B>
+where
+ B: Backend + crate::geometry::Backend,
+ B::Frame: crate::geometry::Frame<Geometry = Primitive<B::Primitive>>,
+{
+ type Frame = B::Frame;
+ type Geometry = Primitive<B::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);
+ }
+}
diff --git a/renderer/src/compositor.rs b/renderer/src/compositor.rs
index 3d0b3ad0..8b137891 100644
--- a/renderer/src/compositor.rs
+++ b/renderer/src/compositor.rs
@@ -1,300 +1 @@
-use crate::core::Color;
-use crate::graphics::compositor::{Information, SurfaceError, Window};
-use crate::graphics::{Error, Viewport};
-use crate::{Renderer, Settings};
-use std::env;
-use std::future::Future;
-
-pub enum Compositor {
- TinySkia(iced_tiny_skia::window::Compositor),
- #[cfg(feature = "wgpu")]
- Wgpu(iced_wgpu::window::Compositor),
- #[cfg(feature = "custom")]
- Custom(Box<dyn crate::custom::Compositor>),
-}
-
-pub enum Surface {
- TinySkia(iced_tiny_skia::window::Surface),
- #[cfg(feature = "wgpu")]
- Wgpu(iced_wgpu::window::Surface<'static>),
- #[cfg(feature = "custom")]
- Custom(Box<dyn crate::custom::Surface>),
-}
-
-impl crate::graphics::Compositor for Compositor {
- type Settings = Settings;
- type Renderer = Renderer;
- type Surface = Surface;
-
- fn new<W: Window + Clone>(
- settings: Self::Settings,
- compatible_window: W,
- ) -> impl Future<Output = Result<Self, Error>> {
- let candidates =
- Candidate::list_from_env().unwrap_or(Candidate::default_list());
-
- async move {
- let mut error = Error::GraphicsAdapterNotFound;
-
- for candidate in candidates {
- match candidate.build(settings, compatible_window.clone()).await
- {
- Ok(compositor) => return Ok(compositor),
- Err(new_error) => {
- error = new_error;
- }
- }
- }
-
- Err(error)
- }
- }
-
- fn create_renderer(&self) -> Self::Renderer {
- match self {
- Compositor::TinySkia(compositor) => {
- Renderer::TinySkia(compositor.create_renderer())
- }
- #[cfg(feature = "wgpu")]
- Compositor::Wgpu(compositor) => {
- Renderer::Wgpu(compositor.create_renderer())
- }
- #[cfg(feature = "custom")]
- Compositor::Custom(compositor) => {
- Renderer::Custom(compositor.create_renderer())
- }
- }
- }
-
- fn create_surface<W: Window + Clone>(
- &mut self,
- window: W,
- width: u32,
- height: u32,
- ) -> Surface {
- match self {
- Self::TinySkia(compositor) => Surface::TinySkia(
- compositor.create_surface(window, width, height),
- ),
- #[cfg(feature = "wgpu")]
- Self::Wgpu(compositor) => {
- Surface::Wgpu(compositor.create_surface(window, width, height))
- }
- #[cfg(feature = "custom")]
- Self::Custom(compositor) => Surface::Custom(
- compositor.create_surface(Box::new(window), width, height),
- ),
- }
- }
-
- fn configure_surface(
- &mut self,
- surface: &mut Surface,
- width: u32,
- height: u32,
- ) {
- match (self, surface) {
- (Self::TinySkia(compositor), Surface::TinySkia(surface)) => {
- compositor.configure_surface(surface, width, height);
- }
- #[cfg(feature = "wgpu")]
- (Self::Wgpu(compositor), Surface::Wgpu(surface)) => {
- compositor.configure_surface(surface, width, height);
- }
- #[cfg(feature = "custom")]
- (Self::Custom(compositor), Surface::Custom(surface)) => {
- compositor.configure_surface(surface.as_mut(), width, height);
- }
- #[allow(unreachable_patterns)]
- _ => panic!(
- "The provided surface is not compatible with the compositor."
- ),
- }
- }
-
- fn fetch_information(&self) -> Information {
- match self {
- Self::TinySkia(compositor) => compositor.fetch_information(),
- #[cfg(feature = "wgpu")]
- Self::Wgpu(compositor) => compositor.fetch_information(),
- #[cfg(feature = "custom")]
- Self::Custom(compositor) => compositor.fetch_information(),
- }
- }
-
- fn present<T: AsRef<str>>(
- &mut self,
- renderer: &mut Self::Renderer,
- surface: &mut Self::Surface,
- viewport: &Viewport,
- background_color: Color,
- overlay: &[T],
- ) -> Result<(), SurfaceError> {
- 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::Renderer::Wgpu(renderer),
- Surface::Wgpu(surface),
- ) => renderer.with_primitives(|backend, primitives| {
- iced_wgpu::window::compositor::present(
- compositor,
- backend,
- surface,
- primitives,
- viewport,
- background_color,
- overlay,
- )
- }),
-
- #[cfg(feature = "custom")]
- (
- Self::Custom(compositor),
- crate::Renderer::Custom(renderer),
- Surface::Custom(surface),
- ) => renderer.present(
- surface.as_mut(),
- viewport,
- background_color,
- compositor.as_mut(),
- ),
- #[allow(unreachable_patterns)]
- _ => panic!(
- "The provided renderer or surface are not compatible \
- with the compositor."
- ),
- }
- }
-
- fn screenshot<T: AsRef<str>>(
- &mut self,
- renderer: &mut Self::Renderer,
- surface: &mut Self::Surface,
- viewport: &Viewport,
- background_color: Color,
- overlay: &[T],
- ) -> Vec<u8> {
- 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),
- Renderer::Wgpu(renderer),
- Surface::Wgpu(_),
- ) => renderer.with_primitives(|backend, primitives| {
- iced_wgpu::window::compositor::screenshot(
- compositor,
- backend,
- primitives,
- viewport,
- background_color,
- overlay,
- )
- }),
- #[allow(unreachable_patterns)]
- _ => panic!(
- "The provided renderer or backend are not compatible \
- with the compositor."
- ),
- }
- }
-}
-
-enum Candidate {
- Wgpu,
- TinySkia,
-}
-
-impl Candidate {
- fn default_list() -> Vec<Self> {
- vec![
- #[cfg(feature = "wgpu")]
- Self::Wgpu,
- Self::TinySkia,
- ]
- }
-
- fn list_from_env() -> Option<Vec<Self>> {
- let backends = env::var("ICED_BACKEND").ok()?;
-
- Some(
- backends
- .split(',')
- .map(str::trim)
- .map(|backend| match backend {
- "wgpu" => Self::Wgpu,
- "tiny-skia" => Self::TinySkia,
- _ => panic!("unknown backend value: \"{backend}\""),
- })
- .collect(),
- )
- }
-
- async fn build<W: Window>(
- self,
- settings: Settings,
- _compatible_window: W,
- ) -> Result<Compositor, Error> {
- match self {
- Self::TinySkia => {
- let compositor = iced_tiny_skia::window::compositor::new(
- iced_tiny_skia::Settings {
- default_font: settings.default_font,
- default_text_size: settings.default_text_size,
- },
- _compatible_window,
- );
-
- Ok(Compositor::TinySkia(compositor))
- }
- #[cfg(feature = "wgpu")]
- Self::Wgpu => {
- let compositor = iced_wgpu::window::compositor::new(
- iced_wgpu::Settings {
- default_font: settings.default_font,
- default_text_size: settings.default_text_size,
- antialiasing: settings.antialiasing,
- ..iced_wgpu::Settings::from_env()
- },
- _compatible_window,
- )
- .await?;
-
- Ok(Compositor::Wgpu(compositor))
- }
- #[cfg(not(feature = "wgpu"))]
- Self::Wgpu => {
- panic!("`wgpu` feature was not enabled in `iced_renderer`")
- }
- }
- }
-}
diff --git a/renderer/src/custom.rs b/renderer/src/custom.rs
index 04090ccb..4addeb86 100644
--- a/renderer/src/custom.rs
+++ b/renderer/src/custom.rs
@@ -94,8 +94,6 @@ pub trait Renderer {
#[cfg(feature = "geometry")]
pub trait Frame: std::any::Any {
- fn new(&self, size: Size) -> Box<dyn Frame>;
-
fn width(&self) -> f32;
fn height(&self) -> f32;
@@ -108,7 +106,7 @@ pub trait Frame: std::any::Any {
fn fill_rectangle(&mut self, top_left: Point, size: Size, fill: Fill);
- fn stroke<'a>(&mut self, path: &Path, stroke: Stroke<'a>);
+ fn stroke(&mut self, path: &Path, stroke: Stroke<'_>);
fn fill_text(&mut self, text: geometry::Text);
diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs
new file mode 100644
index 00000000..a4c725c0
--- /dev/null
+++ b/renderer/src/fallback.rs
@@ -0,0 +1,562 @@
+use crate::core::image;
+use crate::core::renderer;
+use crate::core::svg;
+use crate::core::{
+ self, Background, Color, Point, Rectangle, Size, Transformation,
+};
+use crate::graphics;
+use crate::graphics::compositor;
+use crate::graphics::mesh;
+
+pub enum Renderer<L, R>
+where
+ L: core::Renderer,
+ R: core::Renderer,
+{
+ Left(L),
+ Right(R),
+}
+
+macro_rules! delegate {
+ ($renderer:expr, $name:ident, $body:expr) => {
+ match $renderer {
+ Self::Left($name) => $body,
+ Self::Right($name) => $body,
+ }
+ };
+}
+
+impl<L, R> Renderer<L, R>
+where
+ L: core::Renderer,
+ R: core::Renderer,
+{
+ #[cfg(feature = "geometry")]
+ pub fn draw_geometry<Geometry>(
+ &mut self,
+ layers: impl IntoIterator<Item = Geometry>,
+ ) where
+ L: graphics::geometry::Renderer,
+ R: graphics::geometry::Renderer,
+
+ Geometry: Into<geometry::Geometry<L::Geometry, R::Geometry>>,
+ {
+ use graphics::geometry::Renderer;
+
+ for layer in layers {
+ <Self as Renderer>::draw_geometry(self, layer.into());
+ }
+ }
+}
+
+impl<L, R> core::Renderer for Renderer<L, R>
+where
+ L: core::Renderer,
+ R: core::Renderer,
+{
+ fn fill_quad(
+ &mut self,
+ quad: renderer::Quad,
+ background: impl Into<Background>,
+ ) {
+ delegate!(self, renderer, renderer.fill_quad(quad, background.into()));
+ }
+
+ fn clear(&mut self) {
+ delegate!(self, renderer, renderer.clear());
+ }
+
+ fn start_layer(&mut self) {
+ delegate!(self, renderer, renderer.start_layer());
+ }
+
+ fn end_layer(&mut self, bounds: Rectangle) {
+ delegate!(self, renderer, renderer.end_layer(bounds));
+ }
+
+ fn start_transformation(&mut self) {
+ delegate!(self, renderer, renderer.start_transformation());
+ }
+
+ fn end_transformation(&mut self, transformation: Transformation) {
+ delegate!(self, renderer, renderer.end_transformation(transformation));
+ }
+}
+
+impl<L, R> core::text::Renderer for Renderer<L, R>
+where
+ L: core::text::Renderer,
+ R: core::text::Renderer<
+ Font = L::Font,
+ Paragraph = L::Paragraph,
+ Editor = L::Editor,
+ >,
+{
+ type Font = L::Font;
+ type Paragraph = L::Paragraph;
+ type Editor = L::Editor;
+
+ const ICON_FONT: Self::Font = L::ICON_FONT;
+ const CHECKMARK_ICON: char = L::CHECKMARK_ICON;
+ const ARROW_DOWN_ICON: char = L::ARROW_DOWN_ICON;
+
+ fn default_font(&self) -> Self::Font {
+ delegate!(self, renderer, renderer.default_font())
+ }
+
+ fn default_size(&self) -> core::Pixels {
+ delegate!(self, renderer, renderer.default_size())
+ }
+
+ fn load_font(&mut self, font: std::borrow::Cow<'static, [u8]>) {
+ delegate!(self, renderer, renderer.load_font(font));
+ }
+
+ fn fill_paragraph(
+ &mut self,
+ text: &Self::Paragraph,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ delegate!(
+ self,
+ renderer,
+ renderer.fill_paragraph(text, position, color, clip_bounds)
+ );
+ }
+
+ fn fill_editor(
+ &mut self,
+ editor: &Self::Editor,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ delegate!(
+ self,
+ renderer,
+ renderer.fill_editor(editor, position, color, clip_bounds)
+ );
+ }
+
+ fn fill_text(
+ &mut self,
+ text: core::Text<'_, Self::Font>,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ delegate!(
+ self,
+ renderer,
+ renderer.fill_text(text, position, color, clip_bounds)
+ );
+ }
+}
+
+impl<L, R> image::Renderer for Renderer<L, R>
+where
+ L: image::Renderer,
+ R: image::Renderer<Handle = L::Handle>,
+{
+ type Handle = L::Handle;
+
+ fn measure_image(&self, handle: &Self::Handle) -> Size<u32> {
+ delegate!(self, renderer, renderer.measure_image(handle))
+ }
+
+ fn draw_image(
+ &mut self,
+ handle: Self::Handle,
+ filter_method: image::FilterMethod,
+ bounds: Rectangle,
+ ) {
+ delegate!(
+ self,
+ renderer,
+ renderer.draw_image(handle, filter_method, bounds)
+ );
+ }
+}
+
+impl<L, R> svg::Renderer for Renderer<L, R>
+where
+ L: svg::Renderer,
+ R: svg::Renderer,
+{
+ fn measure_svg(&self, handle: &svg::Handle) -> Size<u32> {
+ delegate!(self, renderer, renderer.measure_svg(handle))
+ }
+
+ fn draw_svg(
+ &mut self,
+ handle: svg::Handle,
+ color: Option<Color>,
+ bounds: Rectangle,
+ ) {
+ delegate!(self, renderer, renderer.draw_svg(handle, color, bounds));
+ }
+}
+
+impl<L, R> mesh::Renderer for Renderer<L, R>
+where
+ L: mesh::Renderer,
+ R: mesh::Renderer,
+{
+ fn draw_mesh(&mut self, mesh: graphics::Mesh) {
+ delegate!(self, renderer, renderer.draw_mesh(mesh));
+ }
+}
+
+pub enum Compositor<L, R>
+where
+ L: graphics::Compositor,
+ R: graphics::Compositor,
+{
+ Left(L),
+ Right(R),
+}
+
+pub enum Surface<L, R> {
+ Left(L),
+ Right(R),
+}
+
+impl<L, R> graphics::Compositor for Compositor<L, R>
+where
+ L: graphics::Compositor,
+ R: graphics::Compositor,
+ L::Settings: From<crate::Settings>,
+ R::Settings: From<crate::Settings>,
+{
+ type Settings = crate::Settings;
+ type Renderer = Renderer<L::Renderer, R::Renderer>;
+ type Surface = Surface<L::Surface, R::Surface>;
+
+ async fn new<W: compositor::Window + Clone>(
+ settings: Self::Settings,
+ compatible_window: W,
+ ) -> Result<Self, graphics::Error> {
+ if let Ok(left) = L::new(settings.into(), compatible_window.clone())
+ .await
+ .map(Self::Left)
+ {
+ return Ok(left);
+ }
+
+ R::new(settings.into(), compatible_window)
+ .await
+ .map(Self::Right)
+ }
+
+ fn create_renderer(&self) -> Self::Renderer {
+ match self {
+ Self::Left(compositor) => {
+ Renderer::Left(compositor.create_renderer())
+ }
+ Self::Right(compositor) => {
+ Renderer::Right(compositor.create_renderer())
+ }
+ }
+ }
+
+ fn create_surface<W: compositor::Window + Clone>(
+ &mut self,
+ window: W,
+ width: u32,
+ height: u32,
+ ) -> Self::Surface {
+ match self {
+ Self::Left(compositor) => {
+ Surface::Left(compositor.create_surface(window, width, height))
+ }
+ Self::Right(compositor) => {
+ Surface::Right(compositor.create_surface(window, width, height))
+ }
+ }
+ }
+
+ fn configure_surface(
+ &mut self,
+ surface: &mut Self::Surface,
+ width: u32,
+ height: u32,
+ ) {
+ match (self, surface) {
+ (Self::Left(compositor), Surface::Left(surface)) => {
+ compositor.configure_surface(surface, width, height);
+ }
+ (Self::Right(compositor), Surface::Right(surface)) => {
+ compositor.configure_surface(surface, width, height);
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ fn fetch_information(&self) -> compositor::Information {
+ delegate!(self, compositor, compositor.fetch_information())
+ }
+
+ fn present<T: AsRef<str>>(
+ &mut self,
+ renderer: &mut Self::Renderer,
+ surface: &mut Self::Surface,
+ viewport: &graphics::Viewport,
+ background_color: Color,
+ overlay: &[T],
+ ) -> Result<(), compositor::SurfaceError> {
+ match (self, renderer, surface) {
+ (
+ Self::Left(compositor),
+ Renderer::Left(renderer),
+ Surface::Left(surface),
+ ) => compositor.present(
+ renderer,
+ surface,
+ viewport,
+ background_color,
+ overlay,
+ ),
+ (
+ Self::Right(compositor),
+ Renderer::Right(renderer),
+ Surface::Right(surface),
+ ) => compositor.present(
+ renderer,
+ surface,
+ viewport,
+ background_color,
+ overlay,
+ ),
+ _ => unreachable!(),
+ }
+ }
+
+ fn screenshot<T: AsRef<str>>(
+ &mut self,
+ renderer: &mut Self::Renderer,
+ surface: &mut Self::Surface,
+ viewport: &graphics::Viewport,
+ background_color: Color,
+ overlay: &[T],
+ ) -> Vec<u8> {
+ match (self, renderer, surface) {
+ (
+ Self::Left(compositor),
+ Renderer::Left(renderer),
+ Surface::Left(surface),
+ ) => compositor.screenshot(
+ renderer,
+ surface,
+ viewport,
+ background_color,
+ overlay,
+ ),
+ (
+ Self::Right(compositor),
+ Renderer::Right(renderer),
+ Surface::Right(surface),
+ ) => compositor.screenshot(
+ renderer,
+ surface,
+ viewport,
+ background_color,
+ overlay,
+ ),
+ _ => unreachable!(),
+ }
+ }
+}
+
+#[cfg(feature = "wgpu")]
+impl<L, R> iced_wgpu::primitive::pipeline::Renderer for Renderer<L, R>
+where
+ L: iced_wgpu::primitive::pipeline::Renderer,
+ R: core::Renderer,
+{
+ fn draw_pipeline_primitive(
+ &mut self,
+ bounds: Rectangle,
+ primitive: impl iced_wgpu::primitive::pipeline::Primitive,
+ ) {
+ match self {
+ Self::Left(renderer) => {
+ renderer.draw_pipeline_primitive(bounds, primitive);
+ }
+ Self::Right(_) => {
+ log::warn!(
+ "Custom shader primitive is not supported with this renderer."
+ );
+ }
+ }
+ }
+}
+
+#[cfg(feature = "geometry")]
+mod geometry {
+ use super::Renderer;
+ use crate::core::{Point, Radians, Size, Vector};
+ use crate::graphics::geometry::{self, Fill, Path, Stroke, Text};
+
+ impl<L, R> geometry::Renderer for Renderer<L, R>
+ where
+ L: geometry::Renderer,
+ R: geometry::Renderer,
+ {
+ type Geometry = Geometry<L::Geometry, R::Geometry>;
+ type Frame = Frame<L::Frame, R::Frame>;
+
+ fn new_frame(&self, size: iced_graphics::core::Size) -> Self::Frame {
+ match self {
+ Self::Left(renderer) => Frame::Left(renderer.new_frame(size)),
+ Self::Right(renderer) => Frame::Right(renderer.new_frame(size)),
+ }
+ }
+
+ fn draw_geometry(&mut self, geometry: Self::Geometry) {
+ match (self, geometry) {
+ (Self::Left(renderer), Geometry::Left(geometry)) => {
+ renderer.draw_geometry(geometry);
+ }
+ (Self::Right(renderer), Geometry::Right(geometry)) => {
+ renderer.draw_geometry(geometry);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub enum Geometry<L, R> {
+ Left(L),
+ Right(R),
+ }
+
+ impl<L, R> geometry::Geometry for Geometry<L, R>
+ where
+ L: geometry::Geometry,
+ R: geometry::Geometry,
+ {
+ type Cache = Geometry<L::Cache, R::Cache>;
+
+ fn load(cache: &Self::Cache) -> Self {
+ match cache {
+ Geometry::Left(cache) => Self::Left(L::load(cache)),
+ Geometry::Right(cache) => Self::Right(R::load(cache)),
+ }
+ }
+
+ fn cache(self) -> Self::Cache {
+ match self {
+ Self::Left(geometry) => Geometry::Left(geometry.cache()),
+ Self::Right(geometry) => Geometry::Right(geometry.cache()),
+ }
+ }
+ }
+
+ pub enum Frame<L, R> {
+ Left(L),
+ Right(R),
+ }
+
+ impl<L, R> geometry::Frame for Frame<L, R>
+ where
+ L: geometry::Frame,
+ R: geometry::Frame,
+ {
+ type Geometry = Geometry<L::Geometry, R::Geometry>;
+
+ fn width(&self) -> f32 {
+ delegate!(self, frame, frame.width())
+ }
+
+ fn height(&self) -> f32 {
+ delegate!(self, frame, frame.height())
+ }
+
+ fn size(&self) -> Size {
+ delegate!(self, frame, frame.size())
+ }
+
+ fn center(&self) -> Point {
+ delegate!(self, frame, frame.center())
+ }
+
+ fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
+ delegate!(self, frame, frame.fill(path, fill));
+ }
+
+ fn fill_rectangle(
+ &mut self,
+ top_left: Point,
+ size: Size,
+ fill: impl Into<Fill>,
+ ) {
+ delegate!(self, frame, frame.fill_rectangle(top_left, size, fill));
+ }
+
+ fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
+ delegate!(self, frame, frame.stroke(path, stroke));
+ }
+
+ fn fill_text(&mut self, text: impl Into<Text>) {
+ delegate!(self, frame, frame.fill_text(text));
+ }
+
+ fn push_transform(&mut self) {
+ delegate!(self, frame, frame.push_transform());
+ }
+
+ fn pop_transform(&mut self) {
+ delegate!(self, frame, frame.pop_transform());
+ }
+
+ fn draft(&mut self, size: Size) -> Self {
+ match self {
+ Self::Left(frame) => Self::Left(frame.draft(size)),
+ Self::Right(frame) => Self::Right(frame.draft(size)),
+ }
+ }
+
+ fn paste(&mut self, frame: Self, at: Point) {
+ match (self, frame) {
+ (Self::Left(target), Self::Left(source)) => {
+ target.paste(source, at);
+ }
+ (Self::Right(target), Self::Right(source)) => {
+ target.paste(source, at);
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ fn translate(&mut self, translation: Vector) {
+ delegate!(self, frame, frame.translate(translation));
+ }
+
+ fn rotate(&mut self, angle: impl Into<Radians>) {
+ delegate!(self, frame, frame.rotate(angle));
+ }
+
+ fn scale(&mut self, scale: impl Into<f32>) {
+ delegate!(self, frame, frame.scale(scale));
+ }
+
+ fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
+ delegate!(self, frame, frame.scale_nonuniform(scale));
+ }
+ }
+
+ impl<L, R> From<Frame<L, R>> for Geometry<L::Geometry, R::Geometry>
+ where
+ L: geometry::Frame,
+ R: geometry::Frame,
+ {
+ fn from(frame: Frame<L, R>) -> Self {
+ match frame {
+ Frame::Left(frame) => Self::Left(frame.into()),
+ Frame::Right(frame) => Self::Right(frame.into()),
+ }
+ }
+ }
+}
diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs
index 67096115..f8aa1157 100644
--- a/renderer/src/lib.rs
+++ b/renderer/src/lib.rs
@@ -4,364 +4,42 @@
#[cfg(feature = "wgpu")]
pub use iced_wgpu as wgpu;
-pub mod compositor;
-pub mod custom;
-
-#[cfg(feature = "geometry")]
-pub mod geometry;
+pub mod fallback;
mod settings;
pub use iced_graphics as graphics;
pub use iced_graphics::core;
-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, Color, Font, Pixels, Point, Rectangle, Transformation,
-};
-use crate::graphics::text::Editor;
-use crate::graphics::text::Paragraph;
-use crate::graphics::Mesh;
+pub use iced_graphics::geometry;
-use std::borrow::Cow;
+pub use settings::Settings;
/// The default graphics renderer for [`iced`].
///
/// [`iced`]: https://github.com/iced-rs/iced
-pub enum Renderer {
- TinySkia(iced_tiny_skia::Renderer),
- #[cfg(feature = "wgpu")]
- Wgpu(iced_wgpu::Renderer),
- #[cfg(feature = "custom")]
- Custom(Box<dyn custom::Renderer>),
-}
-
-macro_rules! delegate {
- ($renderer:expr, $name:ident, $body:expr) => {
- match $renderer {
- Self::TinySkia($name) => $body,
- #[cfg(feature = "wgpu")]
- Self::Wgpu($name) => $body,
- #[cfg(feature = "custom")]
- Self::Custom($name) => $body,
- }
- };
-}
-
-impl Renderer {
- pub fn draw_mesh(&mut self, mesh: Mesh) {
- match self {
- Self::TinySkia(_) => {
- log::warn!("Unsupported mesh primitive: {mesh:?}");
- }
- #[cfg(feature = "wgpu")]
- Self::Wgpu(renderer) => {
- renderer.draw_primitive(iced_wgpu::Primitive::Custom(
- iced_wgpu::primitive::Custom::Mesh(mesh),
- ));
- }
- #[cfg(feature = "custom")]
- Self::Custom(renderer) => {
- renderer.draw_mesh(mesh);
- }
- }
- }
-}
-
-impl core::Renderer for Renderer {
- 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!(),
- }
- }
- #[cfg(feature = "custom")]
- Self::Custom(renderer) => {
- renderer.start_layer();
-
- f(self);
-
- match self {
- Self::Custom(renderer) => {
- renderer.end_layer(bounds);
- }
- _ => unreachable!(),
- }
- }
- }
- }
-
- fn with_transformation(
- &mut self,
- transformation: Transformation,
- f: impl FnOnce(&mut Self),
- ) {
- match self {
- Self::TinySkia(renderer) => {
- let primitives = renderer.start_transformation();
-
- f(self);
-
- match self {
- Self::TinySkia(renderer) => {
- renderer.end_transformation(primitives, transformation);
- }
- #[cfg(feature = "wgpu")]
- _ => unreachable!(),
- }
- }
- #[cfg(feature = "wgpu")]
- Self::Wgpu(renderer) => {
- let primitives = renderer.start_transformation();
-
- f(self);
-
- match self {
- #[cfg(feature = "wgpu")]
- Self::Wgpu(renderer) => {
- renderer.end_transformation(primitives, transformation);
- }
- _ => unreachable!(),
- }
- }
- #[cfg(feature = "custom")]
- Self::Custom(renderer) => {
- renderer.start_transformation();
-
- f(self);
-
- match self {
- Self::Custom(renderer) => {
- renderer.end_transformation(transformation);
- }
- _ => unreachable!(),
- }
- }
- }
- }
-
- fn fill_quad(
- &mut self,
- quad: renderer::Quad,
- background: impl Into<Background>,
- ) {
- delegate!(self, renderer, renderer.fill_quad(quad, background.into()));
- }
-
- fn clear(&mut self) {
- delegate!(self, renderer, renderer.clear());
- }
-}
+#[cfg(not(feature = "wgpu"))]
+pub type Renderer = iced_tiny_skia::Renderer;
-impl text::Renderer for Renderer {
- type Font = Font;
- type Paragraph = Paragraph;
- type Editor = Editor;
-
- const ICON_FONT: Font = iced_tiny_skia::Renderer::ICON_FONT;
- const CHECKMARK_ICON: char = iced_tiny_skia::Renderer::CHECKMARK_ICON;
- const ARROW_DOWN_ICON: char = iced_tiny_skia::Renderer::ARROW_DOWN_ICON;
-
- fn default_font(&self) -> Self::Font {
- delegate!(self, renderer, renderer.default_font())
- }
-
- fn default_size(&self) -> Pixels {
- delegate!(self, renderer, renderer.default_size())
- }
-
- fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
- delegate!(self, renderer, renderer.load_font(bytes));
- }
-
- fn fill_paragraph(
- &mut self,
- paragraph: &Self::Paragraph,
- position: Point,
- color: Color,
- clip_bounds: Rectangle,
- ) {
- delegate!(
- self,
- renderer,
- renderer.fill_paragraph(paragraph, position, color, clip_bounds)
- );
- }
-
- fn fill_editor(
- &mut self,
- editor: &Self::Editor,
- position: Point,
- color: Color,
- clip_bounds: Rectangle,
- ) {
- delegate!(
- self,
- renderer,
- renderer.fill_editor(editor, position, color, clip_bounds)
- );
- }
-
- fn fill_text(
- &mut self,
- text: Text<'_, Self::Font>,
- position: Point,
- color: Color,
- clip_bounds: Rectangle,
- ) {
- delegate!(
- self,
- renderer,
- renderer.fill_text(text, position, color, clip_bounds)
- );
- }
-}
-
-#[cfg(feature = "image")]
-impl crate::core::image::Renderer for Renderer {
- type Handle = crate::core::image::Handle;
-
- fn measure_image(
- &self,
- handle: &crate::core::image::Handle,
- ) -> core::Size<u32> {
- delegate!(self, renderer, renderer.measure_image(handle))
- }
-
- fn draw_image(
- &mut self,
- handle: crate::core::image::Handle,
- filter_method: crate::core::image::FilterMethod,
- bounds: Rectangle,
- ) {
- delegate!(
- self,
- renderer,
- renderer.draw_image(handle, filter_method, bounds)
- );
- }
-}
-
-#[cfg(feature = "svg")]
-impl crate::core::svg::Renderer for Renderer {
- fn measure_svg(
- &self,
- handle: &crate::core::svg::Handle,
- ) -> core::Size<u32> {
- delegate!(self, renderer, renderer.measure_svg(handle))
- }
-
- fn draw_svg(
- &mut self,
- handle: crate::core::svg::Handle,
- color: Option<crate::core::Color>,
- bounds: Rectangle,
- ) {
- delegate!(self, renderer, renderer.draw_svg(handle, color, bounds));
- }
-}
-
-#[cfg(feature = "geometry")]
-impl crate::graphics::geometry::Renderer for Renderer {
- type Geometry = crate::Geometry;
+/// The default graphics renderer for [`iced`].
+///
+/// [`iced`]: https://github.com/iced-rs/iced
+#[cfg(feature = "wgpu")]
+pub type Renderer =
+ fallback::Renderer<iced_wgpu::Renderer, iced_tiny_skia::Renderer>;
- 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);
- }
- #[cfg(feature = "wgpu")]
- crate::Geometry::Wgpu(_) => unreachable!(),
- #[cfg(feature = "custom")]
- crate::Geometry::Custom(_) => unreachable!(),
- }
- }
- }
- #[cfg(feature = "wgpu")]
- Self::Wgpu(renderer) => {
- for layer in layers {
- match layer {
- crate::Geometry::Wgpu(primitive) => {
- renderer.draw_primitive(primitive);
- }
- crate::Geometry::TinySkia(_) => unreachable!(),
- #[cfg(feature = "custom")]
- crate::Geometry::Custom(_) => unreachable!(),
- }
- }
- }
- #[cfg(feature = "custom")]
- Self::Custom(renderer) => {
- for layer in layers {
- match layer {
- crate::Geometry::Custom(geometry) => {
- renderer.draw_geometry(geometry);
- }
- crate::Geometry::TinySkia(_) => unreachable!(),
- #[cfg(feature = "wgpu")]
- crate::Geometry::Wgpu(_) => unreachable!(),
- }
- }
- }
- }
- }
-}
+/// The default graphics compositor for [`iced`].
+///
+/// [`iced`]: https://github.com/iced-rs/iced
+#[cfg(not(feature = "wgpu"))]
+pub type Compositor = iced_tiny_skia::window::Compositor;
+/// The default graphics renderer for [`iced`].
+///
+/// [`iced`]: https://github.com/iced-rs/iced
#[cfg(feature = "wgpu")]
-impl iced_wgpu::primitive::pipeline::Renderer for Renderer {
- fn draw_pipeline_primitive(
- &mut self,
- bounds: Rectangle,
- primitive: impl wgpu::primitive::pipeline::Primitive,
- ) {
- match self {
- Self::TinySkia(_renderer) => {
- log::warn!(
- "Custom shader primitive is unavailable with tiny-skia."
- );
- }
- Self::Wgpu(renderer) => {
- renderer.draw_pipeline_primitive(bounds, primitive);
- }
- #[cfg(feature = "custom")]
- Self::Custom(_renderer) => {
- log::warn!(
- "Custom shader primitive is unavailable with custom renderer."
- );
- }
- }
- }
-}
+pub type Compositor = fallback::Compositor<
+ iced_wgpu::window::Compositor,
+ iced_tiny_skia::window::Compositor,
+>;
diff --git a/renderer/src/settings.rs b/renderer/src/settings.rs
index 432eb8a0..940daa15 100644
--- a/renderer/src/settings.rs
+++ b/renderer/src/settings.rs
@@ -27,3 +27,24 @@ impl Default for Settings {
}
}
}
+
+impl From<Settings> for iced_tiny_skia::Settings {
+ fn from(settings: Settings) -> Self {
+ Self {
+ default_font: settings.default_font,
+ default_text_size: settings.default_text_size,
+ }
+ }
+}
+
+#[cfg(feature = "wgpu")]
+impl From<Settings> for iced_wgpu::Settings {
+ fn from(settings: Settings) -> Self {
+ Self {
+ default_font: settings.default_font,
+ default_text_size: settings.default_text_size,
+ antialiasing: settings.antialiasing,
+ ..iced_wgpu::Settings::default()
+ }
+ }
+}
diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs
index b6487b38..f6bb1c86 100644
--- a/tiny_skia/src/backend.rs
+++ b/tiny_skia/src/backend.rs
@@ -1018,3 +1018,12 @@ impl backend::Svg for Backend {
self.vector_pipeline.viewport_dimensions(handle)
}
}
+
+#[cfg(feature = "geometry")]
+impl crate::graphics::geometry::Backend for Backend {
+ type Frame = crate::geometry::Frame;
+
+ fn new_frame(&self, size: Size) -> Self::Frame {
+ crate::geometry::Frame::new(size)
+ }
+}
diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs
index 16787f89..6b1888d0 100644
--- a/tiny_skia/src/geometry.rs
+++ b/tiny_skia/src/geometry.rs
@@ -4,7 +4,7 @@ use crate::core::{
};
use crate::graphics::geometry::fill::{self, Fill};
use crate::graphics::geometry::stroke::{self, Stroke};
-use crate::graphics::geometry::{Path, Style, Text};
+use crate::graphics::geometry::{self, Path, Style, Text};
use crate::graphics::Gradient;
use crate::primitive::{self, Primitive};
@@ -25,23 +25,36 @@ impl Frame {
}
}
- pub fn width(&self) -> f32 {
+ pub fn into_primitive(self) -> Primitive {
+ Primitive::Clip {
+ bounds: Rectangle::new(Point::ORIGIN, self.size),
+ content: Box::new(Primitive::Group {
+ primitives: self.primitives,
+ }),
+ }
+ }
+}
+
+impl geometry::Frame for Frame {
+ type Geometry = Primitive;
+
+ fn width(&self) -> f32 {
self.size.width
}
- pub fn height(&self) -> f32 {
+ fn height(&self) -> f32 {
self.size.height
}
- pub fn size(&self) -> Size {
+ fn size(&self) -> Size {
self.size
}
- pub fn center(&self) -> Point {
+ fn center(&self) -> Point {
Point::new(self.size.width / 2.0, self.size.height / 2.0)
}
- pub fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
+ fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
let Some(path) =
convert_path(path).and_then(|path| path.transform(self.transform))
else {
@@ -61,7 +74,7 @@ impl Frame {
}));
}
- pub fn fill_rectangle(
+ fn fill_rectangle(
&mut self,
top_left: Point,
size: Size,
@@ -89,7 +102,7 @@ impl Frame {
}));
}
- pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
+ fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
let Some(path) =
convert_path(path).and_then(|path| path.transform(self.transform))
else {
@@ -110,7 +123,7 @@ impl Frame {
}));
}
- pub fn fill_text(&mut self, text: impl Into<Text>) {
+ fn fill_text(&mut self, text: impl Into<Text>) {
let text = text.into();
let (scale_x, scale_y) = self.transform.get_scale();
@@ -174,51 +187,52 @@ impl Frame {
}
}
- pub fn push_transform(&mut self) {
+ fn push_transform(&mut self) {
self.stack.push(self.transform);
}
- pub fn pop_transform(&mut self) {
+ fn pop_transform(&mut self) {
self.transform = self.stack.pop().expect("Pop transform");
}
- pub fn clip(&mut self, frame: Self, at: Point) {
+ fn draft(&mut self, size: Size) -> Self {
+ Self::new(size)
+ }
+
+ fn paste(&mut self, frame: Self, at: Point) {
self.primitives.push(Primitive::Transform {
transformation: Transformation::translate(at.x, at.y),
content: Box::new(frame.into_primitive()),
});
}
- pub fn translate(&mut self, translation: Vector) {
+ fn translate(&mut self, translation: Vector) {
self.transform =
self.transform.pre_translate(translation.x, translation.y);
}
- pub fn rotate(&mut self, angle: impl Into<Radians>) {
+ fn rotate(&mut self, angle: impl Into<Radians>) {
self.transform = self.transform.pre_concat(
tiny_skia::Transform::from_rotate(angle.into().0.to_degrees()),
);
}
- pub fn scale(&mut self, scale: impl Into<f32>) {
+ fn scale(&mut self, scale: impl Into<f32>) {
let scale = scale.into();
self.scale_nonuniform(Vector { x: scale, y: scale });
}
- pub fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
+ fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
let scale = scale.into();
self.transform = self.transform.pre_scale(scale.x, scale.y);
}
+}
- pub fn into_primitive(self) -> Primitive {
- Primitive::Clip {
- bounds: Rectangle::new(Point::ORIGIN, self.size),
- content: Box::new(Primitive::Group {
- primitives: self.primitives,
- }),
- }
+impl From<Frame> for Primitive {
+ fn from(frame: Frame) -> Self {
+ frame.into_primitive()
}
}
diff --git a/tiny_skia/src/primitive.rs b/tiny_skia/src/primitive.rs
index 7718d542..b7c428e4 100644
--- a/tiny_skia/src/primitive.rs
+++ b/tiny_skia/src/primitive.rs
@@ -1,5 +1,5 @@
use crate::core::Rectangle;
-use crate::graphics::Damage;
+use crate::graphics::{Damage, Mesh};
pub type Primitive = crate::graphics::Primitive<Custom>;
@@ -42,3 +42,11 @@ impl Damage for Custom {
}
}
}
+
+impl TryFrom<Mesh> for Custom {
+ type Error = &'static str;
+
+ fn try_from(_mesh: Mesh) -> Result<Self, Self::Error> {
+ Err("unsupported")
+ }
+}
diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 09ddbe4d..924aacf1 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -397,3 +397,12 @@ impl backend::Svg for Backend {
self.image_pipeline.viewport_dimensions(handle)
}
}
+
+#[cfg(feature = "geometry")]
+impl crate::graphics::geometry::Backend for Backend {
+ type Frame = crate::geometry::Frame;
+
+ fn new_frame(&self, size: Size) -> Self::Frame {
+ crate::geometry::Frame::new(size)
+ }
+}
diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index f4e0fbda..4f6b67b1 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -6,7 +6,7 @@ use crate::core::{
use crate::graphics::color;
use crate::graphics::geometry::fill::{self, Fill};
use crate::graphics::geometry::{
- LineCap, LineDash, LineJoin, Path, Stroke, Style, Text,
+ self, LineCap, LineDash, LineJoin, Path, Stroke, Style, Text,
};
use crate::graphics::gradient::{self, Gradient};
use crate::graphics::mesh::{self, Mesh};
@@ -14,6 +14,7 @@ use crate::primitive::{self, Primitive};
use lyon::geom::euclid;
use lyon::tessellation;
+
use std::borrow::Cow;
/// A frame for drawing some geometry.
@@ -27,191 +28,87 @@ pub struct Frame {
stroke_tessellator: tessellation::StrokeTessellator,
}
-enum Buffer {
- Solid(tessellation::VertexBuffers<mesh::SolidVertex2D, u32>),
- Gradient(tessellation::VertexBuffers<mesh::GradientVertex2D, u32>),
-}
-
-struct BufferStack {
- stack: Vec<Buffer>,
-}
-
-impl BufferStack {
- fn new() -> Self {
- Self { stack: Vec::new() }
- }
-
- fn get_mut(&mut self, style: &Style) -> &mut Buffer {
- match style {
- Style::Solid(_) => match self.stack.last() {
- Some(Buffer::Solid(_)) => {}
- _ => {
- self.stack.push(Buffer::Solid(
- tessellation::VertexBuffers::new(),
- ));
- }
- },
- Style::Gradient(_) => match self.stack.last() {
- Some(Buffer::Gradient(_)) => {}
- _ => {
- self.stack.push(Buffer::Gradient(
- tessellation::VertexBuffers::new(),
- ));
- }
+impl Frame {
+ /// Creates a new [`Frame`] with the given [`Size`].
+ pub fn new(size: Size) -> Frame {
+ Frame {
+ size,
+ buffers: BufferStack::new(),
+ primitives: Vec::new(),
+ transforms: Transforms {
+ previous: Vec::new(),
+ current: Transform(lyon::math::Transform::identity()),
},
+ fill_tessellator: tessellation::FillTessellator::new(),
+ stroke_tessellator: tessellation::StrokeTessellator::new(),
}
-
- self.stack.last_mut().unwrap()
}
- fn get_fill<'a>(
- &'a mut self,
- style: &Style,
- ) -> Box<dyn tessellation::FillGeometryBuilder + 'a> {
- match (style, self.get_mut(style)) {
- (Style::Solid(color), Buffer::Solid(buffer)) => {
- Box::new(tessellation::BuffersBuilder::new(
- buffer,
- TriangleVertex2DBuilder(color::pack(*color)),
- ))
- }
- (Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
- Box::new(tessellation::BuffersBuilder::new(
- buffer,
- GradientVertex2DBuilder {
- gradient: gradient.pack(),
- },
- ))
+ fn into_primitives(mut self) -> Vec<Primitive> {
+ for buffer in self.buffers.stack {
+ match buffer {
+ Buffer::Solid(buffer) => {
+ if !buffer.indices.is_empty() {
+ self.primitives.push(Primitive::Custom(
+ primitive::Custom::Mesh(Mesh::Solid {
+ buffers: mesh::Indexed {
+ vertices: buffer.vertices,
+ indices: buffer.indices,
+ },
+ size: self.size,
+ }),
+ ));
+ }
+ }
+ Buffer::Gradient(buffer) => {
+ if !buffer.indices.is_empty() {
+ self.primitives.push(Primitive::Custom(
+ primitive::Custom::Mesh(Mesh::Gradient {
+ buffers: mesh::Indexed {
+ vertices: buffer.vertices,
+ indices: buffer.indices,
+ },
+ size: self.size,
+ }),
+ ));
+ }
+ }
}
- _ => unreachable!(),
}
- }
- fn get_stroke<'a>(
- &'a mut self,
- style: &Style,
- ) -> Box<dyn tessellation::StrokeGeometryBuilder + 'a> {
- match (style, self.get_mut(style)) {
- (Style::Solid(color), Buffer::Solid(buffer)) => {
- Box::new(tessellation::BuffersBuilder::new(
- buffer,
- TriangleVertex2DBuilder(color::pack(*color)),
- ))
- }
- (Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
- Box::new(tessellation::BuffersBuilder::new(
- buffer,
- GradientVertex2DBuilder {
- gradient: gradient.pack(),
- },
- ))
- }
- _ => unreachable!(),
- }
+ self.primitives
}
}
-#[derive(Debug)]
-struct Transforms {
- previous: Vec<Transform>,
- current: Transform,
-}
-
-#[derive(Debug, Clone, Copy)]
-struct Transform(lyon::math::Transform);
-
-impl Transform {
- fn is_identity(&self) -> bool {
- self.0 == lyon::math::Transform::identity()
- }
-
- fn is_scale_translation(&self) -> bool {
- self.0.m12.abs() < 2.0 * f32::EPSILON
- && self.0.m21.abs() < 2.0 * f32::EPSILON
- }
-
- fn scale(&self) -> (f32, f32) {
- (self.0.m11, self.0.m22)
- }
+impl geometry::Frame for Frame {
+ type Geometry = Primitive;
- fn transform_point(&self, point: Point) -> Point {
- let transformed = self
- .0
- .transform_point(euclid::Point2D::new(point.x, point.y));
-
- Point {
- x: transformed.x,
- y: transformed.y,
- }
- }
-
- fn transform_style(&self, style: Style) -> Style {
- match style {
- Style::Solid(color) => Style::Solid(color),
- Style::Gradient(gradient) => {
- Style::Gradient(self.transform_gradient(gradient))
- }
- }
- }
-
- fn transform_gradient(&self, mut gradient: Gradient) -> Gradient {
- match &mut gradient {
- Gradient::Linear(linear) => {
- linear.start = self.transform_point(linear.start);
- linear.end = self.transform_point(linear.end);
- }
- }
-
- gradient
- }
-}
-
-impl Frame {
/// Creates a new empty [`Frame`] with the given dimensions.
///
/// The default coordinate system of a [`Frame`] has its origin at the
/// top-left corner of its bounds.
- pub fn new(size: Size) -> Frame {
- Frame {
- size,
- buffers: BufferStack::new(),
- primitives: Vec::new(),
- transforms: Transforms {
- previous: Vec::new(),
- current: Transform(lyon::math::Transform::identity()),
- },
- fill_tessellator: tessellation::FillTessellator::new(),
- stroke_tessellator: tessellation::StrokeTessellator::new(),
- }
- }
- /// Returns the width of the [`Frame`].
#[inline]
- pub fn width(&self) -> f32 {
+ fn width(&self) -> f32 {
self.size.width
}
- /// Returns the height of the [`Frame`].
#[inline]
- pub fn height(&self) -> f32 {
+ fn height(&self) -> f32 {
self.size.height
}
- /// Returns the dimensions of the [`Frame`].
#[inline]
- pub fn size(&self) -> Size {
+ fn size(&self) -> Size {
self.size
}
- /// Returns the coordinate of the center of the [`Frame`].
#[inline]
- pub fn center(&self) -> Point {
+ fn center(&self) -> Point {
Point::new(self.size.width / 2.0, self.size.height / 2.0)
}
- /// Draws the given [`Path`] on the [`Frame`] by filling it with the
- /// provided style.
- pub fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
+ fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
let Fill { style, rule } = fill.into();
let mut buffer = self
@@ -239,9 +136,7 @@ impl Frame {
.expect("Tessellate path.");
}
- /// 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(
+ fn fill_rectangle(
&mut self,
top_left: Point,
size: Size,
@@ -276,9 +171,7 @@ impl Frame {
.expect("Fill rectangle");
}
- /// 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<Stroke<'a>>) {
+ fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
let stroke = stroke.into();
let mut buffer = self
@@ -315,20 +208,7 @@ impl Frame {
.expect("Stroke path");
}
- /// 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<Text>) {
+ fn fill_text(&mut self, text: impl Into<Text>) {
let text = text.into();
let (scale_x, scale_y) = self.transforms.current.scale();
@@ -384,57 +264,55 @@ impl Frame {
}
}
- /// 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<R>(&mut self, f: impl FnOnce(&mut Frame) -> R) -> R {
- self.push_transform();
-
- let result = f(self);
-
- self.pop_transform();
-
- result
+ fn translate(&mut self, translation: Vector) {
+ self.transforms.current.0 =
+ self.transforms
+ .current
+ .0
+ .pre_translate(lyon::math::Vector::new(
+ translation.x,
+ translation.y,
+ ));
}
- /// Pushes the current transform in the transform stack.
- pub fn push_transform(&mut self) {
- self.transforms.previous.push(self.transforms.current);
+ #[inline]
+ fn rotate(&mut self, angle: impl Into<Radians>) {
+ self.transforms.current.0 = self
+ .transforms
+ .current
+ .0
+ .pre_rotate(lyon::math::Angle::radians(angle.into().0));
}
- /// Pops a transform from the transform stack and sets it as the current transform.
- pub fn pop_transform(&mut self) {
- self.transforms.current = self.transforms.previous.pop().unwrap();
+ #[inline]
+ fn scale(&mut self, scale: impl Into<f32>) {
+ let scale = scale.into();
+
+ self.scale_nonuniform(Vector { x: scale, y: scale });
}
- /// 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<R>(
- &mut self,
- region: Rectangle,
- f: impl FnOnce(&mut Frame) -> R,
- ) -> R {
- let mut frame = Frame::new(region.size());
+ fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
+ let scale = scale.into();
- let result = f(&mut frame);
+ self.transforms.current.0 =
+ self.transforms.current.0.pre_scale(scale.x, scale.y);
+ }
- let origin = Point::new(region.x, region.y);
+ fn push_transform(&mut self) {
+ self.transforms.previous.push(self.transforms.current);
+ }
- self.clip(frame, origin);
+ fn pop_transform(&mut self) {
+ self.transforms.current = self.transforms.previous.pop().unwrap();
+ }
- result
+ fn draft(&mut self, size: Size) -> Frame {
+ Frame::new(size)
}
- /// Draws the clipped contents of the given [`Frame`] with origin at the given [`Point`].
- pub fn clip(&mut self, frame: Frame, at: Point) {
+ fn paste(&mut self, frame: Frame, at: Point) {
let size = frame.size();
let primitives = frame.into_primitives();
let transformation = Transformation::translate(at.x, at.y);
@@ -461,90 +339,153 @@ impl Frame {
],
});
}
+}
+impl From<Frame> for Primitive {
+ fn from(frame: Frame) -> Self {
+ Self::Group {
+ primitives: frame.into_primitives(),
+ }
+ }
+}
- /// Applies a translation to the current transform of the [`Frame`].
- #[inline]
- pub fn translate(&mut self, translation: Vector) {
- self.transforms.current.0 =
- self.transforms
- .current
- .0
- .pre_translate(lyon::math::Vector::new(
- translation.x,
- translation.y,
- ));
+enum Buffer {
+ Solid(tessellation::VertexBuffers<mesh::SolidVertex2D, u32>),
+ Gradient(tessellation::VertexBuffers<mesh::GradientVertex2D, u32>),
+}
+
+struct BufferStack {
+ stack: Vec<Buffer>,
+}
+
+impl BufferStack {
+ fn new() -> Self {
+ Self { stack: Vec::new() }
}
- /// Applies a rotation in radians to the current transform of the [`Frame`].
- #[inline]
- pub fn rotate(&mut self, angle: impl Into<Radians>) {
- self.transforms.current.0 = self
- .transforms
- .current
- .0
- .pre_rotate(lyon::math::Angle::radians(angle.into().0));
+ fn get_mut(&mut self, style: &Style) -> &mut Buffer {
+ match style {
+ Style::Solid(_) => match self.stack.last() {
+ Some(Buffer::Solid(_)) => {}
+ _ => {
+ self.stack.push(Buffer::Solid(
+ tessellation::VertexBuffers::new(),
+ ));
+ }
+ },
+ Style::Gradient(_) => match self.stack.last() {
+ Some(Buffer::Gradient(_)) => {}
+ _ => {
+ self.stack.push(Buffer::Gradient(
+ tessellation::VertexBuffers::new(),
+ ));
+ }
+ },
+ }
+
+ self.stack.last_mut().unwrap()
}
- /// Applies a uniform scaling to the current transform of the [`Frame`].
- #[inline]
- pub fn scale(&mut self, scale: impl Into<f32>) {
- let scale = scale.into();
+ fn get_fill<'a>(
+ &'a mut self,
+ style: &Style,
+ ) -> Box<dyn tessellation::FillGeometryBuilder + 'a> {
+ match (style, self.get_mut(style)) {
+ (Style::Solid(color), Buffer::Solid(buffer)) => {
+ Box::new(tessellation::BuffersBuilder::new(
+ buffer,
+ TriangleVertex2DBuilder(color::pack(*color)),
+ ))
+ }
+ (Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
+ Box::new(tessellation::BuffersBuilder::new(
+ buffer,
+ GradientVertex2DBuilder {
+ gradient: gradient.pack(),
+ },
+ ))
+ }
+ _ => unreachable!(),
+ }
+ }
- self.scale_nonuniform(Vector { x: scale, y: scale });
+ fn get_stroke<'a>(
+ &'a mut self,
+ style: &Style,
+ ) -> Box<dyn tessellation::StrokeGeometryBuilder + 'a> {
+ match (style, self.get_mut(style)) {
+ (Style::Solid(color), Buffer::Solid(buffer)) => {
+ Box::new(tessellation::BuffersBuilder::new(
+ buffer,
+ TriangleVertex2DBuilder(color::pack(*color)),
+ ))
+ }
+ (Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
+ Box::new(tessellation::BuffersBuilder::new(
+ buffer,
+ GradientVertex2DBuilder {
+ gradient: gradient.pack(),
+ },
+ ))
+ }
+ _ => unreachable!(),
+ }
}
+}
- /// Applies a non-uniform scaling to the current transform of the [`Frame`].
- #[inline]
- pub fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
- let scale = scale.into();
+#[derive(Debug)]
+struct Transforms {
+ previous: Vec<Transform>,
+ current: Transform,
+}
- self.transforms.current.0 =
- self.transforms.current.0.pre_scale(scale.x, scale.y);
+#[derive(Debug, Clone, Copy)]
+struct Transform(lyon::math::Transform);
+
+impl Transform {
+ fn is_identity(&self) -> bool {
+ self.0 == lyon::math::Transform::identity()
}
- /// Produces the [`Primitive`] representing everything drawn on the [`Frame`].
- pub fn into_primitive(self) -> Primitive {
- Primitive::Group {
- primitives: self.into_primitives(),
+ fn is_scale_translation(&self) -> bool {
+ self.0.m12.abs() < 2.0 * f32::EPSILON
+ && self.0.m21.abs() < 2.0 * f32::EPSILON
+ }
+
+ fn scale(&self) -> (f32, f32) {
+ (self.0.m11, self.0.m22)
+ }
+
+ fn transform_point(&self, point: Point) -> Point {
+ let transformed = self
+ .0
+ .transform_point(euclid::Point2D::new(point.x, point.y));
+
+ Point {
+ x: transformed.x,
+ y: transformed.y,
}
}
- fn into_primitives(mut self) -> Vec<Primitive> {
- for buffer in self.buffers.stack {
- match buffer {
- Buffer::Solid(buffer) => {
- if !buffer.indices.is_empty() {
- self.primitives.push(Primitive::Custom(
- primitive::Custom::Mesh(Mesh::Solid {
- buffers: mesh::Indexed {
- vertices: buffer.vertices,
- indices: buffer.indices,
- },
- size: self.size,
- }),
- ));
- }
- }
- Buffer::Gradient(buffer) => {
- if !buffer.indices.is_empty() {
- self.primitives.push(Primitive::Custom(
- primitive::Custom::Mesh(Mesh::Gradient {
- buffers: mesh::Indexed {
- vertices: buffer.vertices,
- indices: buffer.indices,
- },
- size: self.size,
- }),
- ));
- }
- }
+ fn transform_style(&self, style: Style) -> Style {
+ match style {
+ Style::Solid(color) => Style::Solid(color),
+ Style::Gradient(gradient) => {
+ Style::Gradient(self.transform_gradient(gradient))
}
}
+ }
- self.primitives
+ fn transform_gradient(&self, mut gradient: Gradient) -> Gradient {
+ match &mut gradient {
+ Gradient::Linear(linear) => {
+ linear.start = self.transform_point(linear.start);
+ linear.end = self.transform_point(linear.end);
+ }
+ }
+
+ gradient
}
}
-
struct GradientVertex2DBuilder {
gradient: gradient::Packed,
}
diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs
index fff927ea..ee9af93c 100644
--- a/wgpu/src/primitive.rs
+++ b/wgpu/src/primitive.rs
@@ -28,3 +28,11 @@ impl Damage for Custom {
}
}
}
+
+impl TryFrom<Mesh> for Custom {
+ type Error = &'static str;
+
+ fn try_from(mesh: Mesh) -> Result<Self, Self::Error> {
+ Ok(Custom::Mesh(mesh))
+ }
+}
diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs
index 0eda0191..fcd91d17 100644
--- a/widget/src/canvas.rs
+++ b/widget/src/canvas.rs
@@ -7,7 +7,6 @@ pub use event::Event;
pub use program::Program;
pub use crate::graphics::geometry::*;
-pub use crate::renderer::geometry::*;
use crate::core;
use crate::core::layout::{self, Layout};
@@ -21,13 +20,19 @@ use crate::graphics::geometry;
use std::marker::PhantomData;
+/// 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 type Cache<Renderer = crate::Renderer> = geometry::Cache<Renderer>;
+
/// A widget capable of drawing 2D graphics.
///
/// ## Drawing a simple circle
/// If you want to get a quick overview, here's how we can draw a simple circle:
///
/// ```no_run
-/// # use iced_widget::canvas::{self, Canvas, Fill, Frame, Geometry, Path, Program};
+/// # use iced_widget::canvas::{self, frame, Canvas, Fill, Frame, Path, Program};
/// # use iced_widget::core::{Color, Rectangle};
/// # use iced_widget::core::mouse;
/// # use iced_widget::{Renderer, Theme};
@@ -42,9 +47,9 @@ use std::marker::PhantomData;
/// impl Program<()> for Circle {
/// type State = ();
///
-/// fn draw(&self, _state: &(), renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: mouse::Cursor) -> Vec<Geometry>{
+/// fn draw(&self, _state: &(), renderer: &mut Renderer, _theme: &Theme, bounds: Rectangle, _cursor: mouse::Cursor) {
/// // We prepare a new `Frame`
-/// let mut frame = Frame::new(renderer, bounds.size());
+/// let mut frame = frame(renderer, bounds.size());
///
/// // We create a `Path` representing a simple circle
/// let circle = Path::circle(frame.center(), self.radius);
@@ -53,7 +58,7 @@ use std::marker::PhantomData;
/// frame.fill(&circle, Color::BLACK);
///
/// // Finally, we produce the geometry
-/// vec![frame.into_geometry()]
+/// renderer.draw_geometry([frame]);
/// }
/// }
///
@@ -210,9 +215,7 @@ where
renderer.with_transformation(
Transformation::translate(bounds.x, bounds.y),
|renderer| {
- renderer.draw(
- self.program.draw(state, renderer, theme, bounds, cursor),
- );
+ self.program.draw(state, renderer, theme, bounds, cursor);
},
);
}
diff --git a/widget/src/canvas/program.rs b/widget/src/canvas/program.rs
index 0bff4bda..307686de 100644
--- a/widget/src/canvas/program.rs
+++ b/widget/src/canvas/program.rs
@@ -37,22 +37,15 @@ where
(event::Status::Ignored, None)
}
- /// Draws the state of the [`Program`], producing a bunch of [`Geometry`].
- ///
- /// [`Geometry`] can be easily generated with a [`Frame`] or stored in a
- /// [`Cache`].
- ///
- /// [`Geometry`]: crate::canvas::Geometry
- /// [`Frame`]: crate::canvas::Frame
- /// [`Cache`]: crate::canvas::Cache
+ /// Draws the state of the [`Program`] with the given [`Renderer`].
fn draw(
&self,
state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
theme: &Theme,
bounds: Rectangle,
cursor: mouse::Cursor,
- ) -> Vec<Renderer::Geometry>;
+ );
/// Returns the current mouse interaction of the [`Program`].
///
@@ -90,12 +83,12 @@ where
fn draw(
&self,
state: &Self::State,
- renderer: &Renderer,
+ renderer: &mut Renderer,
theme: &Theme,
bounds: Rectangle,
cursor: mouse::Cursor,
- ) -> Vec<Renderer::Geometry> {
- T::draw(self, state, renderer, theme, bounds, cursor)
+ ) {
+ T::draw(self, state, renderer, theme, bounds, cursor);
}
fn mouse_interaction(
diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs
index 90c0c970..bc46aaaa 100644
--- a/widget/src/qr_code.rs
+++ b/widget/src/qr_code.rs
@@ -8,7 +8,6 @@ use crate::core::{
Color, Element, Layout, Length, Point, Rectangle, Size, Theme, Vector,
Widget,
};
-use crate::graphics::geometry::Renderer as _;
use crate::Renderer;
use std::cell::RefCell;
@@ -92,6 +91,8 @@ impl<'a, Message, Theme> Widget<Message, Theme, Renderer>
_cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
+ use canvas::Frame;
+
let state = tree.state.downcast_ref::<State>();
let bounds = layout.bounds();
@@ -142,7 +143,7 @@ impl<'a, Message, Theme> Widget<Message, Theme, Renderer>
renderer.with_translation(
bounds.position() - Point::ORIGIN,
|renderer| {
- renderer.draw(vec![geometry]);
+ renderer.draw_geometry(vec![geometry]);
},
);
}
@@ -161,11 +162,11 @@ where
/// The data of a [`QRCode`].
///
/// It stores the contents that will be displayed.
-#[derive(Debug)]
+#[allow(missing_debug_implementations)]
pub struct Data {
contents: Vec<qrcode::Color>,
width: usize,
- cache: canvas::Cache,
+ cache: canvas::Cache<Renderer>,
}
impl Data {
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 13d9282d..29786b65 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -130,7 +130,7 @@ pub fn default(theme: &Theme) -> Appearance {
/// settings.
pub async fn run<A, E, C>(
settings: Settings<A::Flags>,
- compositor_settings: C::Settings,
+ compositor_settings: impl Into<C::Settings>,
) -> Result<(), Error>
where
A: Application + 'static,
@@ -219,7 +219,7 @@ where
};
}
- let compositor = C::new(compositor_settings, window.clone()).await?;
+ let compositor = C::new(compositor_settings.into(), window.clone()).await?;
let mut renderer = compositor.create_renderer();
for font in settings.fonts {
diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs
index 18db1fb5..c865b0ee 100644
--- a/winit/src/multi_window.rs
+++ b/winit/src/multi_window.rs
@@ -105,7 +105,7 @@ where
/// settings.
pub fn run<A, E, C>(
settings: Settings<A::Flags>,
- compositor_settings: C::Settings,
+ compositor_settings: impl Into<C::Settings>,
) -> Result<(), Error>
where
A: Application + 'static,
@@ -186,8 +186,10 @@ where
};
}
- let mut compositor =
- executor::block_on(C::new(compositor_settings, main_window.clone()))?;
+ let mut compositor = executor::block_on(C::new(
+ compositor_settings.into(),
+ main_window.clone(),
+ ))?;
let mut window_manager = WindowManager::new();
let _ = window_manager.insert(