summaryrefslogtreecommitdiffstats
path: root/renderer/src
diff options
context:
space:
mode:
Diffstat (limited to 'renderer/src')
-rw-r--r--renderer/src/compositor.rs269
-rw-r--r--renderer/src/fallback.rs637
-rw-r--r--renderer/src/geometry.rs210
-rw-r--r--renderer/src/geometry/cache.rs125
-rw-r--r--renderer/src/lib.rs314
-rw-r--r--renderer/src/settings.rs29
6 files changed, 673 insertions, 911 deletions
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..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>;
+}
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..220542e1 100644
--- a/renderer/src/lib.rs
+++ b/renderer/src/lib.rs
@@ -1,302 +1,60 @@
-#![forbid(rust_2018_idioms)]
-#![deny(unsafe_code, unused_results, rustdoc::broken_intra_doc_links)]
+//! The official renderer for iced.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[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();
+pub type Renderer = renderer::Renderer;
- f(self);
-
- 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)
- );
- }
+/// The default graphics compositor for [`iced`].
+///
+/// [`iced`]: https://github.com/iced-rs/iced
+pub type Compositor = renderer::Compositor;
- 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)
- );
- }
+#[cfg(all(feature = "wgpu", feature = "tiny-skia"))]
+mod renderer {
+ pub type Renderer = crate::fallback::Renderer<
+ iced_wgpu::Renderer,
+ iced_tiny_skia::Renderer,
+ >;
- 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)
- );
- }
+ pub type Compositor = crate::fallback::Compositor<
+ iced_wgpu::window::Compositor,
+ iced_tiny_skia::window::Compositor,
+ >;
}
-#[cfg(feature = "image")]
-impl crate::core::image::Renderer for Renderer {
- type Handle = crate::core::image::Handle;
-
- fn dimensions(
- &self,
- handle: &crate::core::image::Handle,
- ) -> core::Size<u32> {
- delegate!(self, renderer, renderer.dimensions(handle))
- }
-
- 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));
- }
+#[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 = "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(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 = "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(not(any(feature = "wgpu", feature = "tiny-skia")))]
+mod renderer {
+ #[cfg(not(debug_assertions))]
+ compile_error!(
+ "Cannot compile `iced_renderer` in release mode \
+ without a renderer feature enabled. \
+ Enable either the `wgpu` or `tiny-skia` feature, or both."
+ );
-#[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);
- }
- }
- }
+ pub type Renderer = ();
+ pub type Compositor = ();
}
diff --git a/renderer/src/settings.rs b/renderer/src/settings.rs
deleted file mode 100644
index 432eb8a0..00000000
--- a/renderer/src/settings.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use crate::core::{Font, Pixels};
-use crate::graphics::Antialiasing;
-
-/// The settings of a Backend.
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct Settings {
- /// The default [`Font`] to use.
- pub default_font: Font,
-
- /// The default size of text.
- ///
- /// By default, it will be set to `16.0`.
- pub default_text_size: Pixels,
-
- /// The antialiasing strategy that will be used for triangle primitives.
- ///
- /// By default, it is `None`.
- pub antialiasing: Option<Antialiasing>,
-}
-
-impl Default for Settings {
- fn default() -> Settings {
- Settings {
- default_font: Font::default(),
- default_text_size: Pixels(16.0),
- antialiasing: None,
- }
- }
-}