summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--graphics/src/damage.rs140
-rw-r--r--graphics/src/lib.rs1
-rw-r--r--graphics/src/primitive.rs90
-rw-r--r--tiny_skia/src/backend.rs80
-rw-r--r--tiny_skia/src/window/compositor.rs54
5 files changed, 183 insertions, 182 deletions
diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs
new file mode 100644
index 00000000..63604150
--- /dev/null
+++ b/graphics/src/damage.rs
@@ -0,0 +1,140 @@
+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))
+ .collect();
+ } else {
+ return vec![*bounds_a, *bounds_b];
+ }
+ }
+ (
+ 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 7f2c8ae2..1751d03a 100644
--- a/graphics/src/primitive.rs
+++ b/graphics/src/primitive.rs
@@ -208,96 +208,6 @@ impl Primitive {
Self::Cache { content } => content.bounds(),
}
}
-
- pub fn damage(&self, other: &Self) -> Vec<Rectangle> {
- match (self, other) {
- (
- Primitive::Group {
- primitives: primitives_a,
- },
- Primitive::Group {
- primitives: primitives_b,
- },
- ) => return Self::damage_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 content_a
- .damage(content_b)
- .into_iter()
- .filter_map(|r| r.intersection(bounds_a))
- .collect();
- } else {
- return vec![*bounds_a, *bounds_b];
- }
- }
- (
- Primitive::Translate {
- translation: translation_a,
- content: content_a,
- },
- Primitive::Translate {
- translation: translation_b,
- content: content_b,
- },
- ) => {
- if translation_a == translation_b {
- return content_a
- .damage(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 self == other => return vec![],
- _ => {}
- }
-
- let bounds_a = self.bounds();
- let bounds_b = other.bounds();
-
- if bounds_a == bounds_b {
- vec![bounds_a]
- } else {
- vec![bounds_a, bounds_b]
- }
- }
-
- pub fn damage_list(previous: &[Self], current: &[Self]) -> Vec<Rectangle> {
- let damage =
- previous.iter().zip(current).flat_map(|(a, b)| a.damage(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()
- }
- }
}
/// A set of [`Vertex2D`] and indices representing a list of triangles.
diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs
index 16a7f9b8..9c69e1d2 100644
--- a/tiny_skia/src/backend.rs
+++ b/tiny_skia/src/backend.rs
@@ -16,10 +16,6 @@ pub struct Backend {
#[cfg(feature = "svg")]
vector_pipeline: crate::vector::Pipeline,
-
- last_primitives: Vec<Primitive>,
- last_background_color: Color,
- last_size: Size<u32>,
}
impl Backend {
@@ -34,10 +30,6 @@ impl Backend {
#[cfg(feature = "svg")]
vector_pipeline: crate::vector::Pipeline::new(),
-
- last_primitives: Vec::new(),
- last_background_color: Color::BLACK,
- last_size: Size::new(0, 0),
}
}
@@ -47,31 +39,13 @@ impl Backend {
clip_mask: &mut tiny_skia::Mask,
primitives: &[Primitive],
viewport: &Viewport,
+ damage: &[Rectangle],
background_color: Color,
overlay: &[T],
- ) -> bool {
+ ) {
let physical_size = viewport.physical_size();
-
- let damage = if self.last_background_color == background_color
- && self.last_size == physical_size
- {
- Primitive::damage_list(&self.last_primitives, primitives)
- } else {
- vec![Rectangle::with_size(viewport.logical_size())]
- };
-
- if damage.is_empty() {
- return false;
- }
-
- self.last_primitives = primitives.to_vec();
- self.last_background_color = background_color;
- self.last_size = physical_size;
-
let scale_factor = viewport.scale_factor() as f32;
- let damage = group_damage(damage, scale_factor, physical_size);
-
if !overlay.is_empty() {
let path = tiny_skia::PathBuilder::from_rect(
tiny_skia::Rect::from_xywh(
@@ -99,7 +73,7 @@ impl Backend {
);
}
- for region in damage {
+ for &region in damage {
let path = tiny_skia::PathBuilder::from_rect(
tiny_skia::Rect::from_xywh(
region.x,
@@ -164,8 +138,6 @@ impl Backend {
#[cfg(feature = "svg")]
self.vector_pipeline.trim_cache();
-
- true
}
fn draw_primitive(
@@ -629,52 +601,6 @@ fn adjust_clip_mask(clip_mask: &mut tiny_skia::Mask, bounds: Rectangle) {
);
}
-fn group_damage(
- 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
-}
-
impl iced_graphics::Backend for Backend {
fn trim_measurements(&mut self) {
self.text_pipeline.trim_measurement_cache();
diff --git a/tiny_skia/src/window/compositor.rs b/tiny_skia/src/window/compositor.rs
index 7523e06f..d3dffb18 100644
--- a/tiny_skia/src/window/compositor.rs
+++ b/tiny_skia/src/window/compositor.rs
@@ -1,5 +1,6 @@
-use crate::core::Color;
+use crate::core::{Color, Rectangle};
use crate::graphics::compositor::{self, Information, SurfaceError};
+use crate::graphics::damage;
use crate::graphics::{Error, Primitive, Viewport};
use crate::{Backend, Renderer, Settings};
@@ -14,6 +15,8 @@ pub struct Surface {
window: softbuffer::GraphicsContext,
buffer: Vec<u32>,
clip_mask: tiny_skia::Mask,
+ last_primitives: Vec<Primitive>,
+ last_background_color: Color,
}
impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
@@ -45,6 +48,8 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
buffer: vec![0; width as usize * height as usize],
clip_mask: tiny_skia::Mask::new(width, height)
.expect("Create clip mask"),
+ last_primitives: Vec::new(),
+ last_background_color: Color::BLACK,
}
}
@@ -57,6 +62,8 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
surface.buffer.resize((width * height) as usize, 0);
surface.clip_mask =
tiny_skia::Mask::new(width, height).expect("Create clip mask");
+
+ surface.last_primitives.clear();
}
fn fetch_information(&self) -> Information {
@@ -105,28 +112,45 @@ pub fn present<T: AsRef<str>>(
overlay: &[T],
) -> Result<(), compositor::SurfaceError> {
let physical_size = viewport.physical_size();
+ let scale_factor = viewport.scale_factor() as f32;
+
+ let mut pixels = &mut tiny_skia::PixmapMut::from_bytes(
+ bytemuck::cast_slice_mut(&mut surface.buffer),
+ physical_size.width,
+ physical_size.height,
+ )
+ .expect("Create pixel map");
+
+ let damage = if surface.last_background_color == background_color {
+ damage::list(&surface.last_primitives, primitives)
+ } else {
+ vec![Rectangle::with_size(viewport.logical_size())]
+ };
+
+ if damage.is_empty() {
+ return Ok(());
+ }
+
+ surface.last_primitives = primitives.to_vec();
+ surface.last_background_color = background_color;
+
+ let damage = damage::group(damage, scale_factor, physical_size);
- let drawn = backend.draw(
- &mut tiny_skia::PixmapMut::from_bytes(
- bytemuck::cast_slice_mut(&mut surface.buffer),
- physical_size.width,
- physical_size.height,
- )
- .expect("Create pixel map"),
+ backend.draw(
+ &mut pixels,
&mut surface.clip_mask,
primitives,
viewport,
+ &damage,
background_color,
overlay,
);
- if drawn {
- surface.window.set_buffer(
- &surface.buffer,
- physical_size.width as u16,
- physical_size.height as u16,
- );
- }
+ surface.window.set_buffer(
+ &surface.buffer,
+ physical_size.width as u16,
+ physical_size.height as u16,
+ );
Ok(())
}