From 3645d34d6a1ba1247238e830e9eefd52d9e5b986 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 Mar 2024 22:27:17 +0100 Subject: Implement composable, type-safe renderer fallback --- renderer/src/fallback.rs | 562 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 562 insertions(+) create mode 100644 renderer/src/fallback.rs (limited to 'renderer/src/fallback.rs') 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 +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 Renderer +where + L: core::Renderer, + R: core::Renderer, +{ + #[cfg(feature = "geometry")] + pub fn draw_geometry( + &mut self, + layers: impl IntoIterator, + ) where + L: graphics::geometry::Renderer, + R: graphics::geometry::Renderer, + + Geometry: Into>, + { + use graphics::geometry::Renderer; + + for layer in layers { + ::draw_geometry(self, layer.into()); + } + } +} + +impl core::Renderer for Renderer +where + L: core::Renderer, + R: core::Renderer, +{ + fn fill_quad( + &mut self, + quad: renderer::Quad, + background: impl Into, + ) { + 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 core::text::Renderer for Renderer +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 image::Renderer for Renderer +where + L: image::Renderer, + R: image::Renderer, +{ + type Handle = L::Handle; + + fn measure_image(&self, handle: &Self::Handle) -> Size { + 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 svg::Renderer for Renderer +where + L: svg::Renderer, + R: svg::Renderer, +{ + fn measure_svg(&self, handle: &svg::Handle) -> Size { + delegate!(self, renderer, renderer.measure_svg(handle)) + } + + fn draw_svg( + &mut self, + handle: svg::Handle, + color: Option, + bounds: Rectangle, + ) { + delegate!(self, renderer, renderer.draw_svg(handle, color, bounds)); + } +} + +impl mesh::Renderer for Renderer +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 +where + L: graphics::Compositor, + R: graphics::Compositor, +{ + Left(L), + Right(R), +} + +pub enum Surface { + Left(L), + Right(R), +} + +impl graphics::Compositor for Compositor +where + L: graphics::Compositor, + R: graphics::Compositor, + L::Settings: From, + R::Settings: From, +{ + type Settings = crate::Settings; + type Renderer = Renderer; + type Surface = Surface; + + async fn new( + settings: Self::Settings, + compatible_window: W, + ) -> Result { + 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( + &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>( + &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>( + &mut self, + renderer: &mut Self::Renderer, + surface: &mut Self::Surface, + viewport: &graphics::Viewport, + background_color: Color, + overlay: &[T], + ) -> Vec { + 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 iced_wgpu::primitive::pipeline::Renderer for Renderer +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 geometry::Renderer for Renderer + where + L: geometry::Renderer, + R: geometry::Renderer, + { + type Geometry = Geometry; + type Frame = 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 { + Left(L), + Right(R), + } + + impl geometry::Geometry for Geometry + where + L: geometry::Geometry, + R: geometry::Geometry, + { + type Cache = Geometry; + + 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 { + Left(L), + Right(R), + } + + impl geometry::Frame for Frame + where + L: geometry::Frame, + R: geometry::Frame, + { + type Geometry = 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) { + delegate!(self, frame, frame.fill(path, fill)); + } + + fn fill_rectangle( + &mut self, + top_left: Point, + size: Size, + fill: impl Into, + ) { + delegate!(self, frame, frame.fill_rectangle(top_left, size, fill)); + } + + fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>) { + delegate!(self, frame, frame.stroke(path, stroke)); + } + + fn fill_text(&mut self, text: impl Into) { + 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) { + delegate!(self, frame, frame.rotate(angle)); + } + + fn scale(&mut self, scale: impl Into) { + delegate!(self, frame, frame.scale(scale)); + } + + fn scale_nonuniform(&mut self, scale: impl Into) { + delegate!(self, frame, frame.scale_nonuniform(scale)); + } + } + + impl From> for Geometry + where + L: geometry::Frame, + R: geometry::Frame, + { + fn from(frame: Frame) -> Self { + match frame { + Frame::Left(frame) => Self::Left(frame.into()), + Frame::Right(frame) => Self::Right(frame.into()), + } + } + } +} -- cgit