Implement composable, type-safe renderer fallback

This commit is contained in:
Héctor Ramón Jiménez 2024-03-21 22:27:17 +01:00
parent 7e4ae8450e
commit 3645d34d6a
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
35 changed files with 1474 additions and 1210 deletions

View file

@ -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`")
}
}
}
}

View file

@ -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);

562
renderer/src/fallback.rs Normal file
View file

@ -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()),
}
}
}
}

View file

@ -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;
pub use iced_graphics::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 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());
}
}
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;
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!(),
}
}
}
}
}
}
#[cfg(not(feature = "wgpu"))]
pub type Renderer = iced_tiny_skia::Renderer;
/// 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 Renderer =
fallback::Renderer<iced_wgpu::Renderer, iced_tiny_skia::Renderer>;
/// 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")]
pub type Compositor = fallback::Compositor<
iced_wgpu::window::Compositor,
iced_tiny_skia::window::Compositor,
>;

View file

@ -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()
}
}
}