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}