| // 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/cacheable_layer.h" |
| #include "flutter/flow/layers/container_layer.h" |
| #include "flutter/flow/paint_utils.h" |
| |
| namespace flutter { |
| |
| template <class T> |
| class ClipShapeLayer : public CacheableContainerLayer { |
| public: |
| using ClipShape = T; |
| ClipShapeLayer(const ClipShape& clip_shape, Clip clip_behavior) |
| : CacheableContainerLayer(), |
| clip_shape_(clip_shape), |
| clip_behavior_(clip_behavior) { |
| FML_DCHECK(clip_behavior != Clip::kNone); |
| } |
| |
| void Diff(DiffContext* context, const Layer* old_layer) override { |
| DiffContext::AutoSubtreeRestore subtree(context); |
| auto* prev = static_cast<const ClipShapeLayer<ClipShape>*>(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 (UsesSaveLayer() && context->has_raster_cache()) { |
| context->WillPaintWithIntegralTransform(); |
| } |
| if (context->PushCullRect(clip_shape_bounds())) { |
| DiffChildren(context, prev); |
| } |
| context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); |
| } |
| |
| void Preroll(PrerollContext* context) override { |
| bool uses_save_layer = UsesSaveLayer(); |
| |
| // We can use the raster_cache for children only when the use_save_layer is |
| // true so if use_save_layer is false we pass the layer_raster_item is |
| // nullptr which mean we don't do raster cache logic. |
| AutoCache cache = |
| AutoCache(uses_save_layer ? layer_raster_cache_item_.get() : nullptr, |
| context, context->state_stack.transform_3x3()); |
| |
| Layer::AutoPrerollSaveLayerState save = |
| Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); |
| |
| auto mutator = context->state_stack.save(); |
| ApplyClip(mutator); |
| |
| SkRect child_paint_bounds = SkRect::MakeEmpty(); |
| PrerollChildren(context, &child_paint_bounds); |
| if (child_paint_bounds.intersect(clip_shape_bounds())) { |
| set_paint_bounds(child_paint_bounds); |
| } else { |
| set_paint_bounds(SkRect::MakeEmpty()); |
| } |
| |
| // If we use a SaveLayer then we can accept opacity on behalf |
| // of our children and apply it in the saveLayer. |
| if (uses_save_layer) { |
| context->renderable_state_flags = kSaveLayerRenderFlags; |
| } |
| } |
| |
| void Paint(PaintContext& context) const override { |
| FML_DCHECK(needs_painting(context)); |
| |
| auto mutator = context.state_stack.save(); |
| ApplyClip(mutator); |
| |
| if (!UsesSaveLayer()) { |
| PaintChildren(context); |
| return; |
| } |
| |
| if (context.raster_cache) { |
| mutator.integralTransform(); |
| auto restore_apply = context.state_stack.applyState( |
| paint_bounds(), LayerStateStack::kCallerCanApplyOpacity); |
| |
| DlPaint paint; |
| if (layer_raster_cache_item_->Draw(context, |
| context.state_stack.fill(paint))) { |
| return; |
| } |
| } |
| |
| mutator.saveLayer(paint_bounds()); |
| PaintChildren(context); |
| } |
| |
| bool UsesSaveLayer() const { |
| return clip_behavior_ == Clip::kAntiAliasWithSaveLayer; |
| } |
| |
| protected: |
| virtual const SkRect& clip_shape_bounds() const = 0; |
| virtual void ApplyClip(LayerStateStack::MutatorContext& mutator) 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_ |