summaryrefslogtreecommitdiffstats
path: root/renderer/src
diff options
context:
space:
mode:
Diffstat (limited to 'renderer/src')
-rw-r--r--renderer/src/backend.rs100
-rw-r--r--renderer/src/compositor.rs262
-rw-r--r--renderer/src/geometry.rs184
-rw-r--r--renderer/src/geometry/cache.rs125
-rw-r--r--renderer/src/lib.rs269
-rw-r--r--renderer/src/settings.rs31
-rw-r--r--renderer/src/widget.rs11
7 files changed, 982 insertions, 0 deletions
diff --git a/renderer/src/backend.rs b/renderer/src/backend.rs
new file mode 100644
index 00000000..3f229b52
--- /dev/null
+++ b/renderer/src/backend.rs
@@ -0,0 +1,100 @@
+use crate::core::text;
+use crate::core::{Font, Point, Size};
+use crate::graphics::backend;
+
+use std::borrow::Cow;
+
+#[allow(clippy::large_enum_variant)]
+pub enum Backend {
+ TinySkia(iced_tiny_skia::Backend),
+ #[cfg(feature = "wgpu")]
+ Wgpu(iced_wgpu::Backend),
+}
+
+macro_rules! delegate {
+ ($backend:expr, $name:ident, $body:expr) => {
+ match $backend {
+ Self::TinySkia($name) => $body,
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu($name) => $body,
+ }
+ };
+}
+
+impl backend::Text for Backend {
+ const ICON_FONT: Font = Font::with_name("Iced-Icons");
+ const CHECKMARK_ICON: char = '\u{f00c}';
+ const ARROW_DOWN_ICON: char = '\u{e800}';
+
+ fn default_font(&self) -> Font {
+ delegate!(self, backend, backend.default_font())
+ }
+
+ fn default_size(&self) -> f32 {
+ delegate!(self, backend, backend.default_size())
+ }
+
+ fn measure(
+ &self,
+ contents: &str,
+ size: f32,
+ line_height: text::LineHeight,
+ font: Font,
+ bounds: Size,
+ shaping: text::Shaping,
+ ) -> Size {
+ delegate!(
+ self,
+ backend,
+ backend.measure(contents, size, line_height, font, bounds, shaping)
+ )
+ }
+
+ fn hit_test(
+ &self,
+ contents: &str,
+ size: f32,
+ line_height: text::LineHeight,
+ font: Font,
+ bounds: Size,
+ shaping: text::Shaping,
+ position: Point,
+ nearest_only: bool,
+ ) -> Option<text::Hit> {
+ delegate!(
+ self,
+ backend,
+ backend.hit_test(
+ contents,
+ size,
+ line_height,
+ font,
+ bounds,
+ shaping,
+ position,
+ nearest_only,
+ )
+ )
+ }
+
+ fn load_font(&mut self, font: Cow<'static, [u8]>) {
+ delegate!(self, backend, backend.load_font(font));
+ }
+}
+
+#[cfg(feature = "image")]
+impl backend::Image for Backend {
+ fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> {
+ delegate!(self, backend, backend.dimensions(handle))
+ }
+}
+
+#[cfg(feature = "svg")]
+impl backend::Svg for Backend {
+ fn viewport_dimensions(
+ &self,
+ handle: &crate::core::svg::Handle,
+ ) -> Size<u32> {
+ delegate!(self, backend, backend.viewport_dimensions(handle))
+ }
+}
diff --git a/renderer/src/compositor.rs b/renderer/src/compositor.rs
new file mode 100644
index 00000000..8b17a4b0
--- /dev/null
+++ b/renderer/src/compositor.rs
@@ -0,0 +1,262 @@
+use crate::core::Color;
+use crate::graphics::compositor::{Information, SurfaceError};
+use crate::graphics::{Error, Viewport};
+use crate::{Renderer, Settings};
+
+use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
+use std::env;
+
+pub enum Compositor<Theme> {
+ TinySkia(iced_tiny_skia::window::Compositor<Theme>),
+ #[cfg(feature = "wgpu")]
+ Wgpu(iced_wgpu::window::Compositor<Theme>),
+}
+
+pub enum Surface {
+ TinySkia(iced_tiny_skia::window::Surface),
+ #[cfg(feature = "wgpu")]
+ Wgpu(iced_wgpu::window::Surface),
+}
+
+impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
+ type Settings = Settings;
+ type Renderer = Renderer<Theme>;
+ type Surface = Surface;
+
+ fn new<W: HasRawWindowHandle + HasRawDisplayHandle>(
+ settings: Self::Settings,
+ compatible_window: Option<&W>,
+ ) -> Result<(Self, Self::Renderer), Error> {
+ let candidates =
+ Candidate::list_from_env().unwrap_or(Candidate::default_list());
+
+ let mut error = Error::GraphicsAdapterNotFound;
+
+ for candidate in candidates {
+ match candidate.build(settings, compatible_window) {
+ Ok((compositor, renderer)) => {
+ return Ok((compositor, renderer))
+ }
+ Err(new_error) => {
+ error = new_error;
+ }
+ }
+ }
+
+ Err(error)
+ }
+
+ fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
+ &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))
+ }
+ }
+ }
+
+ 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);
+ }
+ #[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(),
+ }
+ }
+
+ 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,
+ )
+ }),
+ #[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(),
+ )
+ }
+
+ fn build<Theme, W: HasRawWindowHandle + HasRawDisplayHandle>(
+ self,
+ settings: Settings,
+ _compatible_window: Option<&W>,
+ ) -> Result<(Compositor<Theme>, Renderer<Theme>), Error> {
+ match self {
+ Self::TinySkia => {
+ let (compositor, backend) =
+ iced_tiny_skia::window::compositor::new(
+ iced_tiny_skia::Settings {
+ default_font: settings.default_font,
+ default_text_size: settings.default_text_size,
+ },
+ );
+
+ Ok((
+ Compositor::TinySkia(compositor),
+ Renderer::TinySkia(iced_tiny_skia::Renderer::new(backend)),
+ ))
+ }
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu => {
+ let (compositor, backend) = 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,
+ )?;
+
+ Ok((
+ Compositor::Wgpu(compositor),
+ Renderer::Wgpu(iced_wgpu::Renderer::new(backend)),
+ ))
+ }
+ #[cfg(not(feature = "wgpu"))]
+ Self::Wgpu => {
+ panic!("`wgpu` feature was not enabled in `iced_renderer`")
+ }
+ }
+ }
+}
diff --git a/renderer/src/geometry.rs b/renderer/src/geometry.rs
new file mode 100644
index 00000000..04b5d9e6
--- /dev/null
+++ b/renderer/src/geometry.rs
@@ -0,0 +1,184 @@
+mod cache;
+
+pub use cache::Cache;
+
+use crate::core::{Point, Rectangle, Size, Vector};
+use crate::graphics::geometry::{Fill, Path, Stroke, Text};
+use crate::Renderer;
+
+pub enum Frame {
+ TinySkia(iced_tiny_skia::geometry::Frame),
+ #[cfg(feature = "wgpu")]
+ Wgpu(iced_wgpu::geometry::Frame),
+}
+
+pub enum Geometry {
+ TinySkia(iced_tiny_skia::Primitive),
+ #[cfg(feature = "wgpu")]
+ Wgpu(iced_wgpu::Primitive),
+}
+
+macro_rules! delegate {
+ ($frame:expr, $name:ident, $body:expr) => {
+ match $frame {
+ Self::TinySkia($name) => $body,
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu($name) => $body,
+ }
+ };
+}
+
+impl Frame {
+ pub fn new<Theme>(renderer: &Renderer<Theme>, size: Size) -> Self {
+ match renderer {
+ Renderer::TinySkia(_) => {
+ Frame::TinySkia(iced_tiny_skia::geometry::Frame::new(size))
+ }
+ #[cfg(feature = "wgpu")]
+ Renderer::Wgpu(_) => {
+ Frame::Wgpu(iced_wgpu::geometry::Frame::new(size))
+ }
+ }
+ }
+
+ /// Returns the width of the [`Frame`].
+ #[inline]
+ pub fn width(&self) -> f32 {
+ delegate!(self, frame, frame.width())
+ }
+
+ /// Returns the height of the [`Frame`].
+ #[inline]
+ pub fn height(&self) -> f32 {
+ delegate!(self, frame, frame.height())
+ }
+
+ /// Returns the dimensions of the [`Frame`].
+ #[inline]
+ pub fn size(&self) -> Size {
+ delegate!(self, frame, frame.size())
+ }
+
+ /// Returns the coordinate of the center of the [`Frame`].
+ #[inline]
+ pub fn center(&self) -> Point {
+ delegate!(self, frame, frame.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<Fill>) {
+ delegate!(self, frame, frame.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<Fill>,
+ ) {
+ delegate!(self, frame, frame.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<Stroke<'a>>) {
+ delegate!(self, frame, frame.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.
+ ///
+ /// [`Canvas`]: crate::widget::Canvas
+ pub fn fill_text(&mut self, text: impl Into<Text>) {
+ delegate!(self, frame, frame.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 Frame)) {
+ delegate!(self, frame, frame.push_transform());
+
+ f(self);
+
+ delegate!(self, frame, frame.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 Frame)) {
+ let mut frame = match self {
+ Self::TinySkia(_) => Self::TinySkia(
+ iced_tiny_skia::geometry::Frame::new(region.size()),
+ ),
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu(_) => {
+ Self::Wgpu(iced_wgpu::geometry::Frame::new(region.size()))
+ }
+ };
+
+ f(&mut frame);
+
+ let origin = Point::new(region.x, region.y);
+
+ match (self, frame) {
+ (Self::TinySkia(target), Self::TinySkia(frame)) => {
+ target.clip(frame, origin);
+ }
+ #[cfg(feature = "wgpu")]
+ (Self::Wgpu(target), Self::Wgpu(frame)) => {
+ target.clip(frame, origin);
+ }
+ #[allow(unreachable_patterns)]
+ _ => unreachable!(),
+ };
+ }
+
+ /// Applies a translation to the current transform of the [`Frame`].
+ #[inline]
+ pub fn translate(&mut self, translation: Vector) {
+ delegate!(self, frame, frame.translate(translation));
+ }
+
+ /// Applies a rotation in radians to the current transform of the [`Frame`].
+ #[inline]
+ pub fn rotate(&mut self, angle: f32) {
+ delegate!(self, frame, frame.rotate(angle));
+ }
+
+ /// Applies a scaling to the current transform of the [`Frame`].
+ #[inline]
+ pub fn scale(&mut self, scale: f32) {
+ delegate!(self, frame, frame.scale(scale));
+ }
+
+ pub fn into_geometry(self) -> Geometry {
+ match self {
+ Self::TinySkia(frame) => Geometry::TinySkia(frame.into_primitive()),
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu(frame) => Geometry::Wgpu(frame.into_primitive()),
+ }
+ }
+}
diff --git a/renderer/src/geometry/cache.rs b/renderer/src/geometry/cache.rs
new file mode 100644
index 00000000..d82e7f69
--- /dev/null
+++ b/renderer/src/geometry/cache.rs
@@ -0,0 +1,125 @@
+use crate::core::Size;
+use crate::geometry::{Frame, Geometry};
+use crate::Renderer;
+
+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.
+#[derive(Debug, Default)]
+pub struct Cache {
+ state: RefCell<State>,
+}
+
+#[derive(Debug, Default)]
+enum State {
+ #[default]
+ Empty,
+ Filled {
+ bounds: Size,
+ primitive: Internal,
+ },
+}
+
+#[derive(Debug, Clone)]
+enum Internal {
+ TinySkia(Arc<iced_tiny_skia::Primitive>),
+ #[cfg(feature = "wgpu")]
+ Wgpu(Arc<iced_wgpu::Primitive>),
+}
+
+impl Cache {
+ /// Creates a new empty [`Cache`].
+ pub fn new() -> Self {
+ Cache {
+ state: Default::default(),
+ }
+ }
+
+ /// 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<Theme>(
+ &self,
+ renderer: &Renderer<Theme>,
+ bounds: Size,
+ draw_fn: impl FnOnce(&mut Frame),
+ ) -> Geometry {
+ use std::ops::Deref;
+
+ if let State::Filled {
+ bounds: cached_bounds,
+ primitive,
+ } = self.state.borrow().deref()
+ {
+ if *cached_bounds == bounds {
+ match primitive {
+ Internal::TinySkia(primitive) => {
+ return Geometry::TinySkia(
+ iced_tiny_skia::Primitive::Cache {
+ content: primitive.clone(),
+ },
+ );
+ }
+ #[cfg(feature = "wgpu")]
+ Internal::Wgpu(primitive) => {
+ return Geometry::Wgpu(iced_wgpu::Primitive::Cache {
+ content: primitive.clone(),
+ });
+ }
+ }
+ }
+ }
+
+ let mut frame = Frame::new(renderer, bounds);
+ draw_fn(&mut frame);
+
+ let primitive = {
+ let geometry = frame.into_geometry();
+
+ match geometry {
+ Geometry::TinySkia(primitive) => {
+ Internal::TinySkia(Arc::new(primitive))
+ }
+ #[cfg(feature = "wgpu")]
+ Geometry::Wgpu(primitive) => {
+ Internal::Wgpu(Arc::new(primitive))
+ }
+ }
+ };
+
+ *self.state.borrow_mut() = State::Filled {
+ bounds,
+ primitive: primitive.clone(),
+ };
+
+ match primitive {
+ Internal::TinySkia(primitive) => {
+ Geometry::TinySkia(iced_tiny_skia::Primitive::Cache {
+ content: primitive,
+ })
+ }
+ #[cfg(feature = "wgpu")]
+ Internal::Wgpu(primitive) => {
+ Geometry::Wgpu(iced_wgpu::Primitive::Cache {
+ content: primitive,
+ })
+ }
+ }
+ }
+}
diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs
new file mode 100644
index 00000000..7d1a02c2
--- /dev/null
+++ b/renderer/src/lib.rs
@@ -0,0 +1,269 @@
+pub mod compositor;
+
+#[cfg(feature = "geometry")]
+pub mod geometry;
+
+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, Font, Point, Rectangle, Size, Vector};
+use crate::graphics::Mesh;
+
+use std::borrow::Cow;
+
+/// The default graphics renderer for [`iced`].
+///
+/// [`iced`]: https://github.com/iced-rs/iced
+pub enum Renderer<Theme> {
+ TinySkia(iced_tiny_skia::Renderer<Theme>),
+ #[cfg(feature = "wgpu")]
+ Wgpu(iced_wgpu::Renderer<Theme>),
+}
+
+macro_rules! delegate {
+ ($renderer:expr, $name:ident, $body:expr) => {
+ match $renderer {
+ Self::TinySkia($name) => $body,
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu($name) => $body,
+ }
+ };
+}
+
+impl<T> Renderer<T> {
+ 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),
+ ));
+ }
+ }
+ }
+}
+
+impl<T> core::Renderer for Renderer<T> {
+ type Theme = T;
+
+ fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) {
+ match self {
+ Self::TinySkia(renderer) => {
+ let primitives = renderer.start_layer();
+
+ f(self);
+
+ match self {
+ Self::TinySkia(renderer) => {
+ renderer.end_layer(primitives, bounds);
+ }
+ #[cfg(feature = "wgpu")]
+ _ => unreachable!(),
+ }
+ }
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu(renderer) => {
+ let primitives = renderer.start_layer();
+
+ f(self);
+
+ match self {
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu(renderer) => {
+ renderer.end_layer(primitives, bounds);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+ }
+
+ fn with_translation(
+ &mut self,
+ translation: Vector,
+ f: impl FnOnce(&mut Self),
+ ) {
+ match self {
+ Self::TinySkia(renderer) => {
+ let primitives = renderer.start_translation();
+
+ f(self);
+
+ match self {
+ Self::TinySkia(renderer) => {
+ renderer.end_translation(primitives, translation);
+ }
+ #[cfg(feature = "wgpu")]
+ _ => unreachable!(),
+ }
+ }
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu(renderer) => {
+ let primitives = renderer.start_translation();
+
+ f(self);
+
+ match self {
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu(renderer) => {
+ renderer.end_translation(primitives, translation);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+ }
+
+ fn fill_quad(
+ &mut self,
+ quad: renderer::Quad,
+ background: impl Into<Background>,
+ ) {
+ delegate!(self, renderer, renderer.fill_quad(quad, background));
+ }
+
+ fn clear(&mut self) {
+ delegate!(self, renderer, renderer.clear());
+ }
+}
+
+impl<T> text::Renderer for Renderer<T> {
+ type Font = Font;
+
+ const ICON_FONT: Font = iced_tiny_skia::Renderer::<T>::ICON_FONT;
+ const CHECKMARK_ICON: char = iced_tiny_skia::Renderer::<T>::CHECKMARK_ICON;
+ const ARROW_DOWN_ICON: char =
+ iced_tiny_skia::Renderer::<T>::ARROW_DOWN_ICON;
+
+ fn default_font(&self) -> Self::Font {
+ delegate!(self, renderer, renderer.default_font())
+ }
+
+ fn default_size(&self) -> f32 {
+ delegate!(self, renderer, renderer.default_size())
+ }
+
+ fn measure(
+ &self,
+ content: &str,
+ size: f32,
+ line_height: text::LineHeight,
+ font: Font,
+ bounds: Size,
+ shaping: text::Shaping,
+ ) -> Size {
+ delegate!(
+ self,
+ renderer,
+ renderer.measure(content, size, line_height, font, bounds, shaping)
+ )
+ }
+
+ fn hit_test(
+ &self,
+ content: &str,
+ size: f32,
+ line_height: text::LineHeight,
+ font: Font,
+ bounds: Size,
+ shaping: text::Shaping,
+ point: Point,
+ nearest_only: bool,
+ ) -> Option<text::Hit> {
+ delegate!(
+ self,
+ renderer,
+ renderer.hit_test(
+ content,
+ size,
+ line_height,
+ font,
+ bounds,
+ shaping,
+ point,
+ nearest_only
+ )
+ )
+ }
+
+ fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
+ delegate!(self, renderer, renderer.load_font(bytes));
+ }
+
+ fn fill_text(&mut self, text: Text<'_, Self::Font>) {
+ delegate!(self, renderer, renderer.fill_text(text));
+ }
+}
+
+#[cfg(feature = "image")]
+impl<T> crate::core::image::Renderer for Renderer<T> {
+ type Handle = crate::core::image::Handle;
+
+ fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> {
+ delegate!(self, renderer, renderer.dimensions(handle))
+ }
+
+ fn draw(&mut self, handle: crate::core::image::Handle, bounds: Rectangle) {
+ delegate!(self, renderer, renderer.draw(handle, bounds));
+ }
+}
+
+#[cfg(feature = "svg")]
+impl<T> crate::core::svg::Renderer for Renderer<T> {
+ fn dimensions(&self, handle: &crate::core::svg::Handle) -> Size<u32> {
+ delegate!(self, renderer, renderer.dimensions(handle))
+ }
+
+ fn draw(
+ &mut self,
+ handle: crate::core::svg::Handle,
+ color: Option<crate::core::Color>,
+ bounds: Rectangle,
+ ) {
+ delegate!(self, renderer, renderer.draw(handle, color, bounds))
+ }
+}
+
+#[cfg(feature = "geometry")]
+impl<T> crate::graphics::geometry::Renderer for Renderer<T> {
+ type Geometry = crate::Geometry;
+
+ fn draw(&mut self, layers: Vec<Self::Geometry>) {
+ match self {
+ Self::TinySkia(renderer) => {
+ for layer in layers {
+ match layer {
+ crate::Geometry::TinySkia(primitive) => {
+ renderer.draw_primitive(primitive);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu(renderer) => {
+ for layer in layers {
+ match layer {
+ crate::Geometry::Wgpu(primitive) => {
+ renderer.draw_primitive(primitive);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/renderer/src/settings.rs b/renderer/src/settings.rs
new file mode 100644
index 00000000..2e51f339
--- /dev/null
+++ b/renderer/src/settings.rs
@@ -0,0 +1,31 @@
+use crate::core::Font;
+use crate::graphics::Antialiasing;
+
+/// The settings of a [`Backend`].
+///
+/// [`Backend`]: crate::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: f32,
+
+ /// The antialiasing strategy that will be used for triangle primitives.
+ ///
+ /// By default, it is `None`.
+ pub antialiasing: Option<Antialiasing>,
+}
+
+impl Default for Settings {
+ fn default() -> Settings {
+ Settings {
+ default_font: Font::default(),
+ default_text_size: 16.0,
+ antialiasing: None,
+ }
+ }
+}
diff --git a/renderer/src/widget.rs b/renderer/src/widget.rs
new file mode 100644
index 00000000..6c0c2a83
--- /dev/null
+++ b/renderer/src/widget.rs
@@ -0,0 +1,11 @@
+#[cfg(feature = "canvas")]
+pub mod canvas;
+
+#[cfg(feature = "canvas")]
+pub use canvas::Canvas;
+
+#[cfg(feature = "qr_code")]
+pub mod qr_code;
+
+#[cfg(feature = "qr_code")]
+pub use qr_code::QRCode;