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}