diff options
| author | 2024-03-25 21:36:44 +0100 | |
|---|---|---|
| committer | 2024-03-25 21:36:44 +0100 | |
| commit | a2a8381a49ac2dd1cd65eb382b9ee02bbfa17286 (patch) | |
| tree | e6c24928a42e23ff91eea0fc30b4fbbcb6da024b /renderer | |
| parent | 3013463baa71504488a20436beb3db87ecb66df0 (diff) | |
| parent | 6a4f5ac2081699f7cf20c917b367366ab49eeef1 (diff) | |
| download | iced-a2a8381a49ac2dd1cd65eb382b9ee02bbfa17286.tar.gz iced-a2a8381a49ac2dd1cd65eb382b9ee02bbfa17286.tar.bz2 iced-a2a8381a49ac2dd1cd65eb382b9ee02bbfa17286.zip  | |
Merge pull request #2351 from iced-rs/custom-renderer-injection
Type-Driven Renderer Fallback
Diffstat (limited to '')
| -rw-r--r-- | graphics/src/settings.rs (renamed from renderer/src/settings.rs) | 2 | ||||
| -rw-r--r-- | renderer/Cargo.toml | 9 | ||||
| -rw-r--r-- | renderer/src/compositor.rs | 269 | ||||
| -rw-r--r-- | renderer/src/fallback.rs | 570 | ||||
| -rw-r--r-- | renderer/src/geometry.rs | 210 | ||||
| -rw-r--r-- | renderer/src/geometry/cache.rs | 125 | ||||
| -rw-r--r-- | renderer/src/lib.rs | 306 | 
7 files changed, 606 insertions, 885 deletions
diff --git a/renderer/src/settings.rs b/graphics/src/settings.rs index 432eb8a0..68673536 100644 --- a/renderer/src/settings.rs +++ b/graphics/src/settings.rs @@ -1,5 +1,5 @@  use crate::core::{Font, Pixels}; -use crate::graphics::Antialiasing; +use crate::Antialiasing;  /// The settings of a Backend.  #[derive(Debug, Clone, Copy, PartialEq)] diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml index 5cce2427..39c19fa3 100644 --- a/renderer/Cargo.toml +++ b/renderer/Cargo.toml @@ -12,9 +12,10 @@ keywords.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"] +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"]  tracing = ["iced_wgpu?/tracing"]  web-colors = ["iced_wgpu?/web-colors"]  webgl = ["iced_wgpu?/webgl"] @@ -22,7 +23,9 @@ 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..ef9cc9a9 --- /dev/null +++ b/renderer/src/fallback.rs @@ -0,0 +1,570 @@ +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<L, R> { +    Left(L), +    Right(R), +} + +macro_rules! delegate { +    ($renderer:expr, $name:ident, $body:expr) => { +        match $renderer { +            Self::Left($name) => $body, +            Self::Right($name) => $body, +        } +    }; +} + +impl<L, R> core::Renderer for Renderer<L, R> +where +    L: core::Renderer, +    R: 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) { +        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<L, R> core::text::Renderer for Renderer<L, R> +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<L, R> image::Renderer for Renderer<L, R> +where +    L: image::Renderer, +    R: image::Renderer<Handle = L::Handle>, +{ +    type Handle = L::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, +    ) { +        delegate!( +            self, +            renderer, +            renderer.draw_image(handle, filter_method, bounds) +        ); +    } +} + +impl<L, R> svg::Renderer for Renderer<L, R> +where +    L: svg::Renderer, +    R: 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, +    ) { +        delegate!(self, renderer, renderer.draw_svg(handle, color, bounds)); +    } +} + +impl<L, R> mesh::Renderer for Renderer<L, R> +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<L, R> +where +    L: graphics::Compositor, +    R: graphics::Compositor, +{ +    Left(L), +    Right(R), +} + +pub enum Surface<L, R> { +    Left(L), +    Right(R), +} + +impl<L, R> graphics::Compositor for Compositor<L, R> +where +    L: graphics::Compositor, +    R: graphics::Compositor, +{ +    type Renderer = Renderer<L::Renderer, R::Renderer>; +    type Surface = Surface<L::Surface, R::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 L::with_backend(settings, compatible_window.clone(), backend) +                .await +            { +                Ok(compositor) => return Ok(Self::Left(compositor)), +                Err(error) => { +                    errors.push(error); +                } +            } + +            match R::with_backend(settings, compatible_window.clone(), backend) +                .await +            { +                Ok(compositor) => return Ok(Self::Right(compositor)), +                Err(error) => { +                    errors.push(error); +                } +            } +        } + +        Err(graphics::Error::List(errors)) +    } + +    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<W: compositor::Window + Clone>( +        &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<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::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<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::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<L, R> iced_wgpu::primitive::pipeline::Renderer for Renderer<L, R> +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}; +    use crate::graphics::Cached; + +    impl<L, R> geometry::Renderer for Renderer<L, R> +    where +        L: geometry::Renderer, +        R: geometry::Renderer, +    { +        type Geometry = Geometry<L::Geometry, R::Geometry>; +        type Frame = Frame<L::Frame, R::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<L, R> { +        Left(L), +        Right(R), +    } + +    impl<L, R> Cached for Geometry<L, R> +    where +        L: Cached, +        R: Cached, +    { +        type Cache = Geometry<L::Cache, R::Cache>; + +        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<L, R> { +        Left(L), +        Right(R), +    } + +    impl<L, R> geometry::frame::Backend for Frame<L, R> +    where +        L: geometry::frame::Backend, +        R: geometry::frame::Backend, +    { +        type Geometry = Geometry<L::Geometry, R::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, 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<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::Left(frame) => Geometry::Left(frame.into_geometry()), +                Frame::Right(frame) => Geometry::Right(frame.into_geometry()), +            } +        } +    } +} + +impl<L, R> compositor::Default for Renderer<L, R> +where +    L: compositor::Default, +    R: compositor::Default, +{ +    type Compositor = Compositor<L::Compositor, R::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..7c48995d 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -4,299 +4,51 @@  #[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(); - -                f(self); +pub type Renderer = renderer::Renderer; -                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) -        ); -    } - -    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: Text<'_, Self::Font>, -        position: Point, -        color: Color, -        clip_bounds: Rectangle, -    ) { -        delegate!( -            self, -            renderer, -            renderer.fill_text(text, position, color, clip_bounds) -        ); -    } -} - -#[cfg(feature = "image")] -impl crate::core::image::Renderer for Renderer { -    type Handle = crate::core::image::Handle; +/// The default graphics compositor for [`iced`]. +/// +/// [`iced`]: https://github.com/iced-rs/iced +pub type Compositor = renderer::Compositor; -    fn dimensions( -        &self, -        handle: &crate::core::image::Handle, -    ) -> core::Size<u32> { -        delegate!(self, renderer, renderer.dimensions(handle)) -    } +#[cfg(all(feature = "wgpu", feature = "tiny-skia"))] +mod renderer { +    pub type Renderer = crate::fallback::Renderer< +        iced_wgpu::Renderer, +        iced_tiny_skia::Renderer, +    >; -    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)); -    } +    pub type Compositor = crate::fallback::Compositor< +        iced_wgpu::window::Compositor, +        iced_tiny_skia::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(feature = "wgpu", not(feature = "tiny-skia")))] +mod renderer { +    pub type Renderer = iced_wgpu::Renderer; +    pub type Compositor = iced_wgpu::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(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 = "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); -            } -        } -    } +#[cfg(not(any(feature = "wgpu", feature = "tiny-skia")))] +mod renderer { +    pub type Renderer = (); +    pub type Compositor = ();  }  | 
