From c014ce9727f3926fa50b56cc8825b3040b9da589 Mon Sep 17 00:00:00 2001 From: ColdPaleLight <31977171+ColdPaleLight@users.noreply.github.com> Date: Thu, 12 May 2022 16:39:04 +0800 Subject: [PATCH] Refactor clip layers to share common code (flutter/engine#32889) --- .../ci/licenses_golden/licenses_flutter | 1 + engine/src/flutter/flow/BUILD.gn | 1 + .../checkerboard_layertree_unittests.cc | 8 +- .../flutter/flow/layers/clip_path_layer.cc | 79 ++---------- .../src/flutter/flow/layers/clip_path_layer.h | 18 ++- .../flutter/flow/layers/clip_rect_layer.cc | 78 ++---------- .../src/flutter/flow/layers/clip_rect_layer.h | 19 ++- .../flow/layers/clip_rect_layer_unittests.cc | 4 +- .../flutter/flow/layers/clip_rrect_layer.cc | 79 ++---------- .../flutter/flow/layers/clip_rrect_layer.h | 18 ++- .../flutter/flow/layers/clip_shape_layer.h | 113 ++++++++++++++++++ 11 files changed, 186 insertions(+), 232 deletions(-) create mode 100644 engine/src/flutter/flow/layers/clip_shape_layer.h diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index a3d45b4c889..7463d8232ef 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -139,6 +139,7 @@ FILE: ../../../flutter/flow/layers/clip_rect_layer_unittests.cc FILE: ../../../flutter/flow/layers/clip_rrect_layer.cc FILE: ../../../flutter/flow/layers/clip_rrect_layer.h FILE: ../../../flutter/flow/layers/clip_rrect_layer_unittests.cc +FILE: ../../../flutter/flow/layers/clip_shape_layer.h FILE: ../../../flutter/flow/layers/color_filter_layer.cc FILE: ../../../flutter/flow/layers/color_filter_layer.h FILE: ../../../flutter/flow/layers/color_filter_layer_unittests.cc diff --git a/engine/src/flutter/flow/BUILD.gn b/engine/src/flutter/flow/BUILD.gn index 2e49976667c..d99fb4962bd 100644 --- a/engine/src/flutter/flow/BUILD.gn +++ b/engine/src/flutter/flow/BUILD.gn @@ -28,6 +28,7 @@ source_set("flow") { "layers/clip_rect_layer.h", "layers/clip_rrect_layer.cc", "layers/clip_rrect_layer.h", + "layers/clip_shape_layer.h", "layers/color_filter_layer.cc", "layers/color_filter_layer.h", "layers/container_layer.cc", diff --git a/engine/src/flutter/flow/layers/checkerboard_layertree_unittests.cc b/engine/src/flutter/flow/layers/checkerboard_layertree_unittests.cc index 4f7f4cbdc9d..1923b772783 100644 --- a/engine/src/flutter/flow/layers/checkerboard_layertree_unittests.cc +++ b/engine/src/flutter/flow/layers/checkerboard_layertree_unittests.cc @@ -60,8 +60,8 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerNotCheckBoard) { 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kSoft_ClipEdgeStyle}}, MockCanvas::DrawCall{ - 1, - MockCanvas::SaveLayerData{layer_bounds, clip_paint, nullptr, 2}}, + 1, MockCanvas::SaveLayerData{layer->paint_bounds(), clip_paint, + nullptr, 2}}, MockCanvas::DrawCall{ 2, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, @@ -110,8 +110,8 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kSoft_ClipEdgeStyle}}, MockCanvas::DrawCall{ - 1, - MockCanvas::SaveLayerData{layer_bounds, clip_paint, nullptr, 2}}, + 1, MockCanvas::SaveLayerData{layer->paint_bounds(), clip_paint, + nullptr, 2}}, MockCanvas::DrawCall{ 2, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, diff --git a/engine/src/flutter/flow/layers/clip_path_layer.cc b/engine/src/flutter/flow/layers/clip_path_layer.cc index b04f011ad7a..51426168e02 100644 --- a/engine/src/flutter/flow/layers/clip_path_layer.cc +++ b/engine/src/flutter/flow/layers/clip_path_layer.cc @@ -3,86 +3,33 @@ // found in the LICENSE file. #include "flutter/flow/layers/clip_path_layer.h" -#include "flutter/flow/paint_utils.h" namespace flutter { ClipPathLayer::ClipPathLayer(const SkPath& clip_path, Clip clip_behavior) - : clip_path_(clip_path), clip_behavior_(clip_behavior) { - FML_DCHECK(clip_behavior != Clip::none); -} - -void ClipPathLayer::Diff(DiffContext* context, const Layer* old_layer) { - DiffContext::AutoSubtreeRestore subtree(context); - auto* prev = static_cast(old_layer); - if (!context->IsSubtreeDirty()) { - FML_DCHECK(prev); - if (clip_behavior_ != prev->clip_behavior_ || - clip_path_ != prev->clip_path_) { - context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); - } - } - if (context->PushCullRect(clip_path_.getBounds())) { - DiffChildren(context, prev); - } - context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); -} + : ClipShapeLayer(clip_path, clip_behavior) {} void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "ClipPathLayer::Preroll"); - - SkRect previous_cull_rect = context->cull_rect; - SkRect clip_path_bounds = clip_path_.getBounds(); - if (!context->cull_rect.intersect(clip_path_bounds)) { - context->cull_rect.setEmpty(); - } - Layer::AutoPrerollSaveLayerState save = - Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); - context->mutators_stack.PushClipPath(clip_path_); - - // Collect inheritance information on our children in Preroll so that - // we can pass it along by default. - context->subtree_can_inherit_opacity = true; - - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); - if (child_paint_bounds.intersect(clip_path_bounds)) { - set_paint_bounds(child_paint_bounds); - } - - // If we use a SaveLayer then we can accept opacity on behalf - // of our children and apply it in the saveLayer. - if (UsesSaveLayer()) { - context->subtree_can_inherit_opacity = true; - } - - context->mutators_stack.Pop(); - context->cull_rect = previous_cull_rect; + ClipShapeLayer::Preroll(context, matrix); } void ClipPathLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipPathLayer::Paint"); - FML_DCHECK(needs_painting(context)); + ClipShapeLayer::Paint(context); +} - SkAutoCanvasRestore save(context.internal_nodes_canvas, true); - context.internal_nodes_canvas->clipPath(clip_path_, - clip_behavior_ != Clip::hardEdge); +const SkRect& ClipPathLayer::clip_shape_bounds() const { + return clip_shape().getBounds(); +} - if (!UsesSaveLayer()) { - PaintChildren(context); - return; - } +void ClipPathLayer::OnMutatorsStackPushClipShape( + MutatorsStack& mutators_stack) { + mutators_stack.PushClipPath(clip_shape()); +} - AutoCachePaint cache_paint(context); - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - context.internal_nodes_canvas->saveLayer(paint_bounds(), cache_paint.paint()); - - PaintChildren(context); - - context.internal_nodes_canvas->restore(); - if (context.checkerboard_offscreen_layers) { - DrawCheckerboard(context.internal_nodes_canvas, paint_bounds()); - } +void ClipPathLayer::OnCanvasClipShape(SkCanvas* canvas) const { + canvas->clipPath(clip_shape(), clip_behavior() != Clip::hardEdge); } } // namespace flutter diff --git a/engine/src/flutter/flow/layers/clip_path_layer.h b/engine/src/flutter/flow/layers/clip_path_layer.h index 6e19e0a3741..c8ff1bad743 100644 --- a/engine/src/flutter/flow/layers/clip_path_layer.h +++ b/engine/src/flutter/flow/layers/clip_path_layer.h @@ -5,29 +5,27 @@ #ifndef FLUTTER_FLOW_LAYERS_CLIP_PATH_LAYER_H_ #define FLUTTER_FLOW_LAYERS_CLIP_PATH_LAYER_H_ -#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/clip_shape_layer.h" namespace flutter { -class ClipPathLayer : public ContainerLayer { +class ClipPathLayer : public ClipShapeLayer { public: explicit ClipPathLayer(const SkPath& clip_path, Clip clip_behavior = Clip::antiAlias); - void Diff(DiffContext* context, const Layer* old_layer) override; - void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; - bool UsesSaveLayer() const { - return clip_behavior_ == Clip::antiAliasWithSaveLayer; - } + protected: + const SkRect& clip_shape_bounds() const override; + + void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) override; + + void OnCanvasClipShape(SkCanvas* canvas) const override; private: - SkPath clip_path_; - Clip clip_behavior_; - FML_DISALLOW_COPY_AND_ASSIGN(ClipPathLayer); }; diff --git a/engine/src/flutter/flow/layers/clip_rect_layer.cc b/engine/src/flutter/flow/layers/clip_rect_layer.cc index d7697680f8c..e2db2aeac03 100644 --- a/engine/src/flutter/flow/layers/clip_rect_layer.cc +++ b/engine/src/flutter/flow/layers/clip_rect_layer.cc @@ -3,85 +3,33 @@ // found in the LICENSE file. #include "flutter/flow/layers/clip_rect_layer.h" -#include "flutter/flow/paint_utils.h" namespace flutter { ClipRectLayer::ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior) - : clip_rect_(clip_rect), clip_behavior_(clip_behavior) { - FML_DCHECK(clip_behavior != Clip::none); -} - -void ClipRectLayer::Diff(DiffContext* context, const Layer* old_layer) { - DiffContext::AutoSubtreeRestore subtree(context); - auto* prev = static_cast(old_layer); - if (!context->IsSubtreeDirty()) { - FML_DCHECK(prev); - if (clip_behavior_ != prev->clip_behavior_ || - clip_rect_ != prev->clip_rect_) { - context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); - } - } - if (context->PushCullRect(clip_rect_)) { - DiffChildren(context, prev); - } - context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); -} + : ClipShapeLayer(clip_rect, clip_behavior) {} void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "ClipRectLayer::Preroll"); - - SkRect previous_cull_rect = context->cull_rect; - if (!context->cull_rect.intersect(clip_rect_)) { - context->cull_rect.setEmpty(); - } - Layer::AutoPrerollSaveLayerState save = - Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); - context->mutators_stack.PushClipRect(clip_rect_); - - // Collect inheritance information on our children in Preroll so that - // we can pass it along by default. - context->subtree_can_inherit_opacity = true; - - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); - if (child_paint_bounds.intersect(clip_rect_)) { - set_paint_bounds(child_paint_bounds); - } - - // If we use a SaveLayer then we can accept opacity on behalf - // of our children and apply it in the saveLayer. - if (UsesSaveLayer()) { - context->subtree_can_inherit_opacity = true; - } - - context->mutators_stack.Pop(); - context->cull_rect = previous_cull_rect; + ClipShapeLayer::Preroll(context, matrix); } void ClipRectLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipRectLayer::Paint"); - FML_DCHECK(needs_painting(context)); + ClipShapeLayer::Paint(context); +} - SkAutoCanvasRestore save(context.internal_nodes_canvas, true); - context.internal_nodes_canvas->clipRect(clip_rect_, - clip_behavior_ != Clip::hardEdge); +const SkRect& ClipRectLayer::clip_shape_bounds() const { + return clip_shape(); +} - if (!UsesSaveLayer()) { - PaintChildren(context); - return; - } +void ClipRectLayer::OnMutatorsStackPushClipShape( + MutatorsStack& mutators_stack) { + mutators_stack.PushClipRect(clip_shape()); +} - AutoCachePaint cache_paint(context); - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - context.internal_nodes_canvas->saveLayer(clip_rect_, cache_paint.paint()); - - PaintChildren(context); - - context.internal_nodes_canvas->restore(); - if (context.checkerboard_offscreen_layers) { - DrawCheckerboard(context.internal_nodes_canvas, clip_rect_); - } +void ClipRectLayer::OnCanvasClipShape(SkCanvas* canvas) const { + canvas->clipRect(clip_shape(), clip_behavior() != Clip::hardEdge); } } // namespace flutter diff --git a/engine/src/flutter/flow/layers/clip_rect_layer.h b/engine/src/flutter/flow/layers/clip_rect_layer.h index 6c7c8d2f172..967bd9b349c 100644 --- a/engine/src/flutter/flow/layers/clip_rect_layer.h +++ b/engine/src/flutter/flow/layers/clip_rect_layer.h @@ -5,27 +5,26 @@ #ifndef FLUTTER_FLOW_LAYERS_CLIP_RECT_LAYER_H_ #define FLUTTER_FLOW_LAYERS_CLIP_RECT_LAYER_H_ -#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/clip_shape_layer.h" namespace flutter { -class ClipRectLayer : public ContainerLayer { +class ClipRectLayer : public ClipShapeLayer { public: ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior); - void Diff(DiffContext* context, const Layer* old_layer) override; - void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + void Paint(PaintContext& context) const override; - bool UsesSaveLayer() const { - return clip_behavior_ == Clip::antiAliasWithSaveLayer; - } + protected: + const SkRect& clip_shape_bounds() const override; + + void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) override; + + void OnCanvasClipShape(SkCanvas* canvas) const override; private: - SkRect clip_rect_; - Clip clip_behavior_; - FML_DISALLOW_COPY_AND_ASSIGN(ClipRectLayer); }; diff --git a/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc b/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc index bb8607a95b3..7905dcc31e6 100644 --- a/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc @@ -431,6 +431,8 @@ TEST_F(ClipRectLayerTest, OpacityInheritanceSaveLayerPainting) { auto mock1 = MockLayer::MakeOpacityCompatible(path1); auto path2 = SkPath().addRect({20, 20, 40, 40}); auto mock2 = MockLayer::MakeOpacityCompatible(path2); + auto children_bounds = path1.getBounds(); + children_bounds.join(path2.getBounds()); SkRect clip_rect = SkRect::MakeWH(500, 500); auto clip_rect_layer = std::make_shared(clip_rect, Clip::antiAliasWithSaveLayer); @@ -466,7 +468,7 @@ TEST_F(ClipRectLayerTest, OpacityInheritanceSaveLayerPainting) { expected_builder.save(); expected_builder.clipRect(clip_rect, SkClipOp::kIntersect, true); expected_builder.setColor(opacity_alpha << 24); - expected_builder.saveLayer(&clip_rect, true); + expected_builder.saveLayer(&children_bounds, true); /* child layer1 paint */ { expected_builder.setColor(0xFF000000); expected_builder.drawPath(path1); diff --git a/engine/src/flutter/flow/layers/clip_rrect_layer.cc b/engine/src/flutter/flow/layers/clip_rrect_layer.cc index 792acd33f5b..6c1a5cca6cb 100644 --- a/engine/src/flutter/flow/layers/clip_rrect_layer.cc +++ b/engine/src/flutter/flow/layers/clip_rrect_layer.cc @@ -3,86 +3,33 @@ // found in the LICENSE file. #include "flutter/flow/layers/clip_rrect_layer.h" -#include "flutter/flow/paint_utils.h" namespace flutter { ClipRRectLayer::ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior) - : clip_rrect_(clip_rrect), clip_behavior_(clip_behavior) { - FML_DCHECK(clip_behavior != Clip::none); -} - -void ClipRRectLayer::Diff(DiffContext* context, const Layer* old_layer) { - DiffContext::AutoSubtreeRestore subtree(context); - auto* prev = static_cast(old_layer); - if (!context->IsSubtreeDirty()) { - FML_DCHECK(prev); - if (clip_behavior_ != prev->clip_behavior_ || - clip_rrect_ != prev->clip_rrect_) { - context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); - } - } - if (context->PushCullRect(clip_rrect_.getBounds())) { - DiffChildren(context, prev); - } - context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); -} + : ClipShapeLayer(clip_rrect, clip_behavior) {} void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "ClipRRectLayer::Preroll"); - - SkRect previous_cull_rect = context->cull_rect; - SkRect clip_rrect_bounds = clip_rrect_.getBounds(); - if (!context->cull_rect.intersect(clip_rrect_bounds)) { - context->cull_rect.setEmpty(); - } - Layer::AutoPrerollSaveLayerState save = - Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); - context->mutators_stack.PushClipRRect(clip_rrect_); - - // Collect inheritance information on our children in Preroll so that - // we can pass it along by default. - context->subtree_can_inherit_opacity = true; - - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); - if (child_paint_bounds.intersect(clip_rrect_bounds)) { - set_paint_bounds(child_paint_bounds); - } - - // If we use a SaveLayer then we can accept opacity on behalf - // of our children and apply it in the saveLayer. - if (UsesSaveLayer()) { - context->subtree_can_inherit_opacity = true; - } - - context->mutators_stack.Pop(); - context->cull_rect = previous_cull_rect; + ClipShapeLayer::Preroll(context, matrix); } void ClipRRectLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipRRectLayer::Paint"); - FML_DCHECK(needs_painting(context)); + ClipShapeLayer::Paint(context); +} - SkAutoCanvasRestore save(context.internal_nodes_canvas, true); - context.internal_nodes_canvas->clipRRect(clip_rrect_, - clip_behavior_ != Clip::hardEdge); +const SkRect& ClipRRectLayer::clip_shape_bounds() const { + return clip_shape().getBounds(); +} - if (!UsesSaveLayer()) { - PaintChildren(context); - return; - } +void ClipRRectLayer::OnMutatorsStackPushClipShape( + MutatorsStack& mutators_stack) { + mutators_stack.PushClipRRect(clip_shape()); +} - AutoCachePaint cache_paint(context); - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - context.internal_nodes_canvas->saveLayer(paint_bounds(), cache_paint.paint()); - - PaintChildren(context); - - context.internal_nodes_canvas->restore(); - if (context.checkerboard_offscreen_layers) { - DrawCheckerboard(context.internal_nodes_canvas, paint_bounds()); - } +void ClipRRectLayer::OnCanvasClipShape(SkCanvas* canvas) const { + canvas->clipRRect(clip_shape(), clip_behavior() != Clip::hardEdge); } } // namespace flutter diff --git a/engine/src/flutter/flow/layers/clip_rrect_layer.h b/engine/src/flutter/flow/layers/clip_rrect_layer.h index 80953b26f56..15c71a732e4 100644 --- a/engine/src/flutter/flow/layers/clip_rrect_layer.h +++ b/engine/src/flutter/flow/layers/clip_rrect_layer.h @@ -5,28 +5,26 @@ #ifndef FLUTTER_FLOW_LAYERS_CLIP_RRECT_LAYER_H_ #define FLUTTER_FLOW_LAYERS_CLIP_RRECT_LAYER_H_ -#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/clip_shape_layer.h" namespace flutter { -class ClipRRectLayer : public ContainerLayer { +class ClipRRectLayer : public ClipShapeLayer { public: ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior); - void Diff(DiffContext* context, const Layer* old_layer) override; - void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; - bool UsesSaveLayer() const { - return clip_behavior_ == Clip::antiAliasWithSaveLayer; - } + protected: + const SkRect& clip_shape_bounds() const override; + + void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) override; + + void OnCanvasClipShape(SkCanvas* canvas) const override; private: - SkRRect clip_rrect_; - Clip clip_behavior_; - FML_DISALLOW_COPY_AND_ASSIGN(ClipRRectLayer); }; diff --git a/engine/src/flutter/flow/layers/clip_shape_layer.h b/engine/src/flutter/flow/layers/clip_shape_layer.h new file mode 100644 index 00000000000..7781a4c211b --- /dev/null +++ b/engine/src/flutter/flow/layers/clip_shape_layer.h @@ -0,0 +1,113 @@ +// 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. + +#ifndef FLUTTER_FLOW_LAYERS_CLIP_SHAPE_LAYER_H_ +#define FLUTTER_FLOW_LAYERS_CLIP_SHAPE_LAYER_H_ + +#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/paint_utils.h" + +namespace flutter { + +template +class ClipShapeLayer : public ContainerLayer { + public: + using ClipShape = T; + ClipShapeLayer(const ClipShape& clip_shape, Clip clip_behavior) + : clip_shape_(clip_shape), clip_behavior_(clip_behavior) { + FML_DCHECK(clip_behavior != Clip::none); + } + + void Diff(DiffContext* context, const Layer* old_layer) override { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast*>(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (clip_behavior_ != prev->clip_behavior_ || + clip_shape_ != prev->clip_shape_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); + } + } + if (context->PushCullRect(clip_shape_bounds())) { + DiffChildren(context, prev); + } + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); + } + + void Preroll(PrerollContext* context, const SkMatrix& matrix) override { + SkRect previous_cull_rect = context->cull_rect; + if (!context->cull_rect.intersect(clip_shape_bounds())) { + context->cull_rect.setEmpty(); + } + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); + OnMutatorsStackPushClipShape(context->mutators_stack); + + // Collect inheritance information on our children in Preroll so that + // we can pass it along by default. + context->subtree_can_inherit_opacity = true; + + SkRect child_paint_bounds = SkRect::MakeEmpty(); + PrerollChildren(context, matrix, &child_paint_bounds); + if (child_paint_bounds.intersect(clip_shape_bounds())) { + set_paint_bounds(child_paint_bounds); + } + + // If we use a SaveLayer then we can accept opacity on behalf + // of our children and apply it in the saveLayer. + if (UsesSaveLayer()) { + context->subtree_can_inherit_opacity = true; + } + + context->mutators_stack.Pop(); + context->cull_rect = previous_cull_rect; + } + + void Paint(PaintContext& context) const override { + FML_DCHECK(needs_painting(context)); + + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); + OnCanvasClipShape(context.internal_nodes_canvas); + + if (!UsesSaveLayer()) { + PaintChildren(context); + return; + } + + AutoCachePaint cache_paint(context); + TRACE_EVENT0("flutter", "Canvas::saveLayer"); + context.internal_nodes_canvas->saveLayer(paint_bounds(), + cache_paint.paint()); + + PaintChildren(context); + + context.internal_nodes_canvas->restore(); + if (context.checkerboard_offscreen_layers) { + DrawCheckerboard(context.internal_nodes_canvas, paint_bounds()); + } + } + + bool UsesSaveLayer() const { + return clip_behavior_ == Clip::antiAliasWithSaveLayer; + } + + protected: + virtual const SkRect& clip_shape_bounds() const = 0; + virtual void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) = 0; + virtual void OnCanvasClipShape(SkCanvas* canvas) const = 0; + virtual ~ClipShapeLayer() = default; + + const ClipShape& clip_shape() const { return clip_shape_; } + Clip clip_behavior() const { return clip_behavior_; } + + private: + const ClipShape clip_shape_; + Clip clip_behavior_; + + FML_DISALLOW_COPY_AND_ASSIGN(ClipShapeLayer); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_LAYERS_CLIP_SHAPE_LAYER_H_