summaryrefslogtreecommitdiffstats
path: root/graphics
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-04-10 15:21:42 +0200
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-04-10 15:21:42 +0200
commit1e802e776cb591f3860d1bfbaa1423d356fc8456 (patch)
tree6d18b7ed93daac1f56346f1a18b16da9e378419d /graphics
parent14b9708f723f9fc730634e7ded3dec7dc912ce34 (diff)
downloadiced-1e802e776cb591f3860d1bfbaa1423d356fc8456.tar.gz
iced-1e802e776cb591f3860d1bfbaa1423d356fc8456.tar.bz2
iced-1e802e776cb591f3860d1bfbaa1423d356fc8456.zip
Reintroduce damage tracking for `iced_tiny_skia`
Diffstat (limited to 'graphics')
-rw-r--r--graphics/src/damage.rs80
-rw-r--r--graphics/src/layer.rs5
-rw-r--r--graphics/src/lib.rs1
-rw-r--r--graphics/src/text.rs70
4 files changed, 156 insertions, 0 deletions
diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs
new file mode 100644
index 00000000..ff8edaf5
--- /dev/null
+++ b/graphics/src/damage.rs
@@ -0,0 +1,80 @@
+//! Compute the damage between frames.
+use crate::core::Rectangle;
+
+/// Diffs the damage regions given some previous and current primitives.
+pub fn diff<T>(
+ previous: &[T],
+ current: &[T],
+ bounds: impl Fn(&T) -> Vec<Rectangle>,
+ diff: impl Fn(&T, &T) -> Vec<Rectangle>,
+) -> Vec<Rectangle> {
+ let damage = previous.iter().zip(current).flat_map(|(a, b)| diff(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().flat_map(bounds))
+ .collect()
+ }
+}
+
+/// Computes the damage regions given some previous and current primitives.
+pub fn list<T>(
+ previous: &[T],
+ current: &[T],
+ bounds: impl Fn(&T) -> Vec<Rectangle>,
+ are_equal: impl Fn(&T, &T) -> bool,
+) -> Vec<Rectangle> {
+ diff(previous, current, &bounds, |a, b| {
+ if are_equal(a, b) {
+ vec![]
+ } else {
+ bounds(a).into_iter().chain(bounds(b)).collect()
+ }
+ })
+}
+
+/// Groups the given damage regions that are close together inside the given
+/// bounds.
+pub fn group(mut damage: Vec<Rectangle>, bounds: Rectangle) -> Vec<Rectangle> {
+ use std::cmp::Ordering;
+
+ const AREA_THRESHOLD: f32 = 20_000.0;
+
+ 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.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
index 0187cc59..c9a818fb 100644
--- a/graphics/src/layer.rs
+++ b/graphics/src/layer.rs
@@ -113,6 +113,11 @@ impl<T: Layer> Stack<T> {
self.layers[..self.active_count].iter()
}
+ /// Returns the slice of layers in the [`Stack`].
+ pub fn as_slice(&self) -> &[T] {
+ &self.layers[..self.active_count]
+ }
+
/// Flushes and settles any primitives in the current layer of the [`Stack`].
pub fn flush(&mut self) {
self.layers[self.current].flush();
diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs
index a9649c6e..865ebd97 100644
--- a/graphics/src/lib.rs
+++ b/graphics/src/lib.rs
@@ -15,6 +15,7 @@ mod viewport;
pub mod color;
pub mod compositor;
+pub mod damage;
pub mod error;
pub mod gradient;
pub mod image;
diff --git a/graphics/src/text.rs b/graphics/src/text.rs
index c204c850..31b6de28 100644
--- a/graphics/src/text.rs
+++ b/graphics/src/text.rs
@@ -70,6 +70,76 @@ pub enum Text {
},
}
+impl Text {
+ /// Returns the visible bounds of the [`Text`].
+ pub fn visible_bounds(&self) -> Option<Rectangle> {
+ let (bounds, horizontal_alignment, vertical_alignment) = match self {
+ Text::Paragraph {
+ position,
+ paragraph,
+ clip_bounds,
+ ..
+ } => (
+ Rectangle::new(*position, paragraph.min_bounds)
+ .intersection(clip_bounds),
+ Some(paragraph.horizontal_alignment),
+ Some(paragraph.vertical_alignment),
+ ),
+ Text::Editor {
+ editor,
+ position,
+ clip_bounds,
+ ..
+ } => (
+ Rectangle::new(*position, editor.bounds)
+ .intersection(clip_bounds),
+ None,
+ None,
+ ),
+ Text::Cached {
+ bounds,
+ clip_bounds,
+ horizontal_alignment,
+ vertical_alignment,
+ ..
+ } => (
+ bounds.intersection(clip_bounds),
+ Some(*horizontal_alignment),
+ Some(*vertical_alignment),
+ ),
+ Text::Raw { raw, .. } => (Some(raw.clip_bounds), None, None),
+ };
+
+ let mut bounds = bounds?;
+
+ if let Some(alignment) = horizontal_alignment {
+ match alignment {
+ alignment::Horizontal::Left => {}
+ alignment::Horizontal::Center => {
+ bounds.x -= bounds.width / 2.0;
+ }
+ alignment::Horizontal::Right => {
+ bounds.x -= bounds.width;
+ }
+ }
+ }
+
+ if let Some(alignment) = vertical_alignment {
+ match alignment {
+ alignment::Vertical::Top => {}
+ alignment::Vertical::Center => {
+ bounds.y -= bounds.height / 2.0;
+ }
+ alignment::Vertical::Bottom => {
+ bounds.y -= bounds.height;
+ }
+ }
+ }
+
+ Some(bounds)
+ }
+}
+
/// The regular variant of the [Fira Sans] font.
///
/// It is loaded as part of the default fonts in Wasm builds.