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>; +} | 
