blob: faf4c26d31b6ca41f65931c94405b533a7b9ed1d [file] [log] [blame]
// 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::none);
}
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 (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;
bool uses_save_layer = UsesSaveLayer();
if (!context->cull_rect.intersect(clip_shape_bounds())) {
context->cull_rect.setEmpty();
}
// 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, matrix);
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 (uses_save_layer) {
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);
if (context.raster_cache) {
if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) {
return;
}
}
Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create(
context, paint_bounds(), cache_paint.sk_paint());
PaintChildren(context);
}
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_