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