Flutter Engine
 
Loading...
Searching...
No Matches
layer_state_stack.h
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_
6#define FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_
7
11
12namespace flutter {
13
14/// The LayerStateStack manages the inherited state passed down between
15/// |Layer| objects in a |LayerTree| during |Preroll| and |Paint|.
16///
17/// More specifically, it manages the clip and transform state during
18/// recursive rendering and will hold and lazily apply opacity, ImageFilter
19/// and ColorFilter attributes to recursive content. This is not a truly
20/// general state management mechnanism as it makes assumptions that code
21/// will be applying the attributes to rendered content that happens in
22/// recursive calls. The automatic save/restore mechanisms only work in
23/// a context where C++ auto-destruct calls will engage the restore at
24/// the end of a code block and that any applied attributes will only
25/// be applied to the content rendered inside that block. These restrictions
26/// match the organization of the |LayerTree| methods precisely.
27///
28/// The stack can manage a single state delegate. The delegate will provide
29/// tracking of the current transform and clip and will also execute
30/// saveLayer calls at the appropriate time if it is a rendering delegate.
31/// The delegate can be swapped out on the fly (as is typically done by
32/// PlatformViewLayer when recording the state for multiple "overlay"
33/// layers that occur between embedded view subtrees. The old delegate
34/// will be restored to its original state before it became a delegate
35/// and the new delegate will have all of the state recorded by the stack
36/// replayed into it to bring it up to speed with the current rendering
37/// context.
38///
39/// The delegate can be any one of:
40/// - Preroll delegate: used during Preroll to remember the outstanding
41/// state for embedded platform layers
42/// - DlCanvas: used during Paint for rendering output
43/// The stack will know which state needs to be conveyed to any of these
44/// delegates and when is the best time to convey that state (i.e. lazy
45/// saveLayer calls for example).
46///
47/// The rendering state attributes will be automatically applied to the
48/// nested content using a |saveLayer| call at the point at which we
49/// encounter rendered content (i.e. various nested layers that exist only
50/// to apply new state will not trigger the |saveLayer| and the attributes
51/// can accumulate until we reach actual content that is rendered.) Some
52/// rendered content can avoid the |saveLayer| if it reports to the object
53/// that it is able to apply all of the attributes that happen to be
54/// outstanding (accumulated from parent state-modifiers). A |ContainerLayer|
55/// can also monitor the attribute rendering capabilities of a list of
56/// children and can ask the object to apply a protective |saveLayer| or
57/// not based on the negotiated capabilities of the entire group.
58///
59/// Any code that is planning to modify the clip, transform, or rendering
60/// attributes for its child content must start by calling the |save| method
61/// which returns a MutatorContext object. The methods that modify such
62/// state only exist on the MutatorContext object so it is difficult to get
63/// that wrong, but the caller must make sure that the call happens within
64/// a C++ code block that will define the "rendering scope" of those
65/// state changes as they will be automatically restored on exit from that
66/// block. Note that the layer might make similar state calls directly on
67/// the canvas or builder during the Paint cycle (via saveLayer, transform,
68/// or clip calls), but should avoid doing so if there is any nested content
69/// that needs to track or react to those state calls.
70///
71/// Code that needs to render content can simply inform the parent of their
72/// abilities by setting the |PrerollContext::renderable_state_flags| during
73/// |Preroll| and then render with those attributes during |Paint| by
74/// requesting the outstanding values of those attributes from the state_stack
75/// object. Individual leaf layers can ignore this feature as the default
76/// behavior during |Preroll| will have their parent |ContainerLayer| assume
77/// that they cannot render any outstanding state attributes and will apply
78/// the protective saveLayer on their behalf if needed. As such, this object
79/// only provides "opt-in" features for leaf layers and no responsibilities
80/// otherwise.
81/// See |LayerStateStack::fill|
82/// See |LayerStateStack::outstanding_opacity|
83/// See |LayerStateStack::outstanding_color_filter|
84/// See |LayerStateStack::outstanding_image_filter|
85///
86/// State-modifying layers should contain code similar to this pattern in both
87/// their |Preroll| and |Paint| methods.
88///
89/// void [LayerType]::[Preroll/Paint](context) {
90/// auto mutator = context.state_stack.save();
91/// mutator.translate(origin.x, origin.y);
92/// mutator.applyOpacity(content_bounds, opacity_value);
93/// mutator.applyColorFilter(content_bounds, color_filter);
94/// // or any of the mutator transform, clip or attribute methods
95///
96/// // Children will react to the state applied above during their
97/// // Preroll/Paint methods or ContainerLayer will protect them
98/// // conservatively by default.
99/// [Preroll/Paint]Children(context);
100///
101/// // here the mutator will be auto-destructed and the state accumulated
102/// // by it will be restored out of the state_stack and its associated
103/// // delegates.
104/// }
106 public:
108
109 // Clears out any old delegate to make room for a new one.
110 void clear_delegate();
111
112 // Return the DlCanvas delegate if the state stack has such a delegate.
113 // The state stack will only have one delegate at a time holding either
114 // a DlCanvas or a preroll accumulator.
115 DlCanvas* canvas_delegate() { return delegate_->canvas(); }
116
117 // Clears the old delegate and sets the canvas delegate to the indicated
118 // DL canvas (if not nullptr). This ensures that only one delegate - either
119 // a DlCanvas or a preroll accumulator - is present at any one time.
120 void set_delegate(DlCanvas* canvas);
121
122 // Clears the old delegate and sets the state stack up to accumulate
123 // clip and transform information for a Preroll phase. This ensures
124 // that only one delegate - either a DlCanvas or a preroll accumulator -
125 // is present at any one time.
126 void set_preroll_delegate(const DlRect& cull_rect, const DlMatrix& matrix);
127 void set_preroll_delegate(const DlRect& cull_rect);
129
130 // Fills the supplied MatatorsStack object with the mutations recorded
131 // by this LayerStateStack in the order encountered.
132 void fill(MutatorsStack* mutators);
133
135 public:
137 layer_state_stack_->restore_to_count(stack_restore_count_);
138 }
139
140 private:
141 AutoRestore(LayerStateStack* stack, const DlRect& bounds, int flags)
142 : layer_state_stack_(stack),
143 stack_restore_count_(stack->stack_count()) {
144 if (stack->needs_save_layer(flags)) {
145 stack->save_layer(bounds);
146 }
147 }
148 friend class LayerStateStack;
149
150 LayerStateStack* layer_state_stack_;
151 const size_t stack_restore_count_;
152
154 };
155
157 public:
159 layer_state_stack_->restore_to_count(stack_restore_count_);
160 }
161
162 // Immediately executes a saveLayer with all accumulated state
163 // onto the canvas or builder to be applied at the next matching
164 // restore. A saveLayer is always executed by this method even if
165 // there are no outstanding attributes.
166 void saveLayer(const DlRect& bounds);
167
168 // Records the opacity for application at the next call to
169 // saveLayer or applyState. A saveLayer may be executed at
170 // this time if the opacity cannot be batched with other
171 // outstanding attributes.
172 void applyOpacity(const DlRect& bounds, DlScalar opacity);
173
174 // Records the image filter for application at the next call to
175 // saveLayer or applyState. A saveLayer may be executed at
176 // this time if the image filter cannot be batched with other
177 // outstanding attributes.
178 // (Currently only opacity is recorded for batching)
179 void applyImageFilter(const DlRect& bounds,
180 const std::shared_ptr<DlImageFilter>& filter);
181
182 // Records the color filter for application at the next call to
183 // saveLayer or applyState. A saveLayer may be executed at
184 // this time if the color filter cannot be batched with other
185 // outstanding attributes.
186 // (Currently only opacity is recorded for batching)
187 void applyColorFilter(const DlRect& bounds,
188 const std::shared_ptr<const DlColorFilter>& filter);
189
190 // Saves the state stack and immediately executes a saveLayer
191 // with the indicated backdrop filter and any outstanding
192 // state attributes. Since the backdrop filter only applies
193 // to the pixels alrady on the screen when this call is made,
194 // the backdrop filter will only be applied to the canvas or
195 // builder installed at the time that this call is made, and
196 // subsequent canvas or builder objects that are made delegates
197 // will only see a saveLayer with the indicated blend_mode.
198 void applyBackdropFilter(const DlRect& bounds,
199 const std::shared_ptr<DlImageFilter>& filter,
200 DlBlendMode blend_mode,
201 std::optional<int64_t> backdrop_id);
202
204 void translate(const DlPoint& tp) { translate(tp.x, tp.y); }
205 void transform(const DlMatrix& matrix);
206 void integralTransform();
207
208 void clipRect(const DlRect& rect, bool is_aa);
209 void clipRRect(const DlRoundRect& rrect, bool is_aa);
210 void clipRSuperellipse(const DlRoundSuperellipse& rse, bool is_aa);
211 void clipPath(const DlPath& path, bool is_aa);
212
213 private:
214 explicit MutatorContext(LayerStateStack* stack)
215 : layer_state_stack_(stack),
216 stack_restore_count_(stack->stack_count()) {}
217 friend class LayerStateStack;
218
219 LayerStateStack* layer_state_stack_;
220 const size_t stack_restore_count_;
221 bool save_needed_ = true;
222
224 };
225
226 static constexpr int kCallerCanApplyOpacity = 0x1;
227 static constexpr int kCallerCanApplyColorFilter = 0x2;
228 static constexpr int kCallerCanApplyImageFilter = 0x4;
229 static constexpr int kCallerCanApplyAnything =
232
233 // Apply the outstanding state via saveLayer if necessary,
234 // respecting the flags representing which potentially
235 // outstanding attributes the calling layer can apply
236 // themselves.
237 //
238 // A saveLayer may or may not be sent to the delegates depending
239 // on how the outstanding state intersects with the flags supplied
240 // by the caller.
241 //
242 // An AutoRestore instance will always be returned even if there
243 // was no saveLayer applied.
244 [[nodiscard]] inline AutoRestore applyState(const DlRect& bounds,
245 int can_apply_flags) {
246 return AutoRestore(this, bounds, can_apply_flags);
247 }
248
249 DlScalar outstanding_opacity() const { return outstanding_.opacity; }
250
251 std::shared_ptr<const DlColorFilter> outstanding_color_filter() const {
252 return outstanding_.color_filter;
253 }
254
255 std::shared_ptr<DlImageFilter> outstanding_image_filter() const {
256 return outstanding_.image_filter;
257 }
258
259 // The outstanding bounds are the bounds recorded during the last
260 // attribute applied to this state stack. The assumption is that
261 // the nested calls to the state stack will each supply bounds relative
262 // to the content of that single attribute and the bounds of the content
263 // of any outstanding attributes will include the output bounds of
264 // applying any nested attributes. Thus, only the innermost content
265 // bounds received will be sufficient to apply all outstanding attributes.
266 DlRect outstanding_bounds() const { return outstanding_.save_layer_bounds; }
267
268 // Fill the provided paint object with any oustanding attributes and
269 // return a pointer to it, or return a nullptr if there were no
270 // outstanding attributes to paint with.
271 DlPaint* fill(DlPaint& paint) const { return outstanding_.fill(paint); }
272
273 // The cull_rect (not the exact clip) relative to the device pixels.
274 // This rectangle may be a conservative estimate of the true clip region.
275 DlRect device_cull_rect() const { return delegate_->device_cull_rect(); }
276
277 // The cull_rect (not the exact clip) relative to the local coordinates.
278 // This rectangle may be a conservative estimate of the true clip region.
279 DlRect local_cull_rect() const { return delegate_->local_cull_rect(); }
280
281 // The transform from the local coordinates to the device coordinates
282 // in 4x4 DlMatrix representation. This matrix provides all information
283 // needed to compute bounds for a 2D rendering primitive, and it will
284 // accurately concatenate with other 4x4 matrices without losing information.
285 const DlMatrix matrix() const { return delegate_->matrix(); }
286
287 // Tests if painting content with the current outstanding attributes
288 // will produce any content. This method does not check the current
289 // transform or clip for being singular or empty.
290 // See |content_culled|
291 bool painting_is_nop() const { return outstanding_.opacity <= 0; }
292
293 // Tests if painting content with the given bounds will produce any output.
294 // This method does not check the outstanding attributes to verify that
295 // they produce visible results.
296 // See |painting_is_nop|
297 bool content_culled(const DlRect& content_bounds) const {
298 return delegate_->content_culled(content_bounds);
299 }
300
301 // Saves the current state of the state stack and returns a
302 // MutatorContext which can be used to manipulate the state.
303 // The state stack will be restored to its current state
304 // when the MutatorContext object goes out of scope.
305 [[nodiscard]] inline MutatorContext save() { return MutatorContext(this); }
306
307 // Returns true if the state stack is in, or has returned to,
308 // its initial state.
309 bool is_empty() const { return state_stack_.empty(); }
310
311 private:
312 size_t stack_count() const { return state_stack_.size(); }
313 void restore_to_count(size_t restore_count);
314 void reapply_all();
315
316 void apply_last_entry() { state_stack_.back()->apply(this); }
317
318 // The push methods simply push an associated StateEntry on the stack
319 // and then apply it to the current canvas and builder.
320 // ---------------------
321 // void push_attributes();
322 void push_opacity(const DlRect& rect, DlScalar opacity);
323 void push_color_filter(const DlRect& bounds,
324 const std::shared_ptr<const DlColorFilter>& filter);
325 void push_image_filter(const DlRect& bounds,
326 const std::shared_ptr<DlImageFilter>& filter);
327 void push_backdrop(const DlRect& bounds,
328 const std::shared_ptr<DlImageFilter>& filter,
329 DlBlendMode blend_mode,
330 std::optional<int64_t> backdrop_id);
331
332 void push_translate(DlScalar tx, DlScalar ty);
333 void push_transform(const DlMatrix& matrix);
334 void push_integral_transform();
335
336 void push_clip_rect(const DlRect& rect, bool is_aa);
337 void push_clip_rrect(const DlRoundRect& rrect, bool is_aa);
338 void push_clip_rsuperellipse(const DlRoundSuperellipse& rse, bool is_aa);
339 void push_clip_path(const DlPath& path, bool is_aa);
340 // ---------------------
341
342 // The maybe/needs_save_layer methods will determine if the indicated
343 // attribute can be incorporated into the outstanding attributes as is,
344 // or if the apply_flags are compatible with the outstanding attributes.
345 // If the oustanding attributes are incompatible with the new attribute
346 // or the apply flags, then a protective saveLayer will be executed.
347 // ---------------------
348 bool needs_save_layer(int flags) const;
349 void do_save();
350 void save_layer(const DlRect& bounds);
351 void maybe_save_layer_for_transform(bool needs_save);
352 void maybe_save_layer_for_clip(bool needs_save);
353 void maybe_save_layer(int apply_flags);
354 void maybe_save_layer(DlScalar opacity);
355 void maybe_save_layer(const std::shared_ptr<const DlColorFilter>& filter);
356 void maybe_save_layer(const std::shared_ptr<DlImageFilter>& filter);
357 // ---------------------
358
359 struct RenderingAttributes {
360 // We need to record the last bounds we received for the last
361 // attribute that we recorded so that we can perform a saveLayer
362 // on the proper area. When an attribute is applied that cannot
363 // be merged with the existing attributes, it will be submitted
364 // with a bounds for its own source content, not the bounds for
365 // the content that will be included in the saveLayer that applies
366 // the existing outstanding attributes - thus we need to record
367 // the bounds that were supplied with the most recent previous
368 // attribute to be applied.
369 DlRect save_layer_bounds;
370
371 DlScalar opacity = SK_Scalar1;
372 std::shared_ptr<const DlColorFilter> color_filter;
373 std::shared_ptr<DlImageFilter> image_filter;
374
375 DlPaint* fill(DlPaint& paint,
376 DlBlendMode mode = DlBlendMode::kSrcOver) const;
377
378 bool operator==(const RenderingAttributes& other) const {
379 return save_layer_bounds == other.save_layer_bounds &&
380 opacity == other.opacity &&
381 Equals(color_filter, other.color_filter) &&
382 Equals(image_filter, other.image_filter);
383 }
384 };
385
386 class StateEntry {
387 public:
388 virtual ~StateEntry() = default;
389
390 virtual void apply(LayerStateStack* stack) const = 0;
391 virtual void reapply(LayerStateStack* stack) const { apply(stack); }
392 virtual void restore(LayerStateStack* stack) const {}
393 virtual void update_mutators(MutatorsStack* mutators_stack) const {}
394
395 protected:
396 StateEntry() = default;
397
399 };
400 friend class SaveEntry;
401 friend class SaveLayerEntry;
403 friend class OpacityEntry;
404 friend class ImageFilterEntry;
405 friend class ColorFilterEntry;
406 friend class TranslateEntry;
408 friend class TransformM44Entry;
410 friend class ClipEntry;
411 friend class ClipRectEntry;
412 friend class ClipRRectEntry;
414 friend class ClipPathEntry;
415
416 class Delegate {
417 public:
418 virtual ~Delegate() = default;
419
420 // Mormally when a |Paint| or |Preroll| cycle is completed, the
421 // delegate will have been rewound to its initial state by the
422 // trailing recursive actions of the paint and preroll methods.
423 // When a delegate is swapped out, there may be unresolved state
424 // that the delegate received. This method is called when the
425 // delegate is cleared or swapped out to inform it to rewind its
426 // state and finalize all outstanding save or saveLayer operations.
427 virtual void decommission() = 0;
428
429 virtual DlCanvas* canvas() const { return nullptr; }
430
431 virtual DlRect local_cull_rect() const = 0;
432 virtual DlRect device_cull_rect() const = 0;
433 virtual DlMatrix matrix() const = 0;
434 virtual bool content_culled(const DlRect& content_bounds) const = 0;
435
436 virtual void save() = 0;
437 virtual void saveLayer(
438 const DlRect& bounds,
439 RenderingAttributes& attributes,
440 DlBlendMode blend,
441 const DlImageFilter* backdrop,
442 std::optional<int64_t> backdrop_id = std::nullopt) = 0;
443 virtual void restore() = 0;
444
445 virtual void translate(DlScalar tx, DlScalar ty) = 0;
446 virtual void transform(const DlMatrix& matrix) = 0;
447 virtual void integralTransform() = 0;
448
449 virtual void clipRect(const DlRect& rect, DlClipOp op, bool is_aa) = 0;
450 virtual void clipRRect(const DlRoundRect& rrect,
451 DlClipOp op,
452 bool is_aa) = 0;
453 virtual void clipRSuperellipse(const DlRoundSuperellipse& rse,
454 DlClipOp op,
455 bool is_aa) = 0;
456 virtual void clipPath(const DlPath& path, DlClipOp op, bool is_aa) = 0;
457 };
458 friend class DummyDelegate;
459 friend class DlCanvasDelegate;
460 friend class PrerollDelegate;
461
462 std::vector<std::unique_ptr<StateEntry>> state_stack_;
463 friend class MutatorContext;
464
465 std::shared_ptr<Delegate> delegate_;
466 RenderingAttributes outstanding_;
467
468 friend class SaveLayerEntry;
469};
470
471} // namespace flutter
472
473#endif // FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_
Developer-facing API for rendering anything within the engine.
Definition dl_canvas.h:32
void applyColorFilter(const DlRect &bounds, const std::shared_ptr< const DlColorFilter > &filter)
void clipPath(const DlPath &path, bool is_aa)
void clipRSuperellipse(const DlRoundSuperellipse &rse, bool is_aa)
void translate(DlScalar tx, DlScalar ty)
void clipRRect(const DlRoundRect &rrect, bool is_aa)
void applyOpacity(const DlRect &bounds, DlScalar opacity)
void clipRect(const DlRect &rect, bool is_aa)
void applyBackdropFilter(const DlRect &bounds, const std::shared_ptr< DlImageFilter > &filter, DlBlendMode blend_mode, std::optional< int64_t > backdrop_id)
void applyImageFilter(const DlRect &bounds, const std::shared_ptr< DlImageFilter > &filter)
DlScalar outstanding_opacity() const
void fill(MutatorsStack *mutators)
std::shared_ptr< DlImageFilter > outstanding_image_filter() const
DlPaint * fill(DlPaint &paint) const
bool content_culled(const DlRect &content_bounds) const
static constexpr int kCallerCanApplyAnything
static constexpr int kCallerCanApplyColorFilter
void set_delegate(DlCanvas *canvas)
void set_preroll_delegate(const DlRect &cull_rect, const DlMatrix &matrix)
AutoRestore applyState(const DlRect &bounds, int can_apply_flags)
std::shared_ptr< const DlColorFilter > outstanding_color_filter() const
static constexpr int kCallerCanApplyImageFilter
const DlMatrix matrix() const
static constexpr int kCallerCanApplyOpacity
MockDelegate delegate_
#define FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TypeName)
Definition macros.h:31
impeller::Scalar DlScalar
impeller::RoundRect DlRoundRect
impeller::Matrix DlMatrix
impeller::Rect DlRect
impeller::RoundSuperellipse DlRoundSuperellipse
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switch_defs.h:52
bool Equals(const T *a, const U *b)
impeller::BlendMode DlBlendMode
bool operator==(const T &value, const UniqueObject< T, Traits > &object)
BlendMode
Definition color.h:58
flutter::DlPaint DlPaint
A 4x4 matrix using column-major storage.
Definition matrix.h:37