summaryrefslogblamecommitdiffstats
path: root/renderer/src/fallback.rs
blob: 82fc74ed176903a24ea6cbf4b975890a830a0b4d (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
                                                                        



                          
                                                                                




                                

                     









                                                                  




                                                  

                                            



         
                                            
     

                      












                                                                               

                                                                

     

                                                        

     





                                                                        

     

                                                                 


     
                                                  
     

                            



                                     
 


                                  
 


                                                     








                                                          





























                                                                       
                                             











                                                                  
                                             
     

                                           
 
                            




                                                                 

                                                                          


     
                                           
     

                     




                                                               

                                                                  


     
                                            
     

                      





                                                            




                                                  
     

                            
 



                                                       

 








                                                   

 
                                                    
     

                            
 

                                                       
 
                                                         
                                     
                             
                              
                                        























                                                                
                                                                               

                      
                                                                       




                                       
                                                                               

                      
                                                                         



                                       

         
                                          



                                                 

                                                               
             

                                                                 










                                                     





                                                                 









                                    
                                                                       

                                                                     
                                                                           





                                                                     



                                                                













                                                                   


                                            







                                    


                                              













                                      



                                      
















                                                                             





                                
                                                            
     
                                      
                      
 
                      

                          
                                             

                    
                                        
                                                           
             
                                   










                                                                                  
                                                                    
                                               
                                                                           
 
                                                    
         

                              
     

                                                           


                                                                             





                                                              




                                                               
                                                                           

                                                     
                                                                               






                                                     
                           


                             

     
                                        
         

                  
     
                                                  


                                              

                                                                              


             




                                          
                                    


                                                      
                                                                              
                                                    
                                                                  
                 


                                                        
                                                                                
                                                      
                                                                    

                                    



             



                          

     
                                                       
         

                                    
     
                                                           

































                                                                               












                                                              



                                                          

                                                                              

         

                                                                        

         







                                                           
                                                        
                        

                                                                               


             
                                          
                                 
                                                                   
                                         
                 
                                                                       
                                         



















                                                                  
 

                                                  





                                                              



             
 
                                                 
     

                           
 
                                                               
 
//! 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, Image, Point, Rectangle, Size, Svg, 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, image: Image<A::Handle>, bounds: Rectangle) {
        delegate!(self, renderer, renderer.draw_image(image, bounds));
    }
}

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, svg: Svg, bounds: Rectangle) {
        delegate!(self, renderer, renderer.draw_svg(svg, bounds));
    }
}

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,
        viewport: &graphics::Viewport,
        background_color: Color,
        overlay: &[T],
    ) -> Vec<u8> {
        match (self, renderer) {
            (Self::Primary(compositor), Renderer::Primary(renderer)) => {
                compositor.screenshot(
                    renderer,
                    viewport,
                    background_color,
                    overlay,
                )
            }
            (Self::Secondary(compositor), Renderer::Secondary(renderer)) => {
                compositor.screenshot(
                    renderer,
                    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, Svg, Vector};
    use crate::graphics::cache::{self, Cached};
    use crate::graphics::geometry::{self, Fill, Image, 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 stroke_rectangle<'a>(
            &mut self,
            top_left: Point,
            size: Size,
            stroke: impl Into<Stroke<'a>>,
        ) {
            delegate!(
                self,
                frame,
                frame.stroke_rectangle(top_left, size, stroke)
            );
        }

        fn fill_text(&mut self, text: impl Into<Text>) {
            delegate!(self, frame, frame.fill_text(text));
        }

        fn draw_image(&mut self, bounds: Rectangle, image: impl Into<Image>) {
            delegate!(self, frame, frame.draw_image(bounds, image));
        }

        fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) {
            delegate!(self, frame, frame.draw_svg(bounds, svg));
        }

        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) {
            match (self, frame) {
                (Self::Primary(target), Self::Primary(source)) => {
                    target.paste(source);
                }
                (Self::Secondary(target), Self::Secondary(source)) => {
                    target.paste(source);
                }
                _ => 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>;
}