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/// }
105class LayerStateStack {
106 public:
107 LayerStateStack();
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_; }
137 void set_checkerboard_func(CheckerboardFunc checkerboard_func) {
138 checkerboard_func_ = checkerboard_func;
139 }
140
141 class AutoRestore {
142 public:
143 ~AutoRestore() {
144 layer_state_stack_->restore_to_count(restore_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
160 FML_DISALLOW_COPY_ASSIGN_AND_MOVE(AutoRestore);
161 };
162
163 class MutatorContext {
164 public:
165 ~MutatorContext() {
166 layer_state_stack_->restore_to_count(restore_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(tx: tp.fX, ty: 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 MutatorContext(LayerStateStack* stack)
221 : layer_state_stack_(stack),
222 stack_restore_count_(stack->stack_count()),
223 save_needed_(true) {}
224 friend class LayerStateStack;
225
226 LayerStateStack* layer_state_stack_;
227 const size_t stack_restore_count_;
228 bool save_needed_;
229
230 FML_DISALLOW_COPY_ASSIGN_AND_MOVE(MutatorContext);
231 };
232
233 static constexpr int kCallerCanApplyOpacity = 0x1;
234 static constexpr int kCallerCanApplyColorFilter = 0x2;
235 static constexpr int kCallerCanApplyImageFilter = 0x4;
236 static constexpr int kCallerCanApplyAnything =
237 (kCallerCanApplyOpacity | kCallerCanApplyColorFilter |
238 kCallerCanApplyImageFilter);
239
240 // Apply the outstanding state via saveLayer if necessary,
241 // respecting the flags representing which potentially
242 // outstanding attributes the calling layer can apply
243 // themselves.
244 //
245 // A saveLayer may or may not be sent to the delegates depending
246 // on how the outstanding state intersects with the flags supplied
247 // by the caller.
248 //
249 // An AutoRestore instance will always be returned even if there
250 // was no saveLayer applied.
251 [[nodiscard]] inline AutoRestore applyState(const SkRect& bounds,
252 int can_apply_flags) {
253 return AutoRestore(this, bounds, can_apply_flags);
254 }
255
256 SkScalar outstanding_opacity() const { return outstanding_.opacity; }
257
258 std::shared_ptr<const DlColorFilter> outstanding_color_filter() const {
259 return outstanding_.color_filter;
260 }
261
262 std::shared_ptr<const DlImageFilter> outstanding_image_filter() const {
263 return outstanding_.image_filter;
264 }
265
266 // The outstanding bounds are the bounds recorded during the last
267 // attribute applied to this state stack. The assumption is that
268 // the nested calls to the state stack will each supply bounds relative
269 // to the content of that single attribute and the bounds of the content
270 // of any outstanding attributes will include the output bounds of
271 // applying any nested attributes. Thus, only the innermost content
272 // bounds received will be sufficient to apply all outstanding attributes.
273 SkRect outstanding_bounds() const { return outstanding_.save_layer_bounds; }
274
275 // Fill the provided paint object with any oustanding attributes and
276 // return a pointer to it, or return a nullptr if there were no
277 // outstanding attributes to paint with.
278 DlPaint* fill(DlPaint& paint) const { return outstanding_.fill(paint); }
279
280 // The cull_rect (not the exact clip) relative to the device pixels.
281 // This rectangle may be a conservative estimate of the true clip region.
282 SkRect device_cull_rect() const { return delegate_->device_cull_rect(); }
283
284 // The cull_rect (not the exact clip) relative to the local coordinates.
285 // This rectangle may be a conservative estimate of the true clip region.
286 SkRect local_cull_rect() const { return delegate_->local_cull_rect(); }
287
288 // The transform from the local coordinates to the device coordinates
289 // in the most capable 4x4 matrix representation. This matrix may be
290 // more information than is needed to compute bounds for a 2D rendering
291 // primitive, but it will accurately concatenate with other 4x4 matrices
292 // without losing information.
293 SkM44 transform_4x4() const { return delegate_->matrix_4x4(); }
294
295 // The transform from the local coordinates to the device coordinates
296 // in a more compact 3x3 matrix represenation that provides enough
297 // information to accurately transform 2D primitives into their
298 // resulting 2D bounds. This matrix also has enough information to
299 // concat with other 2D affine transforms, but does not carry enough
300 // information to accurately concat with fully perspective matrics.
301 SkMatrix transform_3x3() const { return delegate_->matrix_3x3(); }
302
303 // Tests if painting content with the current outstanding attributes
304 // will produce any content. This method does not check the current
305 // transform or clip for being singular or empty.
306 // See |content_culled|
307 bool painting_is_nop() const { return outstanding_.opacity <= 0; }
308
309 // Tests if painting content with the given bounds will produce any output.
310 // This method does not check the outstanding attributes to verify that
311 // they produce visible results.
312 // See |painting_is_nop|
313 bool content_culled(const SkRect& content_bounds) const {
314 return delegate_->content_culled(content_bounds);
315 }
316
317 // Saves the current state of the state stack and returns a
318 // MutatorContext which can be used to manipulate the state.
319 // The state stack will be restored to its current state
320 // when the MutatorContext object goes out of scope.
321 [[nodiscard]] inline MutatorContext save() { return MutatorContext(this); }
322
323 // Returns true if the state stack is in, or has returned to,
324 // its initial state.
325 bool is_empty() const { return state_stack_.empty(); }
326
327 private:
328 size_t stack_count() const { return state_stack_.size(); }
329 void restore_to_count(size_t restore_count);
330 void reapply_all();
331
332 void apply_last_entry() { state_stack_.back()->apply(stack: this); }
333
334 // The push methods simply push an associated StateEntry on the stack
335 // and then apply it to the current canvas and builder.
336 // ---------------------
337 // void push_attributes();
338 void push_opacity(const SkRect& rect, SkScalar opacity);
339 void push_color_filter(const SkRect& bounds,
340 const std::shared_ptr<const DlColorFilter>& filter);
341 void push_image_filter(const SkRect& bounds,
342 const std::shared_ptr<const DlImageFilter>& filter);
343 void push_backdrop(const SkRect& bounds,
344 const std::shared_ptr<const DlImageFilter>& filter,
345 DlBlendMode blend_mode);
346
347 void push_translate(SkScalar tx, SkScalar ty);
348 void push_transform(const SkM44& matrix);
349 void push_transform(const SkMatrix& matrix);
350 void push_integral_transform();
351
352 void push_clip_rect(const SkRect& rect, bool is_aa);
353 void push_clip_rrect(const SkRRect& rrect, bool is_aa);
354 void push_clip_path(const SkPath& path, bool is_aa);
355 // ---------------------
356
357 // The maybe/needs_save_layer methods will determine if the indicated
358 // attribute can be incorporated into the outstanding attributes as is,
359 // or if the apply_flags are compatible with the outstanding attributes.
360 // If the oustanding attributes are incompatible with the new attribute
361 // or the apply flags, then a protective saveLayer will be executed.
362 // ---------------------
363 bool needs_save_layer(int flags) const;
364 void do_save();
365 void save_layer(const SkRect& bounds);
366 void maybe_save_layer_for_transform(bool needs_save);
367 void maybe_save_layer_for_clip(bool needs_save);
368 void maybe_save_layer(int apply_flags);
369 void maybe_save_layer(SkScalar opacity);
370 void maybe_save_layer(const std::shared_ptr<const DlColorFilter>& filter);
371 void maybe_save_layer(const std::shared_ptr<const DlImageFilter>& filter);
372 // ---------------------
373
374 struct RenderingAttributes {
375 // We need to record the last bounds we received for the last
376 // attribute that we recorded so that we can perform a saveLayer
377 // on the proper area. When an attribute is applied that cannot
378 // be merged with the existing attributes, it will be submitted
379 // with a bounds for its own source content, not the bounds for
380 // the content that will be included in the saveLayer that applies
381 // the existing outstanding attributes - thus we need to record
382 // the bounds that were supplied with the most recent previous
383 // attribute to be applied.
384 SkRect save_layer_bounds{.fLeft: 0, .fTop: 0, .fRight: 0, .fBottom: 0};
385
386 SkScalar opacity = SK_Scalar1;
387 std::shared_ptr<const DlColorFilter> color_filter;
388 std::shared_ptr<const DlImageFilter> image_filter;
389
390 DlPaint* fill(DlPaint& paint,
391 DlBlendMode mode = DlBlendMode::kSrcOver) const;
392
393 bool operator==(const RenderingAttributes& other) const {
394 return save_layer_bounds == other.save_layer_bounds &&
395 opacity == other.opacity &&
396 Equals(a: color_filter, b: other.color_filter) &&
397 Equals(a: image_filter, b: other.image_filter);
398 }
399 };
400
401 class StateEntry {
402 public:
403 virtual ~StateEntry() = default;
404
405 virtual void apply(LayerStateStack* stack) const = 0;
406 virtual void reapply(LayerStateStack* stack) const { apply(stack); }
407 virtual void restore(LayerStateStack* stack) const {}
408 virtual void update_mutators(MutatorsStack* mutators_stack) const {}
409
410 protected:
411 StateEntry() = default;
412
413 FML_DISALLOW_COPY_ASSIGN_AND_MOVE(StateEntry);
414 };
415 friend class SaveEntry;
416 friend class SaveLayerEntry;
417 friend class BackdropFilterEntry;
418 friend class OpacityEntry;
419 friend class ImageFilterEntry;
420 friend class ColorFilterEntry;
421 friend class TranslateEntry;
422 friend class TransformMatrixEntry;
423 friend class TransformM44Entry;
424 friend class IntegralTransformEntry;
425 friend class ClipEntry;
426 friend class ClipRectEntry;
427 friend class ClipRRectEntry;
428 friend class ClipPathEntry;
429
430 class Delegate {
431 protected:
432 using ClipOp = DlCanvas::ClipOp;
433
434 public:
435 virtual ~Delegate() = default;
436
437 // Mormally when a |Paint| or |Preroll| cycle is completed, the
438 // delegate will have been rewound to its initial state by the
439 // trailing recursive actions of the paint and preroll methods.
440 // When a delegate is swapped out, there may be unresolved state
441 // that the delegate received. This method is called when the
442 // delegate is cleared or swapped out to inform it to rewind its
443 // state and finalize all outstanding save or saveLayer operations.
444 virtual void decommission() = 0;
445
446 virtual DlCanvas* canvas() const { return nullptr; }
447
448 virtual SkRect local_cull_rect() const = 0;
449 virtual SkRect device_cull_rect() const = 0;
450 virtual SkM44 matrix_4x4() const = 0;
451 virtual SkMatrix matrix_3x3() const = 0;
452 virtual bool content_culled(const SkRect& content_bounds) const = 0;
453
454 virtual void save() = 0;
455 virtual void saveLayer(const SkRect& bounds,
456 RenderingAttributes& attributes,
457 DlBlendMode blend,
458 const DlImageFilter* backdrop) = 0;
459 virtual void restore() = 0;
460
461 virtual void translate(SkScalar tx, SkScalar ty) = 0;
462 virtual void transform(const SkM44& m44) = 0;
463 virtual void transform(const SkMatrix& matrix) = 0;
464 virtual void integralTransform() = 0;
465
466 virtual void clipRect(const SkRect& rect, ClipOp op, bool is_aa) = 0;
467 virtual void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) = 0;
468 virtual void clipPath(const SkPath& path, ClipOp op, bool is_aa) = 0;
469 };
470 friend class DummyDelegate;
471 friend class DlCanvasDelegate;
472 friend class PrerollDelegate;
473
474 std::vector<std::unique_ptr<StateEntry>> state_stack_;
475 friend class MutatorContext;
476
477 std::shared_ptr<Delegate> delegate_;
478 RenderingAttributes outstanding_;
479 CheckerboardFunc checkerboard_func_ = nullptr;
480
481 friend class SaveLayerEntry;
482};
483
484} // namespace flutter
485
486#endif // FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_
487

source code of flutter_engine/flutter/flow/layers/layer_state_stack.h