summaryrefslogtreecommitdiffstats
path: root/graphics
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-04-09 22:25:16 +0200
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-04-09 22:25:16 +0200
commit6ad5bb3597f640ac329801adf735d633bf0a512f (patch)
treef0928edacd09d6537878d22b00ad7ed7829c9ac0 /graphics
parent2c6fd9ac14c5d270e05b97b7a7fab811d25834c4 (diff)
downloadiced-6ad5bb3597f640ac329801adf735d633bf0a512f.tar.gz
iced-6ad5bb3597f640ac329801adf735d633bf0a512f.tar.bz2
iced-6ad5bb3597f640ac329801adf735d633bf0a512f.zip
Port `iced_tiny_skia` to new layering architecture
Diffstat (limited to 'graphics')
-rw-r--r--graphics/src/backend.rs36
-rw-r--r--graphics/src/cached.rs18
-rw-r--r--graphics/src/compositor.rs14
-rw-r--r--graphics/src/damage.rs257
-rw-r--r--graphics/src/layer.rs139
-rw-r--r--graphics/src/lib.rs10
-rw-r--r--graphics/src/primitive.rs160
-rw-r--r--graphics/src/renderer.rs2
-rw-r--r--graphics/src/text.rs2
9 files changed, 156 insertions, 482 deletions
diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs
deleted file mode 100644
index 7abc42c5..00000000
--- a/graphics/src/backend.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-//! Write a graphics backend.
-use crate::core::image;
-use crate::core::svg;
-use crate::core::Size;
-use crate::{Compositor, Mesh, Renderer};
-
-use std::borrow::Cow;
-
-/// The graphics backend of a [`Renderer`].
-///
-/// [`Renderer`]: crate::Renderer
-pub trait Backend: Sized {
- /// The custom kind of primitives this [`Backend`] supports.
- type Primitive: TryFrom<Mesh, Error = &'static str>;
-
- /// The default compositor of this [`Backend`].
- type Compositor: Compositor<Renderer = Renderer<Self>>;
-}
-
-/// A graphics backend that supports text rendering.
-pub trait Text {
- /// Loads a font from its bytes.
- fn load_font(&mut self, font: Cow<'static, [u8]>);
-}
-
-/// A graphics backend that supports image rendering.
-pub trait Image {
- /// Returns the dimensions of the provided image.
- fn dimensions(&self, handle: &image::Handle) -> Size<u32>;
-}
-
-/// A graphics backend that supports SVG rendering.
-pub trait Svg {
- /// Returns the viewport dimensions of the provided SVG.
- fn viewport_dimensions(&self, handle: &svg::Handle) -> Size<u32>;
-}
diff --git a/graphics/src/cached.rs b/graphics/src/cached.rs
index 1ba82f9f..c0e4e029 100644
--- a/graphics/src/cached.rs
+++ b/graphics/src/cached.rs
@@ -1,7 +1,3 @@
-use crate::Primitive;
-
-use std::sync::Arc;
-
/// A piece of data that can be cached.
pub trait Cached: Sized {
/// The type of cache produced.
@@ -18,20 +14,6 @@ pub trait Cached: Sized {
fn cache(self, previous: Option<Self::Cache>) -> Self::Cache;
}
-impl<T> Cached for Primitive<T> {
- type Cache = Arc<Self>;
-
- fn load(cache: &Arc<Self>) -> Self {
- Self::Cache {
- content: cache.clone(),
- }
- }
-
- fn cache(self, _previous: Option<Arc<Self>>) -> Arc<Self> {
- Arc::new(self)
- }
-}
-
#[cfg(debug_assertions)]
impl Cached for () {
type Cache = ();
diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs
index 86472a58..47521eb0 100644
--- a/graphics/src/compositor.rs
+++ b/graphics/src/compositor.rs
@@ -5,9 +5,11 @@ use crate::futures::{MaybeSend, MaybeSync};
use crate::{Error, Settings, Viewport};
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
-use std::future::Future;
use thiserror::Error;
+use std::borrow::Cow;
+use std::future::Future;
+
/// A graphics compositor that can draw to windows.
pub trait Compositor: Sized {
/// The iced renderer of the backend.
@@ -60,6 +62,14 @@ pub trait Compositor: Sized {
/// Returns [`Information`] used by this [`Compositor`].
fn fetch_information(&self) -> Information;
+ /// Loads a font from its bytes.
+ fn load_font(&mut self, font: Cow<'static, [u8]>) {
+ crate::text::font_system()
+ .write()
+ .expect("Write to font system")
+ .load_font(font);
+ }
+
/// Presents the [`Renderer`] primitives to the next frame of the given [`Surface`].
///
/// [`Renderer`]: Self::Renderer
@@ -168,6 +178,8 @@ impl Compositor for () {
) {
}
+ fn load_font(&mut self, _font: Cow<'static, [u8]>) {}
+
fn fetch_information(&self) -> Information {
Information {
adapter: String::from("Null Renderer"),
diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs
deleted file mode 100644
index 8edf69d7..00000000
--- a/graphics/src/damage.rs
+++ /dev/null
@@ -1,257 +0,0 @@
-//! Track and compute the damage of graphical primitives.
-use crate::core::alignment;
-use crate::core::{Rectangle, Size};
-use crate::Primitive;
-
-use std::sync::Arc;
-
-/// 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::Paragraph {
- paragraph,
- position,
- ..
- } => {
- let mut bounds =
- Rectangle::new(*position, paragraph.min_bounds);
-
- bounds.x = match paragraph.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 paragraph.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::Editor {
- editor, position, ..
- } => {
- let bounds = Rectangle::new(*position, editor.bounds);
-
- bounds.expand(1.5)
- }
- Self::RawText(raw) => {
- // TODO: Add `size` field to `raw` to compute more accurate
- // damage bounds (?)
- raw.clip_bounds.expand(1.5)
- }
- Self::Quad { bounds, shadow, .. } if shadow.color.a > 0.0 => {
- let bounds_with_shadow = Rectangle {
- x: bounds.x + shadow.offset.x.min(0.0) - shadow.blur_radius,
- y: bounds.y + shadow.offset.y.min(0.0) - shadow.blur_radius,
- width: bounds.width
- + shadow.offset.x.abs()
- + shadow.blur_radius * 2.0,
- height: bounds.height
- + shadow.offset.y.abs()
- + shadow.blur_radius * 2.0,
- };
-
- bounds_with_shadow.expand(1.0)
- }
- 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::Transform {
- transformation,
- content,
- } => content.bounds() * *transformation,
- 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 {
- primitives: primitives_a,
- },
- Primitive::Group {
- primitives: primitives_b,
- },
- ) => return list(primitives_a, primitives_b),
- (
- Primitive::Clip {
- bounds: bounds_a,
- content: content_a,
- ..
- },
- Primitive::Clip {
- bounds: bounds_b,
- content: content_b,
- ..
- },
- ) => {
- if bounds_a == bounds_b {
- return regions(content_a, content_b)
- .into_iter()
- .filter_map(|r| r.intersection(&bounds_a.expand(1.0)))
- .collect();
- } else {
- return vec![bounds_a.expand(1.0), bounds_b.expand(1.0)];
- }
- }
- (
- Primitive::Transform {
- transformation: transformation_a,
- content: content_a,
- },
- Primitive::Transform {
- transformation: transformation_b,
- content: content_b,
- },
- ) => {
- if transformation_a == transformation_b {
- return regions(content_a, content_b)
- .into_iter()
- .map(|r| r * *transformation_a)
- .collect();
- }
- }
- (
- Primitive::Cache { content: content_a },
- Primitive::Cache { content: content_b },
- ) => {
- if Arc::ptr_eq(content_a, content_b) {
- return vec![];
- }
- }
- _ if a == b => return vec![],
- _ => {}
- }
-
- let bounds_a = a.bounds();
- let bounds_b = b.bounds();
-
- if bounds_a == bounds_b {
- vec![bounds_a]
- } else {
- vec![bounds_a, bounds_b]
- }
-}
-
-/// Computes the damage regions between the two given lists of primitives.
-pub fn list<T: Damage>(
- previous: &[Primitive<T>],
- current: &[Primitive<T>],
-) -> Vec<Rectangle> {
- let damage = previous
- .iter()
- .zip(current)
- .flat_map(|(a, b)| regions(a, b));
-
- if previous.len() == current.len() {
- damage.collect()
- } else {
- let (smaller, bigger) = if previous.len() < current.len() {
- (previous, current)
- } else {
- (current, previous)
- };
-
- // Extend damage by the added/removed primitives
- damage
- .chain(bigger[smaller.len()..].iter().map(Damage::bounds))
- .collect()
- }
-}
-
-/// Groups the given damage regions that are close together inside the given
-/// bounds.
-pub fn group(
- mut damage: Vec<Rectangle>,
- scale_factor: f32,
- bounds: Size<u32>,
-) -> Vec<Rectangle> {
- use std::cmp::Ordering;
-
- const AREA_THRESHOLD: f32 = 20_000.0;
-
- let bounds = Rectangle {
- x: 0.0,
- y: 0.0,
- width: bounds.width as f32,
- height: bounds.height as f32,
- };
-
- damage.sort_by(|a, b| {
- a.x.partial_cmp(&b.x)
- .unwrap_or(Ordering::Equal)
- .then_with(|| a.y.partial_cmp(&b.y).unwrap_or(Ordering::Equal))
- });
-
- let mut output = Vec::new();
- let mut scaled = damage
- .into_iter()
- .filter_map(|region| (region * scale_factor).intersection(&bounds))
- .filter(|region| region.width >= 1.0 && region.height >= 1.0);
-
- if let Some(mut current) = scaled.next() {
- for region in scaled {
- let union = current.union(&region);
-
- if union.area() - current.area() - region.area() <= AREA_THRESHOLD {
- current = union;
- } else {
- output.push(current);
- current = region;
- }
- }
-
- output.push(current);
- }
-
- output
-}
diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs
new file mode 100644
index 00000000..0187cc59
--- /dev/null
+++ b/graphics/src/layer.rs
@@ -0,0 +1,139 @@
+//! Draw and stack layers of graphical primitives.
+use crate::core::{Rectangle, Transformation};
+
+/// A layer of graphical primitives.
+///
+/// Layers normally dictate a set of primitives that are
+/// rendered in a specific order.
+pub trait Layer: Default {
+ /// Creates a new [`Layer`] with the given bounds.
+ fn with_bounds(bounds: Rectangle) -> Self;
+
+ /// Flushes and settles any pending group of primitives in the [`Layer`].
+ ///
+ /// This will be called when a [`Layer`] is finished. It allows layers to efficiently
+ /// record primitives together and defer grouping until the end.
+ fn flush(&mut self);
+
+ /// Resizes the [`Layer`] to the given bounds.
+ fn resize(&mut self, bounds: Rectangle);
+
+ /// Clears all the layers contents and resets its bounds.
+ fn reset(&mut self);
+}
+
+/// A stack of layers used for drawing.
+#[derive(Debug)]
+pub struct Stack<T: Layer> {
+ layers: Vec<T>,
+ transformations: Vec<Transformation>,
+ previous: Vec<usize>,
+ current: usize,
+ active_count: usize,
+}
+
+impl<T: Layer> Stack<T> {
+ /// Creates a new empty [`Stack`].
+ pub fn new() -> Self {
+ Self {
+ layers: vec![T::default()],
+ transformations: vec![Transformation::IDENTITY],
+ previous: vec![],
+ current: 0,
+ active_count: 1,
+ }
+ }
+
+ /// Returns a mutable reference to the current [`Layer`] of the [`Stack`], together with
+ /// the current [`Transformation`].
+ #[inline]
+ pub fn current_mut(&mut self) -> (&mut T, Transformation) {
+ let transformation = self.transformation();
+
+ (&mut self.layers[self.current], transformation)
+ }
+
+ /// Returns the current [`Transformation`] of the [`Stack`].
+ #[inline]
+ pub fn transformation(&self) -> Transformation {
+ self.transformations.last().copied().unwrap()
+ }
+
+ /// Pushes a new clipping region in the [`Stack`]; creating a new layer in the
+ /// process.
+ pub fn push_clip(&mut self, bounds: Rectangle) {
+ self.previous.push(self.current);
+
+ self.current = self.active_count;
+ self.active_count += 1;
+
+ let bounds = bounds * self.transformation();
+
+ if self.current == self.layers.len() {
+ self.layers.push(T::with_bounds(bounds));
+ } else {
+ self.layers[self.current].resize(bounds);
+ }
+ }
+
+ /// Pops the current clipping region from the [`Stack`] and restores the previous one.
+ ///
+ /// The current layer will be recorded for drawing.
+ pub fn pop_clip(&mut self) {
+ self.flush();
+
+ self.current = self.previous.pop().unwrap();
+ }
+
+ /// Pushes a new [`Transformation`] in the [`Stack`].
+ ///
+ /// Future drawing operations will be affected by this new [`Transformation`] until
+ /// it is popped using [`pop_transformation`].
+ ///
+ /// [`pop_transformation`]: Self::pop_transformation
+ pub fn push_transformation(&mut self, transformation: Transformation) {
+ self.transformations
+ .push(self.transformation() * transformation);
+ }
+
+ /// Pops the current [`Transformation`] in the [`Stack`].
+ pub fn pop_transformation(&mut self) {
+ let _ = self.transformations.pop();
+ }
+
+ /// Returns an iterator over mutable references to the layers in the [`Stack`].
+ pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
+ self.flush();
+
+ self.layers[..self.active_count].iter_mut()
+ }
+
+ /// Returns an iterator over immutable references to the layers in the [`Stack`].
+ pub fn iter(&self) -> impl Iterator<Item = &T> {
+ self.layers[..self.active_count].iter()
+ }
+
+ /// Flushes and settles any primitives in the current layer of the [`Stack`].
+ pub fn flush(&mut self) {
+ self.layers[self.current].flush();
+ }
+
+ /// Clears the layers of the [`Stack`], allowing reuse.
+ ///
+ /// This will normally keep layer allocations for future drawing operations.
+ pub fn clear(&mut self) {
+ for layer in self.layers[..self.active_count].iter_mut() {
+ layer.reset();
+ }
+
+ self.current = 0;
+ self.active_count = 1;
+ self.previous.clear();
+ }
+}
+
+impl<T: Layer> Default for Stack<T> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs
index b79ef70d..a9649c6e 100644
--- a/graphics/src/lib.rs
+++ b/graphics/src/lib.rs
@@ -10,35 +10,29 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
mod antialiasing;
mod cached;
-mod primitive;
mod settings;
mod viewport;
-pub mod backend;
pub mod color;
pub mod compositor;
-pub mod damage;
pub mod error;
pub mod gradient;
pub mod image;
+pub mod layer;
pub mod mesh;
-pub mod renderer;
pub mod text;
#[cfg(feature = "geometry")]
pub mod geometry;
pub use antialiasing::Antialiasing;
-pub use backend::Backend;
pub use cached::Cached;
pub use compositor::Compositor;
-pub use damage::Damage;
pub use error::Error;
pub use gradient::Gradient;
pub use image::Image;
+pub use layer::Layer;
pub use mesh::Mesh;
-pub use primitive::Primitive;
-pub use renderer::Renderer;
pub use settings::Settings;
pub use text::Text;
pub use viewport::Viewport;
diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs
deleted file mode 100644
index 6929b0a1..00000000
--- a/graphics/src/primitive.rs
+++ /dev/null
@@ -1,160 +0,0 @@
-//! Draw using different graphical primitives.
-use crate::core::alignment;
-use crate::core::image;
-use crate::core::svg;
-use crate::core::text;
-use crate::core::{
- Background, Border, Color, Font, Pixels, Point, Rectangle, Shadow,
- Transformation, Vector,
-};
-use crate::text::editor;
-use crate::text::paragraph;
-
-use std::sync::Arc;
-
-/// A rendering primitive.
-#[derive(Debug, Clone, PartialEq)]
-pub enum Primitive<T> {
- /// A text primitive
- Text {
- /// The contents of the text.
- content: String,
- /// The bounds of the text.
- bounds: Rectangle,
- /// The color of the text.
- color: Color,
- /// The size of the text in logical pixels.
- size: Pixels,
- /// The line height of the text.
- line_height: text::LineHeight,
- /// The font of the text.
- font: Font,
- /// The horizontal alignment of the text.
- horizontal_alignment: alignment::Horizontal,
- /// The vertical alignment of the text.
- vertical_alignment: alignment::Vertical,
- /// The shaping strategy of the text.
- shaping: text::Shaping,
- /// The clip bounds of the text.
- clip_bounds: Rectangle,
- },
- /// A paragraph primitive
- Paragraph {
- /// The [`paragraph::Weak`] reference.
- paragraph: paragraph::Weak,
- /// The position of the paragraph.
- position: Point,
- /// The color of the paragraph.
- color: Color,
- /// The clip bounds of the paragraph.
- clip_bounds: Rectangle,
- },
- /// An editor primitive
- Editor {
- /// The [`editor::Weak`] reference.
- editor: editor::Weak,
- /// The position of the editor.
- position: Point,
- /// The color of the editor.
- color: Color,
- /// The clip bounds of the editor.
- clip_bounds: Rectangle,
- },
- /// A raw `cosmic-text` primitive
- RawText(crate::text::Raw),
- /// A quad primitive
- Quad {
- /// The bounds of the quad
- bounds: Rectangle,
- /// The background of the quad
- background: Background,
- /// The [`Border`] of the quad
- border: Border,
- /// The [`Shadow`] of the quad
- shadow: Shadow,
- },
- /// An image primitive
- Image {
- /// The handle of the image
- handle: image::Handle,
- /// The filter method of the image
- filter_method: image::FilterMethod,
- /// The bounds of the image
- bounds: Rectangle,
- },
- /// An SVG primitive
- Svg {
- /// The path of the SVG file
- handle: svg::Handle,
-
- /// The [`Color`] filter
- color: Option<Color>,
-
- /// The bounds of the viewport
- bounds: Rectangle,
- },
- /// A group of primitives
- Group {
- /// The primitives of the group
- primitives: Vec<Primitive<T>>,
- },
- /// A clip primitive
- Clip {
- /// The bounds of the clip
- bounds: Rectangle,
- /// The content of the clip
- content: Box<Primitive<T>>,
- },
- /// A primitive that applies a [`Transformation`]
- Transform {
- /// The [`Transformation`]
- transformation: Transformation,
-
- /// The primitive to transform
- content: Box<Primitive<T>>,
- },
- /// A cached primitive.
- ///
- /// This can be useful if you are implementing a widget where primitive
- /// generation is expensive.
- Cache {
- /// The cached primitive
- content: Arc<Primitive<T>>,
- },
- /// A backend-specific primitive.
- Custom(T),
-}
-
-impl<T> Primitive<T> {
- /// Groups the current [`Primitive`].
- pub fn group(primitives: Vec<Self>) -> Self {
- Self::Group { primitives }
- }
-
- /// Clips the current [`Primitive`].
- pub fn clip(self, bounds: Rectangle) -> Self {
- Self::Clip {
- bounds,
- content: Box::new(self),
- }
- }
-
- /// Translates the current [`Primitive`].
- pub fn translate(self, translation: Vector) -> Self {
- Self::Transform {
- transformation: Transformation::translate(
- translation.x,
- translation.y,
- ),
- content: Box::new(self),
- }
- }
-
- /// Transforms the current [`Primitive`].
- pub fn transform(self, transformation: Transformation) -> Self {
- Self::Transform {
- transformation,
- content: Box::new(self),
- }
- }
-}
diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs
index d4f91dab..695759a4 100644
--- a/graphics/src/renderer.rs
+++ b/graphics/src/renderer.rs
@@ -66,7 +66,7 @@ impl<B: Backend> iced_core::Renderer for Renderer<B> {
self.stack.push(std::mem::take(&mut self.primitives));
}
- fn end_layer(&mut self, bounds: Rectangle) {
+ fn end_layer(&mut self) {
let layer = std::mem::replace(
&mut self.primitives,
self.stack.pop().expect("a layer should be recording"),
diff --git a/graphics/src/text.rs b/graphics/src/text.rs
index f9fc1fec..c204c850 100644
--- a/graphics/src/text.rs
+++ b/graphics/src/text.rs
@@ -19,7 +19,7 @@ use std::borrow::Cow;
use std::sync::{Arc, RwLock, Weak};
/// A text primitive.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
pub enum Text {
/// A paragraph.
#[allow(missing_docs)]