Introduce custom backend-specific primitives
This commit is contained in:
parent
8d65e40a11
commit
0ae1baa37b
28 changed files with 618 additions and 263 deletions
|
|
@ -6,6 +6,13 @@ use iced_core::{Font, Point, Size};
|
|||
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// The graphics backend of a [`Renderer`].
|
||||
///
|
||||
/// [`Renderer`]: crate::Renderer
|
||||
pub trait Backend {
|
||||
type Primitive;
|
||||
}
|
||||
|
||||
/// A graphics backend that supports text rendering.
|
||||
pub trait Text {
|
||||
/// The icon font of the backend.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,68 @@
|
|||
//! Track and compute the damage of graphical primitives.
|
||||
use crate::core::alignment;
|
||||
use crate::core::{Rectangle, Size};
|
||||
use crate::Primitive;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Computes the damage regions between the two given primitives.
|
||||
pub fn regions(a: &Primitive, b: &Primitive) -> Vec<Rectangle> {
|
||||
pub trait Damage: PartialEq {
|
||||
/// Returns the bounds of the [`Damage`].
|
||||
fn bounds(&self) -> Rectangle;
|
||||
}
|
||||
|
||||
impl<T: Damage> Damage for Primitive<T> {
|
||||
fn bounds(&self) -> Rectangle {
|
||||
match self {
|
||||
Self::Text {
|
||||
bounds,
|
||||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
..
|
||||
} => {
|
||||
let mut bounds = *bounds;
|
||||
|
||||
bounds.x = match horizontal_alignment {
|
||||
alignment::Horizontal::Left => bounds.x,
|
||||
alignment::Horizontal::Center => {
|
||||
bounds.x - bounds.width / 2.0
|
||||
}
|
||||
alignment::Horizontal::Right => bounds.x - bounds.width,
|
||||
};
|
||||
|
||||
bounds.y = match vertical_alignment {
|
||||
alignment::Vertical::Top => bounds.y,
|
||||
alignment::Vertical::Center => {
|
||||
bounds.y - bounds.height / 2.0
|
||||
}
|
||||
alignment::Vertical::Bottom => bounds.y - bounds.height,
|
||||
};
|
||||
|
||||
bounds.expand(1.5)
|
||||
}
|
||||
Self::Quad { bounds, .. }
|
||||
| Self::Image { bounds, .. }
|
||||
| Self::Svg { bounds, .. } => bounds.expand(1.0),
|
||||
Self::Clip { bounds, .. } => bounds.expand(1.0),
|
||||
Self::SolidMesh { size, .. } | Self::GradientMesh { size, .. } => {
|
||||
Rectangle::with_size(*size)
|
||||
}
|
||||
Self::Group { primitives } => primitives
|
||||
.iter()
|
||||
.map(Self::bounds)
|
||||
.fold(Rectangle::with_size(Size::ZERO), |a, b| {
|
||||
Rectangle::union(&a, &b)
|
||||
}),
|
||||
Self::Translate {
|
||||
translation,
|
||||
content,
|
||||
} => content.bounds() + *translation,
|
||||
Self::Cache { content } => content.bounds(),
|
||||
Self::Custom(custom) => custom.bounds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn regions<T: Damage>(a: &Primitive<T>, b: &Primitive<T>) -> Vec<Rectangle> {
|
||||
match (a, b) {
|
||||
(
|
||||
Primitive::Group {
|
||||
|
|
@ -76,7 +133,10 @@ pub fn regions(a: &Primitive, b: &Primitive) -> Vec<Rectangle> {
|
|||
}
|
||||
|
||||
/// Computes the damage regions between the two given lists of primitives.
|
||||
pub fn list(previous: &[Primitive], current: &[Primitive]) -> Vec<Rectangle> {
|
||||
pub fn list<T: Damage>(
|
||||
previous: &[Primitive<T>],
|
||||
current: &[Primitive<T>],
|
||||
) -> Vec<Rectangle> {
|
||||
let damage = previous
|
||||
.iter()
|
||||
.zip(current)
|
||||
|
|
@ -93,7 +153,7 @@ pub fn list(previous: &[Primitive], current: &[Primitive]) -> Vec<Rectangle> {
|
|||
|
||||
// Extend damage by the added/removed primitives
|
||||
damage
|
||||
.chain(bigger[smaller.len()..].iter().map(Primitive::bounds))
|
||||
.chain(bigger[smaller.len()..].iter().map(Damage::bounds))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,20 +14,10 @@ pub use text::Text;
|
|||
|
||||
pub use crate::gradient::{self, Gradient};
|
||||
|
||||
use crate::Primitive;
|
||||
|
||||
/// A bunch of shapes that can be drawn.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Geometry(pub Primitive);
|
||||
|
||||
impl From<Geometry> for Primitive {
|
||||
fn from(geometry: Geometry) -> Self {
|
||||
geometry.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A renderer capable of drawing some [`Geometry`].
|
||||
pub trait Renderer: crate::core::Renderer {
|
||||
type Geometry;
|
||||
|
||||
/// Draws the given layers of [`Geometry`].
|
||||
fn draw(&mut self, layers: Vec<Geometry>);
|
||||
fn draw(&mut self, layers: Vec<Self::Geometry>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
|
||||
)]
|
||||
#![deny(
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
//missing_debug_implementations,
|
||||
//missing_docs,
|
||||
unsafe_code,
|
||||
unused_results,
|
||||
clippy::extra_unused_lifetimes,
|
||||
|
|
@ -41,7 +41,9 @@ pub mod geometry;
|
|||
pub mod image;
|
||||
|
||||
pub use antialiasing::Antialiasing;
|
||||
pub use backend::Backend;
|
||||
pub use compositor::Compositor;
|
||||
pub use damage::Damage;
|
||||
pub use error::Error;
|
||||
pub use gradient::Gradient;
|
||||
pub use primitive::Primitive;
|
||||
|
|
@ -49,7 +51,4 @@ pub use renderer::Renderer;
|
|||
pub use transformation::Transformation;
|
||||
pub use viewport::Viewport;
|
||||
|
||||
#[cfg(feature = "geometry")]
|
||||
pub use geometry::Geometry;
|
||||
|
||||
pub use iced_core as core;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ use std::sync::Arc;
|
|||
|
||||
/// A rendering primitive.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Primitive {
|
||||
pub enum Primitive<T> {
|
||||
/// A text primitive
|
||||
Text {
|
||||
/// The contents of the text
|
||||
|
|
@ -90,41 +89,17 @@ pub enum Primitive {
|
|||
/// Any geometry that falls out of this region will be clipped.
|
||||
size: Size,
|
||||
},
|
||||
/// A [`tiny_skia`] path filled with some paint.
|
||||
#[cfg(feature = "tiny-skia")]
|
||||
Fill {
|
||||
/// The path to fill.
|
||||
path: tiny_skia::Path,
|
||||
/// The paint to use.
|
||||
paint: tiny_skia::Paint<'static>,
|
||||
/// The fill rule to follow.
|
||||
rule: tiny_skia::FillRule,
|
||||
/// The transform to apply to the path.
|
||||
transform: tiny_skia::Transform,
|
||||
},
|
||||
/// A [`tiny_skia`] path stroked with some paint.
|
||||
#[cfg(feature = "tiny-skia")]
|
||||
Stroke {
|
||||
/// The path to stroke.
|
||||
path: tiny_skia::Path,
|
||||
/// The paint to use.
|
||||
paint: tiny_skia::Paint<'static>,
|
||||
/// The stroke settings.
|
||||
stroke: tiny_skia::Stroke,
|
||||
/// The transform to apply to the path.
|
||||
transform: tiny_skia::Transform,
|
||||
},
|
||||
/// A group of primitives
|
||||
Group {
|
||||
/// The primitives of the group
|
||||
primitives: Vec<Primitive>,
|
||||
primitives: Vec<Primitive<T>>,
|
||||
},
|
||||
/// A clip primitive
|
||||
Clip {
|
||||
/// The bounds of the clip
|
||||
bounds: Rectangle,
|
||||
/// The content of the clip
|
||||
content: Box<Primitive>,
|
||||
content: Box<Primitive<T>>,
|
||||
},
|
||||
/// A primitive that applies a translation
|
||||
Translate {
|
||||
|
|
@ -132,7 +107,7 @@ pub enum Primitive {
|
|||
translation: Vector,
|
||||
|
||||
/// The primitive to translate
|
||||
content: Box<Primitive>,
|
||||
content: Box<Primitive<T>>,
|
||||
},
|
||||
/// A cached primitive.
|
||||
///
|
||||
|
|
@ -140,11 +115,13 @@ pub enum Primitive {
|
|||
/// generation is expensive.
|
||||
Cache {
|
||||
/// The cached primitive
|
||||
content: Arc<Primitive>,
|
||||
content: Arc<Primitive<T>>,
|
||||
},
|
||||
/// A backend-specific primitive.
|
||||
Custom(T),
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
impl<T> Primitive<T> {
|
||||
/// Creates a [`Primitive::Group`].
|
||||
pub fn group(primitives: Vec<Self>) -> Self {
|
||||
Self::Group { primitives }
|
||||
|
|
@ -165,68 +142,6 @@ impl Primitive {
|
|||
content: Box::new(self),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the bounds of the [`Primitive`].
|
||||
pub fn bounds(&self) -> Rectangle {
|
||||
match self {
|
||||
Self::Text {
|
||||
bounds,
|
||||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
..
|
||||
} => {
|
||||
let mut bounds = *bounds;
|
||||
|
||||
bounds.x = match horizontal_alignment {
|
||||
alignment::Horizontal::Left => bounds.x,
|
||||
alignment::Horizontal::Center => {
|
||||
bounds.x - bounds.width / 2.0
|
||||
}
|
||||
alignment::Horizontal::Right => bounds.x - bounds.width,
|
||||
};
|
||||
|
||||
bounds.y = match vertical_alignment {
|
||||
alignment::Vertical::Top => bounds.y,
|
||||
alignment::Vertical::Center => {
|
||||
bounds.y - bounds.height / 2.0
|
||||
}
|
||||
alignment::Vertical::Bottom => bounds.y - bounds.height,
|
||||
};
|
||||
|
||||
bounds.expand(1.5)
|
||||
}
|
||||
Self::Quad { bounds, .. }
|
||||
| Self::Image { bounds, .. }
|
||||
| Self::Svg { bounds, .. } => bounds.expand(1.0),
|
||||
Self::Clip { bounds, .. } => bounds.expand(1.0),
|
||||
Self::SolidMesh { size, .. } | Self::GradientMesh { size, .. } => {
|
||||
Rectangle::with_size(*size)
|
||||
}
|
||||
#[cfg(feature = "tiny-skia")]
|
||||
Self::Fill { path, .. } | Self::Stroke { path, .. } => {
|
||||
let bounds = path.bounds();
|
||||
|
||||
Rectangle {
|
||||
x: bounds.x(),
|
||||
y: bounds.y(),
|
||||
width: bounds.width(),
|
||||
height: bounds.height(),
|
||||
}
|
||||
.expand(1.0)
|
||||
}
|
||||
Self::Group { primitives } => primitives
|
||||
.iter()
|
||||
.map(Self::bounds)
|
||||
.fold(Rectangle::with_size(Size::ZERO), |a, b| {
|
||||
Rectangle::union(&a, &b)
|
||||
}),
|
||||
Self::Translate {
|
||||
translation,
|
||||
content,
|
||||
} => content.bounds() + *translation,
|
||||
Self::Cache { content } => content.bounds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of [`Vertex2D`] and indices representing a list of triangles.
|
||||
|
|
@ -268,9 +183,3 @@ unsafe impl Zeroable for GradientVertex2D {}
|
|||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl Pod for GradientVertex2D {}
|
||||
|
||||
impl From<()> for Primitive {
|
||||
fn from(_: ()) -> Self {
|
||||
Self::Group { primitives: vec![] }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//! Create a renderer from a [`Backend`].
|
||||
use crate::backend;
|
||||
use crate::backend::{self, Backend};
|
||||
use crate::Primitive;
|
||||
|
||||
use iced_core::image;
|
||||
|
|
@ -16,13 +16,13 @@ use std::marker::PhantomData;
|
|||
|
||||
/// A backend-agnostic renderer that supports all the built-in widgets.
|
||||
#[derive(Debug)]
|
||||
pub struct Renderer<B, Theme> {
|
||||
pub struct Renderer<B: Backend, Theme> {
|
||||
backend: B,
|
||||
primitives: Vec<Primitive>,
|
||||
primitives: Vec<Primitive<B::Primitive>>,
|
||||
theme: PhantomData<Theme>,
|
||||
}
|
||||
|
||||
impl<B, T> Renderer<B, T> {
|
||||
impl<B: Backend, T> Renderer<B, T> {
|
||||
/// Creates a new [`Renderer`] from the given [`Backend`].
|
||||
pub fn new(backend: B) -> Self {
|
||||
Self {
|
||||
|
|
@ -38,7 +38,7 @@ impl<B, T> Renderer<B, T> {
|
|||
}
|
||||
|
||||
/// Enqueues the given [`Primitive`] in the [`Renderer`] for drawing.
|
||||
pub fn draw_primitive(&mut self, primitive: Primitive) {
|
||||
pub fn draw_primitive(&mut self, primitive: Primitive<B::Primitive>) {
|
||||
self.primitives.push(primitive);
|
||||
}
|
||||
|
||||
|
|
@ -46,13 +46,42 @@ impl<B, T> Renderer<B, T> {
|
|||
/// of the [`Renderer`].
|
||||
pub fn with_primitives<O>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut B, &[Primitive]) -> O,
|
||||
f: impl FnOnce(&mut B, &[Primitive<B::Primitive>]) -> O,
|
||||
) -> O {
|
||||
f(&mut self.backend, &self.primitives)
|
||||
}
|
||||
|
||||
pub fn start_layer(&mut self) -> Vec<Primitive<B::Primitive>> {
|
||||
std::mem::take(&mut self.primitives)
|
||||
}
|
||||
|
||||
pub fn end_layer(
|
||||
&mut self,
|
||||
primitives: Vec<Primitive<B::Primitive>>,
|
||||
bounds: Rectangle,
|
||||
) {
|
||||
let layer = std::mem::replace(&mut self.primitives, primitives);
|
||||
|
||||
self.primitives.push(Primitive::group(layer).clip(bounds));
|
||||
}
|
||||
|
||||
pub fn start_translation(&mut self) -> Vec<Primitive<B::Primitive>> {
|
||||
std::mem::take(&mut self.primitives)
|
||||
}
|
||||
|
||||
pub fn end_translation(
|
||||
&mut self,
|
||||
primitives: Vec<Primitive<B::Primitive>>,
|
||||
translation: Vector,
|
||||
) {
|
||||
let layer = std::mem::replace(&mut self.primitives, primitives);
|
||||
|
||||
self.primitives
|
||||
.push(Primitive::group(layer).translate(translation));
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, T> iced_core::Renderer for Renderer<B, T> {
|
||||
impl<B: Backend, T> iced_core::Renderer for Renderer<B, T> {
|
||||
type Theme = T;
|
||||
|
||||
fn layout<Message>(
|
||||
|
|
@ -64,13 +93,11 @@ impl<B, T> iced_core::Renderer for Renderer<B, T> {
|
|||
}
|
||||
|
||||
fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) {
|
||||
let current = std::mem::take(&mut self.primitives);
|
||||
let current = self.start_layer();
|
||||
|
||||
f(self);
|
||||
|
||||
let layer = std::mem::replace(&mut self.primitives, current);
|
||||
|
||||
self.primitives.push(Primitive::group(layer).clip(bounds));
|
||||
self.end_layer(current, bounds);
|
||||
}
|
||||
|
||||
fn with_translation(
|
||||
|
|
@ -78,14 +105,11 @@ impl<B, T> iced_core::Renderer for Renderer<B, T> {
|
|||
translation: Vector,
|
||||
f: impl FnOnce(&mut Self),
|
||||
) {
|
||||
let current = std::mem::take(&mut self.primitives);
|
||||
let current = self.start_translation();
|
||||
|
||||
f(self);
|
||||
|
||||
let layer = std::mem::replace(&mut self.primitives, current);
|
||||
|
||||
self.primitives
|
||||
.push(Primitive::group(layer).translate(translation));
|
||||
self.end_translation(current, translation);
|
||||
}
|
||||
|
||||
fn fill_quad(
|
||||
|
|
@ -109,7 +133,7 @@ impl<B, T> iced_core::Renderer for Renderer<B, T> {
|
|||
|
||||
impl<B, T> text::Renderer for Renderer<B, T>
|
||||
where
|
||||
B: backend::Text,
|
||||
B: Backend + backend::Text,
|
||||
{
|
||||
type Font = Font;
|
||||
|
||||
|
|
@ -188,7 +212,7 @@ where
|
|||
|
||||
impl<B, T> image::Renderer for Renderer<B, T>
|
||||
where
|
||||
B: backend::Image,
|
||||
B: Backend + backend::Image,
|
||||
{
|
||||
type Handle = image::Handle;
|
||||
|
||||
|
|
@ -203,7 +227,7 @@ where
|
|||
|
||||
impl<B, T> svg::Renderer for Renderer<B, T>
|
||||
where
|
||||
B: backend::Svg,
|
||||
B: Backend + backend::Svg,
|
||||
{
|
||||
fn dimensions(&self, handle: &svg::Handle) -> Size<u32> {
|
||||
self.backend().viewport_dimensions(handle)
|
||||
|
|
@ -222,11 +246,3 @@ where
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "geometry")]
|
||||
impl<B, T> crate::geometry::Renderer for Renderer<B, T> {
|
||||
fn draw(&mut self, layers: Vec<crate::Geometry>) {
|
||||
self.primitives
|
||||
.extend(layers.into_iter().map(crate::Geometry::into));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue