mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Introduces a mechanism to allow backdrop filters to 1) share backdrop inputs and 2) fuse filter applications for faster blurs. This is a proposed solution to https://github.com/flutter/flutter/issues/131568 Implemented: * Developer can specify a "backdrop id" which indicates that a backdrop layer should share the input texture and potentially cached filter for a layer. * Removes second save layer for each backdrop filter * Removes save layer trace event for backdrop filter * Can fuse backdrop filters if there is more than one identical filter TBD: * Adjust heruristic to avoid applying bdf filter to entire screen Suggestions: applying a bdf should be a distinct operation from a save layer in the DL builder/dispatcher. The saveLayer implmenentation in the impeller dispatcher is super convoluted because it needs to handle both. ### Video Video starts with normal bdf then I hot reload to specify that the bdfs share inputs/filters. This is running on a pixel 8 pro Change to the macrobenchmark app is just: ```dart Widget build(BuildContext context) { Widget addBlur(Widget child, bool shouldBlur) { if (shouldBlur) { return ClipRect( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), backdropId: 1, // Added ID child: child, ), ); } else { return child; } } ``` https://github.com/user-attachments/assets/22707f97-5825-43f1-91b4-1a02a43437f5 Requires framework changes in https://github.com/jonahwilliams/flutter/pull/new/backdrop_id
839 lines
26 KiB
C++
839 lines
26 KiB
C++
// 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,
|
|
std::optional<int64_t> backdrop_id) 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,
|
|
std::optional<int64_t> backdrop_id) override {
|
|
TRACE_EVENT0("flutter", "Canvas::saveLayer");
|
|
DlPaint paint;
|
|
std::optional<const DlRect> rect = ToDlRect(bounds);
|
|
canvas_->SaveLayer(rect, attributes.fill(paint, blend_mode), backdrop,
|
|
backdrop_id);
|
|
}
|
|
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 integral;
|
|
if (RasterCacheUtil::ComputeIntegralTransCTM(matrix_4x4(), &integral)) {
|
|
canvas_->SetTransform(integral);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
save_stack_.emplace_back(cull_rect, matrix);
|
|
}
|
|
|
|
void decommission() override {}
|
|
|
|
SkM44 matrix_4x4() const override { return state().matrix_4x4(); }
|
|
SkMatrix matrix_3x3() const override { return state().matrix_3x3(); }
|
|
SkRect local_cull_rect() const override { return state().local_cull_rect(); }
|
|
SkRect device_cull_rect() const override {
|
|
return state().device_cull_rect();
|
|
}
|
|
bool content_culled(const SkRect& content_bounds) const override {
|
|
return state().content_culled(content_bounds);
|
|
}
|
|
|
|
void save() override { save_stack_.emplace_back(state()); }
|
|
void saveLayer(const SkRect& bounds,
|
|
LayerStateStack::RenderingAttributes& attributes,
|
|
DlBlendMode blend,
|
|
const DlImageFilter* backdrop,
|
|
std::optional<int64_t> backdrop_id) override {
|
|
save_stack_.emplace_back(state());
|
|
}
|
|
void restore() override { save_stack_.pop_back(); }
|
|
|
|
void translate(SkScalar tx, SkScalar ty) override {
|
|
state().translate(tx, ty);
|
|
}
|
|
void transform(const SkM44& m44) override { state().transform(m44); }
|
|
void transform(const SkMatrix& matrix) override { state().transform(matrix); }
|
|
void integralTransform() override {
|
|
if (state().using_4x4_matrix()) {
|
|
SkM44 integral;
|
|
if (RasterCacheUtil::ComputeIntegralTransCTM(state().matrix_4x4(),
|
|
&integral)) {
|
|
state().setTransform(integral);
|
|
}
|
|
} else {
|
|
SkMatrix integral;
|
|
if (RasterCacheUtil::ComputeIntegralTransCTM(state().matrix_3x3(),
|
|
&integral)) {
|
|
state().setTransform(integral);
|
|
}
|
|
}
|
|
}
|
|
|
|
void clipRect(const SkRect& rect, ClipOp op, bool is_aa) override {
|
|
state().clipRect(rect, op, is_aa);
|
|
}
|
|
void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) override {
|
|
state().clipRRect(rrect, op, is_aa);
|
|
}
|
|
void clipPath(const SkPath& path, ClipOp op, bool is_aa) override {
|
|
state().clipPath(path, op, is_aa);
|
|
}
|
|
|
|
private:
|
|
DisplayListMatrixClipState& state() { return save_stack_.back(); }
|
|
const DisplayListMatrixClipState& state() const { return save_stack_.back(); }
|
|
|
|
std::vector<DisplayListMatrixClipState> save_stack_;
|
|
};
|
|
|
|
// ==============================================================
|
|
// 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 {
|
|
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,
|
|
std::optional<int64_t> backdrop_id,
|
|
const LayerStateStack::RenderingAttributes& prev)
|
|
: SaveLayerEntry(bounds, blend_mode, prev),
|
|
filter_(filter),
|
|
backdrop_id_(backdrop_id) {}
|
|
~BackdropFilterEntry() override = default;
|
|
|
|
void apply(LayerStateStack* stack) const override {
|
|
stack->delegate_->saveLayer(bounds_, stack->outstanding_, blend_mode_,
|
|
filter_.get(), backdrop_id_);
|
|
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_;
|
|
std::optional<int64_t> backdrop_id_;
|
|
|
|
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,
|
|
std::optional<int64_t> backdrop_id) {
|
|
layer_state_stack_->push_backdrop(bounds, filter, blend_mode, backdrop_id);
|
|
}
|
|
|
|
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 (DisplayListMatrixClipState::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,
|
|
std::optional<int64_t> backdrop_id) {
|
|
state_stack_.emplace_back(std::make_unique<BackdropFilterEntry>(
|
|
bounds, filter, blend_mode, backdrop_id, 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
|