summaryrefslogtreecommitdiffstats
path: root/renderer/src/fallback.rs
diff options
context:
space:
mode:
Diffstat (limited to 'renderer/src/fallback.rs')
-rw-r--r--renderer/src/fallback.rs637
1 files changed, 637 insertions, 0 deletions
diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs
new file mode 100644
index 00000000..6a169692
--- /dev/null
+++ b/renderer/src/fallback.rs
@@ -0,0 +1,637 @@
+//! Compose existing renderers and create type-safe fallback strategies.
+use crate::core::image;
+use crate::core::renderer;
+use crate::core::svg;
+use crate::core::{
+ self, Background, Color, Point, Radians, Rectangle, Size, Transformation,
+};
+use crate::graphics;
+use crate::graphics::compositor;
+use crate::graphics::mesh;
+
+use std::borrow::Cow;
+
+/// A renderer `A` with a fallback strategy `B`.
+///
+/// This type can be used to easily compose existing renderers and
+/// create custom, type-safe fallback strategies.
+#[derive(Debug)]
+pub enum Renderer<A, B> {
+ /// The primary rendering option.
+ Primary(A),
+ /// The secondary (or fallback) rendering option.
+ Secondary(B),
+}
+
+macro_rules! delegate {
+ ($renderer:expr, $name:ident, $body:expr) => {
+ match $renderer {
+ Self::Primary($name) => $body,
+ Self::Secondary($name) => $body,
+ }
+ };
+}
+
+impl<A, B> core::Renderer for Renderer<A, B>
+where
+ A: core::Renderer,
+ B: core::Renderer,
+{
+ fn fill_quad(
+ &mut self,
+ quad: renderer::Quad,
+ background: impl Into<Background>,
+ ) {
+ delegate!(self, renderer, renderer.fill_quad(quad, background.into()));
+ }
+
+ fn clear(&mut self) {
+ delegate!(self, renderer, renderer.clear());
+ }
+
+ fn start_layer(&mut self, bounds: Rectangle) {
+ delegate!(self, renderer, renderer.start_layer(bounds));
+ }
+
+ fn end_layer(&mut self) {
+ delegate!(self, renderer, renderer.end_layer());
+ }
+
+ fn start_transformation(&mut self, transformation: Transformation) {
+ delegate!(
+ self,
+ renderer,
+ renderer.start_transformation(transformation)
+ );
+ }
+
+ fn end_transformation(&mut self) {
+ delegate!(self, renderer, renderer.end_transformation());
+ }
+}
+
+impl<A, B> core::text::Renderer for Renderer<A, B>
+where
+ A: core::text::Renderer,
+ B: core::text::Renderer<
+ Font = A::Font,
+ Paragraph = A::Paragraph,
+ Editor = A::Editor,
+ >,
+{
+ type Font = A::Font;
+ type Paragraph = A::Paragraph;
+ type Editor = A::Editor;
+
+ const ICON_FONT: Self::Font = A::ICON_FONT;
+ const CHECKMARK_ICON: char = A::CHECKMARK_ICON;
+ const ARROW_DOWN_ICON: char = A::ARROW_DOWN_ICON;
+
+ fn default_font(&self) -> Self::Font {
+ delegate!(self, renderer, renderer.default_font())
+ }
+
+ fn default_size(&self) -> core::Pixels {
+ delegate!(self, renderer, renderer.default_size())
+ }
+
+ fn fill_paragraph(
+ &mut self,
+ text: &Self::Paragraph,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ delegate!(
+ self,
+ renderer,
+ renderer.fill_paragraph(text, position, color, clip_bounds)
+ );
+ }
+
+ fn fill_editor(
+ &mut self,
+ editor: &Self::Editor,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ delegate!(
+ self,
+ renderer,
+ renderer.fill_editor(editor, position, color, clip_bounds)
+ );
+ }
+
+ fn fill_text(
+ &mut self,
+ text: core::Text<String, Self::Font>,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ delegate!(
+ self,
+ renderer,
+ renderer.fill_text(text, position, color, clip_bounds)
+ );
+ }
+}
+
+impl<A, B> image::Renderer for Renderer<A, B>
+where
+ A: image::Renderer,
+ B: image::Renderer<Handle = A::Handle>,
+{
+ type Handle = A::Handle;
+
+ fn measure_image(&self, handle: &Self::Handle) -> Size<u32> {
+ delegate!(self, renderer, renderer.measure_image(handle))
+ }
+
+ fn draw_image(
+ &mut self,
+ handle: Self::Handle,
+ filter_method: image::FilterMethod,
+ bounds: Rectangle,
+ rotation: Radians,
+ opacity: f32,
+ ) {
+ delegate!(
+ self,
+ renderer,
+ renderer.draw_image(
+ handle,
+ filter_method,
+ bounds,
+ rotation,
+ opacity
+ )
+ );
+ }
+}
+
+impl<A, B> svg::Renderer for Renderer<A, B>
+where
+ A: svg::Renderer,
+ B: svg::Renderer,
+{
+ fn measure_svg(&self, handle: &svg::Handle) -> Size<u32> {
+ delegate!(self, renderer, renderer.measure_svg(handle))
+ }
+
+ fn draw_svg(
+ &mut self,
+ handle: svg::Handle,
+ color: Option<Color>,
+ bounds: Rectangle,
+ rotation: Radians,
+ opacity: f32,
+ ) {
+ delegate!(
+ self,
+ renderer,
+ renderer.draw_svg(handle, color, bounds, rotation, opacity)
+ );
+ }
+}
+
+impl<A, B> mesh::Renderer for Renderer<A, B>
+where
+ A: mesh::Renderer,
+ B: mesh::Renderer,
+{
+ fn draw_mesh(&mut self, mesh: graphics::Mesh) {
+ delegate!(self, renderer, renderer.draw_mesh(mesh));
+ }
+}
+
+/// A compositor `A` with a fallback strategy `B`.
+///
+/// It works analogously to [`Renderer`].
+#[derive(Debug)]
+pub enum Compositor<A, B>
+where
+ A: graphics::Compositor,
+ B: graphics::Compositor,
+{
+ /// The primary compositing option.
+ Primary(A),
+ /// The secondary (or fallback) compositing option.
+ Secondary(B),
+}
+
+/// A surface `A` with a fallback strategy `B`.
+///
+/// It works analogously to [`Renderer`].
+#[derive(Debug)]
+pub enum Surface<A, B> {
+ /// The primary surface option.
+ Primary(A),
+ /// The secondary (or fallback) surface option.
+ Secondary(B),
+}
+
+impl<A, B> graphics::Compositor for Compositor<A, B>
+where
+ A: graphics::Compositor,
+ B: graphics::Compositor,
+{
+ type Renderer = Renderer<A::Renderer, B::Renderer>;
+ type Surface = Surface<A::Surface, B::Surface>;
+
+ async fn with_backend<W: compositor::Window + Clone>(
+ settings: graphics::Settings,
+ compatible_window: W,
+ backend: Option<&str>,
+ ) -> Result<Self, graphics::Error> {
+ use std::env;
+
+ let backends = backend
+ .map(str::to_owned)
+ .or_else(|| env::var("ICED_BACKEND").ok());
+
+ let mut candidates: Vec<_> = backends
+ .map(|backends| {
+ backends
+ .split(',')
+ .filter(|candidate| !candidate.is_empty())
+ .map(str::to_owned)
+ .map(Some)
+ .collect()
+ })
+ .unwrap_or_default();
+
+ if candidates.is_empty() {
+ candidates.push(None);
+ }
+
+ let mut errors = vec![];
+
+ for backend in candidates.iter().map(Option::as_deref) {
+ match A::with_backend(settings, compatible_window.clone(), backend)
+ .await
+ {
+ Ok(compositor) => return Ok(Self::Primary(compositor)),
+ Err(error) => {
+ errors.push(error);
+ }
+ }
+
+ match B::with_backend(settings, compatible_window.clone(), backend)
+ .await
+ {
+ Ok(compositor) => return Ok(Self::Secondary(compositor)),
+ Err(error) => {
+ errors.push(error);
+ }
+ }
+ }
+
+ Err(graphics::Error::List(errors))
+ }
+
+ fn create_renderer(&self) -> Self::Renderer {
+ match self {
+ Self::Primary(compositor) => {
+ Renderer::Primary(compositor.create_renderer())
+ }
+ Self::Secondary(compositor) => {
+ Renderer::Secondary(compositor.create_renderer())
+ }
+ }
+ }
+
+ fn create_surface<W: compositor::Window + Clone>(
+ &mut self,
+ window: W,
+ width: u32,
+ height: u32,
+ ) -> Self::Surface {
+ match self {
+ Self::Primary(compositor) => Surface::Primary(
+ compositor.create_surface(window, width, height),
+ ),
+ Self::Secondary(compositor) => Surface::Secondary(
+ compositor.create_surface(window, width, height),
+ ),
+ }
+ }
+
+ fn configure_surface(
+ &mut self,
+ surface: &mut Self::Surface,
+ width: u32,
+ height: u32,
+ ) {
+ match (self, surface) {
+ (Self::Primary(compositor), Surface::Primary(surface)) => {
+ compositor.configure_surface(surface, width, height);
+ }
+ (Self::Secondary(compositor), Surface::Secondary(surface)) => {
+ compositor.configure_surface(surface, width, height);
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ fn load_font(&mut self, font: Cow<'static, [u8]>) {
+ delegate!(self, compositor, compositor.load_font(font));
+ }
+
+ fn fetch_information(&self) -> compositor::Information {
+ delegate!(self, compositor, compositor.fetch_information())
+ }
+
+ fn present<T: AsRef<str>>(
+ &mut self,
+ renderer: &mut Self::Renderer,
+ surface: &mut Self::Surface,
+ viewport: &graphics::Viewport,
+ background_color: Color,
+ overlay: &[T],
+ ) -> Result<(), compositor::SurfaceError> {
+ match (self, renderer, surface) {
+ (
+ Self::Primary(compositor),
+ Renderer::Primary(renderer),
+ Surface::Primary(surface),
+ ) => compositor.present(
+ renderer,
+ surface,
+ viewport,
+ background_color,
+ overlay,
+ ),
+ (
+ Self::Secondary(compositor),
+ Renderer::Secondary(renderer),
+ Surface::Secondary(surface),
+ ) => compositor.present(
+ renderer,
+ surface,
+ viewport,
+ background_color,
+ overlay,
+ ),
+ _ => unreachable!(),
+ }
+ }
+
+ fn screenshot<T: AsRef<str>>(
+ &mut self,
+ renderer: &mut Self::Renderer,
+ surface: &mut Self::Surface,
+ viewport: &graphics::Viewport,
+ background_color: Color,
+ overlay: &[T],
+ ) -> Vec<u8> {
+ match (self, renderer, surface) {
+ (
+ Self::Primary(compositor),
+ Renderer::Primary(renderer),
+ Surface::Primary(surface),
+ ) => compositor.screenshot(
+ renderer,
+ surface,
+ viewport,
+ background_color,
+ overlay,
+ ),
+ (
+ Self::Secondary(compositor),
+ Renderer::Secondary(renderer),
+ Surface::Secondary(surface),
+ ) => compositor.screenshot(
+ renderer,
+ surface,
+ viewport,
+ background_color,
+ overlay,
+ ),
+ _ => unreachable!(),
+ }
+ }
+}
+
+#[cfg(feature = "wgpu")]
+impl<A, B> iced_wgpu::primitive::Renderer for Renderer<A, B>
+where
+ A: iced_wgpu::primitive::Renderer,
+ B: core::Renderer,
+{
+ fn draw_primitive(
+ &mut self,
+ bounds: Rectangle,
+ primitive: impl iced_wgpu::Primitive,
+ ) {
+ match self {
+ Self::Primary(renderer) => {
+ renderer.draw_primitive(bounds, primitive);
+ }
+ Self::Secondary(_) => {
+ log::warn!(
+ "Custom shader primitive is not supported with this renderer."
+ );
+ }
+ }
+ }
+}
+
+#[cfg(feature = "geometry")]
+mod geometry {
+ use super::Renderer;
+ use crate::core::{Point, Radians, Rectangle, Size, Vector};
+ use crate::graphics::cache::{self, Cached};
+ use crate::graphics::geometry::{self, Fill, Path, Stroke, Text};
+
+ impl<A, B> geometry::Renderer for Renderer<A, B>
+ where
+ A: geometry::Renderer,
+ B: geometry::Renderer,
+ {
+ type Geometry = Geometry<A::Geometry, B::Geometry>;
+ type Frame = Frame<A::Frame, B::Frame>;
+
+ fn new_frame(&self, size: iced_graphics::core::Size) -> Self::Frame {
+ match self {
+ Self::Primary(renderer) => {
+ Frame::Primary(renderer.new_frame(size))
+ }
+ Self::Secondary(renderer) => {
+ Frame::Secondary(renderer.new_frame(size))
+ }
+ }
+ }
+
+ fn draw_geometry(&mut self, geometry: Self::Geometry) {
+ match (self, geometry) {
+ (Self::Primary(renderer), Geometry::Primary(geometry)) => {
+ renderer.draw_geometry(geometry);
+ }
+ (Self::Secondary(renderer), Geometry::Secondary(geometry)) => {
+ renderer.draw_geometry(geometry);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ #[derive(Debug, Clone)]
+ pub enum Geometry<A, B> {
+ Primary(A),
+ Secondary(B),
+ }
+
+ impl<A, B> Cached for Geometry<A, B>
+ where
+ A: Cached,
+ B: Cached,
+ {
+ type Cache = Geometry<A::Cache, B::Cache>;
+
+ fn load(cache: &Self::Cache) -> Self {
+ match cache {
+ Geometry::Primary(cache) => Self::Primary(A::load(cache)),
+ Geometry::Secondary(cache) => Self::Secondary(B::load(cache)),
+ }
+ }
+
+ fn cache(
+ self,
+ group: cache::Group,
+ previous: Option<Self::Cache>,
+ ) -> Self::Cache {
+ match (self, previous) {
+ (
+ Self::Primary(geometry),
+ Some(Geometry::Primary(previous)),
+ ) => Geometry::Primary(geometry.cache(group, Some(previous))),
+ (Self::Primary(geometry), None) => {
+ Geometry::Primary(geometry.cache(group, None))
+ }
+ (
+ Self::Secondary(geometry),
+ Some(Geometry::Secondary(previous)),
+ ) => Geometry::Secondary(geometry.cache(group, Some(previous))),
+ (Self::Secondary(geometry), None) => {
+ Geometry::Secondary(geometry.cache(group, None))
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ #[derive(Debug)]
+ pub enum Frame<A, B> {
+ Primary(A),
+ Secondary(B),
+ }
+
+ impl<A, B> geometry::frame::Backend for Frame<A, B>
+ where
+ A: geometry::frame::Backend,
+ B: geometry::frame::Backend,
+ {
+ type Geometry = Geometry<A::Geometry, B::Geometry>;
+
+ fn width(&self) -> f32 {
+ delegate!(self, frame, frame.width())
+ }
+
+ fn height(&self) -> f32 {
+ delegate!(self, frame, frame.height())
+ }
+
+ fn size(&self) -> Size {
+ delegate!(self, frame, frame.size())
+ }
+
+ fn center(&self) -> Point {
+ delegate!(self, frame, frame.center())
+ }
+
+ fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
+ delegate!(self, frame, frame.fill(path, fill));
+ }
+
+ fn fill_rectangle(
+ &mut self,
+ top_left: Point,
+ size: Size,
+ fill: impl Into<Fill>,
+ ) {
+ delegate!(self, frame, frame.fill_rectangle(top_left, size, fill));
+ }
+
+ fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
+ delegate!(self, frame, frame.stroke(path, stroke));
+ }
+
+ fn fill_text(&mut self, text: impl Into<Text>) {
+ delegate!(self, frame, frame.fill_text(text));
+ }
+
+ fn push_transform(&mut self) {
+ delegate!(self, frame, frame.push_transform());
+ }
+
+ fn pop_transform(&mut self) {
+ delegate!(self, frame, frame.pop_transform());
+ }
+
+ fn draft(&mut self, bounds: Rectangle) -> Self {
+ match self {
+ Self::Primary(frame) => Self::Primary(frame.draft(bounds)),
+ Self::Secondary(frame) => Self::Secondary(frame.draft(bounds)),
+ }
+ }
+
+ fn paste(&mut self, frame: Self, at: Point) {
+ match (self, frame) {
+ (Self::Primary(target), Self::Primary(source)) => {
+ target.paste(source, at);
+ }
+ (Self::Secondary(target), Self::Secondary(source)) => {
+ target.paste(source, at);
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ fn translate(&mut self, translation: Vector) {
+ delegate!(self, frame, frame.translate(translation));
+ }
+
+ fn rotate(&mut self, angle: impl Into<Radians>) {
+ delegate!(self, frame, frame.rotate(angle));
+ }
+
+ fn scale(&mut self, scale: impl Into<f32>) {
+ delegate!(self, frame, frame.scale(scale));
+ }
+
+ fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
+ delegate!(self, frame, frame.scale_nonuniform(scale));
+ }
+
+ fn into_geometry(self) -> Self::Geometry {
+ match self {
+ Frame::Primary(frame) => {
+ Geometry::Primary(frame.into_geometry())
+ }
+ Frame::Secondary(frame) => {
+ Geometry::Secondary(frame.into_geometry())
+ }
+ }
+ }
+ }
+}
+
+impl<A, B> compositor::Default for Renderer<A, B>
+where
+ A: compositor::Default,
+ B: compositor::Default,
+{
+ type Compositor = Compositor<A::Compositor, B::Compositor>;
+}