iced_tiny_skia/window/
compositor.rs

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