Skip to main content

iced_tiny_skia/window/
compositor.rs

1use crate::core::renderer;
2use crate::core::{Color, Rectangle, Size};
3use crate::graphics::compositor::{self, Information};
4use crate::graphics::damage;
5use crate::graphics::error::{self, Error};
6use crate::graphics::{Shell, Viewport};
7use crate::{Layer, Renderer};
8
9use std::collections::VecDeque;
10use std::num::NonZeroU32;
11
12pub struct Compositor {
13    context: softbuffer::Context<Box<dyn compositor::Display>>,
14}
15
16pub struct Surface {
17    window: softbuffer::Surface<Box<dyn compositor::Display>, Box<dyn compositor::Window>>,
18    clip_mask: tiny_skia::Mask,
19    frames: VecDeque<Frame>,
20    max_age: u8,
21}
22
23#[derive(Clone)]
24struct Frame {
25    background: Color,
26    layers: Vec<Layer>,
27}
28
29impl crate::graphics::Compositor for Compositor {
30    type Renderer = Renderer;
31    type Surface = Surface;
32
33    async fn with_backend(
34        _settings: compositor::Settings,
35        display: impl compositor::Display,
36        _compatible_window: impl compositor::Window,
37        _shell: Shell,
38        backend: Option<&str>,
39    ) -> Result<Self, Error> {
40        match backend {
41            None | Some("tiny-skia") | Some("tiny_skia") => Ok(new(display)),
42            Some(backend) => Err(Error::GraphicsAdapterNotFound {
43                backend: "tiny-skia",
44                reason: error::Reason::DidNotMatch {
45                    preferred_backend: backend.to_owned(),
46                },
47            }),
48        }
49    }
50
51    fn create_renderer(&self, settings: renderer::Settings) -> Self::Renderer {
52        Renderer::new(settings)
53    }
54
55    fn create_surface(
56        &mut self,
57        window: impl compositor::Window + Clone,
58        width: u32,
59        height: u32,
60    ) -> Self::Surface {
61        let window = softbuffer::Surface::new(&self.context, Box::new(window.clone()) as _)
62            .expect("Create softbuffer surface for window");
63
64        let mut surface = Surface {
65            window,
66            clip_mask: tiny_skia::Mask::new(1, 1).expect("Create clip mask"),
67            frames: VecDeque::new(),
68            max_age: 0,
69        };
70
71        if width > 0 && height > 0 {
72            self.configure_surface(&mut surface, width, height);
73        }
74
75        surface
76    }
77
78    fn configure_surface(&mut self, surface: &mut Self::Surface, width: u32, height: u32) {
79        surface
80            .window
81            .resize(
82                NonZeroU32::new(width).expect("Non-zero width"),
83                NonZeroU32::new(height).expect("Non-zero height"),
84            )
85            .expect("Resize surface");
86
87        surface.clip_mask = tiny_skia::Mask::new(width, height).expect("Create clip mask");
88        surface.frames.clear();
89    }
90
91    fn information(&self) -> Information {
92        Information {
93            adapter: String::from("CPU"),
94            backend: String::from("tiny-skia"),
95        }
96    }
97
98    fn present(
99        &mut self,
100        renderer: &mut Self::Renderer,
101        surface: &mut Self::Surface,
102        viewport: &Viewport,
103        background_color: Color,
104        on_pre_present: impl FnOnce(),
105    ) -> Result<(), compositor::SurfaceError> {
106        present(
107            renderer,
108            surface,
109            viewport,
110            background_color,
111            on_pre_present,
112        )
113    }
114
115    fn screenshot(
116        &mut self,
117        renderer: &mut Self::Renderer,
118        viewport: &Viewport,
119        background_color: Color,
120    ) -> Vec<u8> {
121        screenshot(renderer, viewport, background_color)
122    }
123}
124
125pub fn new(display: impl compositor::Display) -> Compositor {
126    #[allow(unsafe_code)]
127    let context =
128        softbuffer::Context::new(Box::new(display) as _).expect("Create softbuffer context");
129
130    Compositor { context }
131}
132
133pub fn present(
134    renderer: &mut Renderer,
135    surface: &mut Surface,
136    viewport: &Viewport,
137    background: Color,
138    on_pre_present: impl FnOnce(),
139) -> Result<(), compositor::SurfaceError> {
140    let physical_size = viewport.physical_size();
141
142    let mut buffer = surface
143        .window
144        .buffer_mut()
145        .map_err(|_| compositor::SurfaceError::Lost)?;
146
147    let last_frame = {
148        let age = buffer.age();
149
150        surface.max_age = surface.max_age.max(age);
151        surface.frames.truncate(surface.max_age as usize);
152
153        if age > 0 {
154            surface.frames.get(age as usize - 1)
155        } else {
156            None
157        }
158    };
159
160    let damage = last_frame
161        .and_then(|last_frame| {
162            (last_frame.background == background).then(|| {
163                damage::diff(
164                    &last_frame.layers,
165                    renderer.layers(),
166                    |layer| vec![layer.bounds],
167                    Layer::damage,
168                )
169            })
170        })
171        .unwrap_or_else(|| vec![Rectangle::with_size(viewport.logical_size())]);
172
173    if damage.is_empty() {
174        if let Some(last_frame) = last_frame {
175            surface.frames.push_front(last_frame.clone());
176        }
177    } else {
178        surface.frames.push_front(Frame {
179            background,
180            layers: renderer.layers().to_vec(),
181        });
182
183        let damage = damage::group(damage, Rectangle::with_size(viewport.logical_size()));
184
185        let mut pixels = tiny_skia::PixmapMut::from_bytes(
186            bytemuck::cast_slice_mut(&mut buffer),
187            physical_size.width,
188            physical_size.height,
189        )
190        .expect("Create pixel map");
191
192        renderer.draw(
193            &mut pixels,
194            &mut surface.clip_mask,
195            viewport,
196            &damage,
197            background,
198        );
199    }
200
201    on_pre_present();
202    buffer.present().map_err(|_| compositor::SurfaceError::Lost)
203}
204
205pub fn screenshot(
206    renderer: &mut Renderer,
207    viewport: &Viewport,
208    background_color: Color,
209) -> Vec<u8> {
210    let size = viewport.physical_size();
211
212    let mut offscreen_buffer: Vec<u32> = vec![0; size.width as usize * size.height as usize];
213
214    let mut clip_mask = tiny_skia::Mask::new(size.width, size.height).expect("Create clip mask");
215
216    renderer.draw(
217        &mut tiny_skia::PixmapMut::from_bytes(
218            bytemuck::cast_slice_mut(&mut offscreen_buffer),
219            size.width,
220            size.height,
221        )
222        .expect("Create offscreen pixel map"),
223        &mut clip_mask,
224        viewport,
225        &[Rectangle::with_size(Size::new(
226            size.width as f32,
227            size.height as f32,
228        ))],
229        background_color,
230    );
231
232    offscreen_buffer.iter().fold(
233        Vec::with_capacity(offscreen_buffer.len() * 4),
234        |mut acc, pixel| {
235            const A_MASK: u32 = 0xFF_00_00_00;
236            const R_MASK: u32 = 0x00_FF_00_00;
237            const G_MASK: u32 = 0x00_00_FF_00;
238            const B_MASK: u32 = 0x00_00_00_FF;
239
240            let a = ((A_MASK & pixel) >> 24) as u8;
241            let r = ((R_MASK & pixel) >> 16) as u8;
242            let g = ((G_MASK & pixel) >> 8) as u8;
243            let b = (B_MASK & pixel) as u8;
244
245            acc.extend([r, g, b, a]);
246            acc
247        },
248    )
249}