Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion flow/layers/backdrop_filter_layer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ void BackdropFilterLayer::Preroll(PrerollContext* context,
const SkMatrix& matrix) {
Layer::AutoPrerollSaveLayerState save =
Layer::AutoPrerollSaveLayerState::Create(context, true, bool(filter_));
ContainerLayer::Preroll(context, matrix);
SkRect child_paint_bounds = SkRect::MakeEmpty();
PrerollChildren(context, matrix, &child_paint_bounds);
child_paint_bounds.join(context->cull_rect);
set_paint_bounds(child_paint_bounds);
}

void BackdropFilterLayer::Paint(PaintContext& context) const {
Expand Down
61 changes: 37 additions & 24 deletions flow/layers/backdrop_filter_layer_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

#include "flutter/flow/layers/backdrop_filter_layer.h"
#include "flutter/flow/layers/clip_rect_layer.h"

#include "flutter/flow/layers/clip_rect_layer.h"
#include "flutter/flow/layers/transform_layer.h"
Expand All @@ -22,8 +23,10 @@ using BackdropFilterLayerTest = LayerTest;
#ifndef NDEBUG
TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies) {
auto layer = std::make_shared<BackdropFilterLayer>(sk_sp<SkImageFilter>());
auto parent = std::make_shared<ClipRectLayer>(kEmptyRect, Clip::hardEdge);
parent->Add(layer);

layer->Preroll(preroll_context(), SkMatrix());
parent->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_FALSE(layer->needs_system_composite());
Expand Down Expand Up @@ -53,8 +56,10 @@ TEST_F(BackdropFilterLayerTest, EmptyFilter) {
auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
auto layer = std::make_shared<BackdropFilterLayer>(nullptr);
layer->Add(mock_layer);
auto parent = std::make_shared<ClipRectLayer>(child_bounds, Clip::hardEdge);
parent->Add(layer);

layer->Preroll(preroll_context(), initial_transform);
parent->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
Expand All @@ -79,8 +84,10 @@ TEST_F(BackdropFilterLayerTest, SimpleFilter) {
auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
auto layer = std::make_shared<BackdropFilterLayer>(layer_filter);
layer->Add(mock_layer);
auto parent = std::make_shared<ClipRectLayer>(child_bounds, Clip::hardEdge);
parent->Add(layer);

layer->Preroll(preroll_context(), initial_transform);
parent->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
Expand All @@ -104,16 +111,19 @@ TEST_F(BackdropFilterLayerTest, MultipleChildren) {
SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f));
const SkPaint child_paint1 = SkPaint(SkColors::kYellow);
const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
SkRect children_bounds = child_path1.getBounds();
children_bounds.join(child_path2.getBounds());
auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
auto layer = std::make_shared<BackdropFilterLayer>(layer_filter);
layer->Add(mock_layer1);
layer->Add(mock_layer2);
auto parent =
std::make_shared<ClipRectLayer>(children_bounds, Clip::hardEdge);
parent->Add(layer);

SkRect children_bounds = child_path1.getBounds();
children_bounds.join(child_path2.getBounds());
layer->Preroll(preroll_context(), initial_transform);
parent->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer->paint_bounds(), children_bounds);
Expand Down Expand Up @@ -144,6 +154,8 @@ TEST_F(BackdropFilterLayerTest, Nested) {
SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f));
const SkPaint child_paint1 = SkPaint(SkColors::kYellow);
const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
SkRect children_bounds = child_path1.getBounds();
children_bounds.join(child_path2.getBounds());
auto layer_filter1 = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
auto layer_filter2 = SkImageFilters::Paint(SkPaint(SkColors::kDkGray));
auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
Expand All @@ -153,14 +165,15 @@ TEST_F(BackdropFilterLayerTest, Nested) {
layer2->Add(mock_layer2);
layer1->Add(mock_layer1);
layer1->Add(layer2);
auto parent =
std::make_shared<ClipRectLayer>(children_bounds, Clip::hardEdge);
parent->Add(layer1);

SkRect children_bounds = child_path1.getBounds();
children_bounds.join(child_path2.getBounds());
layer1->Preroll(preroll_context(), initial_transform);
parent->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer1->paint_bounds(), children_bounds);
EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds());
EXPECT_EQ(layer2->paint_bounds(), children_bounds);
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer1->needs_painting(paint_context()));
Expand All @@ -169,20 +182,20 @@ TEST_F(BackdropFilterLayerTest, Nested) {
EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform);

layer1->Paint(paint_context());
EXPECT_EQ(mock_canvas().draw_calls(),
std::vector(
{MockCanvas::DrawCall{
0, MockCanvas::SaveLayerData{children_bounds, SkPaint(),
layer_filter1, 1}},
MockCanvas::DrawCall{
1, MockCanvas::DrawPathData{child_path1, child_paint1}},
MockCanvas::DrawCall{
1, MockCanvas::SaveLayerData{child_path2.getBounds(),
SkPaint(), layer_filter2, 2}},
MockCanvas::DrawCall{
2, MockCanvas::DrawPathData{child_path2, child_paint2}},
MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
EXPECT_EQ(
mock_canvas().draw_calls(),
std::vector({MockCanvas::DrawCall{
0, MockCanvas::SaveLayerData{children_bounds, SkPaint(),
layer_filter1, 1}},
MockCanvas::DrawCall{
1, MockCanvas::DrawPathData{child_path1, child_paint1}},
MockCanvas::DrawCall{
1, MockCanvas::SaveLayerData{children_bounds, SkPaint(),
layer_filter2, 2}},
MockCanvas::DrawCall{
2, MockCanvas::DrawPathData{child_path2, child_paint2}},
MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
}

TEST_F(BackdropFilterLayerTest, Readback) {
Expand Down
4 changes: 3 additions & 1 deletion flow/layers/clip_path_layer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {

SkRect previous_cull_rect = context->cull_rect;
SkRect clip_path_bounds = clip_path_.getBounds();
context->cull_rect.intersect(clip_path_bounds);
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_);
Expand Down
2 changes: 1 addition & 1 deletion flow/layers/clip_path_layer_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) {
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), distant_bounds);
EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect);
EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)}));

Expand Down
4 changes: 3 additions & 1 deletion flow/layers/clip_rect_layer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
TRACE_EVENT0("flutter", "ClipRectLayer::Preroll");

SkRect previous_cull_rect = context->cull_rect;
context->cull_rect.intersect(clip_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_);
Expand Down
2 changes: 1 addition & 1 deletion flow/layers/clip_rect_layer_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ TEST_F(ClipRectLayerTest, PaintingCulledLayerDies) {
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), distant_bounds);
EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect);
EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
EXPECT_EQ(mock_layer->parent_mutators(),
std::vector({Mutator(layer_bounds)}));
Expand Down
4 changes: 3 additions & 1 deletion flow/layers/clip_rrect_layer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {

SkRect previous_cull_rect = context->cull_rect;
SkRect clip_rrect_bounds = clip_rrect_.getBounds();
context->cull_rect.intersect(clip_rrect_bounds);
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_);
Expand Down
2 changes: 1 addition & 1 deletion flow/layers/clip_rrect_layer_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) {
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), distant_bounds);
EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect);
EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)}));

Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/dev/goldens_lock.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
repository: https://github.com/flutter/goldens.git
revision: cb8e029f9a5ebe0608f78fcdf20754f6ef261c81
revision: 041056fc3ae057574586fa6f69b2cc9465c4a5bf
28 changes: 28 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,28 @@ class PrerollContext {
final MutatorsStack mutatorsStack = MutatorsStack();

PrerollContext(this.rasterCache, this.viewEmbedder);

ui.Rect get cullRect {
ui.Rect cullRect = ui.Rect.largest;
for (Mutator m in mutatorsStack) {
ui.Rect clipRect;
switch (m.type) {
case MutatorType.clipRect:
clipRect = m.rect!;
break;
case MutatorType.clipRRect:
clipRect = m.rrect!.outerRect;
break;
case MutatorType.clipPath:
clipRect = m.path!.getBounds();
break;
default:
continue;
}
cullRect = cullRect.intersect(clipRect);
}
return cullRect;
}
}

/// A context shared by all layers during the paint pass.
Expand Down Expand Up @@ -133,6 +155,12 @@ class BackdropFilterEngineLayer extends ContainerLayer implements ui.BackdropFil

BackdropFilterEngineLayer(this._filter);

@override
void preroll(PrerollContext preRollContext, Matrix4 matrix) {
ui.Rect childBounds = prerollChildren(preRollContext, matrix);
paintBounds = childBounds.expandToInclude(preRollContext.cullRect);
}

@override
void paint(PaintContext context) {
context.internalNodesCanvas.saveLayerWithFilter(paintBounds, _filter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,38 @@ void testMain() async {
await matchGoldenFile('backdrop_filter_clip_moved.png', region: region,
maxDiffRatePercent: 0.8);
});

// The blur filter should be applied to the background inside the clip even
// though there are no children of the backdrop filter.
test('Background should blur even if child does not paint', () async {
final Rect region = Rect.fromLTWH(0, 0, 190, 130);

final SurfaceSceneBuilder builder = SurfaceSceneBuilder();
final Picture backgroundPicture = _drawBackground(region);
builder.addPicture(Offset.zero, backgroundPicture);

builder.pushClipRect(
const Rect.fromLTRB(10, 10, 180, 120),
);
final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30);
builder.addPicture(Offset.zero, circles1);

builder.pushClipRect(
const Rect.fromLTRB(60, 10, 180, 120),
);
builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
oldLayer: null);
builder.pop();
builder.pop();
builder.pop();

html.document.body.append(builder
.build()
.webOnlyRootElement);

await matchGoldenFile('backdrop_filter_no_child_rendering.png', region: region,
maxDiffRatePercent: 0.8);
});
}

Picture _drawTestPictureWithCircles(Rect region, double offsetX, double offsetY) {
Expand Down