blob: 45106713ec9888d0d7373fb5cd7d73121c511e3b [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::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_