inset_embedder/scene/layer.rs
1//! The nodes of a frame's layer tree, after the engine's `flow/layers`: containers that do one
2//! thing to their children, the pictures the framework recorded, the composition layers it
3//! places, and the video frames it draws.
4//!
5//! A node never changes once built: a container the framework retains is the same `Arc` in every
6//! scene that holds it, so a host compares it by pointer and a later frame walks it again. The
7//! invariant a later editor must keep: nothing here draws or composites; the drawing is the
8//! pictures', and what the containers do is replayed by whoever walks the tree (`slices.rs`).
9
10use std::fmt;
11use std::sync::Arc;
12
13use crate::composition::CompositionLayer;
14use crate::geometry::{Clip, Matrix4, Offset, RRect, Rect};
15use crate::media::VideoFrameReader;
16use crate::painting::{BlendMode, ImageFilter, Path, Picture};
17
18/// One node of a frame's layer tree: the engine's `flow::Layer`.
19#[derive(Clone, Debug)]
20pub enum Layer {
21 /// Children under one effect: what a `push_*` opens and `pop` closes, or a subtree a
22 /// previous frame built, added again.
23 Container(EngineLayer),
24 /// A recorded picture at an offset: flow's `DisplayListLayer`, from `add_picture`.
25 DisplayList(DisplayListLayer),
26 /// A composition layer at its place in paint order: flow's `PlatformViewLayer`, from
27 /// `add_embedded_layer`. It draws nothing; the host places the layer and makes room for it.
28 Embedded(EmbeddedLayer),
29 /// A video, drawn as part of the picture at its place in paint order: Chromium's
30 /// `cc::VideoLayer`, from `add_video`. The host supplies the frame when it draws.
31 Video(VideoLayer),
32}
33
34/// Two layers are equal when they are the same node or show the same: a container by pointer
35/// first, so a retained subtree compares at once.
36impl PartialEq for Layer {
37 fn eq(&self, other: &Layer) -> bool {
38 match (self, other) {
39 (Layer::Container(a), Layer::Container(b)) => Arc::ptr_eq(a, b) || a == b,
40 (Layer::DisplayList(a), Layer::DisplayList(b)) => a == b,
41 (Layer::Embedded(a), Layer::Embedded(b)) => a == b,
42 (Layer::Video(a), Layer::Video(b)) => a == b,
43 _ => false,
44 }
45 }
46}
47
48/// dart:ui's `EngineLayer`: a container of the tree, which [`pop`](crate::SceneBuilder::pop)
49/// returns and [`add_retained`](crate::SceneBuilder::add_retained) adds again, as the same node,
50/// while the framework's subtree has not changed.
51pub type EngineLayer = Arc<ContainerLayer>;
52
53/// flow's `ContainerLayer` and the containers derived from it: children in paint order, the
54/// bottom-most first, under one effect.
55#[derive(Debug, PartialEq)]
56pub struct ContainerLayer {
57 pub effect: Effect,
58 pub children: Vec<Layer>,
59}
60
61/// What a container does to its children: which of flow's containers it is.
62#[derive(Clone, Debug, PartialEq)]
63pub enum Effect {
64 /// flow's `ContainerLayer` itself, which does nothing: the scene's root.
65 None,
66 /// `TransformLayer`: the children's coordinates taken through the matrix. `push_transform`,
67 /// and `push_offset` as a translation.
68 Transform(Matrix4),
69 /// `ClipRectLayer`: nothing of the children outside the rectangle.
70 ClipRect { rect: Rect, clip_behavior: Clip },
71 /// `ClipRRectLayer`: nothing of the children outside the rounded rectangle.
72 ClipRRect { rrect: RRect, clip_behavior: Clip },
73 /// `ClipPathLayer`: nothing of the children outside the path, filled non-zero.
74 ClipPath {
75 path: Arc<Path>,
76 clip_behavior: Clip,
77 },
78 /// `OpacityLayer`: the children moved by `offset` and composited as one at `alpha`, out of
79 /// 255.
80 Opacity { alpha: u8, offset: Offset },
81 /// `BackdropFilterLayer`: what lies beneath filtered, then blended back with `blend_mode`,
82 /// before the children paint. Filters sharing a `backdrop_id` sample one backdrop.
83 BackdropFilter {
84 filter: ImageFilter,
85 blend_mode: BlendMode,
86 backdrop_id: Option<u64>,
87 },
88}
89
90/// flow's `DisplayListLayer`: a picture the framework recorded, drawn at `offset`.
91#[derive(Clone, Debug, PartialEq)]
92pub struct DisplayListLayer {
93 pub offset: Offset,
94 pub picture: Arc<Picture>,
95}
96
97/// flow's `PlatformViewLayer`, named after Flutter's embedded views: a composition layer the
98/// host composites, embedded in the frame at `rect` in its container's coordinates.
99#[derive(Clone)]
100pub struct EmbeddedLayer {
101 /// The producer's own handle, so a host reaches the native object behind it directly.
102 pub layer: Arc<dyn CompositionLayer>,
103 pub rect: Rect,
104}
105
106/// Two are equal when they put the same composition layer, by pointer, at the same place.
107impl PartialEq for EmbeddedLayer {
108 fn eq(&self, other: &EmbeddedLayer) -> bool {
109 Arc::ptr_eq(&self.layer, &other.layer) && self.rect == other.rect
110 }
111}
112
113impl fmt::Debug for EmbeddedLayer {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 f.debug_struct("EmbeddedLayer")
116 .field("layer", &Arc::as_ptr(&self.layer).cast::<()>())
117 .field("rect", &self.rect)
118 .finish()
119 }
120}
121
122/// A video, drawn into `rect` in its container's coordinates: Chromium's `cc::VideoLayer` over its
123/// frame provider. The layer holds the reader, never a frame: whoever draws the scene asks the
124/// reader then for the frame of the moment the picture reaches the screen, so a frame is held only
125/// while it is on screen.
126#[derive(Clone)]
127pub struct VideoLayer {
128 pub reader: Arc<dyn VideoFrameReader>,
129 pub rect: Rect,
130}
131
132/// Two are equal when they draw the same reader, by pointer, into the same place: a new frame of
133/// the reader is not a change of the scene.
134impl PartialEq for VideoLayer {
135 fn eq(&self, other: &VideoLayer) -> bool {
136 Arc::ptr_eq(&self.reader, &other.reader) && self.rect == other.rect
137 }
138}
139
140impl fmt::Debug for VideoLayer {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 f.debug_struct("VideoLayer")
143 .field("reader", &Arc::as_ptr(&self.reader).cast::<()>())
144 .field("rect", &self.rect)
145 .finish()
146 }
147}