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