Restore canvas::Frame API
This commit is contained in:
parent
b972ebca8f
commit
53a183fe0d
22 changed files with 378 additions and 679 deletions
|
|
@ -459,10 +459,10 @@ mod geometry {
|
|||
Right(R),
|
||||
}
|
||||
|
||||
impl<L, R> geometry::Frame for Frame<L, R>
|
||||
impl<L, R> geometry::frame::Backend for Frame<L, R>
|
||||
where
|
||||
L: geometry::Frame,
|
||||
R: geometry::Frame,
|
||||
L: geometry::frame::Backend,
|
||||
R: geometry::frame::Backend,
|
||||
{
|
||||
type Geometry = Geometry<L::Geometry, R::Geometry>;
|
||||
|
||||
|
|
@ -545,17 +545,11 @@ mod geometry {
|
|||
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()),
|
||||
fn into_geometry(self) -> Self::Geometry {
|
||||
match self {
|
||||
Frame::Left(frame) => Geometry::Left(frame.into_geometry()),
|
||||
Frame::Right(frame) => Geometry::Right(frame.into_geometry()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,236 +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,
|
||||
#[cfg(feature = "custom")]
|
||||
Self::Custom($name) => $body,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub enum Geometry {
|
||||
TinySkia(iced_tiny_skia::Primitive),
|
||||
#[cfg(feature = "wgpu")]
|
||||
Wgpu(iced_wgpu::Primitive),
|
||||
#[cfg(feature = "custom")]
|
||||
Custom(Box<dyn crate::custom::Geometry>),
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
Self::Custom(geometry) => {
|
||||
Self::Custom(geometry.transform(transformation))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Frame {
|
||||
TinySkia(iced_tiny_skia::geometry::Frame),
|
||||
#[cfg(feature = "wgpu")]
|
||||
Wgpu(iced_wgpu::geometry::Frame),
|
||||
#[cfg(feature = "custom")]
|
||||
Custom(Box<dyn crate::custom::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))
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
Renderer::Custom(renderer) => {
|
||||
Frame::Custom(renderer.new_frame(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.into()));
|
||||
}
|
||||
|
||||
/// 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.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.into()));
|
||||
}
|
||||
|
||||
/// 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.into()));
|
||||
}
|
||||
|
||||
/// 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()))
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
Self::Custom(frame) => Self::Custom(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);
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
(Self::Custom(target), Self::Custom(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.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.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.into()));
|
||||
}
|
||||
|
||||
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()),
|
||||
#[cfg(feature = "custom")]
|
||||
Self::Custom(frame) => Geometry::Custom(frame.into_geometry()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,137 +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>),
|
||||
#[cfg(feature = "custom")]
|
||||
Custom(Arc<dyn crate::custom::Geometry>),
|
||||
}
|
||||
|
||||
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(),
|
||||
});
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
Internal::Custom(geometry) => {
|
||||
return Geometry::Custom(geometry.clone().load())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
Geometry::Custom(geometry) => {
|
||||
Internal::Custom(geometry.cache())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
*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,
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
Internal::Custom(geometry) => Geometry::Custom(geometry.load()),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue