diff options
Diffstat (limited to 'renderer')
-rw-r--r-- | renderer/Cargo.toml | 13 | ||||
-rw-r--r-- | renderer/src/compositor.rs | 269 | ||||
-rw-r--r-- | renderer/src/fallback.rs | 637 | ||||
-rw-r--r-- | renderer/src/geometry.rs | 210 | ||||
-rw-r--r-- | renderer/src/geometry/cache.rs | 125 | ||||
-rw-r--r-- | renderer/src/lib.rs | 314 | ||||
-rw-r--r-- | renderer/src/settings.rs | 29 |
7 files changed, 682 insertions, 915 deletions
diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml index 5cce2427..458681dd 100644 --- a/renderer/Cargo.toml +++ b/renderer/Cargo.toml @@ -10,19 +10,24 @@ homepage.workspace = true categories.workspace = true keywords.workspace = true +[lints] +workspace = true + [features] wgpu = ["iced_wgpu"] -image = ["iced_tiny_skia/image", "iced_wgpu?/image"] -svg = ["iced_tiny_skia/svg", "iced_wgpu?/svg"] -geometry = ["iced_graphics/geometry", "iced_tiny_skia/geometry", "iced_wgpu?/geometry"] -tracing = ["iced_wgpu?/tracing"] +tiny-skia = ["iced_tiny_skia"] +image = ["iced_tiny_skia?/image", "iced_wgpu?/image"] +svg = ["iced_tiny_skia?/svg", "iced_wgpu?/svg"] +geometry = ["iced_graphics/geometry", "iced_tiny_skia?/geometry", "iced_wgpu?/geometry"] web-colors = ["iced_wgpu?/web-colors"] webgl = ["iced_wgpu?/webgl"] fira-sans = ["iced_graphics/fira-sans"] [dependencies] iced_graphics.workspace = true + iced_tiny_skia.workspace = true +iced_tiny_skia.optional = true iced_wgpu.workspace = true iced_wgpu.optional = true diff --git a/renderer/src/compositor.rs b/renderer/src/compositor.rs index c23a814c..8b137891 100644 --- a/renderer/src/compositor.rs +++ b/renderer/src/compositor.rs @@ -1,270 +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), -} - -pub enum Surface { - TinySkia(iced_tiny_skia::window::Surface), - #[cfg(feature = "wgpu")] - Wgpu(iced_wgpu::window::Surface<'static>), -} - -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()) - } - } - } - - 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)) - } - } - } - - 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(), - ) - } - - 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/fallback.rs b/renderer/src/fallback.rs new file mode 100644 index 00000000..6a169692 --- /dev/null +++ b/renderer/src/fallback.rs @@ -0,0 +1,637 @@ +//! Compose existing renderers and create type-safe fallback strategies. +use crate::core::image; +use crate::core::renderer; +use crate::core::svg; +use crate::core::{ + self, Background, Color, Point, Radians, Rectangle, Size, Transformation, +}; +use crate::graphics; +use crate::graphics::compositor; +use crate::graphics::mesh; + +use std::borrow::Cow; + +/// A renderer `A` with a fallback strategy `B`. +/// +/// This type can be used to easily compose existing renderers and +/// create custom, type-safe fallback strategies. +#[derive(Debug)] +pub enum Renderer<A, B> { + /// The primary rendering option. + Primary(A), + /// The secondary (or fallback) rendering option. + Secondary(B), +} + +macro_rules! delegate { + ($renderer:expr, $name:ident, $body:expr) => { + match $renderer { + Self::Primary($name) => $body, + Self::Secondary($name) => $body, + } + }; +} + +impl<A, B> core::Renderer for Renderer<A, B> +where + A: core::Renderer, + B: 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, bounds: Rectangle) { + delegate!(self, renderer, renderer.start_layer(bounds)); + } + + fn end_layer(&mut self) { + delegate!(self, renderer, renderer.end_layer()); + } + + fn start_transformation(&mut self, transformation: Transformation) { + delegate!( + self, + renderer, + renderer.start_transformation(transformation) + ); + } + + fn end_transformation(&mut self) { + delegate!(self, renderer, renderer.end_transformation()); + } +} + +impl<A, B> core::text::Renderer for Renderer<A, B> +where + A: core::text::Renderer, + B: core::text::Renderer< + Font = A::Font, + Paragraph = A::Paragraph, + Editor = A::Editor, + >, +{ + type Font = A::Font; + type Paragraph = A::Paragraph; + type Editor = A::Editor; + + const ICON_FONT: Self::Font = A::ICON_FONT; + const CHECKMARK_ICON: char = A::CHECKMARK_ICON; + const ARROW_DOWN_ICON: char = A::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 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<String, Self::Font>, + position: Point, + color: Color, + clip_bounds: Rectangle, + ) { + delegate!( + self, + renderer, + renderer.fill_text(text, position, color, clip_bounds) + ); + } +} + +impl<A, B> image::Renderer for Renderer<A, B> +where + A: image::Renderer, + B: image::Renderer<Handle = A::Handle>, +{ + type Handle = A::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, + rotation: Radians, + opacity: f32, + ) { + delegate!( + self, + renderer, + renderer.draw_image( + handle, + filter_method, + bounds, + rotation, + opacity + ) + ); + } +} + +impl<A, B> svg::Renderer for Renderer<A, B> +where + A: svg::Renderer, + B: 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, + rotation: Radians, + opacity: f32, + ) { + delegate!( + self, + renderer, + renderer.draw_svg(handle, color, bounds, rotation, opacity) + ); + } +} + +impl<A, B> mesh::Renderer for Renderer<A, B> +where + A: mesh::Renderer, + B: mesh::Renderer, +{ + fn draw_mesh(&mut self, mesh: graphics::Mesh) { + delegate!(self, renderer, renderer.draw_mesh(mesh)); + } +} + +/// A compositor `A` with a fallback strategy `B`. +/// +/// It works analogously to [`Renderer`]. +#[derive(Debug)] +pub enum Compositor<A, B> +where + A: graphics::Compositor, + B: graphics::Compositor, +{ + /// The primary compositing option. + Primary(A), + /// The secondary (or fallback) compositing option. + Secondary(B), +} + +/// A surface `A` with a fallback strategy `B`. +/// +/// It works analogously to [`Renderer`]. +#[derive(Debug)] +pub enum Surface<A, B> { + /// The primary surface option. + Primary(A), + /// The secondary (or fallback) surface option. + Secondary(B), +} + +impl<A, B> graphics::Compositor for Compositor<A, B> +where + A: graphics::Compositor, + B: graphics::Compositor, +{ + type Renderer = Renderer<A::Renderer, B::Renderer>; + type Surface = Surface<A::Surface, B::Surface>; + + async fn with_backend<W: compositor::Window + Clone>( + settings: graphics::Settings, + compatible_window: W, + backend: Option<&str>, + ) -> Result<Self, graphics::Error> { + use std::env; + + let backends = backend + .map(str::to_owned) + .or_else(|| env::var("ICED_BACKEND").ok()); + + let mut candidates: Vec<_> = backends + .map(|backends| { + backends + .split(',') + .filter(|candidate| !candidate.is_empty()) + .map(str::to_owned) + .map(Some) + .collect() + }) + .unwrap_or_default(); + + if candidates.is_empty() { + candidates.push(None); + } + + let mut errors = vec![]; + + for backend in candidates.iter().map(Option::as_deref) { + match A::with_backend(settings, compatible_window.clone(), backend) + .await + { + Ok(compositor) => return Ok(Self::Primary(compositor)), + Err(error) => { + errors.push(error); + } + } + + match B::with_backend(settings, compatible_window.clone(), backend) + .await + { + Ok(compositor) => return Ok(Self::Secondary(compositor)), + Err(error) => { + errors.push(error); + } + } + } + + Err(graphics::Error::List(errors)) + } + + fn create_renderer(&self) -> Self::Renderer { + match self { + Self::Primary(compositor) => { + Renderer::Primary(compositor.create_renderer()) + } + Self::Secondary(compositor) => { + Renderer::Secondary(compositor.create_renderer()) + } + } + } + + fn create_surface<W: compositor::Window + Clone>( + &mut self, + window: W, + width: u32, + height: u32, + ) -> Self::Surface { + match self { + Self::Primary(compositor) => Surface::Primary( + compositor.create_surface(window, width, height), + ), + Self::Secondary(compositor) => Surface::Secondary( + compositor.create_surface(window, width, height), + ), + } + } + + fn configure_surface( + &mut self, + surface: &mut Self::Surface, + width: u32, + height: u32, + ) { + match (self, surface) { + (Self::Primary(compositor), Surface::Primary(surface)) => { + compositor.configure_surface(surface, width, height); + } + (Self::Secondary(compositor), Surface::Secondary(surface)) => { + compositor.configure_surface(surface, width, height); + } + _ => unreachable!(), + } + } + + fn load_font(&mut self, font: Cow<'static, [u8]>) { + delegate!(self, compositor, compositor.load_font(font)); + } + + 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::Primary(compositor), + Renderer::Primary(renderer), + Surface::Primary(surface), + ) => compositor.present( + renderer, + surface, + viewport, + background_color, + overlay, + ), + ( + Self::Secondary(compositor), + Renderer::Secondary(renderer), + Surface::Secondary(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::Primary(compositor), + Renderer::Primary(renderer), + Surface::Primary(surface), + ) => compositor.screenshot( + renderer, + surface, + viewport, + background_color, + overlay, + ), + ( + Self::Secondary(compositor), + Renderer::Secondary(renderer), + Surface::Secondary(surface), + ) => compositor.screenshot( + renderer, + surface, + viewport, + background_color, + overlay, + ), + _ => unreachable!(), + } + } +} + +#[cfg(feature = "wgpu")] +impl<A, B> iced_wgpu::primitive::Renderer for Renderer<A, B> +where + A: iced_wgpu::primitive::Renderer, + B: core::Renderer, +{ + fn draw_primitive( + &mut self, + bounds: Rectangle, + primitive: impl iced_wgpu::Primitive, + ) { + match self { + Self::Primary(renderer) => { + renderer.draw_primitive(bounds, primitive); + } + Self::Secondary(_) => { + log::warn!( + "Custom shader primitive is not supported with this renderer." + ); + } + } + } +} + +#[cfg(feature = "geometry")] +mod geometry { + use super::Renderer; + use crate::core::{Point, Radians, Rectangle, Size, Vector}; + use crate::graphics::cache::{self, Cached}; + use crate::graphics::geometry::{self, Fill, Path, Stroke, Text}; + + impl<A, B> geometry::Renderer for Renderer<A, B> + where + A: geometry::Renderer, + B: geometry::Renderer, + { + type Geometry = Geometry<A::Geometry, B::Geometry>; + type Frame = Frame<A::Frame, B::Frame>; + + fn new_frame(&self, size: iced_graphics::core::Size) -> Self::Frame { + match self { + Self::Primary(renderer) => { + Frame::Primary(renderer.new_frame(size)) + } + Self::Secondary(renderer) => { + Frame::Secondary(renderer.new_frame(size)) + } + } + } + + fn draw_geometry(&mut self, geometry: Self::Geometry) { + match (self, geometry) { + (Self::Primary(renderer), Geometry::Primary(geometry)) => { + renderer.draw_geometry(geometry); + } + (Self::Secondary(renderer), Geometry::Secondary(geometry)) => { + renderer.draw_geometry(geometry); + } + _ => unreachable!(), + } + } + } + + #[derive(Debug, Clone)] + pub enum Geometry<A, B> { + Primary(A), + Secondary(B), + } + + impl<A, B> Cached for Geometry<A, B> + where + A: Cached, + B: Cached, + { + type Cache = Geometry<A::Cache, B::Cache>; + + fn load(cache: &Self::Cache) -> Self { + match cache { + Geometry::Primary(cache) => Self::Primary(A::load(cache)), + Geometry::Secondary(cache) => Self::Secondary(B::load(cache)), + } + } + + fn cache( + self, + group: cache::Group, + previous: Option<Self::Cache>, + ) -> Self::Cache { + match (self, previous) { + ( + Self::Primary(geometry), + Some(Geometry::Primary(previous)), + ) => Geometry::Primary(geometry.cache(group, Some(previous))), + (Self::Primary(geometry), None) => { + Geometry::Primary(geometry.cache(group, None)) + } + ( + Self::Secondary(geometry), + Some(Geometry::Secondary(previous)), + ) => Geometry::Secondary(geometry.cache(group, Some(previous))), + (Self::Secondary(geometry), None) => { + Geometry::Secondary(geometry.cache(group, None)) + } + _ => unreachable!(), + } + } + } + + #[derive(Debug)] + pub enum Frame<A, B> { + Primary(A), + Secondary(B), + } + + impl<A, B> geometry::frame::Backend for Frame<A, B> + where + A: geometry::frame::Backend, + B: geometry::frame::Backend, + { + type Geometry = Geometry<A::Geometry, B::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, bounds: Rectangle) -> Self { + match self { + Self::Primary(frame) => Self::Primary(frame.draft(bounds)), + Self::Secondary(frame) => Self::Secondary(frame.draft(bounds)), + } + } + + fn paste(&mut self, frame: Self, at: Point) { + match (self, frame) { + (Self::Primary(target), Self::Primary(source)) => { + target.paste(source, at); + } + (Self::Secondary(target), Self::Secondary(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)); + } + + fn into_geometry(self) -> Self::Geometry { + match self { + Frame::Primary(frame) => { + Geometry::Primary(frame.into_geometry()) + } + Frame::Secondary(frame) => { + Geometry::Secondary(frame.into_geometry()) + } + } + } + } +} + +impl<A, B> compositor::Default for Renderer<A, B> +where + A: compositor::Default, + B: compositor::Default, +{ + type Compositor = Compositor<A::Compositor, B::Compositor>; +} diff --git a/renderer/src/geometry.rs b/renderer/src/geometry.rs deleted file mode 100644 index 36435148..00000000 --- a/renderer/src/geometry.rs +++ /dev/null @@ -1,210 +0,0 @@ -mod cache; - -pub use cache::Cache; - -use crate::core::{Point, Radians, Rectangle, Size, Transformation, Vector}; -use crate::graphics::geometry::{Fill, Path, Stroke, Text}; -use crate::Renderer; - -macro_rules! delegate { - ($frame:expr, $name:ident, $body:expr) => { - match $frame { - Self::TinySkia($name) => $body, - #[cfg(feature = "wgpu")] - Self::Wgpu($name) => $body, - } - }; -} - -pub enum Geometry { - TinySkia(iced_tiny_skia::Primitive), - #[cfg(feature = "wgpu")] - Wgpu(iced_wgpu::Primitive), -} - -impl Geometry { - pub fn transform(self, transformation: Transformation) -> Self { - match self { - Self::TinySkia(primitive) => { - Self::TinySkia(primitive.transform(transformation)) - } - #[cfg(feature = "wgpu")] - Self::Wgpu(primitive) => { - Self::Wgpu(primitive.transform(transformation)) - } - } - } -} - -pub enum Frame { - TinySkia(iced_tiny_skia::geometry::Frame), - #[cfg(feature = "wgpu")] - Wgpu(iced_wgpu::geometry::Frame), -} - -impl Frame { - pub fn new(renderer: &Renderer, 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. - 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<R>(&mut self, f: impl FnOnce(&mut Frame) -> R) -> R { - delegate!(self, frame, frame.push_transform()); - - let result = f(self); - - delegate!(self, frame, frame.pop_transform()); - - result - } - - /// 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 = 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())) - } - }; - - let result = 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!(), - }; - - result - } - - /// 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: impl Into<Radians>) { - delegate!(self, frame, frame.rotate(angle)); - } - - /// Applies a uniform scaling to the current transform of the [`Frame`]. - #[inline] - pub fn scale(&mut self, scale: impl Into<f32>) { - delegate!(self, frame, frame.scale(scale)); - } - - /// Applies a non-uniform scaling to the current transform of the [`Frame`]. - #[inline] - pub fn scale_nonuniform(&mut self, scale: impl Into<Vector>) { - delegate!(self, frame, frame.scale_nonuniform(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 deleted file mode 100644 index 3aff76b9..00000000 --- a/renderer/src/geometry/cache.rs +++ /dev/null @@ -1,125 +0,0 @@ -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: RefCell::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( - &self, - renderer: &Renderer, - 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 index 757c264d..220542e1 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -1,302 +1,60 @@ -#![forbid(rust_2018_idioms)] -#![deny(unsafe_code, unused_results, rustdoc::broken_intra_doc_links)] +//! The official renderer for iced. #![cfg_attr(docsrs, feature(doc_auto_cfg))] #[cfg(feature = "wgpu")] pub use iced_wgpu as wgpu; -pub mod compositor; - -#[cfg(feature = "geometry")] -pub mod geometry; - -mod settings; +pub mod fallback; 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; - -use std::borrow::Cow; +pub use iced_graphics::geometry; /// 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), -} - -macro_rules! delegate { - ($renderer:expr, $name:ident, $body:expr) => { - match $renderer { - Self::TinySkia($name) => $body, - #[cfg(feature = "wgpu")] - Self::Wgpu($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), - )); - } - } - } -} - -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!(), - } - } - } - } - - 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(); +pub type Renderer = renderer::Renderer; - f(self); - - match self { - #[cfg(feature = "wgpu")] - Self::Wgpu(renderer) => { - renderer.end_transformation(primitives, transformation); - } - _ => 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 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) - ); - } +/// The default graphics compositor for [`iced`]. +/// +/// [`iced`]: https://github.com/iced-rs/iced +pub type Compositor = renderer::Compositor; - 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) - ); - } +#[cfg(all(feature = "wgpu", feature = "tiny-skia"))] +mod renderer { + pub type Renderer = crate::fallback::Renderer< + iced_wgpu::Renderer, + iced_tiny_skia::Renderer, + >; - 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) - ); - } + pub type Compositor = crate::fallback::Compositor< + iced_wgpu::window::Compositor, + iced_tiny_skia::window::Compositor, + >; } -#[cfg(feature = "image")] -impl crate::core::image::Renderer for Renderer { - type Handle = crate::core::image::Handle; - - fn dimensions( - &self, - handle: &crate::core::image::Handle, - ) -> core::Size<u32> { - delegate!(self, renderer, renderer.dimensions(handle)) - } - - fn draw( - &mut self, - handle: crate::core::image::Handle, - filter_method: crate::core::image::FilterMethod, - bounds: Rectangle, - ) { - delegate!(self, renderer, renderer.draw(handle, filter_method, bounds)); - } +#[cfg(all(feature = "wgpu", not(feature = "tiny-skia")))] +mod renderer { + pub type Renderer = iced_wgpu::Renderer; + pub type Compositor = iced_wgpu::window::Compositor; } -#[cfg(feature = "svg")] -impl crate::core::svg::Renderer for Renderer { - fn dimensions(&self, handle: &crate::core::svg::Handle) -> core::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(all(not(feature = "wgpu"), feature = "tiny-skia"))] +mod renderer { + pub type Renderer = iced_tiny_skia::Renderer; + pub type Compositor = iced_tiny_skia::window::Compositor; } -#[cfg(feature = "geometry")] -impl crate::graphics::geometry::Renderer for Renderer { - 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); - } - #[cfg(feature = "wgpu")] - crate::Geometry::Wgpu(_) => 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(not(any(feature = "wgpu", feature = "tiny-skia")))] +mod renderer { + #[cfg(not(debug_assertions))] + compile_error!( + "Cannot compile `iced_renderer` in release mode \ + without a renderer feature enabled. \ + Enable either the `wgpu` or `tiny-skia` feature, or both." + ); -#[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); - } - } - } + pub type Renderer = (); + pub type Compositor = (); } diff --git a/renderer/src/settings.rs b/renderer/src/settings.rs deleted file mode 100644 index 432eb8a0..00000000 --- a/renderer/src/settings.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::core::{Font, Pixels}; -use crate::graphics::Antialiasing; - -/// The settings of a Backend. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Settings { - /// The default [`Font`] to use. - pub default_font: Font, - - /// The default size of text. - /// - /// By default, it will be set to `16.0`. - pub default_text_size: Pixels, - - /// The antialiasing strategy that will be used for triangle primitives. - /// - /// By default, it is `None`. - pub antialiasing: Option<Antialiasing>, -} - -impl Default for Settings { - fn default() -> Settings { - Settings { - default_font: Font::default(), - default_text_size: Pixels(16.0), - antialiasing: None, - } - } -} |