diff options
Diffstat (limited to 'graphics')
-rw-r--r-- | graphics/Cargo.toml | 4 | ||||
-rw-r--r-- | graphics/src/backend.rs | 8 | ||||
-rw-r--r-- | graphics/src/damage.rs | 66 | ||||
-rw-r--r-- | graphics/src/geometry.rs | 17 | ||||
-rw-r--r-- | graphics/src/gradient.rs | 3 | ||||
-rw-r--r-- | graphics/src/lib.rs | 9 | ||||
-rw-r--r-- | graphics/src/mesh.rs | 76 | ||||
-rw-r--r-- | graphics/src/primitive.rs | 176 | ||||
-rw-r--r-- | graphics/src/renderer.rs | 85 |
9 files changed, 213 insertions, 231 deletions
diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index 02621695..7a9e6aee 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -32,10 +32,6 @@ features = ["derive"] version = "0.9" path = "../core" -[dependencies.tiny-skia] -version = "0.9" -optional = true - [dependencies.image] version = "0.24" optional = true diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index 70ccc664..77bb650b 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -6,6 +6,14 @@ use iced_core::{Font, Point, Size}; use std::borrow::Cow; +/// The graphics backend of a [`Renderer`]. +/// +/// [`Renderer`]: crate::Renderer +pub trait Backend { + /// The custom kind of primitives this [`Backend`] supports. + type Primitive; +} + /// A graphics backend that supports text rendering. pub trait Text { /// The icon font of the backend. diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs index c6b0f759..2f29956e 100644 --- a/graphics/src/damage.rs +++ b/graphics/src/damage.rs @@ -1,11 +1,66 @@ //! 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> { +/// A type that has some damage bounds. +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::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 +131,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 +151,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() } } diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index 729c3d44..7cd3dd3a 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -14,20 +14,11 @@ 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 { + /// The kind of geometry this renderer can draw. + type Geometry; + /// Draws the given layers of [`Geometry`]. - fn draw(&mut self, layers: Vec<Geometry>); + fn draw(&mut self, layers: Vec<Self::Geometry>); } diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index 3f5d0509..4db565d8 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -7,6 +7,7 @@ use crate::color; use crate::core::gradient::ColorStop; use crate::core::{self, Color, Point, Rectangle}; +use bytemuck::{Pod, Zeroable}; use half::f16; use std::cmp::Ordering; @@ -135,7 +136,7 @@ impl Linear { } /// Packed [`Gradient`] data for use in shader code. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Zeroable, Pod)] #[repr(C)] pub struct Packed { // 8 colors, each channel = 16 bit float, 2 colors packed into 1 u32 diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 226b245b..af374a2f 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -23,6 +23,7 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] mod antialiasing; mod error; +mod primitive; mod transformation; mod viewport; @@ -31,7 +32,7 @@ pub mod color; pub mod compositor; pub mod damage; pub mod gradient; -pub mod primitive; +pub mod mesh; pub mod renderer; #[cfg(feature = "geometry")] @@ -41,15 +42,15 @@ 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 mesh::Mesh; pub use primitive::Primitive; 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; diff --git a/graphics/src/mesh.rs b/graphics/src/mesh.rs new file mode 100644 index 00000000..cfb5a60f --- /dev/null +++ b/graphics/src/mesh.rs @@ -0,0 +1,76 @@ +//! Draw triangles! +use crate::color; +use crate::core::{Rectangle, Size}; +use crate::gradient; +use crate::Damage; + +use bytemuck::{Pod, Zeroable}; + +/// A low-level primitive to render a mesh of triangles. +#[derive(Debug, Clone, PartialEq)] +pub enum Mesh { + /// A mesh with a solid color. + Solid { + /// The vertices and indices of the mesh. + buffers: Indexed<SolidVertex2D>, + + /// The size of the drawable region of the mesh. + /// + /// Any geometry that falls out of this region will be clipped. + size: Size, + }, + /// A mesh with a gradient. + Gradient { + /// The vertices and indices of the mesh. + buffers: Indexed<GradientVertex2D>, + + /// The size of the drawable region of the mesh. + /// + /// Any geometry that falls out of this region will be clipped. + size: Size, + }, +} + +impl Damage for Mesh { + fn bounds(&self) -> Rectangle { + match self { + Self::Solid { size, .. } | Self::Gradient { size, .. } => { + Rectangle::with_size(*size) + } + } + } +} + +/// A set of [`Vertex2D`] and indices representing a list of triangles. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Indexed<T> { + /// The vertices of the mesh + pub vertices: Vec<T>, + + /// The list of vertex indices that defines the triangles of the mesh. + /// + /// Therefore, this list should always have a length that is a multiple of 3. + pub indices: Vec<u32>, +} + +/// A two-dimensional vertex with a color. +#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)] +#[repr(C)] +pub struct SolidVertex2D { + /// The vertex position in 2D space. + pub position: [f32; 2], + + /// The color of the vertex in __linear__ RGBA. + pub color: color::Packed, +} + +/// A vertex which contains 2D position & packed gradient data. +#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)] +#[repr(C)] +pub struct GradientVertex2D { + /// The vertex position in 2D space. + pub position: [f32; 2], + + /// The packed vertex data of the gradient. + pub gradient: gradient::Packed, +} diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 187c6c6c..7592a410 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -1,19 +1,15 @@ //! Draw using different graphical primitives. -use crate::color; use crate::core::alignment; use crate::core::image; use crate::core::svg; use crate::core::text; -use crate::core::{Background, Color, Font, Rectangle, Size, Vector}; -use crate::gradient; +use crate::core::{Background, Color, Font, Rectangle, Vector}; -use bytemuck::{Pod, Zeroable}; 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 @@ -66,65 +62,17 @@ pub enum Primitive { /// The bounds of the viewport bounds: Rectangle, }, - /// A low-level primitive to render a mesh of triangles with a solid color. - /// - /// It can be used to render many kinds of geometry freely. - SolidMesh { - /// The vertices and indices of the mesh. - buffers: Mesh2D<ColoredVertex2D>, - - /// The size of the drawable region of the mesh. - /// - /// Any geometry that falls out of this region will be clipped. - size: Size, - }, - /// A low-level primitive to render a mesh of triangles with a gradient. - /// - /// It can be used to render many kinds of geometry freely. - GradientMesh { - /// The vertices and indices of the mesh. - buffers: Mesh2D<GradientVertex2D>, - - /// The size of the drawable region of the mesh. - /// - /// 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 +80,7 @@ pub enum Primitive { translation: Vector, /// The primitive to translate - content: Box<Primitive>, + content: Box<Primitive<T>>, }, /// A cached primitive. /// @@ -140,11 +88,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,112 +115,4 @@ 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. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Mesh2D<T> { - /// The vertices of the mesh - pub vertices: Vec<T>, - - /// The list of vertex indices that defines the triangles of the mesh. - /// - /// Therefore, this list should always have a length that is a multiple of 3. - pub indices: Vec<u32>, -} - -/// A two-dimensional vertex with a color. -#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)] -#[repr(C)] -pub struct ColoredVertex2D { - /// The vertex position in 2D space. - pub position: [f32; 2], - - /// The color of the vertex in __linear__ RGBA. - pub color: color::Packed, -} - -/// A vertex which contains 2D position & packed gradient data. -#[derive(Copy, Clone, Debug, PartialEq)] -#[repr(C)] -pub struct GradientVertex2D { - /// The vertex position in 2D space. - pub position: [f32; 2], - - /// The packed vertex data of the gradient. - pub gradient: gradient::Packed, -} - -#[allow(unsafe_code)] -unsafe impl Zeroable for GradientVertex2D {} - -#[allow(unsafe_code)] -unsafe impl Pod for GradientVertex2D {} - -impl From<()> for Primitive { - fn from(_: ()) -> Self { - Self::Group { primitives: vec![] } - } } diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 7e70a766..d80dea34 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -1,28 +1,25 @@ //! Create a renderer from a [`Backend`]. -use crate::backend; +use crate::backend::{self, Backend}; use crate::Primitive; use iced_core::image; -use iced_core::layout; use iced_core::renderer; use iced_core::svg; use iced_core::text::{self, Text}; -use iced_core::{ - Background, Color, Element, Font, Point, Rectangle, Size, Vector, -}; +use iced_core::{Background, Color, Font, Point, Rectangle, Size, Vector}; use std::borrow::Cow; 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 +35,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,31 +43,54 @@ 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) } -} -impl<B, T> iced_core::Renderer for Renderer<B, T> { - type Theme = T; + /// Starts recording a new layer. + pub fn start_layer(&mut self) -> Vec<Primitive<B::Primitive>> { + std::mem::take(&mut self.primitives) + } - fn layout<Message>( + /// Ends the recording of a layer. + pub fn end_layer( &mut self, - element: &Element<'_, Message, Self>, - limits: &layout::Limits, - ) -> layout::Node { - element.as_widget().layout(self, limits) + primitives: Vec<Primitive<B::Primitive>>, + bounds: Rectangle, + ) { + let layer = std::mem::replace(&mut self.primitives, primitives); + + self.primitives.push(Primitive::group(layer).clip(bounds)); } + /// Starts recording a translation. + pub fn start_translation(&mut self) -> Vec<Primitive<B::Primitive>> { + std::mem::take(&mut self.primitives) + } + + /// Ends the recording of a translation. + 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: Backend, T> iced_core::Renderer for Renderer<B, T> { + type Theme = 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 +98,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 +126,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 +205,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 +220,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 +239,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)); - } -} |