diff options
Diffstat (limited to 'renderer/src/fallback.rs')
-rw-r--r-- | renderer/src/fallback.rs | 637 |
1 files changed, 637 insertions, 0 deletions
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>; +} |