diff options
| -rw-r--r-- | graphics/src/damage.rs | 140 | ||||
| -rw-r--r-- | graphics/src/lib.rs | 1 | ||||
| -rw-r--r-- | graphics/src/primitive.rs | 90 | ||||
| -rw-r--r-- | tiny_skia/src/backend.rs | 80 | ||||
| -rw-r--r-- | tiny_skia/src/window/compositor.rs | 54 | 
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(®ion); + +            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 ®ion 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(®ion); - -            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(())  } | 
