Draft support for dynamic custom renderer injection

This commit is contained in:
Héctor Ramón Jiménez 2024-03-21 05:52:48 +01:00
parent 2b00e8b145
commit 188db4da48
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
12 changed files with 316 additions and 35 deletions

View file

@ -186,11 +186,11 @@ pub trait Renderer: crate::Renderer {
type Handle: Clone + Hash;
/// Returns the dimensions of an image for the given [`Handle`].
fn dimensions(&self, handle: &Self::Handle) -> Size<u32>;
fn measure_image(&self, handle: &Self::Handle) -> Size<u32>;
/// Draws an image with the given [`Handle`] and inside the provided
/// `bounds`.
fn draw(
fn draw_image(
&mut self,
handle: Self::Handle,
filter_method: FilterMethod,

View file

@ -10,7 +10,7 @@ use crate::{
};
/// A component that can be used by widgets to draw themselves on a screen.
pub trait Renderer: Sized {
pub trait Renderer {
/// Draws the primitives recorded in the given closure in a new layer.
///
/// The layer will clip its contents to the provided `bounds`.

View file

@ -91,8 +91,13 @@ impl std::fmt::Debug for Data {
/// [renderer]: crate::renderer
pub trait Renderer: crate::Renderer {
/// Returns the default dimensions of an SVG for the given [`Handle`].
fn dimensions(&self, handle: &Handle) -> Size<u32>;
fn measure_svg(&self, handle: &Handle) -> Size<u32>;
/// Draws an SVG with the given [`Handle`], an optional [`Color`] filter, and inside the provided `bounds`.
fn draw(&mut self, handle: Handle, color: Option<Color>, bounds: Rectangle);
fn draw_svg(
&mut self,
handle: Handle,
color: Option<Color>,
bounds: Rectangle,
);
}

View file

@ -211,11 +211,11 @@ where
{
type Handle = image::Handle;
fn dimensions(&self, handle: &image::Handle) -> Size<u32> {
fn measure_image(&self, handle: &image::Handle) -> Size<u32> {
self.backend().dimensions(handle)
}
fn draw(
fn draw_image(
&mut self,
handle: image::Handle,
filter_method: image::FilterMethod,
@ -233,11 +233,11 @@ impl<B> svg::Renderer for Renderer<B>
where
B: Backend + backend::Svg,
{
fn dimensions(&self, handle: &svg::Handle) -> Size<u32> {
fn measure_svg(&self, handle: &svg::Handle) -> Size<u32> {
self.backend().viewport_dimensions(handle)
}
fn draw(
fn draw_svg(
&mut self,
handle: svg::Handle,
color: Option<Color>,

View file

@ -1,4 +1,5 @@
use crate::core::Color;
use crate::custom;
use crate::graphics::compositor::{Information, SurfaceError, Window};
use crate::graphics::{Error, Viewport};
use crate::{Renderer, Settings};
@ -10,12 +11,14 @@ pub enum Compositor {
TinySkia(iced_tiny_skia::window::Compositor),
#[cfg(feature = "wgpu")]
Wgpu(iced_wgpu::window::Compositor),
Custom(Box<dyn custom::Compositor>),
}
pub enum Surface {
TinySkia(iced_tiny_skia::window::Surface),
#[cfg(feature = "wgpu")]
Wgpu(iced_wgpu::window::Surface<'static>),
Custom(Box<dyn custom::Surface>),
}
impl crate::graphics::Compositor for Compositor {
@ -56,6 +59,9 @@ impl crate::graphics::Compositor for Compositor {
Compositor::Wgpu(compositor) => {
Renderer::Wgpu(compositor.create_renderer())
}
Compositor::Custom(compositor) => {
Renderer::Custom(compositor.create_renderer())
}
}
}
@ -73,6 +79,9 @@ impl crate::graphics::Compositor for Compositor {
Self::Wgpu(compositor) => {
Surface::Wgpu(compositor.create_surface(window, width, height))
}
Self::Custom(compositor) => Surface::Custom(
compositor.create_surface(Box::new(window), width, height),
),
}
}
@ -90,6 +99,9 @@ impl crate::graphics::Compositor for Compositor {
(Self::Wgpu(compositor), Surface::Wgpu(surface)) => {
compositor.configure_surface(surface, width, height);
}
(Self::Custom(compositor), Surface::Custom(surface)) => {
compositor.configure_surface(surface, width, height);
}
#[allow(unreachable_patterns)]
_ => panic!(
"The provided surface is not compatible with the compositor."
@ -102,6 +114,7 @@ impl crate::graphics::Compositor for Compositor {
Self::TinySkia(compositor) => compositor.fetch_information(),
#[cfg(feature = "wgpu")]
Self::Wgpu(compositor) => compositor.fetch_information(),
Self::Custom(compositor) => compositor.fetch_information(),
}
}
@ -144,6 +157,18 @@ impl crate::graphics::Compositor for Compositor {
overlay,
)
}),
#[cfg(feature = "wgpu")]
(
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 \

164
renderer/src/custom.rs Normal file
View file

@ -0,0 +1,164 @@
use crate::core::image;
use crate::core::renderer;
use crate::core::svg;
use crate::core::text::Text;
use crate::core::{
Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
};
use crate::graphics::compositor;
use crate::graphics::text::{Editor, Paragraph};
use crate::graphics::{Mesh, Viewport};
#[cfg(feature = "geometry")]
use crate::graphics::geometry::{self, Fill, Path, Stroke};
use std::borrow::Cow;
pub trait Renderer {
fn draw_mesh(&mut self, mesh: Mesh);
fn start_layer(&mut self);
fn end_layer(&mut self, bounds: Rectangle);
fn start_transformation(&mut self);
fn end_transformation(&mut self, transformation: Transformation);
fn fill_quad(&mut self, quad: renderer::Quad, background: Background);
fn clear(&mut self);
fn default_font(&self) -> Font;
fn default_size(&self) -> Pixels;
fn load_font(&mut self, bytes: Cow<'static, [u8]>);
fn fill_paragraph(
&mut self,
paragraph: &Paragraph,
position: Point,
color: Color,
clip_bounds: Rectangle,
);
fn fill_editor(
&mut self,
editor: &Editor,
position: Point,
color: Color,
clip_bounds: Rectangle,
);
fn fill_text(
&mut self,
text: Text<'_, Font>,
position: Point,
color: Color,
clip_bounds: Rectangle,
);
fn measure_image(&self, handle: &image::Handle) -> Size<u32>;
fn draw_image(
&mut self,
handle: image::Handle,
filter_method: image::FilterMethod,
bounds: Rectangle,
);
fn measure_svg(&self, handle: &svg::Handle) -> Size<u32>;
fn draw_svg(
&mut self,
handle: crate::core::svg::Handle,
color: Option<crate::core::Color>,
bounds: Rectangle,
);
#[cfg(feature = "geometry")]
fn new_frame(&self, size: Size) -> Box<dyn Frame>;
#[cfg(feature = "geometry")]
fn draw_geometry(&mut self, geometry: Box<dyn Geometry>);
fn present(
&mut self,
surface: &mut dyn Surface,
viewport: &Viewport,
background_color: Color,
compositor: &mut dyn Compositor,
) -> Result<(), compositor::SurfaceError>;
}
#[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;
fn size(&self) -> Size;
fn center(&self) -> Point;
fn fill(&mut self, path: &Path, fill: Fill);
fn fill_rectangle(&mut self, top_left: Point, size: Size, fill: Fill);
fn stroke<'a>(&mut self, path: &Path, stroke: Stroke<'a>);
fn fill_text(&mut self, text: geometry::Text);
fn translate(&mut self, translation: crate::core::Vector);
fn rotate(&mut self, angle: crate::core::Radians);
fn scale(&mut self, scale: f32);
fn scale_nonuniform(&mut self, scale: crate::core::Vector);
fn push_transform(&mut self);
fn pop_transform(&mut self);
fn clip(&mut self, frame: Box<dyn Frame>, origin: Point);
fn into_geometry(self: Box<Self>) -> Box<dyn Geometry>;
}
#[cfg(feature = "geometry")]
pub trait Geometry: std::any::Any + std::fmt::Debug {
fn transform(
self: Box<Self>,
transformation: Transformation,
) -> Box<dyn Geometry>;
fn cache(self: Box<Self>) -> std::sync::Arc<dyn Geometry>;
fn load(self: std::sync::Arc<Self>) -> Box<dyn Geometry>;
}
pub trait Compositor: std::any::Any {
fn create_renderer(&self) -> Box<dyn Renderer>;
fn create_surface(
&mut self,
window: Box<dyn compositor::Window>,
width: u32,
height: u32,
) -> Box<dyn Surface>;
fn configure_surface(
&mut self,
surface: &mut Box<dyn Surface>,
width: u32,
height: u32,
);
fn fetch_information(&self) -> compositor::Information;
}
pub trait Surface: std::any::Any {}

View file

@ -3,6 +3,7 @@ mod cache;
pub use cache::Cache;
use crate::core::{Point, Radians, Rectangle, Size, Transformation, Vector};
use crate::custom;
use crate::graphics::geometry::{Fill, Path, Stroke, Text};
use crate::Renderer;
@ -12,6 +13,7 @@ macro_rules! delegate {
Self::TinySkia($name) => $body,
#[cfg(feature = "wgpu")]
Self::Wgpu($name) => $body,
Self::Custom($name) => $body,
}
};
}
@ -20,6 +22,7 @@ pub enum Geometry {
TinySkia(iced_tiny_skia::Primitive),
#[cfg(feature = "wgpu")]
Wgpu(iced_wgpu::Primitive),
Custom(Box<dyn custom::Geometry>),
}
impl Geometry {
@ -32,6 +35,9 @@ impl Geometry {
Self::Wgpu(primitive) => {
Self::Wgpu(primitive.transform(transformation))
}
Self::Custom(geometry) => {
Self::Custom(geometry.transform(transformation))
}
}
}
}
@ -40,6 +46,7 @@ pub enum Frame {
TinySkia(iced_tiny_skia::geometry::Frame),
#[cfg(feature = "wgpu")]
Wgpu(iced_wgpu::geometry::Frame),
Custom(Box<dyn custom::Frame>),
}
impl Frame {
@ -52,6 +59,9 @@ impl Frame {
Renderer::Wgpu(_) => {
Frame::Wgpu(iced_wgpu::geometry::Frame::new(size))
}
Renderer::Custom(renderer) => {
Frame::Custom(renderer.new_frame(size))
}
}
}
@ -82,7 +92,7 @@ impl Frame {
/// 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));
delegate!(self, frame, frame.fill(path, fill.into()));
}
/// Draws an axis-aligned rectangle given its top-left corner coordinate and
@ -93,13 +103,17 @@ impl Frame {
size: Size,
fill: impl Into<Fill>,
) {
delegate!(self, frame, frame.fill_rectangle(top_left, size, fill));
delegate!(
self,
frame,
frame.fill_rectangle(top_left, size, fill.into())
);
}
/// 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));
delegate!(self, frame, frame.stroke(path, stroke.into()));
}
/// Draws the characters of the given [`Text`] on the [`Frame`], filling
@ -116,7 +130,7 @@ impl Frame {
/// 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));
delegate!(self, frame, frame.fill_text(text.into()));
}
/// Stores the current transform of the [`Frame`] and executes the given
@ -155,6 +169,7 @@ impl Frame {
Self::Wgpu(_) => {
Self::Wgpu(iced_wgpu::geometry::Frame::new(region.size()))
}
Self::Custom(frame) => Self::Custom(frame.new(region.size())),
};
let result = f(&mut frame);
@ -169,6 +184,9 @@ impl Frame {
(Self::Wgpu(target), Self::Wgpu(frame)) => {
target.clip(frame, origin);
}
(Self::Custom(target), Self::Custom(frame)) => {
target.clip(frame, origin);
}
#[allow(unreachable_patterns)]
_ => unreachable!(),
};
@ -185,19 +203,19 @@ impl Frame {
/// 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));
delegate!(self, frame, frame.rotate(angle.into()));
}
/// 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));
delegate!(self, frame, frame.scale(scale.into()));
}
/// 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));
delegate!(self, frame, frame.scale_nonuniform(scale.into()));
}
pub fn into_geometry(self) -> Geometry {
@ -205,6 +223,7 @@ impl Frame {
Self::TinySkia(frame) => Geometry::TinySkia(frame.into_primitive()),
#[cfg(feature = "wgpu")]
Self::Wgpu(frame) => Geometry::Wgpu(frame.into_primitive()),
Self::Custom(frame) => Geometry::Custom(frame.into_geometry()),
}
}
}

View file

@ -1,4 +1,5 @@
use crate::core::Size;
use crate::custom;
use crate::geometry::{Frame, Geometry};
use crate::Renderer;
@ -29,6 +30,7 @@ enum Internal {
TinySkia(Arc<iced_tiny_skia::Primitive>),
#[cfg(feature = "wgpu")]
Wgpu(Arc<iced_wgpu::Primitive>),
Custom(Arc<dyn custom::Geometry>),
}
impl Cache {
@ -82,6 +84,9 @@ impl Cache {
content: primitive.clone(),
});
}
Internal::Custom(geometry) => {
return Geometry::Custom(geometry.clone().load())
}
}
}
}
@ -100,6 +105,9 @@ impl Cache {
Geometry::Wgpu(primitive) => {
Internal::Wgpu(Arc::new(primitive))
}
Geometry::Custom(geometry) => {
Internal::Custom(geometry.cache())
}
}
};
@ -120,6 +128,7 @@ impl Cache {
content: primitive,
})
}
Internal::Custom(geometry) => Geometry::Custom(geometry.load()),
}
}
}

View file

@ -5,6 +5,7 @@
pub use iced_wgpu as wgpu;
pub mod compositor;
pub mod custom;
#[cfg(feature = "geometry")]
pub mod geometry;
@ -38,6 +39,7 @@ pub enum Renderer {
TinySkia(iced_tiny_skia::Renderer),
#[cfg(feature = "wgpu")]
Wgpu(iced_wgpu::Renderer),
Custom(Box<dyn custom::Renderer>),
}
macro_rules! delegate {
@ -46,6 +48,7 @@ macro_rules! delegate {
Self::TinySkia($name) => $body,
#[cfg(feature = "wgpu")]
Self::Wgpu($name) => $body,
Self::Custom($name) => $body,
}
};
}
@ -62,6 +65,9 @@ impl Renderer {
iced_wgpu::primitive::Custom::Mesh(mesh),
));
}
Self::Custom(renderer) => {
renderer.draw_mesh(mesh);
}
}
}
}
@ -96,6 +102,18 @@ impl core::Renderer for Renderer {
_ => unreachable!(),
}
}
Self::Custom(renderer) => {
renderer.start_layer();
f(self);
match self {
Self::Custom(renderer) => {
renderer.end_layer(bounds);
}
_ => unreachable!(),
}
}
}
}
@ -132,6 +150,18 @@ impl core::Renderer for Renderer {
_ => unreachable!(),
}
}
Self::Custom(renderer) => {
renderer.start_transformation();
f(self);
match self {
Self::Custom(renderer) => {
renderer.end_transformation(transformation);
}
_ => unreachable!(),
}
}
}
}
@ -140,7 +170,7 @@ impl core::Renderer for Renderer {
quad: renderer::Quad,
background: impl Into<Background>,
) {
delegate!(self, renderer, renderer.fill_quad(quad, background));
delegate!(self, renderer, renderer.fill_quad(quad, background.into()));
}
fn clear(&mut self) {
@ -216,36 +246,43 @@ impl text::Renderer for Renderer {
impl crate::core::image::Renderer for Renderer {
type Handle = crate::core::image::Handle;
fn dimensions(
fn measure_image(
&self,
handle: &crate::core::image::Handle,
) -> core::Size<u32> {
delegate!(self, renderer, renderer.dimensions(handle))
delegate!(self, renderer, renderer.measure_image(handle))
}
fn draw(
fn draw_image(
&mut self,
handle: crate::core::image::Handle,
filter_method: crate::core::image::FilterMethod,
bounds: Rectangle,
) {
delegate!(self, renderer, renderer.draw(handle, filter_method, bounds));
delegate!(
self,
renderer,
renderer.draw_image(handle, filter_method, bounds)
);
}
}
#[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 measure_svg(
&self,
handle: &crate::core::svg::Handle,
) -> core::Size<u32> {
delegate!(self, renderer, renderer.measure_svg(handle))
}
fn draw(
fn draw_svg(
&mut self,
handle: crate::core::svg::Handle,
color: Option<crate::core::Color>,
bounds: Rectangle,
) {
delegate!(self, renderer, renderer.draw(handle, color, bounds));
delegate!(self, renderer, renderer.draw_svg(handle, color, bounds));
}
}
@ -263,6 +300,7 @@ impl crate::graphics::geometry::Renderer for Renderer {
}
#[cfg(feature = "wgpu")]
crate::Geometry::Wgpu(_) => unreachable!(),
crate::Geometry::Custom(_) => unreachable!(),
}
}
}
@ -274,6 +312,19 @@ impl crate::graphics::geometry::Renderer for Renderer {
renderer.draw_primitive(primitive);
}
crate::Geometry::TinySkia(_) => unreachable!(),
crate::Geometry::Custom(_) => unreachable!(),
}
}
}
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!(),
}
}
}
@ -297,6 +348,11 @@ impl iced_wgpu::primitive::pipeline::Renderer for Renderer {
Self::Wgpu(renderer) => {
renderer.draw_pipeline_primitive(bounds, primitive);
}
Self::Custom(_renderer) => {
log::warn!(
"Custom shader primitive is unavailable with custom renderer."
);
}
}
}
}

View file

@ -93,7 +93,7 @@ where
{
// The raw w/h of the underlying image
let image_size = {
let Size { width, height } = renderer.dimensions(handle);
let Size { width, height } = renderer.measure_image(handle);
Size::new(width as f32, height as f32)
};
@ -130,7 +130,7 @@ pub fn draw<Renderer, Handle>(
Renderer: image::Renderer<Handle = Handle>,
Handle: Clone + Hash,
{
let Size { width, height } = renderer.dimensions(handle);
let Size { width, height } = renderer.measure_image(handle);
let image_size = Size::new(width as f32, height as f32);
let bounds = layout.bounds();
@ -148,7 +148,11 @@ pub fn draw<Renderer, Handle>(
..bounds
};
renderer.draw(handle.clone(), filter_method, drawing_bounds + offset);
renderer.draw_image(
handle.clone(),
filter_method,
drawing_bounds + offset,
);
};
if adjusted_fit.width > bounds.width || adjusted_fit.height > bounds.height

View file

@ -117,7 +117,7 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let Size { width, height } = renderer.dimensions(&self.handle);
let Size { width, height } = renderer.measure_image(&self.handle);
let mut size = limits.resolve(
self.width,
@ -335,8 +335,7 @@ where
renderer.with_layer(bounds, |renderer| {
renderer.with_translation(translation, |renderer| {
image::Renderer::draw(
renderer,
renderer.draw_image(
self.handle.clone(),
self.filter_method,
Rectangle {
@ -421,7 +420,7 @@ pub fn image_size<Renderer>(
where
Renderer: image::Renderer,
{
let Size { width, height } = renderer.dimensions(handle);
let Size { width, height } = renderer.measure_image(handle);
let (width, height) = {
let dimensions = (width as f32, height as f32);

View file

@ -108,7 +108,7 @@ where
limits: &layout::Limits,
) -> layout::Node {
// The raw w/h of the underlying image
let Size { width, height } = renderer.dimensions(&self.handle);
let Size { width, height } = renderer.measure_svg(&self.handle);
let image_size = Size::new(width as f32, height as f32);
// The size to be available to the widget prior to `Shrink`ing
@ -142,7 +142,7 @@ where
cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
let Size { width, height } = renderer.dimensions(&self.handle);
let Size { width, height } = renderer.measure_svg(&self.handle);
let image_size = Size::new(width as f32, height as f32);
let bounds = layout.bounds();
@ -169,7 +169,7 @@ where
let appearance = (self.style)(theme, status);
renderer.draw(
renderer.draw_svg(
self.handle.clone(),
appearance.color,
drawing_bounds + offset,