Reintroduce damage tracking for iced_tiny_skia

This commit is contained in:
Héctor Ramón Jiménez 2024-04-10 15:21:42 +02:00
parent 14b9708f72
commit 1e802e776c
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
10 changed files with 347 additions and 92 deletions

80
graphics/src/damage.rs Normal file
View file

@ -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
}

View file

@ -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();

View file

@ -15,6 +15,7 @@ mod viewport;
pub mod color;
pub mod compositor;
pub mod damage;
pub mod error;
pub mod gradient;
pub mod image;

View file

@ -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.