summaryrefslogtreecommitdiffstats
path: root/graphics
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2023-04-27 16:09:39 +0200
committerLibravatar GitHub <noreply@github.com>2023-04-27 16:09:39 +0200
commitc31ab8eee6a49a48b4e6ad92207b8ee0360a0eff (patch)
tree0b099fc1e3810c2709a40627e11da5dc4988822b /graphics
parente3730106e9d4f75de199e1b83cf285b8ff031968 (diff)
parenta755472ee35dfb7839f989becafc6028921a3b99 (diff)
downloadiced-c31ab8eee6a49a48b4e6ad92207b8ee0360a0eff.tar.gz
iced-c31ab8eee6a49a48b4e6ad92207b8ee0360a0eff.tar.bz2
iced-c31ab8eee6a49a48b4e6ad92207b8ee0360a0eff.zip
Merge pull request #1811 from iced-rs/incremental-rendering
Incremental rendering
Diffstat (limited to 'graphics')
-rw-r--r--graphics/Cargo.toml2
-rw-r--r--graphics/src/damage.rs142
-rw-r--r--graphics/src/lib.rs1
-rw-r--r--graphics/src/primitive.rs69
-rw-r--r--graphics/src/renderer.rs2
5 files changed, 210 insertions, 6 deletions
diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml
index f1ce6b3a..125ea17d 100644
--- a/graphics/Cargo.toml
+++ b/graphics/Cargo.toml
@@ -31,7 +31,7 @@ version = "0.9"
path = "../core"
[dependencies.tiny-skia]
-version = "0.8"
+version = "0.9"
optional = true
[dependencies.image]
diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs
new file mode 100644
index 00000000..5aab06b1
--- /dev/null
+++ b/graphics/src/damage.rs
@@ -0,0 +1,142 @@
+use crate::core::{Rectangle, Size};
+use crate::Primitive;
+
+use std::sync::Arc;
+
+pub fn regions(a: &Primitive, b: &Primitive) -> 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::Translate {
+ translation: translation_a,
+ content: content_a,
+ },
+ Primitive::Translate {
+ translation: translation_b,
+ content: content_b,
+ },
+ ) => {
+ if translation_a == translation_b {
+ return regions(content_a, content_b)
+ .into_iter()
+ .map(|r| r + *translation_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]
+ }
+}
+
+pub fn list(previous: &[Primitive], current: &[Primitive]) -> 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(Primitive::bounds))
+ .collect()
+ }
+}
+
+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/lib.rs b/graphics/src/lib.rs
index 0c50db52..e3de4025 100644
--- a/graphics/src/lib.rs
+++ b/graphics/src/lib.rs
@@ -28,6 +28,7 @@ mod viewport;
pub mod backend;
pub mod compositor;
+pub mod damage;
pub mod primitive;
pub mod renderer;
diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs
index 195b62da..d6a2c4c4 100644
--- a/graphics/src/primitive.rs
+++ b/graphics/src/primitive.rs
@@ -7,7 +7,7 @@ use bytemuck::{Pod, Zeroable};
use std::sync::Arc;
/// A rendering primitive.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Primitive {
/// A text primitive
@@ -147,10 +147,71 @@ impl Primitive {
content: Box::new(self),
}
}
+
+ 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)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Mesh2D<T> {
/// The vertices of the mesh
pub vertices: Vec<T>,
@@ -162,7 +223,7 @@ pub struct Mesh2D<T> {
}
/// A two-dimensional vertex.
-#[derive(Copy, Clone, Debug, Zeroable, Pod)]
+#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
#[repr(C)]
pub struct Vertex2D {
/// The vertex position in 2D space.
@@ -170,7 +231,7 @@ pub struct Vertex2D {
}
/// A two-dimensional vertex with a color.
-#[derive(Copy, Clone, Debug, Zeroable, Pod)]
+#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
#[repr(C)]
pub struct ColoredVertex2D {
/// The vertex position in 2D space.
diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs
index 7bc462ef..23e594be 100644
--- a/graphics/src/renderer.rs
+++ b/graphics/src/renderer.rs
@@ -32,7 +32,7 @@ impl<B: Backend, T> Renderer<B, T> {
}
}
- /// Returns the [`Backend`] of the [`Renderer`].
+ /// Returns a reference to the [`Backend`] of the [`Renderer`].
pub fn backend(&self) -> &B {
&self.backend
}