diff options
| author | 2024-01-19 20:41:52 +0100 | |
|---|---|---|
| committer | 2024-01-19 20:41:52 +0100 | |
| commit | 1781068e1c3a65551db1e832fdbaddba99124051 (patch) | |
| tree | 60e0b3854cc0541712572fbb0e56f14435951ea9 /tiny_skia | |
| parent | 41dec5bd203ff5b1574a33a17d5f7358ae1beea2 (diff) | |
| parent | 7ae7fcb89855002519bab752fd3686106ce448db (diff) | |
| download | iced-1781068e1c3a65551db1e832fdbaddba99124051.tar.gz iced-1781068e1c3a65551db1e832fdbaddba99124051.tar.bz2 iced-1781068e1c3a65551db1e832fdbaddba99124051.zip  | |
Merge branch 'master' into remove-vertex-indexing
Diffstat (limited to 'tiny_skia')
| -rw-r--r-- | tiny_skia/Cargo.toml | 7 | ||||
| -rw-r--r-- | tiny_skia/src/backend.rs | 102 | ||||
| -rw-r--r-- | tiny_skia/src/geometry.rs | 122 | ||||
| -rw-r--r-- | tiny_skia/src/primitive.rs | 4 | ||||
| -rw-r--r-- | tiny_skia/src/raster.rs | 12 | ||||
| -rw-r--r-- | tiny_skia/src/text.rs | 90 | ||||
| -rw-r--r-- | tiny_skia/src/vector.rs | 14 | ||||
| -rw-r--r-- | tiny_skia/src/window/compositor.rs | 141 | 
8 files changed, 354 insertions, 138 deletions
diff --git a/tiny_skia/Cargo.toml b/tiny_skia/Cargo.toml index 15a6928a..68b2a03a 100644 --- a/tiny_skia/Cargo.toml +++ b/tiny_skia/Cargo.toml @@ -22,15 +22,10 @@ bytemuck.workspace = true  cosmic-text.workspace = true  kurbo.workspace = true  log.workspace = true -raw-window-handle.workspace = true  rustc-hash.workspace = true  softbuffer.workspace = true  tiny-skia.workspace = true -twox-hash.workspace = true +xxhash-rust.workspace = true  resvg.workspace = true  resvg.optional = true - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -twox-hash.workspace = true -twox-hash.features = ["std"] diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 65aca4b0..d1393b4d 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -1,7 +1,7 @@  use crate::core::{Background, Color, Gradient, Rectangle, Vector};  use crate::graphics::backend;  use crate::graphics::text; -use crate::graphics::{Damage, Viewport}; +use crate::graphics::Viewport;  use crate::primitive::{self, Primitive};  use std::borrow::Cow; @@ -362,11 +362,10 @@ impl Backend {                  paragraph,                  position,                  color, +                clip_bounds: text_clip_bounds,              } => {                  let physical_bounds = -                    (Rectangle::new(*position, paragraph.min_bounds) -                        + translation) -                        * scale_factor; +                    (*text_clip_bounds + translation) * scale_factor;                  if !clip_bounds.intersects(&physical_bounds) {                      return; @@ -384,6 +383,31 @@ impl Backend {                      clip_mask,                  );              } +            Primitive::Editor { +                editor, +                position, +                color, +                clip_bounds: text_clip_bounds, +            } => { +                let physical_bounds = +                    (*text_clip_bounds + translation) * scale_factor; + +                if !clip_bounds.intersects(&physical_bounds) { +                    return; +                } + +                let clip_mask = (!physical_bounds.is_within(&clip_bounds)) +                    .then_some(clip_mask as &_); + +                self.text_pipeline.draw_editor( +                    editor, +                    *position + translation, +                    *color, +                    scale_factor, +                    pixels, +                    clip_mask, +                ); +            }              Primitive::Text {                  content,                  bounds, @@ -394,9 +418,10 @@ impl Backend {                  horizontal_alignment,                  vertical_alignment,                  shaping, +                clip_bounds: text_clip_bounds,              } => {                  let physical_bounds = -                    (primitive.bounds() + translation) * scale_factor; +                    (*text_clip_bounds + translation) * scale_factor;                  if !clip_bounds.intersects(&physical_bounds) {                      return; @@ -420,8 +445,41 @@ impl Backend {                      clip_mask,                  );              } +            Primitive::RawText(text::Raw { +                buffer, +                position, +                color, +                clip_bounds: text_clip_bounds, +            }) => { +                let Some(buffer) = buffer.upgrade() else { +                    return; +                }; + +                let physical_bounds = +                    (*text_clip_bounds + translation) * scale_factor; + +                if !clip_bounds.intersects(&physical_bounds) { +                    return; +                } + +                let clip_mask = (!physical_bounds.is_within(&clip_bounds)) +                    .then_some(clip_mask as &_); + +                self.text_pipeline.draw_raw( +                    &buffer, +                    *position + translation, +                    *color, +                    scale_factor, +                    pixels, +                    clip_mask, +                ); +            }              #[cfg(feature = "image")] -            Primitive::Image { handle, bounds } => { +            Primitive::Image { +                handle, +                filter_method, +                bounds, +            } => {                  let physical_bounds = (*bounds + translation) * scale_factor;                  if !clip_bounds.intersects(&physical_bounds) { @@ -437,8 +495,14 @@ impl Backend {                  )                  .post_scale(scale_factor, scale_factor); -                self.raster_pipeline -                    .draw(handle, *bounds, pixels, transform, clip_mask); +                self.raster_pipeline.draw( +                    handle, +                    *filter_method, +                    *bounds, +                    pixels, +                    transform, +                    clip_mask, +                );              }              #[cfg(not(feature = "image"))]              Primitive::Image { .. } => { @@ -479,7 +543,6 @@ impl Backend {                  path,                  paint,                  rule, -                transform,              }) => {                  let bounds = path.bounds(); @@ -502,9 +565,11 @@ impl Backend {                      path,                      paint,                      *rule, -                    transform -                        .post_translate(translation.x, translation.y) -                        .post_scale(scale_factor, scale_factor), +                    tiny_skia::Transform::from_translate( +                        translation.x, +                        translation.y, +                    ) +                    .post_scale(scale_factor, scale_factor),                      clip_mask,                  );              } @@ -512,7 +577,6 @@ impl Backend {                  path,                  paint,                  stroke, -                transform,              }) => {                  let bounds = path.bounds(); @@ -535,9 +599,11 @@ impl Backend {                      path,                      paint,                      stroke, -                    transform -                        .post_translate(translation.x, translation.y) -                        .post_scale(scale_factor, scale_factor), +                    tiny_skia::Transform::from_translate( +                        translation.x, +                        translation.y, +                    ) +                    .post_scale(scale_factor, scale_factor),                      clip_mask,                  );              } @@ -803,10 +869,6 @@ impl iced_graphics::Backend for Backend {  }  impl backend::Text for Backend { -    fn font_system(&self) -> &text::FontSystem { -        self.text_pipeline.font_system() -    } -      fn load_font(&mut self, font: Cow<'static, [u8]>) {          self.text_pipeline.load_font(font);      } diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 1d14aa03..74a08d38 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -1,4 +1,5 @@ -use crate::core::{Point, Rectangle, Size, Vector}; +use crate::core::text::LineHeight; +use crate::core::{Pixels, Point, Rectangle, Size, Vector};  use crate::graphics::geometry::fill::{self, Fill};  use crate::graphics::geometry::stroke::{self, Stroke};  use crate::graphics::geometry::{Path, Style, Text}; @@ -39,17 +40,22 @@ impl Frame {      }      pub fn fill(&mut self, path: &Path, fill: impl Into<Fill>) { -        let Some(path) = convert_path(path) else { +        let Some(path) = +            convert_path(path).and_then(|path| path.transform(self.transform)) +        else {              return;          }; +          let fill = fill.into(); +        let mut paint = into_paint(fill.style); +        paint.shader.transform(self.transform); +          self.primitives              .push(Primitive::Custom(primitive::Custom::Fill {                  path, -                paint: into_paint(fill.style), +                paint,                  rule: into_fill_rule(fill.rule), -                transform: self.transform,              }));      } @@ -59,73 +65,111 @@ impl Frame {          size: Size,          fill: impl Into<Fill>,      ) { -        let Some(path) = convert_path(&Path::rectangle(top_left, size)) else { +        let Some(path) = convert_path(&Path::rectangle(top_left, size)) +            .and_then(|path| path.transform(self.transform)) +        else {              return;          }; +          let fill = fill.into(); +        let mut paint = tiny_skia::Paint { +            anti_alias: false, +            ..into_paint(fill.style) +        }; +        paint.shader.transform(self.transform); +          self.primitives              .push(Primitive::Custom(primitive::Custom::Fill {                  path, -                paint: tiny_skia::Paint { -                    anti_alias: false, -                    ..into_paint(fill.style) -                }, +                paint,                  rule: into_fill_rule(fill.rule), -                transform: self.transform,              }));      }      pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) { -        let Some(path) = convert_path(path) else { +        let Some(path) = +            convert_path(path).and_then(|path| path.transform(self.transform)) +        else {              return;          };          let stroke = stroke.into();          let skia_stroke = into_stroke(&stroke); +        let mut paint = into_paint(stroke.style); +        paint.shader.transform(self.transform); +          self.primitives              .push(Primitive::Custom(primitive::Custom::Stroke {                  path, -                paint: into_paint(stroke.style), +                paint,                  stroke: skia_stroke, -                transform: self.transform,              }));      }      pub fn fill_text(&mut self, text: impl Into<Text>) {          let text = text.into(); -        let position = if self.transform.is_identity() { -            text.position -        } else { -            let mut transformed = [tiny_skia::Point { -                x: text.position.x, -                y: text.position.y, -            }]; - -            self.transform.map_points(&mut transformed); - -            Point::new(transformed[0].x, transformed[0].y) -        }; - -        // TODO: Use vectorial text instead of primitive -        self.primitives.push(Primitive::Text { -            content: text.content, -            bounds: Rectangle { +        let (scale_x, scale_y) = self.transform.get_scale(); + +        if self.transform.is_scale_translate() +            && scale_x == scale_y +            && scale_x > 0.0 +            && scale_y > 0.0 +        { +            let (position, size, line_height) = if self.transform.is_identity() +            { +                (text.position, text.size, text.line_height) +            } else { +                let mut position = [tiny_skia::Point { +                    x: text.position.x, +                    y: text.position.y, +                }]; + +                self.transform.map_points(&mut position); + +                let size = text.size.0 * scale_y; + +                let line_height = match text.line_height { +                    LineHeight::Absolute(size) => { +                        LineHeight::Absolute(Pixels(size.0 * scale_y)) +                    } +                    LineHeight::Relative(factor) => { +                        LineHeight::Relative(factor) +                    } +                }; + +                ( +                    Point::new(position[0].x, position[0].y), +                    size.into(), +                    line_height, +                ) +            }; + +            let bounds = Rectangle {                  x: position.x,                  y: position.y,                  width: f32::INFINITY,                  height: f32::INFINITY, -            }, -            color: text.color, -            size: text.size, -            line_height: text.line_height, -            font: text.font, -            horizontal_alignment: text.horizontal_alignment, -            vertical_alignment: text.vertical_alignment, -            shaping: text.shaping, -        }); +            }; + +            // TODO: Honor layering! +            self.primitives.push(Primitive::Text { +                content: text.content, +                bounds, +                color: text.color, +                size, +                line_height, +                font: text.font, +                horizontal_alignment: text.horizontal_alignment, +                vertical_alignment: text.vertical_alignment, +                shaping: text.shaping, +                clip_bounds: Rectangle::with_size(Size::INFINITY), +            }); +        } else { +            text.draw_with(|path, color| self.fill(&path, color)); +        }      }      pub fn push_transform(&mut self) { diff --git a/tiny_skia/src/primitive.rs b/tiny_skia/src/primitive.rs index 0ed24969..7718d542 100644 --- a/tiny_skia/src/primitive.rs +++ b/tiny_skia/src/primitive.rs @@ -13,8 +13,6 @@ pub enum Custom {          paint: tiny_skia::Paint<'static>,          /// The fill rule to follow.          rule: tiny_skia::FillRule, -        /// The transform to apply to the path. -        transform: tiny_skia::Transform,      },      /// A path stroked with some paint.      Stroke { @@ -24,8 +22,6 @@ pub enum Custom {          paint: tiny_skia::Paint<'static>,          /// The stroke settings.          stroke: tiny_skia::Stroke, -        /// The transform to apply to the path. -        transform: tiny_skia::Transform,      },  } diff --git a/tiny_skia/src/raster.rs b/tiny_skia/src/raster.rs index d13b1167..5f17ae60 100644 --- a/tiny_skia/src/raster.rs +++ b/tiny_skia/src/raster.rs @@ -28,6 +28,7 @@ impl Pipeline {      pub fn draw(          &mut self,          handle: &raster::Handle, +        filter_method: raster::FilterMethod,          bounds: Rectangle,          pixels: &mut tiny_skia::PixmapMut<'_>,          transform: tiny_skia::Transform, @@ -39,12 +40,21 @@ impl Pipeline {              let transform = transform.pre_scale(width_scale, height_scale); +            let quality = match filter_method { +                raster::FilterMethod::Linear => { +                    tiny_skia::FilterQuality::Bilinear +                } +                raster::FilterMethod::Nearest => { +                    tiny_skia::FilterQuality::Nearest +                } +            }; +              pixels.draw_pixmap(                  (bounds.x / width_scale) as i32,                  (bounds.y / height_scale) as i32,                  image,                  &tiny_skia::PixmapPaint { -                    quality: tiny_skia::FilterQuality::Bilinear, +                    quality,                      ..Default::default()                  },                  transform, diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index cb3ef54c..9413e311 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -1,9 +1,10 @@  use crate::core::alignment;  use crate::core::text::{LineHeight, Shaping}; -use crate::core::{Color, Font, Pixels, Point, Rectangle}; +use crate::core::{Color, Font, Pixels, Point, Rectangle, Size};  use crate::graphics::text::cache::{self, Cache}; +use crate::graphics::text::editor; +use crate::graphics::text::font_system;  use crate::graphics::text::paragraph; -use crate::graphics::text::FontSystem;  use rustc_hash::{FxHashMap, FxHashSet};  use std::borrow::Cow; @@ -12,7 +13,6 @@ use std::collections::hash_map;  #[allow(missing_debug_implementations)]  pub struct Pipeline { -    font_system: FontSystem,      glyph_cache: GlyphCache,      cache: RefCell<Cache>,  } @@ -20,18 +20,16 @@ pub struct Pipeline {  impl Pipeline {      pub fn new() -> Self {          Pipeline { -            font_system: FontSystem::new(),              glyph_cache: GlyphCache::new(),              cache: RefCell::new(Cache::new()),          }      } -    pub fn font_system(&self) -> &FontSystem { -        &self.font_system -    } -      pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) { -        self.font_system.load_font(bytes); +        font_system() +            .write() +            .expect("Write font system") +            .load_font(bytes);          self.cache = RefCell::new(Cache::new());      } @@ -51,8 +49,10 @@ impl Pipeline {              return;          }; +        let mut font_system = font_system().write().expect("Write font system"); +          draw( -            self.font_system.get_mut(), +            font_system.raw(),              &mut self.glyph_cache,              paragraph.buffer(),              Rectangle::new(position, paragraph.min_bounds()), @@ -65,6 +65,37 @@ impl Pipeline {          );      } +    pub fn draw_editor( +        &mut self, +        editor: &editor::Weak, +        position: Point, +        color: Color, +        scale_factor: f32, +        pixels: &mut tiny_skia::PixmapMut<'_>, +        clip_mask: Option<&tiny_skia::Mask>, +    ) { +        use crate::core::text::Editor as _; + +        let Some(editor) = editor.upgrade() else { +            return; +        }; + +        let mut font_system = font_system().write().expect("Write font system"); + +        draw( +            font_system.raw(), +            &mut self.glyph_cache, +            editor.buffer(), +            Rectangle::new(position, editor.bounds()), +            color, +            alignment::Horizontal::Left, +            alignment::Vertical::Top, +            scale_factor, +            pixels, +            clip_mask, +        ); +    } +      pub fn draw_cached(          &mut self,          content: &str, @@ -82,7 +113,9 @@ impl Pipeline {      ) {          let line_height = f32::from(line_height.to_absolute(size)); -        let font_system = self.font_system.get_mut(); +        let mut font_system = font_system().write().expect("Write font system"); +        let font_system = font_system.raw(); +          let key = cache::Key {              bounds: bounds.size(),              content, @@ -115,6 +148,33 @@ impl Pipeline {          );      } +    pub fn draw_raw( +        &mut self, +        buffer: &cosmic_text::Buffer, +        position: Point, +        color: Color, +        scale_factor: f32, +        pixels: &mut tiny_skia::PixmapMut<'_>, +        clip_mask: Option<&tiny_skia::Mask>, +    ) { +        let mut font_system = font_system().write().expect("Write font system"); + +        let (width, height) = buffer.size(); + +        draw( +            font_system.raw(), +            &mut self.glyph_cache, +            buffer, +            Rectangle::new(position, Size::new(width, height)), +            color, +            alignment::Horizontal::Left, +            alignment::Vertical::Top, +            scale_factor, +            pixels, +            clip_mask, +        ); +    } +      pub fn trim_cache(&mut self) {          self.cache.get_mut().trim();          self.glyph_cache.trim(); @@ -155,7 +215,7 @@ fn draw(              if let Some((buffer, placement)) = glyph_cache.allocate(                  physical_glyph.cache_key, -                color, +                glyph.color_opt.map(from_color).unwrap_or(color),                  font_system,                  &mut swash,              ) { @@ -180,6 +240,12 @@ fn draw(      }  } +fn from_color(color: cosmic_text::Color) -> Color { +    let [r, g, b, a] = color.as_rgba(); + +    Color::from_rgba8(r, g, b, a as f32 / 255.0) +} +  #[derive(Debug, Clone, Default)]  struct GlyphCache {      entries: FxHashMap< diff --git a/tiny_skia/src/vector.rs b/tiny_skia/src/vector.rs index a1cd269d..fd1ab3de 100644 --- a/tiny_skia/src/vector.rs +++ b/tiny_skia/src/vector.rs @@ -1,7 +1,8 @@  use crate::core::svg::{Data, Handle};  use crate::core::{Color, Rectangle, Size}; +use crate::graphics::text; -use resvg::usvg; +use resvg::usvg::{self, TreeTextToPath};  use rustc_hash::{FxHashMap, FxHashSet};  use std::cell::RefCell; @@ -77,7 +78,7 @@ impl Cache {          let id = handle.id();          if let hash_map::Entry::Vacant(entry) = self.trees.entry(id) { -            let svg = match handle.data() { +            let mut svg = match handle.data() {                  Data::Path(path) => {                      fs::read_to_string(path).ok().and_then(|contents| {                          usvg::Tree::from_str( @@ -92,6 +93,15 @@ impl Cache {                  }              }; +            if let Some(svg) = &mut svg { +                if svg.has_text_nodes() { +                    let mut font_system = +                        text::font_system().write().expect("Write font system"); + +                    svg.convert_text(font_system.raw().db_mut()); +                } +            } +              let _ = entry.insert(svg);          } diff --git a/tiny_skia/src/window/compositor.rs b/tiny_skia/src/window/compositor.rs index 828e522f..781ed8a5 100644 --- a/tiny_skia/src/window/compositor.rs +++ b/tiny_skia/src/window/compositor.rs @@ -4,19 +4,25 @@ use crate::graphics::damage;  use crate::graphics::{Error, Viewport};  use crate::{Backend, Primitive, Renderer, Settings}; -use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; +use std::collections::VecDeque;  use std::marker::PhantomData; +use std::num::NonZeroU32;  pub struct Compositor<Theme> { +    context: softbuffer::Context<Box<dyn compositor::Window>>, +    settings: Settings,      _theme: PhantomData<Theme>,  }  pub struct Surface { -    window: softbuffer::GraphicsContext, -    buffer: Vec<u32>, +    window: softbuffer::Surface< +        Box<dyn compositor::Window>, +        Box<dyn compositor::Window>, +    >,      clip_mask: tiny_skia::Mask, -    primitives: Option<Vec<Primitive>>, +    primitive_stack: VecDeque<Vec<Primitive>>,      background_color: Color, +    max_age: u8,  }  impl<Theme> crate::graphics::Compositor for Compositor<Theme> { @@ -24,53 +30,64 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> {      type Renderer = Renderer<Theme>;      type Surface = Surface; -    fn new<W: HasRawWindowHandle + HasRawDisplayHandle>( +    fn new<W: compositor::Window>(          settings: Self::Settings, -        _compatible_window: Option<&W>, -    ) -> Result<(Self, Self::Renderer), Error> { -        let (compositor, backend) = new(); +        compatible_window: W, +    ) -> Result<Self, Error> { +        Ok(new(settings, compatible_window)) +    } -        Ok(( -            compositor, -            Renderer::new( -                backend, -                settings.default_font, -                settings.default_text_size, -            ), -        )) +    fn create_renderer(&self) -> Self::Renderer { +        Renderer::new( +            Backend::new(), +            self.settings.default_font, +            self.settings.default_text_size, +        )      } -    fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>( +    fn create_surface<W: compositor::Window + Clone>(          &mut self, -        window: &W, +        window: W,          width: u32,          height: u32, -    ) -> Surface { -        #[allow(unsafe_code)] -        let window = -            unsafe { softbuffer::GraphicsContext::new(window, window) } -                .expect("Create softbuffer for window"); +    ) -> Self::Surface { +        let window = softbuffer::Surface::new( +            &self.context, +            Box::new(window.clone()) as _, +        ) +        .expect("Create softbuffer surface for window"); -        Surface { +        let mut surface = Surface {              window, -            buffer: vec![0; width as usize * height as usize],              clip_mask: tiny_skia::Mask::new(width, height)                  .expect("Create clip mask"), -            primitives: None, +            primitive_stack: VecDeque::new(),              background_color: Color::BLACK, -        } +            max_age: 0, +        }; + +        self.configure_surface(&mut surface, width, height); + +        surface      }      fn configure_surface(          &mut self, -        surface: &mut Surface, +        surface: &mut Self::Surface,          width: u32,          height: u32,      ) { -        surface.buffer.resize((width * height) as usize, 0); +        surface +            .window +            .resize( +                NonZeroU32::new(width).expect("Non-zero width"), +                NonZeroU32::new(height).expect("Non-zero height"), +            ) +            .expect("Resize surface"); +          surface.clip_mask =              tiny_skia::Mask::new(width, height).expect("Create clip mask"); -        surface.primitives = None; +        surface.primitive_stack.clear();      }      fn fetch_information(&self) -> Information { @@ -121,13 +138,19 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> {      }  } -pub fn new<Theme>() -> (Compositor<Theme>, Backend) { -    ( -        Compositor { -            _theme: PhantomData, -        }, -        Backend::new(), -    ) +pub fn new<W: compositor::Window, Theme>( +    settings: Settings, +    compatible_window: W, +) -> Compositor<Theme> { +    #[allow(unsafe_code)] +    let context = softbuffer::Context::new(Box::new(compatible_window) as _) +        .expect("Create softbuffer context"); + +    Compositor { +        context, +        settings, +        _theme: PhantomData, +    }  }  pub fn present<T: AsRef<str>>( @@ -141,16 +164,25 @@ pub fn present<T: AsRef<str>>(      let physical_size = viewport.physical_size();      let scale_factor = viewport.scale_factor() as f32; -    let mut pixels = tiny_skia::PixmapMut::from_bytes( -        bytemuck::cast_slice_mut(&mut surface.buffer), -        physical_size.width, -        physical_size.height, -    ) -    .expect("Create pixel map"); +    let mut buffer = surface +        .window +        .buffer_mut() +        .map_err(|_| compositor::SurfaceError::Lost)?; + +    let last_primitives = { +        let age = buffer.age(); -    let damage = surface -        .primitives -        .as_deref() +        surface.max_age = surface.max_age.max(age); +        surface.primitive_stack.truncate(surface.max_age as usize); + +        if age > 0 { +            surface.primitive_stack.get(age as usize - 1) +        } else { +            None +        } +    }; + +    let damage = last_primitives          .and_then(|last_primitives| {              (surface.background_color == background_color)                  .then(|| damage::list(last_primitives, primitives)) @@ -161,11 +193,18 @@ pub fn present<T: AsRef<str>>(          return Ok(());      } -    surface.primitives = Some(primitives.to_vec()); +    surface.primitive_stack.push_front(primitives.to_vec());      surface.background_color = background_color;      let damage = damage::group(damage, scale_factor, physical_size); +    let mut pixels = tiny_skia::PixmapMut::from_bytes( +        bytemuck::cast_slice_mut(&mut buffer), +        physical_size.width, +        physical_size.height, +    ) +    .expect("Create pixel map"); +      backend.draw(          &mut pixels,          &mut surface.clip_mask, @@ -176,13 +215,7 @@ pub fn present<T: AsRef<str>>(          overlay,      ); -    surface.window.set_buffer( -        &surface.buffer, -        physical_size.width as u16, -        physical_size.height as u16, -    ); - -    Ok(()) +    buffer.present().map_err(|_| compositor::SurfaceError::Lost)  }  pub fn screenshot<T: AsRef<str>>(  | 
