| // Copyright 2013 The Flutter Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "flutter/flow/layers/layer_state_stack.h" | 
 |  | 
 | #include "flutter/display_list/utils/dl_matrix_clip_tracker.h" | 
 | #include "flutter/flow/layers/layer.h" | 
 | #include "flutter/flow/paint_utils.h" | 
 | #include "flutter/flow/raster_cache_util.h" | 
 |  | 
 | namespace flutter { | 
 |  | 
 | // ============================================================== | 
 | // Delegate subclasses | 
 | // ============================================================== | 
 |  | 
 | // The DummyDelegate class implements most Delegate methods as NOP | 
 | // but throws errors if the caller starts executing query methods | 
 | // that require an active delegate to be tracking. It is specifically | 
 | // designed to be immutable, lightweight, and a singleton so that it | 
 | // can be substituted into the delegate slot in a LayerStateStack | 
 | // quickly and cheaply when no externally supplied delegates are present. | 
 | class DummyDelegate : public LayerStateStack::Delegate { | 
 |  public: | 
 |   static const std::shared_ptr<DummyDelegate> kInstance; | 
 |  | 
 |   void decommission() override {} | 
 |  | 
 |   SkRect local_cull_rect() const override { | 
 |     error(); | 
 |     return {}; | 
 |   } | 
 |   SkRect device_cull_rect() const override { | 
 |     error(); | 
 |     return {}; | 
 |   } | 
 |   SkM44 matrix_4x4() const override { | 
 |     error(); | 
 |     return {}; | 
 |   } | 
 |   SkMatrix matrix_3x3() const override { | 
 |     error(); | 
 |     return {}; | 
 |   } | 
 |   bool content_culled(const SkRect& content_bounds) const override { | 
 |     error(); | 
 |     return true; | 
 |   } | 
 |  | 
 |   void save() override {} | 
 |   void saveLayer(const SkRect& bounds, | 
 |                  LayerStateStack::RenderingAttributes& attributes, | 
 |                  DlBlendMode blend, | 
 |                  const DlImageFilter* backdrop) override {} | 
 |   void restore() override {} | 
 |  | 
 |   void translate(SkScalar tx, SkScalar ty) override {} | 
 |   void transform(const SkM44& m44) override {} | 
 |   void transform(const SkMatrix& matrix) override {} | 
 |   void integralTransform() override {} | 
 |  | 
 |   void clipRect(const SkRect& rect, ClipOp op, bool is_aa) override {} | 
 |   void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) override {} | 
 |   void clipPath(const SkPath& path, ClipOp op, bool is_aa) override {} | 
 |  | 
 |  private: | 
 |   static void error() { | 
 |     FML_DCHECK(false) << "LayerStateStack state queried without a delegate"; | 
 |   } | 
 | }; | 
 | const std::shared_ptr<DummyDelegate> DummyDelegate::kInstance = | 
 |     std::make_shared<DummyDelegate>(); | 
 |  | 
 | class DlCanvasDelegate : public LayerStateStack::Delegate { | 
 |  public: | 
 |   explicit DlCanvasDelegate(DlCanvas* canvas) | 
 |       : canvas_(canvas), initial_save_level_(canvas->GetSaveCount()) {} | 
 |  | 
 |   void decommission() override { canvas_->RestoreToCount(initial_save_level_); } | 
 |  | 
 |   DlCanvas* canvas() const override { return canvas_; } | 
 |  | 
 |   SkRect local_cull_rect() const override { | 
 |     return canvas_->GetLocalClipBounds(); | 
 |   } | 
 |   SkRect device_cull_rect() const override { | 
 |     return canvas_->GetDestinationClipBounds(); | 
 |   } | 
 |   SkM44 matrix_4x4() const override { | 
 |     return canvas_->GetTransformFullPerspective(); | 
 |   } | 
 |   SkMatrix matrix_3x3() const override { return canvas_->GetTransform(); } | 
 |   bool content_culled(const SkRect& content_bounds) const override { | 
 |     return canvas_->QuickReject(content_bounds); | 
 |   } | 
 |  | 
 |   void save() override { canvas_->Save(); } | 
 |   void saveLayer(const SkRect& bounds, | 
 |                  LayerStateStack::RenderingAttributes& attributes, | 
 |                  DlBlendMode blend_mode, | 
 |                  const DlImageFilter* backdrop) override { | 
 |     TRACE_EVENT0("flutter", "Canvas::saveLayer"); | 
 |     DlPaint paint; | 
 |     canvas_->SaveLayer(&bounds, attributes.fill(paint, blend_mode), backdrop); | 
 |   } | 
 |   void restore() override { canvas_->Restore(); } | 
 |  | 
 |   void translate(SkScalar tx, SkScalar ty) override { | 
 |     canvas_->Translate(tx, ty); | 
 |   } | 
 |   void transform(const SkM44& m44) override { canvas_->Transform(m44); } | 
 |   void transform(const SkMatrix& matrix) override { | 
 |     canvas_->Transform(matrix); | 
 |   } | 
 |   void integralTransform() override { | 
 |     SkM44 matrix = RasterCacheUtil::GetIntegralTransCTM(matrix_4x4()); | 
 |     canvas_->SetTransform(matrix); | 
 |   } | 
 |  | 
 |   void clipRect(const SkRect& rect, ClipOp op, bool is_aa) override { | 
 |     canvas_->ClipRect(rect, op, is_aa); | 
 |   } | 
 |   void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) override { | 
 |     canvas_->ClipRRect(rrect, op, is_aa); | 
 |   } | 
 |   void clipPath(const SkPath& path, ClipOp op, bool is_aa) override { | 
 |     canvas_->ClipPath(path, op, is_aa); | 
 |   } | 
 |  | 
 |  private: | 
 |   DlCanvas* canvas_; | 
 |   const int initial_save_level_; | 
 | }; | 
 |  | 
 | class PrerollDelegate : public LayerStateStack::Delegate { | 
 |  public: | 
 |   PrerollDelegate(const SkRect& cull_rect, const SkMatrix& matrix) | 
 |       : tracker_(cull_rect, matrix) {} | 
 |  | 
 |   void decommission() override {} | 
 |  | 
 |   SkM44 matrix_4x4() const override { return tracker_.matrix_4x4(); } | 
 |   SkMatrix matrix_3x3() const override { return tracker_.matrix_3x3(); } | 
 |   SkRect local_cull_rect() const override { return tracker_.local_cull_rect(); } | 
 |   SkRect device_cull_rect() const override { | 
 |     return tracker_.device_cull_rect(); | 
 |   } | 
 |   bool content_culled(const SkRect& content_bounds) const override { | 
 |     return tracker_.content_culled(content_bounds); | 
 |   } | 
 |  | 
 |   void save() override { tracker_.save(); } | 
 |   void saveLayer(const SkRect& bounds, | 
 |                  LayerStateStack::RenderingAttributes& attributes, | 
 |                  DlBlendMode blend, | 
 |                  const DlImageFilter* backdrop) override { | 
 |     tracker_.save(); | 
 |   } | 
 |   void restore() override { tracker_.restore(); } | 
 |  | 
 |   void translate(SkScalar tx, SkScalar ty) override { | 
 |     tracker_.translate(tx, ty); | 
 |   } | 
 |   void transform(const SkM44& m44) override { tracker_.transform(m44); } | 
 |   void transform(const SkMatrix& matrix) override { | 
 |     tracker_.transform(matrix); | 
 |   } | 
 |   void integralTransform() override { | 
 |     if (tracker_.using_4x4_matrix()) { | 
 |       tracker_.setTransform( | 
 |           RasterCacheUtil::GetIntegralTransCTM(tracker_.matrix_4x4())); | 
 |     } else { | 
 |       tracker_.setTransform( | 
 |           RasterCacheUtil::GetIntegralTransCTM(tracker_.matrix_3x3())); | 
 |     } | 
 |   } | 
 |  | 
 |   void clipRect(const SkRect& rect, ClipOp op, bool is_aa) override { | 
 |     tracker_.clipRect(rect, op, is_aa); | 
 |   } | 
 |   void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) override { | 
 |     tracker_.clipRRect(rrect, op, is_aa); | 
 |   } | 
 |   void clipPath(const SkPath& path, ClipOp op, bool is_aa) override { | 
 |     tracker_.clipPath(path, op, is_aa); | 
 |   } | 
 |  | 
 |  private: | 
 |   DisplayListMatrixClipTracker tracker_; | 
 | }; | 
 |  | 
 | // ============================================================== | 
 | // StateEntry subclasses | 
 | // ============================================================== | 
 |  | 
 | class SaveEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   SaveEntry() = default; | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->delegate_->save(); | 
 |   } | 
 |   void restore(LayerStateStack* stack) const override { | 
 |     stack->delegate_->restore(); | 
 |   } | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(SaveEntry); | 
 | }; | 
 |  | 
 | class SaveLayerEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   SaveLayerEntry(const SkRect& bounds, | 
 |                  DlBlendMode blend_mode, | 
 |                  const LayerStateStack::RenderingAttributes& prev) | 
 |       : bounds_(bounds), blend_mode_(blend_mode), old_attributes_(prev) {} | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->delegate_->saveLayer(bounds_, stack->outstanding_, blend_mode_, | 
 |                                 nullptr); | 
 |     stack->outstanding_ = {}; | 
 |   } | 
 |   void restore(LayerStateStack* stack) const override { | 
 |     if (stack->checkerboard_func_) { | 
 |       DlCanvas* canvas = stack->canvas_delegate(); | 
 |       if (canvas != nullptr) { | 
 |         (*stack->checkerboard_func_)(canvas, bounds_); | 
 |       } | 
 |     } | 
 |     stack->delegate_->restore(); | 
 |     stack->outstanding_ = old_attributes_; | 
 |   } | 
 |  | 
 |  protected: | 
 |   const SkRect bounds_; | 
 |   const DlBlendMode blend_mode_; | 
 |   const LayerStateStack::RenderingAttributes old_attributes_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(SaveLayerEntry); | 
 | }; | 
 |  | 
 | class OpacityEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   OpacityEntry(const SkRect& bounds, | 
 |                SkScalar opacity, | 
 |                const LayerStateStack::RenderingAttributes& prev) | 
 |       : bounds_(bounds), | 
 |         opacity_(opacity), | 
 |         old_opacity_(prev.opacity), | 
 |         old_bounds_(prev.save_layer_bounds) {} | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->outstanding_.save_layer_bounds = bounds_; | 
 |     stack->outstanding_.opacity *= opacity_; | 
 |   } | 
 |   void restore(LayerStateStack* stack) const override { | 
 |     stack->outstanding_.save_layer_bounds = old_bounds_; | 
 |     stack->outstanding_.opacity = old_opacity_; | 
 |   } | 
 |   void update_mutators(MutatorsStack* mutators_stack) const override { | 
 |     mutators_stack->PushOpacity(DlColor::toAlpha(opacity_)); | 
 |   } | 
 |  | 
 |  private: | 
 |   const SkRect bounds_; | 
 |   const SkScalar opacity_; | 
 |   const SkScalar old_opacity_; | 
 |   const SkRect old_bounds_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(OpacityEntry); | 
 | }; | 
 |  | 
 | class ImageFilterEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   ImageFilterEntry(const SkRect& bounds, | 
 |                    const std::shared_ptr<const DlImageFilter>& filter, | 
 |                    const LayerStateStack::RenderingAttributes& prev) | 
 |       : bounds_(bounds), | 
 |         filter_(filter), | 
 |         old_filter_(prev.image_filter), | 
 |         old_bounds_(prev.save_layer_bounds) {} | 
 |   ~ImageFilterEntry() override = default; | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->outstanding_.save_layer_bounds = bounds_; | 
 |     stack->outstanding_.image_filter = filter_; | 
 |   } | 
 |   void restore(LayerStateStack* stack) const override { | 
 |     stack->outstanding_.save_layer_bounds = old_bounds_; | 
 |     stack->outstanding_.image_filter = old_filter_; | 
 |   } | 
 |  | 
 |   // There is no ImageFilter mutator currently | 
 |   // void update_mutators(MutatorsStack* mutators_stack) const override; | 
 |  | 
 |  private: | 
 |   const SkRect bounds_; | 
 |   const std::shared_ptr<const DlImageFilter> filter_; | 
 |   const std::shared_ptr<const DlImageFilter> old_filter_; | 
 |   const SkRect old_bounds_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ImageFilterEntry); | 
 | }; | 
 |  | 
 | class ColorFilterEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   ColorFilterEntry(const SkRect& bounds, | 
 |                    const std::shared_ptr<const DlColorFilter>& filter, | 
 |                    const LayerStateStack::RenderingAttributes& prev) | 
 |       : bounds_(bounds), | 
 |         filter_(filter), | 
 |         old_filter_(prev.color_filter), | 
 |         old_bounds_(prev.save_layer_bounds) {} | 
 |   ~ColorFilterEntry() override = default; | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->outstanding_.save_layer_bounds = bounds_; | 
 |     stack->outstanding_.color_filter = filter_; | 
 |   } | 
 |   void restore(LayerStateStack* stack) const override { | 
 |     stack->outstanding_.save_layer_bounds = old_bounds_; | 
 |     stack->outstanding_.color_filter = old_filter_; | 
 |   } | 
 |  | 
 |   // There is no ColorFilter mutator currently | 
 |   // void update_mutators(MutatorsStack* mutators_stack) const override; | 
 |  | 
 |  private: | 
 |   const SkRect bounds_; | 
 |   const std::shared_ptr<const DlColorFilter> filter_; | 
 |   const std::shared_ptr<const DlColorFilter> old_filter_; | 
 |   const SkRect old_bounds_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ColorFilterEntry); | 
 | }; | 
 |  | 
 | class BackdropFilterEntry : public SaveLayerEntry { | 
 |  public: | 
 |   BackdropFilterEntry(const SkRect& bounds, | 
 |                       const std::shared_ptr<const DlImageFilter>& filter, | 
 |                       DlBlendMode blend_mode, | 
 |                       const LayerStateStack::RenderingAttributes& prev) | 
 |       : SaveLayerEntry(bounds, blend_mode, prev), filter_(filter) {} | 
 |   ~BackdropFilterEntry() override = default; | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->delegate_->saveLayer(bounds_, stack->outstanding_, blend_mode_, | 
 |                                 filter_.get()); | 
 |     stack->outstanding_ = {}; | 
 |   } | 
 |  | 
 |   void reapply(LayerStateStack* stack) const override { | 
 |     // On the reapply for subsequent overlay layers, we do not | 
 |     // want to reapply the backdrop filter, but we do need to | 
 |     // do a saveLayer to encapsulate the contents and match the | 
 |     // restore that will be forthcoming. Note that this is not | 
 |     // perfect if the BlendMode is not associative as we will be | 
 |     // compositing multiple parts of the content in batches. | 
 |     // Luckily the most common SrcOver is associative. | 
 |     SaveLayerEntry::apply(stack); | 
 |   } | 
 |  | 
 |  private: | 
 |   const std::shared_ptr<const DlImageFilter> filter_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(BackdropFilterEntry); | 
 | }; | 
 |  | 
 | class TranslateEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   TranslateEntry(SkScalar tx, SkScalar ty) : tx_(tx), ty_(ty) {} | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->delegate_->translate(tx_, ty_); | 
 |   } | 
 |   void update_mutators(MutatorsStack* mutators_stack) const override { | 
 |     mutators_stack->PushTransform(SkMatrix::Translate(tx_, ty_)); | 
 |   } | 
 |  | 
 |  private: | 
 |   const SkScalar tx_; | 
 |   const SkScalar ty_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TranslateEntry); | 
 | }; | 
 |  | 
 | class TransformMatrixEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   explicit TransformMatrixEntry(const SkMatrix& matrix) : matrix_(matrix) {} | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->delegate_->transform(matrix_); | 
 |   } | 
 |   void update_mutators(MutatorsStack* mutators_stack) const override { | 
 |     mutators_stack->PushTransform(matrix_); | 
 |   } | 
 |  | 
 |  private: | 
 |   const SkMatrix matrix_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TransformMatrixEntry); | 
 | }; | 
 |  | 
 | class TransformM44Entry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   explicit TransformM44Entry(const SkM44& m44) : m44_(m44) {} | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->delegate_->transform(m44_); | 
 |   } | 
 |   void update_mutators(MutatorsStack* mutators_stack) const override { | 
 |     mutators_stack->PushTransform(m44_.asM33()); | 
 |   } | 
 |  | 
 |  private: | 
 |   const SkM44 m44_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TransformM44Entry); | 
 | }; | 
 |  | 
 | class IntegralTransformEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   IntegralTransformEntry() = default; | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->delegate_->integralTransform(); | 
 |   } | 
 |  | 
 |  private: | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(IntegralTransformEntry); | 
 | }; | 
 |  | 
 | class ClipRectEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   ClipRectEntry(const SkRect& clip_rect, bool is_aa) | 
 |       : clip_rect_(clip_rect), is_aa_(is_aa) {} | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->delegate_->clipRect(clip_rect_, DlCanvas::ClipOp::kIntersect, | 
 |                                is_aa_); | 
 |   } | 
 |   void update_mutators(MutatorsStack* mutators_stack) const override { | 
 |     mutators_stack->PushClipRect(clip_rect_); | 
 |   } | 
 |  | 
 |  private: | 
 |   const SkRect clip_rect_; | 
 |   const bool is_aa_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ClipRectEntry); | 
 | }; | 
 |  | 
 | class ClipRRectEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   ClipRRectEntry(const SkRRect& clip_rrect, bool is_aa) | 
 |       : clip_rrect_(clip_rrect), is_aa_(is_aa) {} | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->delegate_->clipRRect(clip_rrect_, DlCanvas::ClipOp::kIntersect, | 
 |                                 is_aa_); | 
 |   } | 
 |   void update_mutators(MutatorsStack* mutators_stack) const override { | 
 |     mutators_stack->PushClipRRect(clip_rrect_); | 
 |   } | 
 |  | 
 |  private: | 
 |   const SkRRect clip_rrect_; | 
 |   const bool is_aa_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ClipRRectEntry); | 
 | }; | 
 |  | 
 | class ClipPathEntry : public LayerStateStack::StateEntry { | 
 |  public: | 
 |   ClipPathEntry(const SkPath& clip_path, bool is_aa) | 
 |       : clip_path_(clip_path), is_aa_(is_aa) {} | 
 |   ~ClipPathEntry() override = default; | 
 |  | 
 |   void apply(LayerStateStack* stack) const override { | 
 |     stack->delegate_->clipPath(clip_path_, DlCanvas::ClipOp::kIntersect, | 
 |                                is_aa_); | 
 |   } | 
 |   void update_mutators(MutatorsStack* mutators_stack) const override { | 
 |     mutators_stack->PushClipPath(clip_path_); | 
 |   } | 
 |  | 
 |  private: | 
 |   const SkPath clip_path_; | 
 |   const bool is_aa_; | 
 |  | 
 |   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ClipPathEntry); | 
 | }; | 
 |  | 
 | // ============================================================== | 
 | // RenderingAttributes methods | 
 | // ============================================================== | 
 |  | 
 | DlPaint* LayerStateStack::RenderingAttributes::fill(DlPaint& paint, | 
 |                                                     DlBlendMode mode) const { | 
 |   DlPaint* ret = nullptr; | 
 |   if (opacity < SK_Scalar1) { | 
 |     paint.setOpacity(std::max(opacity, 0.0f)); | 
 |     ret = &paint; | 
 |   } else { | 
 |     paint.setOpacity(SK_Scalar1); | 
 |   } | 
 |   paint.setColorFilter(color_filter); | 
 |   if (color_filter) { | 
 |     ret = &paint; | 
 |   } | 
 |   paint.setImageFilter(image_filter); | 
 |   if (image_filter) { | 
 |     ret = &paint; | 
 |   } | 
 |   paint.setBlendMode(mode); | 
 |   if (mode != DlBlendMode::kSrcOver) { | 
 |     ret = &paint; | 
 |   } | 
 |   return ret; | 
 | } | 
 |  | 
 | // ============================================================== | 
 | // MutatorContext methods | 
 | // ============================================================== | 
 |  | 
 | using MutatorContext = LayerStateStack::MutatorContext; | 
 |  | 
 | void MutatorContext::saveLayer(const SkRect& bounds) { | 
 |   layer_state_stack_->save_layer(bounds); | 
 | } | 
 |  | 
 | void MutatorContext::applyOpacity(const SkRect& bounds, SkScalar opacity) { | 
 |   if (opacity < SK_Scalar1) { | 
 |     layer_state_stack_->push_opacity(bounds, opacity); | 
 |   } | 
 | } | 
 |  | 
 | void MutatorContext::applyImageFilter( | 
 |     const SkRect& bounds, | 
 |     const std::shared_ptr<const DlImageFilter>& filter) { | 
 |   if (filter) { | 
 |     layer_state_stack_->push_image_filter(bounds, filter); | 
 |   } | 
 | } | 
 |  | 
 | void MutatorContext::applyColorFilter( | 
 |     const SkRect& bounds, | 
 |     const std::shared_ptr<const DlColorFilter>& filter) { | 
 |   if (filter) { | 
 |     layer_state_stack_->push_color_filter(bounds, filter); | 
 |   } | 
 | } | 
 |  | 
 | void MutatorContext::applyBackdropFilter( | 
 |     const SkRect& bounds, | 
 |     const std::shared_ptr<const DlImageFilter>& filter, | 
 |     DlBlendMode blend_mode) { | 
 |   layer_state_stack_->push_backdrop(bounds, filter, blend_mode); | 
 | } | 
 |  | 
 | void MutatorContext::translate(SkScalar tx, SkScalar ty) { | 
 |   if (!(tx == 0 && ty == 0)) { | 
 |     layer_state_stack_->maybe_save_layer_for_transform(save_needed_); | 
 |     save_needed_ = false; | 
 |     layer_state_stack_->push_translate(tx, ty); | 
 |   } | 
 | } | 
 |  | 
 | void MutatorContext::transform(const SkMatrix& matrix) { | 
 |   if (matrix.isTranslate()) { | 
 |     translate(matrix.getTranslateX(), matrix.getTranslateY()); | 
 |   } else if (!matrix.isIdentity()) { | 
 |     layer_state_stack_->maybe_save_layer_for_transform(save_needed_); | 
 |     save_needed_ = false; | 
 |     layer_state_stack_->push_transform(matrix); | 
 |   } | 
 | } | 
 |  | 
 | void MutatorContext::transform(const SkM44& m44) { | 
 |   if (DisplayListMatrixClipTracker::is_3x3(m44)) { | 
 |     transform(m44.asM33()); | 
 |   } else { | 
 |     layer_state_stack_->maybe_save_layer_for_transform(save_needed_); | 
 |     save_needed_ = false; | 
 |     layer_state_stack_->push_transform(m44); | 
 |   } | 
 | } | 
 |  | 
 | void MutatorContext::integralTransform() { | 
 |   layer_state_stack_->maybe_save_layer_for_transform(save_needed_); | 
 |   save_needed_ = false; | 
 |   layer_state_stack_->push_integral_transform(); | 
 | } | 
 |  | 
 | void MutatorContext::clipRect(const SkRect& rect, bool is_aa) { | 
 |   layer_state_stack_->maybe_save_layer_for_clip(save_needed_); | 
 |   save_needed_ = false; | 
 |   layer_state_stack_->push_clip_rect(rect, is_aa); | 
 | } | 
 |  | 
 | void MutatorContext::clipRRect(const SkRRect& rrect, bool is_aa) { | 
 |   layer_state_stack_->maybe_save_layer_for_clip(save_needed_); | 
 |   save_needed_ = false; | 
 |   layer_state_stack_->push_clip_rrect(rrect, is_aa); | 
 | } | 
 |  | 
 | void MutatorContext::clipPath(const SkPath& path, bool is_aa) { | 
 |   layer_state_stack_->maybe_save_layer_for_clip(save_needed_); | 
 |   save_needed_ = false; | 
 |   layer_state_stack_->push_clip_path(path, is_aa); | 
 | } | 
 |  | 
 | // ============================================================== | 
 | // LayerStateStack methods | 
 | // ============================================================== | 
 |  | 
 | LayerStateStack::LayerStateStack() : delegate_(DummyDelegate::kInstance) {} | 
 |  | 
 | void LayerStateStack::clear_delegate() { | 
 |   delegate_->decommission(); | 
 |   delegate_ = DummyDelegate::kInstance; | 
 | } | 
 |  | 
 | void LayerStateStack::set_delegate(DlCanvas* canvas) { | 
 |   if (delegate_) { | 
 |     if (canvas == delegate_->canvas()) { | 
 |       return; | 
 |     } | 
 |     clear_delegate(); | 
 |   } | 
 |   if (canvas) { | 
 |     delegate_ = std::make_shared<DlCanvasDelegate>(canvas); | 
 |     reapply_all(); | 
 |   } | 
 | } | 
 |  | 
 | void LayerStateStack::set_preroll_delegate(const SkRect& cull_rect) { | 
 |   set_preroll_delegate(cull_rect, SkMatrix::I()); | 
 | } | 
 | void LayerStateStack::set_preroll_delegate(const SkMatrix& matrix) { | 
 |   set_preroll_delegate(kGiantRect, matrix); | 
 | } | 
 | void LayerStateStack::set_preroll_delegate(const SkRect& cull_rect, | 
 |                                            const SkMatrix& matrix) { | 
 |   clear_delegate(); | 
 |   delegate_ = std::make_shared<PrerollDelegate>(cull_rect, matrix); | 
 |   reapply_all(); | 
 | } | 
 |  | 
 | void LayerStateStack::reapply_all() { | 
 |   // We use a local RenderingAttributes instance so that it can track the | 
 |   // necessary state changes independently as they occur in the stack. | 
 |   // Reusing |outstanding_| would wreak havoc on the current state of | 
 |   // the stack. When we are finished, though, the local attributes | 
 |   // contents should match the current outstanding_ values; | 
 |   RenderingAttributes attributes = outstanding_; | 
 |   outstanding_ = {}; | 
 |   for (auto& state : state_stack_) { | 
 |     state->reapply(this); | 
 |   } | 
 |   FML_DCHECK(attributes == outstanding_); | 
 | } | 
 |  | 
 | void LayerStateStack::fill(MutatorsStack* mutators) { | 
 |   for (auto& state : state_stack_) { | 
 |     state->update_mutators(mutators); | 
 |   } | 
 | } | 
 |  | 
 | void LayerStateStack::restore_to_count(size_t restore_count) { | 
 |   while (state_stack_.size() > restore_count) { | 
 |     state_stack_.back()->restore(this); | 
 |     state_stack_.pop_back(); | 
 |   } | 
 | } | 
 |  | 
 | void LayerStateStack::push_opacity(const SkRect& bounds, SkScalar opacity) { | 
 |   maybe_save_layer(opacity); | 
 |   state_stack_.emplace_back( | 
 |       std::make_unique<OpacityEntry>(bounds, opacity, outstanding_)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::push_color_filter( | 
 |     const SkRect& bounds, | 
 |     const std::shared_ptr<const DlColorFilter>& filter) { | 
 |   maybe_save_layer(filter); | 
 |   state_stack_.emplace_back( | 
 |       std::make_unique<ColorFilterEntry>(bounds, filter, outstanding_)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::push_image_filter( | 
 |     const SkRect& bounds, | 
 |     const std::shared_ptr<const DlImageFilter>& filter) { | 
 |   maybe_save_layer(filter); | 
 |   state_stack_.emplace_back( | 
 |       std::make_unique<ImageFilterEntry>(bounds, filter, outstanding_)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::push_backdrop( | 
 |     const SkRect& bounds, | 
 |     const std::shared_ptr<const DlImageFilter>& filter, | 
 |     DlBlendMode blend_mode) { | 
 |   state_stack_.emplace_back(std::make_unique<BackdropFilterEntry>( | 
 |       bounds, filter, blend_mode, outstanding_)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::push_translate(SkScalar tx, SkScalar ty) { | 
 |   state_stack_.emplace_back(std::make_unique<TranslateEntry>(tx, ty)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::push_transform(const SkM44& m44) { | 
 |   state_stack_.emplace_back(std::make_unique<TransformM44Entry>(m44)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::push_transform(const SkMatrix& matrix) { | 
 |   state_stack_.emplace_back(std::make_unique<TransformMatrixEntry>(matrix)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::push_integral_transform() { | 
 |   state_stack_.emplace_back(std::make_unique<IntegralTransformEntry>()); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::push_clip_rect(const SkRect& rect, bool is_aa) { | 
 |   state_stack_.emplace_back(std::make_unique<ClipRectEntry>(rect, is_aa)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::push_clip_rrect(const SkRRect& rrect, bool is_aa) { | 
 |   state_stack_.emplace_back(std::make_unique<ClipRRectEntry>(rrect, is_aa)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::push_clip_path(const SkPath& path, bool is_aa) { | 
 |   state_stack_.emplace_back(std::make_unique<ClipPathEntry>(path, is_aa)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | bool LayerStateStack::needs_save_layer(int flags) const { | 
 |   if (outstanding_.opacity < SK_Scalar1 && | 
 |       (flags & LayerStateStack::kCallerCanApplyOpacity) == 0) { | 
 |     return true; | 
 |   } | 
 |   if (outstanding_.image_filter && | 
 |       (flags & LayerStateStack::kCallerCanApplyImageFilter) == 0) { | 
 |     return true; | 
 |   } | 
 |   if (outstanding_.color_filter && | 
 |       (flags & LayerStateStack::kCallerCanApplyColorFilter) == 0) { | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | void LayerStateStack::do_save() { | 
 |   state_stack_.emplace_back(std::make_unique<SaveEntry>()); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::save_layer(const SkRect& bounds) { | 
 |   state_stack_.emplace_back(std::make_unique<SaveLayerEntry>( | 
 |       bounds, DlBlendMode::kSrcOver, outstanding_)); | 
 |   apply_last_entry(); | 
 | } | 
 |  | 
 | void LayerStateStack::maybe_save_layer_for_transform(bool save_needed) { | 
 |   // Alpha and ColorFilter don't care about transform | 
 |   if (outstanding_.image_filter) { | 
 |     save_layer(outstanding_.save_layer_bounds); | 
 |   } else if (save_needed) { | 
 |     do_save(); | 
 |   } | 
 | } | 
 |  | 
 | void LayerStateStack::maybe_save_layer_for_clip(bool save_needed) { | 
 |   // Alpha and ColorFilter don't care about clipping | 
 |   // - Alpha of clipped content == clip of alpha content | 
 |   // - Color-filtering of clipped content == clip of color-filtered content | 
 |   if (outstanding_.image_filter) { | 
 |     save_layer(outstanding_.save_layer_bounds); | 
 |   } else if (save_needed) { | 
 |     do_save(); | 
 |   } | 
 | } | 
 |  | 
 | void LayerStateStack::maybe_save_layer(int apply_flags) { | 
 |   if (needs_save_layer(apply_flags)) { | 
 |     save_layer(outstanding_.save_layer_bounds); | 
 |   } | 
 | } | 
 |  | 
 | void LayerStateStack::maybe_save_layer(SkScalar opacity) { | 
 |   if (outstanding_.image_filter) { | 
 |     save_layer(outstanding_.save_layer_bounds); | 
 |   } | 
 | } | 
 |  | 
 | void LayerStateStack::maybe_save_layer( | 
 |     const std::shared_ptr<const DlColorFilter>& filter) { | 
 |   if (outstanding_.color_filter || outstanding_.image_filter || | 
 |       (outstanding_.opacity < SK_Scalar1 && | 
 |        !filter->can_commute_with_opacity())) { | 
 |     // TBD: compose the 2 color filters together. | 
 |     save_layer(outstanding_.save_layer_bounds); | 
 |   } | 
 | } | 
 |  | 
 | void LayerStateStack::maybe_save_layer( | 
 |     const std::shared_ptr<const DlImageFilter>& filter) { | 
 |   if (outstanding_.image_filter) { | 
 |     // TBD: compose the 2 image filters together. | 
 |     save_layer(outstanding_.save_layer_bounds); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace flutter |