summaryrefslogtreecommitdiffstats
path: root/renderer
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-03-21 22:27:17 +0100
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-03-21 22:27:17 +0100
commit3645d34d6a1ba1247238e830e9eefd52d9e5b986 (patch)
tree2d38961161df0a85c1667474b2b696aab86b7160 /renderer
parent7e4ae8450e1f28c15717ca5ca9748981af9c9541 (diff)
downloadiced-3645d34d6a1ba1247238e830e9eefd52d9e5b986.tar.gz
iced-3645d34d6a1ba1247238e830e9eefd52d9e5b986.tar.bz2
iced-3645d34d6a1ba1247238e830e9eefd52d9e5b986.zip
Implement composable, type-safe renderer fallback
Diffstat (limited to 'renderer')
-rw-r--r--renderer/src/compositor.rs299
-rw-r--r--renderer/src/custom.rs4
-rw-r--r--renderer/src/fallback.rs562
-rw-r--r--renderer/src/lib.rs368
-rw-r--r--renderer/src/settings.rs21
5 files changed, 607 insertions, 647 deletions
diff --git a/renderer/src/compositor.rs b/renderer/src/compositor.rs
index 3d0b3ad0..8b137891 100644
--- a/renderer/src/compositor.rs
+++ b/renderer/src/compositor.rs
@@ -1,300 +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),
- #[cfg(feature = "custom")]
- Custom(Box<dyn crate::custom::Compositor>),
-}
-
-pub enum Surface {
- TinySkia(iced_tiny_skia::window::Surface),
- #[cfg(feature = "wgpu")]
- Wgpu(iced_wgpu::window::Surface<'static>),
- #[cfg(feature = "custom")]
- Custom(Box<dyn crate::custom::Surface>),
-}
-
-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())
- }
- #[cfg(feature = "custom")]
- Compositor::Custom(compositor) => {
- Renderer::Custom(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))
- }
- #[cfg(feature = "custom")]
- Self::Custom(compositor) => Surface::Custom(
- compositor.create_surface(Box::new(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);
- }
- #[cfg(feature = "custom")]
- (Self::Custom(compositor), Surface::Custom(surface)) => {
- compositor.configure_surface(surface.as_mut(), 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(),
- #[cfg(feature = "custom")]
- Self::Custom(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,
- )
- }),
-
- #[cfg(feature = "custom")]
- (
- Self::Custom(compositor),
- crate::Renderer::Custom(renderer),
- Surface::Custom(surface),
- ) => renderer.present(
- surface.as_mut(),
- viewport,
- background_color,
- compositor.as_mut(),
- ),
- #[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/custom.rs b/renderer/src/custom.rs
index 04090ccb..4addeb86 100644
--- a/renderer/src/custom.rs
+++ b/renderer/src/custom.rs
@@ -94,8 +94,6 @@ pub trait Renderer {
#[cfg(feature = "geometry")]
pub trait Frame: std::any::Any {
- fn new(&self, size: Size) -> Box<dyn Frame>;
-
fn width(&self) -> f32;
fn height(&self) -> f32;
@@ -108,7 +106,7 @@ pub trait Frame: std::any::Any {
fn fill_rectangle(&mut self, top_left: Point, size: Size, fill: Fill);
- fn stroke<'a>(&mut self, path: &Path, stroke: Stroke<'a>);
+ fn stroke(&mut self, path: &Path, stroke: Stroke<'_>);
fn fill_text(&mut self, text: geometry::Text);
diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs
new file mode 100644
index 00000000..a4c725c0
--- /dev/null
+++ b/renderer/src/fallback.rs
@@ -0,0 +1,562 @@
+use crate::core::image;
+use crate::core::renderer;
+use crate::core::svg;
+use crate::core::{
+ self, Background, Color, Point, Rectangle, Size, Transformation,
+};
+use crate::graphics;
+use crate::graphics::compositor;
+use crate::graphics::mesh;
+
+pub enum Renderer<L, R>
+where
+ L: core::Renderer,
+ R: core::Renderer,
+{
+ Left(L),
+ Right(R),
+}
+
+macro_rules! delegate {
+ ($renderer:expr, $name:ident, $body:expr) => {
+ match $renderer {
+ Self::Left($name) => $body,
+ Self::Right($name) => $body,
+ }
+ };
+}
+
+impl<L, R> Renderer<L, R>
+where
+ L: core::Renderer,
+ R: core::Renderer,
+{
+ #[cfg(feature = "geometry")]
+ pub fn draw_geometry<Geometry>(
+ &mut self,
+ layers: impl IntoIterator<Item = Geometry>,
+ ) where
+ L: graphics::geometry::Renderer,
+ R: graphics::geometry::Renderer,
+
+ Geometry: Into<geometry::Geometry<L::Geometry, R::Geometry>>,
+ {
+ use graphics::geometry::Renderer;
+
+ for layer in layers {
+ <Self as Renderer>::draw_geometry(self, layer.into());
+ }
+ }
+}
+
+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,
+ L::Settings: From<crate::Settings>,
+ R::Settings: From<crate::Settings>,
+{
+ type Settings = crate::Settings;
+ type Renderer = Renderer<L::Renderer, R::Renderer>;
+ type Surface = Surface<L::Surface, R::Surface>;
+
+ async fn new<W: compositor::Window + Clone>(
+ settings: Self::Settings,
+ compatible_window: W,
+ ) -> Result<Self, graphics::Error> {
+ if let Ok(left) = L::new(settings.into(), compatible_window.clone())
+ .await
+ .map(Self::Left)
+ {
+ return Ok(left);
+ }
+
+ R::new(settings.into(), compatible_window)
+ .await
+ .map(Self::Right)
+ }
+
+ fn create_renderer(&self) -> Self::Renderer {
+ match self {
+ Self::Left(compositor) => {
+ Renderer::Left(compositor.create_renderer())
+ }
+ Self::Right(compositor) => {
+ Renderer::Right(compositor.create_renderer())
+ }
+ }
+ }
+
+ fn create_surface<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};
+
+ 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> geometry::Geometry for Geometry<L, R>
+ where
+ L: geometry::Geometry,
+ R: geometry::Geometry,
+ {
+ 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 for Frame<L, R>
+ where
+ L: geometry::Frame,
+ R: geometry::Frame,
+ {
+ 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));
+ }
+ }
+
+ impl<L, R> From<Frame<L, R>> for Geometry<L::Geometry, R::Geometry>
+ where
+ L: geometry::Frame,
+ R: geometry::Frame,
+ {
+ fn from(frame: Frame<L, R>) -> Self {
+ match frame {
+ Frame::Left(frame) => Self::Left(frame.into()),
+ Frame::Right(frame) => Self::Right(frame.into()),
+ }
+ }
+ }
+}
diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs
index 67096115..f8aa1157 100644
--- a/renderer/src/lib.rs
+++ b/renderer/src/lib.rs
@@ -4,364 +4,42 @@
#[cfg(feature = "wgpu")]
pub use iced_wgpu as wgpu;
-pub mod compositor;
-pub mod custom;
-
-#[cfg(feature = "geometry")]
-pub mod geometry;
+pub mod fallback;
mod settings;
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;
+pub use iced_graphics::geometry;
-use std::borrow::Cow;
+pub use settings::Settings;
/// 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),
- #[cfg(feature = "custom")]
- Custom(Box<dyn custom::Renderer>),
-}
-
-macro_rules! delegate {
- ($renderer:expr, $name:ident, $body:expr) => {
- match $renderer {
- Self::TinySkia($name) => $body,
- #[cfg(feature = "wgpu")]
- Self::Wgpu($name) => $body,
- #[cfg(feature = "custom")]
- Self::Custom($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),
- ));
- }
- #[cfg(feature = "custom")]
- Self::Custom(renderer) => {
- renderer.draw_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!(),
- }
- }
- #[cfg(feature = "custom")]
- Self::Custom(renderer) => {
- renderer.start_layer();
-
- f(self);
-
- match self {
- Self::Custom(renderer) => {
- renderer.end_layer(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);
-
- match self {
- #[cfg(feature = "wgpu")]
- Self::Wgpu(renderer) => {
- renderer.end_transformation(primitives, transformation);
- }
- _ => unreachable!(),
- }
- }
- #[cfg(feature = "custom")]
- Self::Custom(renderer) => {
- renderer.start_transformation();
-
- f(self);
-
- match self {
- Self::Custom(renderer) => {
- renderer.end_transformation(transformation);
- }
- _ => unreachable!(),
- }
- }
- }
- }
-
- 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());
- }
-}
+#[cfg(not(feature = "wgpu"))]
+pub type Renderer = iced_tiny_skia::Renderer;
-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;
-
- fn measure_image(
- &self,
- handle: &crate::core::image::Handle,
- ) -> core::Size<u32> {
- delegate!(self, renderer, renderer.measure_image(handle))
- }
-
- fn draw_image(
- &mut self,
- handle: crate::core::image::Handle,
- filter_method: crate::core::image::FilterMethod,
- bounds: Rectangle,
- ) {
- delegate!(
- self,
- renderer,
- renderer.draw_image(handle, filter_method, bounds)
- );
- }
-}
-
-#[cfg(feature = "svg")]
-impl crate::core::svg::Renderer for Renderer {
- fn measure_svg(
- &self,
- handle: &crate::core::svg::Handle,
- ) -> core::Size<u32> {
- delegate!(self, renderer, renderer.measure_svg(handle))
- }
-
- fn draw_svg(
- &mut self,
- handle: crate::core::svg::Handle,
- color: Option<crate::core::Color>,
- bounds: Rectangle,
- ) {
- delegate!(self, renderer, renderer.draw_svg(handle, color, bounds));
- }
-}
-
-#[cfg(feature = "geometry")]
-impl crate::graphics::geometry::Renderer for Renderer {
- type Geometry = crate::Geometry;
+/// The default graphics renderer for [`iced`].
+///
+/// [`iced`]: https://github.com/iced-rs/iced
+#[cfg(feature = "wgpu")]
+pub type Renderer =
+ fallback::Renderer<iced_wgpu::Renderer, iced_tiny_skia::Renderer>;
- 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 = "custom")]
- crate::Geometry::Custom(_) => 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(feature = "custom")]
- crate::Geometry::Custom(_) => unreachable!(),
- }
- }
- }
- #[cfg(feature = "custom")]
- Self::Custom(renderer) => {
- for layer in layers {
- match layer {
- crate::Geometry::Custom(geometry) => {
- renderer.draw_geometry(geometry);
- }
- crate::Geometry::TinySkia(_) => unreachable!(),
- #[cfg(feature = "wgpu")]
- crate::Geometry::Wgpu(_) => unreachable!(),
- }
- }
- }
- }
- }
-}
+/// The default graphics compositor for [`iced`].
+///
+/// [`iced`]: https://github.com/iced-rs/iced
+#[cfg(not(feature = "wgpu"))]
+pub type Compositor = iced_tiny_skia::window::Compositor;
+/// The default graphics renderer for [`iced`].
+///
+/// [`iced`]: https://github.com/iced-rs/iced
#[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(feature = "custom")]
- Self::Custom(_renderer) => {
- log::warn!(
- "Custom shader primitive is unavailable with custom renderer."
- );
- }
- }
- }
-}
+pub type Compositor = fallback::Compositor<
+ iced_wgpu::window::Compositor,
+ iced_tiny_skia::window::Compositor,
+>;
diff --git a/renderer/src/settings.rs b/renderer/src/settings.rs
index 432eb8a0..940daa15 100644
--- a/renderer/src/settings.rs
+++ b/renderer/src/settings.rs
@@ -27,3 +27,24 @@ impl Default for Settings {
}
}
}
+
+impl From<Settings> for iced_tiny_skia::Settings {
+ fn from(settings: Settings) -> Self {
+ Self {
+ default_font: settings.default_font,
+ default_text_size: settings.default_text_size,
+ }
+ }
+}
+
+#[cfg(feature = "wgpu")]
+impl From<Settings> for iced_wgpu::Settings {
+ fn from(settings: Settings) -> Self {
+ Self {
+ default_font: settings.default_font,
+ default_text_size: settings.default_text_size,
+ antialiasing: settings.antialiasing,
+ ..iced_wgpu::Settings::default()
+ }
+ }
+}