Flutter Engine
The 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
8#include "flutter/display_list/dl_canvas.h"
9#include "flutter/flow/embedded_views.h"
10#include "flutter/flow/paint_utils.h"
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 SkRect& cull_rect, const SkMatrix& matrix);
127 void set_preroll_delegate(const SkRect& cull_rect);
128 void set_preroll_delegate(const SkMatrix& matrix);
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
134 // Sets up a checkerboard function that will be used to checkerboard the
135 // contents of any saveLayer executed by the state stack.
136 CheckerboardFunc checkerboard_func() const { return checkerboard_func_; }
140
142 public:
144 layer_state_stack_->restore_to_count(stack_restore_count_);
145 }
146
147 private:
148 AutoRestore(LayerStateStack* stack, const SkRect& bounds, int flags)
149 : layer_state_stack_(stack),
150 stack_restore_count_(stack->stack_count()) {
151 if (stack->needs_save_layer(flags)) {
152 stack->save_layer(bounds);
153 }
154 }
155 friend class LayerStateStack;
156
157 LayerStateStack* layer_state_stack_;
158 const size_t stack_restore_count_;
159
161 };
162
164 public:
166 layer_state_stack_->restore_to_count(stack_restore_count_);
167 }
168
169 // Immediately executes a saveLayer with all accumulated state
170 // onto the canvas or builder to be applied at the next matching
171 // restore. A saveLayer is always executed by this method even if
172 // there are no outstanding attributes.
173 void saveLayer(const SkRect& bounds);
174
175 // Records the opacity for application at the next call to
176 // saveLayer or applyState. A saveLayer may be executed at
177 // this time if the opacity cannot be batched with other
178 // outstanding attributes.
179 void applyOpacity(const SkRect& bounds, SkScalar opacity);
180
181 // Records the image filter for application at the next call to
182 // saveLayer or applyState. A saveLayer may be executed at
183 // this time if the image filter cannot be batched with other
184 // outstanding attributes.
185 // (Currently only opacity is recorded for batching)
186 void applyImageFilter(const SkRect& bounds,
187 const std::shared_ptr<const DlImageFilter>& filter);
188
189 // Records the color filter for application at the next call to
190 // saveLayer or applyState. A saveLayer may be executed at
191 // this time if the color filter cannot be batched with other
192 // outstanding attributes.
193 // (Currently only opacity is recorded for batching)
194 void applyColorFilter(const SkRect& bounds,
195 const std::shared_ptr<const DlColorFilter>& filter);
196
197 // Saves the state stack and immediately executes a saveLayer
198 // with the indicated backdrop filter and any outstanding
199 // state attributes. Since the backdrop filter only applies
200 // to the pixels alrady on the screen when this call is made,
201 // the backdrop filter will only be applied to the canvas or
202 // builder installed at the time that this call is made, and
203 // subsequent canvas or builder objects that are made delegates
204 // will only see a saveLayer with the indicated blend_mode.
205 void applyBackdropFilter(const SkRect& bounds,
206 const std::shared_ptr<const DlImageFilter>& filter,
207 DlBlendMode blend_mode);
208
209 void translate(SkScalar tx, SkScalar ty);
210 void translate(SkPoint tp) { translate(tp.fX, tp.fY); }
211 void transform(const SkM44& m44);
212 void transform(const SkMatrix& matrix);
213 void integralTransform();
214
215 void clipRect(const SkRect& rect, bool is_aa);
216 void clipRRect(const SkRRect& rrect, bool is_aa);
217 void clipPath(const SkPath& path, bool is_aa);
218
219 private:
220 explicit MutatorContext(LayerStateStack* stack)
221 : layer_state_stack_(stack),
222 stack_restore_count_(stack->stack_count()) {}
223 friend class LayerStateStack;
224
225 LayerStateStack* layer_state_stack_;
226 const size_t stack_restore_count_;
227 bool save_needed_ = true;
228
230 };
231
232 static constexpr int kCallerCanApplyOpacity = 0x1;
233 static constexpr int kCallerCanApplyColorFilter = 0x2;
234 static constexpr int kCallerCanApplyImageFilter = 0x4;
235 static constexpr int kCallerCanApplyAnything =
238
239 // Apply the outstanding state via saveLayer if necessary,
240 // respecting the flags representing which potentially
241 // outstanding attributes the calling layer can apply
242 // themselves.
243 //
244 // A saveLayer may or may not be sent to the delegates depending
245 // on how the outstanding state intersects with the flags supplied
246 // by the caller.
247 //
248 // An AutoRestore instance will always be returned even if there
249 // was no saveLayer applied.
250 [[nodiscard]] inline AutoRestore applyState(const SkRect& bounds,
251 int can_apply_flags) {
252 return AutoRestore(this, bounds, can_apply_flags);
253 }
254
255 SkScalar outstanding_opacity() const { return outstanding_.opacity; }
256
257 std::shared_ptr<const DlColorFilter> outstanding_color_filter() const {
258 return outstanding_.color_filter;
259 }
260
261 std::shared_ptr<const DlImageFilter> outstanding_image_filter() const {
262 return outstanding_.image_filter;
263 }
264
265 // The outstanding bounds are the bounds recorded during the last
266 // attribute applied to this state stack. The assumption is that
267 // the nested calls to the state stack will each supply bounds relative
268 // to the content of that single attribute and the bounds of the content
269 // of any outstanding attributes will include the output bounds of
270 // applying any nested attributes. Thus, only the innermost content
271 // bounds received will be sufficient to apply all outstanding attributes.
272 SkRect outstanding_bounds() const { return outstanding_.save_layer_bounds; }
273
274 // Fill the provided paint object with any oustanding attributes and
275 // return a pointer to it, or return a nullptr if there were no
276 // outstanding attributes to paint with.
277 DlPaint* fill(DlPaint& paint) const { return outstanding_.fill(paint); }
278
279 // The cull_rect (not the exact clip) relative to the device pixels.
280 // This rectangle may be a conservative estimate of the true clip region.
281 SkRect device_cull_rect() const { return delegate_->device_cull_rect(); }
282
283 // The cull_rect (not the exact clip) relative to the local coordinates.
284 // This rectangle may be a conservative estimate of the true clip region.
285 SkRect local_cull_rect() const { return delegate_->local_cull_rect(); }
286
287 // The transform from the local coordinates to the device coordinates
288 // in the most capable 4x4 matrix representation. This matrix may be
289 // more information than is needed to compute bounds for a 2D rendering
290 // primitive, but it will accurately concatenate with other 4x4 matrices
291 // without losing information.
292 SkM44 transform_4x4() const { return delegate_->matrix_4x4(); }
293
294 // The transform from the local coordinates to the device coordinates
295 // in a more compact 3x3 matrix represenation that provides enough
296 // information to accurately transform 2D primitives into their
297 // resulting 2D bounds. This matrix also has enough information to
298 // concat with other 2D affine transforms, but does not carry enough
299 // information to accurately concat with fully perspective matrics.
300 SkMatrix transform_3x3() const { return delegate_->matrix_3x3(); }
301
302 // Tests if painting content with the current outstanding attributes
303 // will produce any content. This method does not check the current
304 // transform or clip for being singular or empty.
305 // See |content_culled|
306 bool painting_is_nop() const { return outstanding_.opacity <= 0; }
307
308 // Tests if painting content with the given bounds will produce any output.
309 // This method does not check the outstanding attributes to verify that
310 // they produce visible results.
311 // See |painting_is_nop|
312 bool content_culled(const SkRect& content_bounds) const {
313 return delegate_->content_culled(content_bounds);
314 }
315
316 // Saves the current state of the state stack and returns a
317 // MutatorContext which can be used to manipulate the state.
318 // The state stack will be restored to its current state
319 // when the MutatorContext object goes out of scope.
320 [[nodiscard]] inline MutatorContext save() { return MutatorContext(this); }
321
322 // Returns true if the state stack is in, or has returned to,
323 // its initial state.
324 bool is_empty() const { return state_stack_.empty(); }
325
326 private:
327 size_t stack_count() const { return state_stack_.size(); }
328 void restore_to_count(size_t restore_count);
329 void reapply_all();
330
331 void apply_last_entry() { state_stack_.back()->apply(this); }
332
333 // The push methods simply push an associated StateEntry on the stack
334 // and then apply it to the current canvas and builder.
335 // ---------------------
336 // void push_attributes();
337 void push_opacity(const SkRect& rect, SkScalar opacity);
338 void push_color_filter(const SkRect& bounds,
339 const std::shared_ptr<const DlColorFilter>& filter);
340 void push_image_filter(const SkRect& bounds,
341 const std::shared_ptr<const DlImageFilter>& filter);
342 void push_backdrop(const SkRect& bounds,
343 const std::shared_ptr<const DlImageFilter>& filter,
344 DlBlendMode blend_mode);
345
346 void push_translate(SkScalar tx, SkScalar ty);
347 void push_transform(const SkM44& matrix);
348 void push_transform(const SkMatrix& matrix);
349 void push_integral_transform();
350
351 void push_clip_rect(const SkRect& rect, bool is_aa);
352 void push_clip_rrect(const SkRRect& rrect, bool is_aa);
353 void push_clip_path(const SkPath& path, bool is_aa);
354 // ---------------------
355
356 // The maybe/needs_save_layer methods will determine if the indicated
357 // attribute can be incorporated into the outstanding attributes as is,
358 // or if the apply_flags are compatible with the outstanding attributes.
359 // If the oustanding attributes are incompatible with the new attribute
360 // or the apply flags, then a protective saveLayer will be executed.
361 // ---------------------
362 bool needs_save_layer(int flags) const;
363 void do_save();
364 void save_layer(const SkRect& bounds);
365 void maybe_save_layer_for_transform(bool needs_save);
366 void maybe_save_layer_for_clip(bool needs_save);
367 void maybe_save_layer(int apply_flags);
368 void maybe_save_layer(SkScalar opacity);
369 void maybe_save_layer(const std::shared_ptr<const DlColorFilter>& filter);
370 void maybe_save_layer(const std::shared_ptr<const DlImageFilter>& filter);
371 // ---------------------
372
373 struct RenderingAttributes {
374 // We need to record the last bounds we received for the last
375 // attribute that we recorded so that we can perform a saveLayer
376 // on the proper area. When an attribute is applied that cannot
377 // be merged with the existing attributes, it will be submitted
378 // with a bounds for its own source content, not the bounds for
379 // the content that will be included in the saveLayer that applies
380 // the existing outstanding attributes - thus we need to record
381 // the bounds that were supplied with the most recent previous
382 // attribute to be applied.
383 SkRect save_layer_bounds{0, 0, 0, 0};
384
385 SkScalar opacity = SK_Scalar1;
386 std::shared_ptr<const DlColorFilter> color_filter;
387 std::shared_ptr<const DlImageFilter> image_filter;
388
389 DlPaint* fill(DlPaint& paint,
391
392 bool operator==(const RenderingAttributes& other) const {
393 return save_layer_bounds == other.save_layer_bounds &&
394 opacity == other.opacity &&
395 Equals(color_filter, other.color_filter) &&
396 Equals(image_filter, other.image_filter);
397 }
398 };
399
400 class StateEntry {
401 public:
402 virtual ~StateEntry() = default;
403
404 virtual void apply(LayerStateStack* stack) const = 0;
405 virtual void reapply(LayerStateStack* stack) const { apply(stack); }
406 virtual void restore(LayerStateStack* stack) const {}
407 virtual void update_mutators(MutatorsStack* mutators_stack) const {}
408
409 protected:
410 StateEntry() = default;
411
413 };
414 friend class SaveEntry;
415 friend class SaveLayerEntry;
417 friend class OpacityEntry;
418 friend class ImageFilterEntry;
419 friend class ColorFilterEntry;
420 friend class TranslateEntry;
422 friend class TransformM44Entry;
424 friend class ClipEntry;
425 friend class ClipRectEntry;
426 friend class ClipRRectEntry;
427 friend class ClipPathEntry;
428
429 class Delegate {
430 protected:
431 using ClipOp = DlCanvas::ClipOp;
432
433 public:
434 virtual ~Delegate() = default;
435
436 // Mormally when a |Paint| or |Preroll| cycle is completed, the
437 // delegate will have been rewound to its initial state by the
438 // trailing recursive actions of the paint and preroll methods.
439 // When a delegate is swapped out, there may be unresolved state
440 // that the delegate received. This method is called when the
441 // delegate is cleared or swapped out to inform it to rewind its
442 // state and finalize all outstanding save or saveLayer operations.
443 virtual void decommission() = 0;
444
445 virtual DlCanvas* canvas() const { return nullptr; }
446
447 virtual SkRect local_cull_rect() const = 0;
448 virtual SkRect device_cull_rect() const = 0;
449 virtual SkM44 matrix_4x4() const = 0;
450 virtual SkMatrix matrix_3x3() const = 0;
451 virtual bool content_culled(const SkRect& content_bounds) const = 0;
452
453 virtual void save() = 0;
454 virtual void saveLayer(const SkRect& bounds,
455 RenderingAttributes& attributes,
457 const DlImageFilter* backdrop) = 0;
458 virtual void restore() = 0;
459
460 virtual void translate(SkScalar tx, SkScalar ty) = 0;
461 virtual void transform(const SkM44& m44) = 0;
462 virtual void transform(const SkMatrix& matrix) = 0;
463 virtual void integralTransform() = 0;
464
465 virtual void clipRect(const SkRect& rect, ClipOp op, bool is_aa) = 0;
466 virtual void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) = 0;
467 virtual void clipPath(const SkPath& path, ClipOp op, bool is_aa) = 0;
468 };
469 friend class DummyDelegate;
470 friend class DlCanvasDelegate;
471 friend class PrerollDelegate;
472
473 std::vector<std::unique_ptr<StateEntry>> state_stack_;
474 friend class MutatorContext;
475
476 std::shared_ptr<Delegate> delegate_;
477 RenderingAttributes outstanding_;
478 CheckerboardFunc checkerboard_func_ = nullptr;
479
480 friend class SaveLayerEntry;
481};
482
483} // namespace flutter
484
485#endif // FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_
static void apply_flags(SkFont *font, unsigned flags)
Definition FontTest.cpp:65
#define SK_Scalar1
Definition SkScalar.h:18
Definition SkM44.h:150
Developer-facing API for rendering anything within the engine.
Definition dl_canvas.h:37
void applyColorFilter(const SkRect &bounds, const std::shared_ptr< const DlColorFilter > &filter)
void translate(SkScalar tx, SkScalar ty)
void applyImageFilter(const SkRect &bounds, const std::shared_ptr< const DlImageFilter > &filter)
void clipPath(const SkPath &path, bool is_aa)
void applyBackdropFilter(const SkRect &bounds, const std::shared_ptr< const DlImageFilter > &filter, DlBlendMode blend_mode)
void clipRRect(const SkRRect &rrect, bool is_aa)
void clipRect(const SkRect &rect, bool is_aa)
void applyOpacity(const SkRect &bounds, SkScalar opacity)
std::shared_ptr< const DlImageFilter > outstanding_image_filter() const
void fill(MutatorsStack *mutators)
AutoRestore applyState(const SkRect &bounds, int can_apply_flags)
DlPaint * fill(DlPaint &paint) const
CheckerboardFunc checkerboard_func() const
void set_preroll_delegate(const SkRect &cull_rect, const SkMatrix &matrix)
SkScalar outstanding_opacity() const
static constexpr int kCallerCanApplyAnything
static constexpr int kCallerCanApplyColorFilter
void set_checkerboard_func(CheckerboardFunc checkerboard_func)
void set_delegate(DlCanvas *canvas)
bool content_culled(const SkRect &content_bounds) const
std::shared_ptr< const DlColorFilter > outstanding_color_filter() const
static constexpr int kCallerCanApplyImageFilter
static constexpr int kCallerCanApplyOpacity
const Paint & paint
MockDelegate delegate_
float SkScalar
Definition extension.cpp:12
FlutterSemanticsFlag flags
#define FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TypeName)
Definition macros.h:31
static SkColor blend(SkColor dst, SkColor src, void(*mode)(float, float, float, float *, float *, float *))
Definition hsl.cpp:142
bool Equals(const T *a, const T *b)
void(* CheckerboardFunc)(DlCanvas *, const SkRect &)
Definition paint_utils.h:15
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 switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition switches.h:228
@ kSrcOver
r = s + (1-sa)*d
bool operator==(C p1, const scoped_nsprotocol< C > &p2)
float fX
x-axis value
float fY
y-axis value