Skip to content

Commit 9061823

Browse files
authored
backdrop filter layer should report cull_rect as its paint bounds (flutter#24489)
1 parent e697715 commit 9061823

11 files changed

+114
-32
lines changed

flow/layers/backdrop_filter_layer.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ void BackdropFilterLayer::Preroll(PrerollContext* context,
4747
const SkMatrix& matrix) {
4848
Layer::AutoPrerollSaveLayerState save =
4949
Layer::AutoPrerollSaveLayerState::Create(context, true, bool(filter_));
50-
ContainerLayer::Preroll(context, matrix);
50+
SkRect child_paint_bounds = SkRect::MakeEmpty();
51+
PrerollChildren(context, matrix, &child_paint_bounds);
52+
child_paint_bounds.join(context->cull_rect);
53+
set_paint_bounds(child_paint_bounds);
5154
}
5255

5356
void BackdropFilterLayer::Paint(PaintContext& context) const {

flow/layers/backdrop_filter_layer_unittests.cc

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
#include "flutter/flow/layers/backdrop_filter_layer.h"
6+
#include "flutter/flow/layers/clip_rect_layer.h"
67

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

26-
layer->Preroll(preroll_context(), SkMatrix());
29+
parent->Preroll(preroll_context(), SkMatrix());
2730
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
2831
EXPECT_FALSE(layer->needs_painting(paint_context()));
2932
EXPECT_FALSE(layer->needs_system_composite());
@@ -53,8 +56,10 @@ TEST_F(BackdropFilterLayerTest, EmptyFilter) {
5356
auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
5457
auto layer = std::make_shared<BackdropFilterLayer>(nullptr);
5558
layer->Add(mock_layer);
59+
auto parent = std::make_shared<ClipRectLayer>(child_bounds, Clip::hardEdge);
60+
parent->Add(layer);
5661

57-
layer->Preroll(preroll_context(), initial_transform);
62+
parent->Preroll(preroll_context(), initial_transform);
5863
EXPECT_EQ(layer->paint_bounds(), child_bounds);
5964
EXPECT_TRUE(layer->needs_painting(paint_context()));
6065
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@@ -79,8 +84,10 @@ TEST_F(BackdropFilterLayerTest, SimpleFilter) {
7984
auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
8085
auto layer = std::make_shared<BackdropFilterLayer>(layer_filter);
8186
layer->Add(mock_layer);
87+
auto parent = std::make_shared<ClipRectLayer>(child_bounds, Clip::hardEdge);
88+
parent->Add(layer);
8289

83-
layer->Preroll(preroll_context(), initial_transform);
90+
parent->Preroll(preroll_context(), initial_transform);
8491
EXPECT_EQ(layer->paint_bounds(), child_bounds);
8592
EXPECT_TRUE(layer->needs_painting(paint_context()));
8693
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@@ -104,16 +111,19 @@ TEST_F(BackdropFilterLayerTest, MultipleChildren) {
104111
SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f));
105112
const SkPaint child_paint1 = SkPaint(SkColors::kYellow);
106113
const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
114+
SkRect children_bounds = child_path1.getBounds();
115+
children_bounds.join(child_path2.getBounds());
107116
auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
108117
auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
109118
auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
110119
auto layer = std::make_shared<BackdropFilterLayer>(layer_filter);
111120
layer->Add(mock_layer1);
112121
layer->Add(mock_layer2);
122+
auto parent =
123+
std::make_shared<ClipRectLayer>(children_bounds, Clip::hardEdge);
124+
parent->Add(layer);
113125

114-
SkRect children_bounds = child_path1.getBounds();
115-
children_bounds.join(child_path2.getBounds());
116-
layer->Preroll(preroll_context(), initial_transform);
126+
parent->Preroll(preroll_context(), initial_transform);
117127
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
118128
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
119129
EXPECT_EQ(layer->paint_bounds(), children_bounds);
@@ -144,6 +154,8 @@ TEST_F(BackdropFilterLayerTest, Nested) {
144154
SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f));
145155
const SkPaint child_paint1 = SkPaint(SkColors::kYellow);
146156
const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
157+
SkRect children_bounds = child_path1.getBounds();
158+
children_bounds.join(child_path2.getBounds());
147159
auto layer_filter1 = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
148160
auto layer_filter2 = SkImageFilters::Paint(SkPaint(SkColors::kDkGray));
149161
auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
@@ -153,14 +165,15 @@ TEST_F(BackdropFilterLayerTest, Nested) {
153165
layer2->Add(mock_layer2);
154166
layer1->Add(mock_layer1);
155167
layer1->Add(layer2);
168+
auto parent =
169+
std::make_shared<ClipRectLayer>(children_bounds, Clip::hardEdge);
170+
parent->Add(layer1);
156171

157-
SkRect children_bounds = child_path1.getBounds();
158-
children_bounds.join(child_path2.getBounds());
159-
layer1->Preroll(preroll_context(), initial_transform);
172+
parent->Preroll(preroll_context(), initial_transform);
160173
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
161174
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
162175
EXPECT_EQ(layer1->paint_bounds(), children_bounds);
163-
EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds());
176+
EXPECT_EQ(layer2->paint_bounds(), children_bounds);
164177
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
165178
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
166179
EXPECT_TRUE(layer1->needs_painting(paint_context()));
@@ -169,20 +182,20 @@ TEST_F(BackdropFilterLayerTest, Nested) {
169182
EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform);
170183

171184
layer1->Paint(paint_context());
172-
EXPECT_EQ(mock_canvas().draw_calls(),
173-
std::vector(
174-
{MockCanvas::DrawCall{
175-
0, MockCanvas::SaveLayerData{children_bounds, SkPaint(),
176-
layer_filter1, 1}},
177-
MockCanvas::DrawCall{
178-
1, MockCanvas::DrawPathData{child_path1, child_paint1}},
179-
MockCanvas::DrawCall{
180-
1, MockCanvas::SaveLayerData{child_path2.getBounds(),
181-
SkPaint(), layer_filter2, 2}},
182-
MockCanvas::DrawCall{
183-
2, MockCanvas::DrawPathData{child_path2, child_paint2}},
184-
MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
185-
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
185+
EXPECT_EQ(
186+
mock_canvas().draw_calls(),
187+
std::vector({MockCanvas::DrawCall{
188+
0, MockCanvas::SaveLayerData{children_bounds, SkPaint(),
189+
layer_filter1, 1}},
190+
MockCanvas::DrawCall{
191+
1, MockCanvas::DrawPathData{child_path1, child_paint1}},
192+
MockCanvas::DrawCall{
193+
1, MockCanvas::SaveLayerData{children_bounds, SkPaint(),
194+
layer_filter2, 2}},
195+
MockCanvas::DrawCall{
196+
2, MockCanvas::DrawPathData{child_path2, child_paint2}},
197+
MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
198+
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
186199
}
187200

188201
TEST_F(BackdropFilterLayerTest, Readback) {

flow/layers/clip_path_layer.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
4141

4242
SkRect previous_cull_rect = context->cull_rect;
4343
SkRect clip_path_bounds = clip_path_.getBounds();
44-
context->cull_rect.intersect(clip_path_bounds);
44+
if (!context->cull_rect.intersect(clip_path_bounds)) {
45+
context->cull_rect.setEmpty();
46+
}
4547
Layer::AutoPrerollSaveLayerState save =
4648
Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer());
4749
context->mutators_stack.PushClipPath(clip_path_);

flow/layers/clip_path_layer_unittests.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) {
6565
EXPECT_EQ(layer->paint_bounds(), child_bounds);
6666
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
6767
EXPECT_TRUE(layer->needs_painting(paint_context()));
68-
EXPECT_EQ(mock_layer->parent_cull_rect(), distant_bounds);
68+
EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect);
6969
EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
7070
EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)}));
7171

flow/layers/clip_rect_layer.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
3636
TRACE_EVENT0("flutter", "ClipRectLayer::Preroll");
3737

3838
SkRect previous_cull_rect = context->cull_rect;
39-
context->cull_rect.intersect(clip_rect_);
39+
if (!context->cull_rect.intersect(clip_rect_)) {
40+
context->cull_rect.setEmpty();
41+
}
4042
Layer::AutoPrerollSaveLayerState save =
4143
Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer());
4244
context->mutators_stack.PushClipRect(clip_rect_);

flow/layers/clip_rect_layer_unittests.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ TEST_F(ClipRectLayerTest, PaintingCulledLayerDies) {
6363
EXPECT_EQ(layer->paint_bounds(), child_bounds);
6464
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
6565
EXPECT_TRUE(layer->needs_painting(paint_context()));
66-
EXPECT_EQ(mock_layer->parent_cull_rect(), distant_bounds);
66+
EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect);
6767
EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
6868
EXPECT_EQ(mock_layer->parent_mutators(),
6969
std::vector({Mutator(layer_bounds)}));

flow/layers/clip_rrect_layer.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
3737

3838
SkRect previous_cull_rect = context->cull_rect;
3939
SkRect clip_rrect_bounds = clip_rrect_.getBounds();
40-
context->cull_rect.intersect(clip_rrect_bounds);
40+
if (!context->cull_rect.intersect(clip_rrect_bounds)) {
41+
context->cull_rect.setEmpty();
42+
}
4143
Layer::AutoPrerollSaveLayerState save =
4244
Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer());
4345
context->mutators_stack.PushClipRRect(clip_rrect_);

flow/layers/clip_rrect_layer_unittests.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) {
6868
EXPECT_EQ(layer->paint_bounds(), child_bounds);
6969
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
7070
EXPECT_TRUE(layer->needs_painting(paint_context()));
71-
EXPECT_EQ(mock_layer->parent_cull_rect(), distant_bounds);
71+
EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect);
7272
EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
7373
EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)}));
7474

lib/web_ui/dev/goldens_lock.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
repository: https://github.com/flutter/goldens.git
2-
revision: cb8e029f9a5ebe0608f78fcdf20754f6ef261c81
2+
revision: 041056fc3ae057574586fa6f69b2cc9465c4a5bf

lib/web_ui/lib/src/engine/canvaskit/layer.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ class PrerollContext {
4141
final MutatorsStack mutatorsStack = MutatorsStack();
4242

4343
PrerollContext(this.rasterCache, this.viewEmbedder);
44+
45+
ui.Rect get cullRect {
46+
ui.Rect cullRect = ui.Rect.largest;
47+
for (Mutator m in mutatorsStack) {
48+
ui.Rect clipRect;
49+
switch (m.type) {
50+
case MutatorType.clipRect:
51+
clipRect = m.rect!;
52+
break;
53+
case MutatorType.clipRRect:
54+
clipRect = m.rrect!.outerRect;
55+
break;
56+
case MutatorType.clipPath:
57+
clipRect = m.path!.getBounds();
58+
break;
59+
default:
60+
continue;
61+
}
62+
cullRect = cullRect.intersect(clipRect);
63+
}
64+
return cullRect;
65+
}
4466
}
4567

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

134156
BackdropFilterEngineLayer(this._filter);
135157

158+
@override
159+
void preroll(PrerollContext preRollContext, Matrix4 matrix) {
160+
ui.Rect childBounds = prerollChildren(preRollContext, matrix);
161+
paintBounds = childBounds.expandToInclude(preRollContext.cullRect);
162+
}
163+
136164
@override
137165
void paint(PaintContext context) {
138166
context.internalNodesCanvas.saveLayerWithFilter(paintBounds, _filter);

lib/web_ui/test/golden_tests/engine/backdrop_filter_golden_test.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,38 @@ void testMain() async {
114114
await matchGoldenFile('backdrop_filter_clip_moved.png', region: region,
115115
maxDiffRatePercent: 0.8);
116116
});
117+
118+
// The blur filter should be applied to the background inside the clip even
119+
// though there are no children of the backdrop filter.
120+
test('Background should blur even if child does not paint', () async {
121+
final Rect region = Rect.fromLTWH(0, 0, 190, 130);
122+
123+
final SurfaceSceneBuilder builder = SurfaceSceneBuilder();
124+
final Picture backgroundPicture = _drawBackground(region);
125+
builder.addPicture(Offset.zero, backgroundPicture);
126+
127+
builder.pushClipRect(
128+
const Rect.fromLTRB(10, 10, 180, 120),
129+
);
130+
final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30);
131+
builder.addPicture(Offset.zero, circles1);
132+
133+
builder.pushClipRect(
134+
const Rect.fromLTRB(60, 10, 180, 120),
135+
);
136+
builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
137+
oldLayer: null);
138+
builder.pop();
139+
builder.pop();
140+
builder.pop();
141+
142+
html.document.body.append(builder
143+
.build()
144+
.webOnlyRootElement);
145+
146+
await matchGoldenFile('backdrop_filter_no_child_rendering.png', region: region,
147+
maxDiffRatePercent: 0.8);
148+
});
117149
}
118150

119151
Picture _drawTestPictureWithCircles(Rect region, double offsetX, double offsetY) {

0 commit comments

Comments
 (0)