Flutter Engine
backdrop_filter_layer_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/flow/layers/backdrop_filter_layer.h"
6 #include "flutter/flow/layers/clip_rect_layer.h"
7 
8 #include "flutter/flow/layers/clip_rect_layer.h"
9 #include "flutter/flow/layers/transform_layer.h"
10 #include "flutter/flow/testing/diff_context_test.h"
11 #include "flutter/flow/testing/layer_test.h"
12 #include "flutter/flow/testing/mock_layer.h"
13 #include "flutter/fml/macros.h"
14 #include "flutter/testing/mock_canvas.h"
15 #include "third_party/skia/include/core/SkImageFilter.h"
16 #include "third_party/skia/include/effects/SkImageFilters.h"
17 
18 namespace flutter {
19 namespace testing {
20 
22 
23 #ifndef NDEBUG
24 TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies) {
25  auto layer = std::make_shared<BackdropFilterLayer>(sk_sp<SkImageFilter>(),
26  SkBlendMode::kSrcOver);
27  auto parent = std::make_shared<ClipRectLayer>(kEmptyRect, Clip::hardEdge);
28  parent->Add(layer);
29 
30  parent->Preroll(preroll_context(), SkMatrix());
31  EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
32  EXPECT_FALSE(layer->needs_painting(paint_context()));
33 
34  EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
35  "needs_painting\\(context\\)");
36 }
37 
38 TEST_F(BackdropFilterLayerTest, PaintBeforePrerollDies) {
39  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
40  const SkPath child_path = SkPath().addRect(child_bounds);
41  auto mock_layer = std::make_shared<MockLayer>(child_path);
42  auto layer = std::make_shared<BackdropFilterLayer>(sk_sp<SkImageFilter>(),
43  SkBlendMode::kSrcOver);
44  layer->Add(mock_layer);
45 
46  EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
47  EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
48  "needs_painting\\(context\\)");
49 }
50 #endif
51 
53  const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
54  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
55  const SkPath child_path = SkPath().addRect(child_bounds);
56  const SkPaint child_paint = SkPaint(SkColors::kYellow);
57  auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
58  auto layer =
59  std::make_shared<BackdropFilterLayer>(nullptr, SkBlendMode::kSrcOver);
60  layer->Add(mock_layer);
61  auto parent = std::make_shared<ClipRectLayer>(child_bounds, Clip::hardEdge);
62  parent->Add(layer);
63 
64  parent->Preroll(preroll_context(), initial_transform);
65  EXPECT_EQ(layer->paint_bounds(), child_bounds);
66  EXPECT_TRUE(layer->needs_painting(paint_context()));
67  EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
68 
69  layer->Paint(paint_context());
70  EXPECT_EQ(
71  mock_canvas().draw_calls(),
72  std::vector({MockCanvas::DrawCall{
73  0, MockCanvas::SaveLayerData{child_bounds, SkPaint(),
74  nullptr, 1}},
76  1, MockCanvas::DrawPathData{child_path, child_paint}},
78 }
79 
81  const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
82  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
83  const SkPath child_path = SkPath().addRect(child_bounds);
84  const SkPaint child_paint = SkPaint(SkColors::kYellow);
85  auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
86  auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
87  auto layer = std::make_shared<BackdropFilterLayer>(layer_filter,
88  SkBlendMode::kSrcOver);
89  layer->Add(mock_layer);
90  auto parent = std::make_shared<ClipRectLayer>(child_bounds, Clip::hardEdge);
91  parent->Add(layer);
92 
93  parent->Preroll(preroll_context(), initial_transform);
94  EXPECT_EQ(layer->paint_bounds(), child_bounds);
95  EXPECT_TRUE(layer->needs_painting(paint_context()));
96  EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
97 
98  layer->Paint(paint_context());
99  EXPECT_EQ(
100  mock_canvas().draw_calls(),
101  std::vector({MockCanvas::DrawCall{
102  0, MockCanvas::SaveLayerData{child_bounds, SkPaint(),
103  layer_filter, 1}},
105  1, MockCanvas::DrawPathData{child_path, child_paint}},
107 }
108 
109 TEST_F(BackdropFilterLayerTest, NonSrcOverBlend) {
110  const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
111  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
112  const SkPath child_path = SkPath().addRect(child_bounds);
113  const SkPaint child_paint = SkPaint(SkColors::kYellow);
114  auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
115  auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
116  auto layer =
117  std::make_shared<BackdropFilterLayer>(layer_filter, SkBlendMode::kSrc);
118  layer->Add(mock_layer);
119  auto parent = std::make_shared<ClipRectLayer>(child_bounds, Clip::hardEdge);
120  parent->Add(layer);
121 
122  parent->Preroll(preroll_context(), initial_transform);
123  EXPECT_EQ(layer->paint_bounds(), child_bounds);
124  EXPECT_TRUE(layer->needs_painting(paint_context()));
125  EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
126 
127  SkPaint filter_paint = SkPaint();
128  filter_paint.setBlendMode(SkBlendMode::kSrc);
129 
130  layer->Paint(paint_context());
131  EXPECT_EQ(
132  mock_canvas().draw_calls(),
133  std::vector({MockCanvas::DrawCall{
134  0, MockCanvas::SaveLayerData{child_bounds, filter_paint,
135  layer_filter, 1}},
137  1, MockCanvas::DrawPathData{child_path, child_paint}},
139 }
140 
141 TEST_F(BackdropFilterLayerTest, MultipleChildren) {
142  const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
143  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f);
144  const SkPath child_path1 = SkPath().addRect(child_bounds);
145  const SkPath child_path2 =
146  SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f));
147  const SkPaint child_paint1 = SkPaint(SkColors::kYellow);
148  const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
149  SkRect children_bounds = child_path1.getBounds();
150  children_bounds.join(child_path2.getBounds());
151  auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
152  auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
153  auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
154  auto layer = std::make_shared<BackdropFilterLayer>(layer_filter,
155  SkBlendMode::kSrcOver);
156  layer->Add(mock_layer1);
157  layer->Add(mock_layer2);
158  auto parent =
159  std::make_shared<ClipRectLayer>(children_bounds, Clip::hardEdge);
160  parent->Add(layer);
161 
162  parent->Preroll(preroll_context(), initial_transform);
163  EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
164  EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
165  EXPECT_EQ(layer->paint_bounds(), children_bounds);
166  EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
167  EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
168  EXPECT_TRUE(layer->needs_painting(paint_context()));
169  EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform);
170  EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform);
171 
172  layer->Paint(paint_context());
173  EXPECT_EQ(
174  mock_canvas().draw_calls(),
175  std::vector({MockCanvas::DrawCall{
176  0, MockCanvas::SaveLayerData{children_bounds, SkPaint(),
177  layer_filter, 1}},
179  1, MockCanvas::DrawPathData{child_path1, child_paint1}},
181  1, MockCanvas::DrawPathData{child_path2, child_paint2}},
183 }
184 
186  const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
187  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f);
188  const SkPath child_path1 = SkPath().addRect(child_bounds);
189  const SkPath child_path2 =
190  SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f));
191  const SkPaint child_paint1 = SkPaint(SkColors::kYellow);
192  const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
193  SkRect children_bounds = child_path1.getBounds();
194  children_bounds.join(child_path2.getBounds());
195  auto layer_filter1 = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
196  auto layer_filter2 = SkImageFilters::Paint(SkPaint(SkColors::kDkGray));
197  auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
198  auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
199  auto layer1 = std::make_shared<BackdropFilterLayer>(layer_filter1,
200  SkBlendMode::kSrcOver);
201  auto layer2 = std::make_shared<BackdropFilterLayer>(layer_filter2,
202  SkBlendMode::kSrcOver);
203  layer2->Add(mock_layer2);
204  layer1->Add(mock_layer1);
205  layer1->Add(layer2);
206  auto parent =
207  std::make_shared<ClipRectLayer>(children_bounds, Clip::hardEdge);
208  parent->Add(layer1);
209 
210  parent->Preroll(preroll_context(), initial_transform);
211  EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
212  EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
213  EXPECT_EQ(layer1->paint_bounds(), children_bounds);
214  EXPECT_EQ(layer2->paint_bounds(), children_bounds);
215  EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
216  EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
217  EXPECT_TRUE(layer1->needs_painting(paint_context()));
218  EXPECT_TRUE(layer2->needs_painting(paint_context()));
219  EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform);
220  EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform);
221 
222  layer1->Paint(paint_context());
223  EXPECT_EQ(
224  mock_canvas().draw_calls(),
225  std::vector({MockCanvas::DrawCall{
226  0, MockCanvas::SaveLayerData{children_bounds, SkPaint(),
227  layer_filter1, 1}},
229  1, MockCanvas::DrawPathData{child_path1, child_paint1}},
231  1, MockCanvas::SaveLayerData{children_bounds, SkPaint(),
232  layer_filter2, 2}},
234  2, MockCanvas::DrawPathData{child_path2, child_paint2}},
237 }
238 
240  sk_sp<SkImageFilter> no_filter;
241  auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
242  auto initial_transform = SkMatrix();
243 
244  // BDF with filter always reads from surface
245  auto layer1 = std::make_shared<BackdropFilterLayer>(layer_filter,
246  SkBlendMode::kSrcOver);
247  preroll_context()->surface_needs_readback = false;
248  layer1->Preroll(preroll_context(), initial_transform);
249  EXPECT_TRUE(preroll_context()->surface_needs_readback);
250 
251  // BDF with no filter does not read from surface itself
252  auto layer2 =
253  std::make_shared<BackdropFilterLayer>(no_filter, SkBlendMode::kSrcOver);
254  preroll_context()->surface_needs_readback = false;
255  layer2->Preroll(preroll_context(), initial_transform);
256  EXPECT_FALSE(preroll_context()->surface_needs_readback);
257 
258  // BDF with no filter does not block prior readback value
259  preroll_context()->surface_needs_readback = true;
260  layer2->Preroll(preroll_context(), initial_transform);
261  EXPECT_TRUE(preroll_context()->surface_needs_readback);
262 
263  // BDF with no filter blocks child with readback
264  auto mock_layer =
265  std::make_shared<MockLayer>(SkPath(), SkPaint(), false, true);
266  layer2->Add(mock_layer);
267  preroll_context()->surface_needs_readback = false;
268  layer2->Preroll(preroll_context(), initial_transform);
269  EXPECT_FALSE(preroll_context()->surface_needs_readback);
270 }
271 
272 #ifdef FLUTTER_ENABLE_DIFF_CONTEXT
273 
274 using BackdropLayerDiffTest = DiffContextTest;
275 
276 TEST_F(BackdropLayerDiffTest, BackdropLayer) {
277  auto filter = SkImageFilters::Blur(10, 10, SkTileMode::kClamp, nullptr);
278 
279  {
280  // tests later assume 30px readback area, fail early if that's not the case
281  auto readback = filter->filterBounds(SkIRect::MakeWH(10, 10), SkMatrix::I(),
282  SkImageFilter::kReverse_MapDirection);
283  EXPECT_EQ(readback, SkIRect::MakeLTRB(-30, -30, 40, 40));
284  }
285 
286  MockLayerTree l1(SkISize::Make(100, 100));
287  l1.root()->Add(
288  std::make_shared<BackdropFilterLayer>(filter, SkBlendMode::kSrcOver));
289 
290  // no clip, effect over entire surface
291  auto damage = DiffLayerTree(l1, MockLayerTree(SkISize::Make(100, 100)));
292  EXPECT_EQ(damage.frame_damage, SkIRect::MakeWH(100, 100));
293 
294  MockLayerTree l2(SkISize::Make(100, 100));
295 
296  auto clip = std::make_shared<ClipRectLayer>(SkRect::MakeLTRB(20, 20, 60, 60),
298  clip->Add(
299  std::make_shared<BackdropFilterLayer>(filter, SkBlendMode::kSrcOver));
300  l2.root()->Add(clip);
301  damage = DiffLayerTree(l2, MockLayerTree(SkISize::Make(100, 100)));
302 
303  EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 90, 90));
304 
305  MockLayerTree l3;
306  auto scale = std::make_shared<TransformLayer>(SkMatrix::Scale(2.0, 2.0));
307  scale->Add(clip);
308  l3.root()->Add(scale);
309 
310  damage = DiffLayerTree(l3, MockLayerTree());
311  EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 180, 180));
312 
313  MockLayerTree l4;
314  l4.root()->Add(scale);
315 
316  // path just outside of readback region, doesn't affect blur
317  auto path1 = SkPath().addRect(SkRect::MakeLTRB(180, 180, 190, 190));
318  l4.root()->Add(std::make_shared<MockLayer>(path1));
319  damage = DiffLayerTree(l4, l3);
320  EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(180, 180, 190, 190));
321 
322  MockLayerTree l5;
323  l5.root()->Add(scale);
324 
325  // path just inside of readback region, must trigger backdrop repaint
326  auto path2 = SkPath().addRect(SkRect::MakeLTRB(179, 179, 189, 189));
327  l5.root()->Add(std::make_shared<MockLayer>(path2));
328  damage = DiffLayerTree(l5, l4);
329  EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 190, 190));
330 }
331 
332 #endif
333 
334 } // namespace testing
335 } // namespace flutter
LayerTestBase<::testing::Test > LayerTest
Definition: layer_test.h:151
TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies)
static constexpr SkRect kEmptyRect
Definition: mock_canvas.h:28