1use std::fmt;
12use std::sync::Arc;
13
14use super::Scene;
15use super::layer::{
16 ContainerLayer, DisplayListLayer, Effect, EmbeddedLayer, EngineLayer, Layer, VideoLayer,
17};
18use crate::composition::CompositionLayer;
19use crate::geometry::{Clip, Matrix4, Offset, RRect, Rect};
20use crate::media::VideoFrameReader;
21use crate::painting::{BlendMode, ImageFilter, Path, Picture};
22
23pub struct SceneBuilder {
32 layer_stack: Vec<ContainerLayerBuilder>,
36}
37
38struct ContainerLayerBuilder {
41 effect: Effect,
42 children: Vec<Layer>,
43}
44
45impl ContainerLayerBuilder {
46 fn new(effect: Effect) -> ContainerLayerBuilder {
47 ContainerLayerBuilder {
48 effect,
49 children: Vec::new(),
50 }
51 }
52
53 fn build(self) -> ContainerLayer {
54 ContainerLayer {
55 effect: self.effect,
56 children: self.children,
57 }
58 }
59}
60
61impl fmt::Debug for SceneBuilder {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 f.debug_struct("SceneBuilder")
64 .field("open_pushes", &(self.layer_stack.len() - 1))
65 .finish_non_exhaustive()
66 }
67}
68
69impl Default for SceneBuilder {
70 fn default() -> SceneBuilder {
71 SceneBuilder::new()
72 }
73}
74
75impl SceneBuilder {
76 pub fn new() -> SceneBuilder {
78 SceneBuilder {
79 layer_stack: vec![ContainerLayerBuilder::new(Effect::None)],
80 }
81 }
82
83 pub fn push_transform(&mut self, matrix4: &Matrix4) {
87 self.push_layer(Effect::Transform(*matrix4));
88 }
89
90 pub fn push_offset(&mut self, dx: f64, dy: f64) {
95 self.push_layer(Effect::Transform(Matrix4::translation(
96 dx as f32, dy as f32,
97 )));
98 }
99
100 pub fn push_clip_rect(&mut self, rect: Rect, clip_behavior: Clip) {
104 self.push_layer(Effect::ClipRect {
105 rect,
106 clip_behavior,
107 });
108 }
109
110 pub fn push_clip_rrect(&mut self, rrect: RRect, clip_behavior: Clip) {
114 self.push_layer(Effect::ClipRRect {
115 rrect,
116 clip_behavior,
117 });
118 }
119
120 pub fn push_clip_path(&mut self, path: &Arc<Path>, clip_behavior: Clip) {
124 self.push_layer(Effect::ClipPath {
125 path: Arc::clone(path),
126 clip_behavior,
127 });
128 }
129
130 pub fn push_opacity(&mut self, alpha: i32, offset: Offset) {
136 self.push_layer(Effect::Opacity {
137 alpha: alpha.clamp(0, 255) as u8,
138 offset,
139 });
140 }
141
142 pub fn push_backdrop_filter(
151 &mut self,
152 filter: &ImageFilter,
153 blend_mode: BlendMode,
154 backdrop_id: Option<u64>,
155 ) {
156 self.push_layer(Effect::BackdropFilter {
157 filter: filter.clone(),
158 blend_mode,
159 backdrop_id,
160 });
161 }
162
163 pub fn pop(&mut self) -> EngineLayer {
174 assert!(
175 self.layer_stack.len() > 1,
176 "SceneBuilder::pop without a push"
177 );
178 let built = self.layer_stack.pop().expect("a push is open").build();
179 let layer = Arc::new(built);
180 self.add_layer(Layer::Container(Arc::clone(&layer)));
181 layer
182 }
183
184 pub fn add_embedded_layer(&mut self, layer: Arc<dyn CompositionLayer>, rect: Rect) {
188 self.add_layer(Layer::Embedded(EmbeddedLayer { layer, rect }));
189 }
190
191 pub fn add_video(&mut self, reader: Arc<dyn VideoFrameReader>, rect: Rect) {
195 self.add_layer(Layer::Video(VideoLayer { reader, rect }));
196 }
197
198 pub fn add_retained(&mut self, retained_layer: &EngineLayer) {
203 self.add_layer(Layer::Container(Arc::clone(retained_layer)));
204 }
205
206 pub fn add_picture(
211 &mut self,
212 offset: Offset,
213 picture: &Arc<Picture>,
214 is_complex_hint: bool,
215 will_change_hint: bool,
216 ) {
217 let _ = (is_complex_hint, will_change_hint);
218 self.add_layer(Layer::DisplayList(DisplayListLayer {
219 offset,
220 picture: Arc::clone(picture),
221 }));
222 }
223
224 pub fn build(mut self) -> Scene {
230 debug_assert!(
231 self.layer_stack.len() == 1,
232 "SceneBuilder::build with an open push"
233 );
234 while self.layer_stack.len() > 1 {
235 self.pop();
236 }
237 let root = self.layer_stack.pop().expect("the root is being built");
238 Scene { root: root.build() }
239 }
240
241 fn push_layer(&mut self, effect: Effect) {
243 self.layer_stack.push(ContainerLayerBuilder::new(effect));
244 }
245
246 fn add_layer(&mut self, layer: Layer) {
248 self.layer_stack
249 .last_mut()
250 .expect("the root is built only by build")
251 .children
252 .push(layer);
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259 use crate::{Canvas, Paint};
260
261 fn square() -> Arc<Picture> {
262 let mut canvas = Canvas::new();
263 canvas.draw_rect(Rect::from_ltwh(0.0, 0.0, 10.0, 10.0), &Paint::default());
264 Arc::new(canvas.build())
265 }
266
267 fn container(layer: &Layer) -> &EngineLayer {
268 match layer {
269 Layer::Container(container) => container,
270 other => panic!("not a container: {other:?}"),
271 }
272 }
273
274 #[test]
275 fn a_popped_container_is_a_node_of_its_parent() {
276 let picture = square();
277 let mut builder = SceneBuilder::new();
278 builder.push_opacity(128, Offset::new(1.0, 2.0));
279 builder.push_transform(&Matrix4::scale(2.0, 2.0));
280 builder.add_picture(Offset::new(3.0, 4.0), &picture, false, false);
281 let inner = builder.pop();
282 let outer = builder.pop();
283 let scene = builder.build();
284
285 assert_eq!(scene.root.effect, Effect::None);
286 let [only] = scene.root.children.as_slice() else {
287 panic!("one child: {:?}", scene.root.children);
288 };
289 assert!(Arc::ptr_eq(container(only), &outer));
290 assert_eq!(
291 outer.effect,
292 Effect::Opacity {
293 alpha: 128,
294 offset: Offset::new(1.0, 2.0)
295 }
296 );
297 assert!(Arc::ptr_eq(container(&outer.children[0]), &inner));
298 assert_eq!(inner.effect, Effect::Transform(Matrix4::scale(2.0, 2.0)));
299 assert_eq!(
300 inner.children,
301 [Layer::DisplayList(DisplayListLayer {
302 offset: Offset::new(3.0, 4.0),
303 picture,
304 })]
305 );
306 }
307
308 #[test]
309 fn a_retained_container_is_the_same_node_in_the_next_scene() {
310 let mut builder = SceneBuilder::new();
311 builder.push_clip_rect(Rect::from_ltwh(0.0, 0.0, 5.0, 5.0), Clip::HardEdge);
312 builder.add_picture(Offset::ZERO, &square(), false, false);
313 let retained = builder.pop();
314 let _first = builder.build();
315
316 let mut builder = SceneBuilder::new();
317 builder.push_offset(10.0, 0.0);
318 builder.add_retained(&retained);
319 builder.pop();
320 let next = builder.build();
321 let moved = container(&next.root.children[0]);
322 assert!(Arc::ptr_eq(container(&moved.children[0]), &retained));
323 }
324
325 #[test]
326 fn an_opacity_is_kept_between_none_and_all() {
327 let mut builder = SceneBuilder::new();
328 builder.push_opacity(300, Offset::ZERO);
329 let all = builder.pop();
330 builder.push_opacity(-1, Offset::ZERO);
331 let none = builder.pop();
332 let alpha = |layer: &EngineLayer| match layer.effect {
333 Effect::Opacity { alpha, .. } => alpha,
334 _ => unreachable!(),
335 };
336 assert_eq!((alpha(&all), alpha(&none)), (255, 0));
337 }
338
339 #[test]
340 fn a_composition_layer_is_a_leaf_at_its_place_in_paint_order() {
341 let layer = super::super::idle_layer();
342 let rect = Rect::from_ltwh(1.0, 2.0, 3.0, 4.0);
343 let mut builder = SceneBuilder::new();
344 builder.add_picture(Offset::ZERO, &square(), false, false);
345 builder.add_embedded_layer(Arc::clone(&layer), rect);
346 let scene = builder.build();
347 assert!(matches!(scene.root.children[0], Layer::DisplayList(_)));
348 assert_eq!(
349 scene.root.children[1],
350 Layer::Embedded(EmbeddedLayer { layer, rect })
351 );
352 }
353
354 #[test]
355 fn a_scene_without_layers_has_an_empty_root() {
356 let scene = SceneBuilder::new().build();
357 assert!(scene.root.children.is_empty());
358 }
359
360 #[test]
361 #[should_panic(expected = "open push")]
362 fn build_with_an_open_push_panics() {
363 let mut builder = SceneBuilder::new();
364 builder.push_offset(0.0, 0.0);
365 let _scene = builder.build();
366 }
367
368 #[test]
369 #[should_panic(expected = "without a push")]
370 fn pop_without_a_push_panics() {
371 SceneBuilder::new().pop();
372 }
373}