Flutter Engine
image_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/image_filter_layer.h"
6 
7 #include "flutter/flow/layers/transform_layer.h"
8 #include "flutter/flow/testing/diff_context_test.h"
9 #include "flutter/flow/testing/layer_test.h"
10 #include "flutter/flow/testing/mock_layer.h"
11 #include "flutter/fml/macros.h"
12 #include "flutter/testing/mock_canvas.h"
13 #include "third_party/skia/include/effects/SkImageFilters.h"
14 
15 namespace flutter {
16 namespace testing {
17 
19 
20 #ifndef NDEBUG
21 TEST_F(ImageFilterLayerTest, PaintingEmptyLayerDies) {
22  auto layer = std::make_shared<ImageFilterLayer>(sk_sp<SkImageFilter>());
23 
24  layer->Preroll(preroll_context(), SkMatrix());
25  EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
26  EXPECT_FALSE(layer->needs_painting(paint_context()));
27 
28  EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
29  "needs_painting\\(context\\)");
30 }
31 
32 TEST_F(ImageFilterLayerTest, PaintBeforePrerollDies) {
33  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
34  const SkPath child_path = SkPath().addRect(child_bounds);
35  auto mock_layer = std::make_shared<MockLayer>(child_path);
36  auto layer = std::make_shared<ImageFilterLayer>(sk_sp<SkImageFilter>());
37  layer->Add(mock_layer);
38 
39  EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
40  EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
41  "needs_painting\\(context\\)");
42 }
43 #endif
44 
45 TEST_F(ImageFilterLayerTest, EmptyFilter) {
46  const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
47  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
48  const SkPath child_path = SkPath().addRect(child_bounds);
49  const SkPaint child_paint = SkPaint(SkColors::kYellow);
50  auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
51  auto layer = std::make_shared<ImageFilterLayer>(nullptr);
52  layer->Add(mock_layer);
53 
54  layer->Preroll(preroll_context(), initial_transform);
55  EXPECT_EQ(layer->paint_bounds(), child_bounds);
56  EXPECT_TRUE(layer->needs_painting(paint_context()));
57  EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
58 
59  SkPaint filter_paint;
60  filter_paint.setImageFilter(nullptr);
61  layer->Paint(paint_context());
62  EXPECT_EQ(mock_canvas().draw_calls(),
63  std::vector({
65  0, MockCanvas::SaveLayerData{child_bounds, filter_paint,
66  nullptr, 1}},
68  1, MockCanvas::DrawPathData{child_path, child_paint}},
70  }));
71 }
72 
73 TEST_F(ImageFilterLayerTest, SimpleFilter) {
74  const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
75  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
76  const SkPath child_path = SkPath().addRect(child_bounds);
77  const SkPaint child_paint = SkPaint(SkColors::kYellow);
78  auto layer_filter = SkImageFilters::MatrixTransform(
79  SkMatrix(),
80  SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr);
81  auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
82  auto layer = std::make_shared<ImageFilterLayer>(layer_filter);
83  layer->Add(mock_layer);
84 
85  const SkRect child_rounded_bounds =
86  SkRect::MakeLTRB(5.0f, 6.0f, 21.0f, 22.0f);
87 
88  layer->Preroll(preroll_context(), initial_transform);
89  EXPECT_EQ(layer->paint_bounds(), child_rounded_bounds);
90  EXPECT_TRUE(layer->needs_painting(paint_context()));
91  EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
92 
93  SkPaint filter_paint;
94  filter_paint.setImageFilter(layer_filter);
95  layer->Paint(paint_context());
96  EXPECT_EQ(mock_canvas().draw_calls(),
97  std::vector({
99  0, MockCanvas::SaveLayerData{child_bounds, filter_paint,
100  nullptr, 1}},
102  1, MockCanvas::DrawPathData{child_path, child_paint}},
104  }));
105 }
106 
107 TEST_F(ImageFilterLayerTest, SimpleFilterBounds) {
108  const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
109  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
110  const SkPath child_path = SkPath().addRect(child_bounds);
111  const SkPaint child_paint = SkPaint(SkColors::kYellow);
112  const SkMatrix filter_transform = SkMatrix::Scale(2.0, 2.0);
113  auto layer_filter = SkImageFilters::MatrixTransform(
114  filter_transform,
115  SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr);
116  auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
117  auto layer = std::make_shared<ImageFilterLayer>(layer_filter);
118  layer->Add(mock_layer);
119 
120  const SkRect filter_bounds = SkRect::MakeLTRB(10.0f, 12.0f, 42.0f, 44.0f);
121 
122  layer->Preroll(preroll_context(), initial_transform);
123  EXPECT_EQ(layer->paint_bounds(), filter_bounds);
124  EXPECT_TRUE(layer->needs_painting(paint_context()));
125  EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
126 
127  SkPaint filter_paint;
128  filter_paint.setImageFilter(layer_filter);
129  layer->Paint(paint_context());
130  EXPECT_EQ(mock_canvas().draw_calls(),
131  std::vector({
133  0, MockCanvas::SaveLayerData{child_bounds, filter_paint,
134  nullptr, 1}},
136  1, MockCanvas::DrawPathData{child_path, child_paint}},
138  }));
139 }
140 
141 TEST_F(ImageFilterLayerTest, 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  auto layer_filter = SkImageFilters::MatrixTransform(
150  SkMatrix(),
151  SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr);
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<ImageFilterLayer>(layer_filter);
155  layer->Add(mock_layer1);
156  layer->Add(mock_layer2);
157 
158  SkRect children_bounds = child_path1.getBounds();
159  children_bounds.join(child_path2.getBounds());
160  SkRect children_rounded_bounds = SkRect::Make(children_bounds.roundOut());
161 
162  layer->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_rounded_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  SkPaint filter_paint;
173  filter_paint.setImageFilter(layer_filter);
174  layer->Paint(paint_context());
175  EXPECT_EQ(
176  mock_canvas().draw_calls(),
177  std::vector({MockCanvas::DrawCall{
178  0, MockCanvas::SaveLayerData{children_bounds,
179  filter_paint, nullptr, 1}},
181  1, MockCanvas::DrawPathData{child_path1, child_paint1}},
183  1, MockCanvas::DrawPathData{child_path2, child_paint2}},
185 }
186 
187 TEST_F(ImageFilterLayerTest, Nested) {
188  const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
189  const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f);
190  const SkPath child_path1 = SkPath().addRect(child_bounds);
191  const SkPath child_path2 =
192  SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f));
193  const SkPaint child_paint1 = SkPaint(SkColors::kYellow);
194  const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
195  auto layer_filter1 = SkImageFilters::MatrixTransform(
196  SkMatrix(),
197  SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr);
198  auto layer_filter2 = SkImageFilters::MatrixTransform(
199  SkMatrix(),
200  SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr);
201  auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
202  auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
203  auto layer1 = std::make_shared<ImageFilterLayer>(layer_filter1);
204  auto layer2 = std::make_shared<ImageFilterLayer>(layer_filter2);
205  layer2->Add(mock_layer2);
206  layer1->Add(mock_layer1);
207  layer1->Add(layer2);
208 
209  SkRect children_bounds = child_path1.getBounds();
210  children_bounds.join(SkRect::Make(child_path2.getBounds().roundOut()));
211  const SkRect children_rounded_bounds =
212  SkRect::Make(children_bounds.roundOut());
213  const SkRect mock_layer2_rounded_bounds =
214  SkRect::Make(child_path2.getBounds().roundOut());
215 
216  layer1->Preroll(preroll_context(), initial_transform);
217  EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
218  EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
219  EXPECT_EQ(layer1->paint_bounds(), children_rounded_bounds);
220  EXPECT_EQ(layer2->paint_bounds(), mock_layer2_rounded_bounds);
221  EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
222  EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
223  EXPECT_TRUE(layer1->needs_painting(paint_context()));
224  EXPECT_TRUE(layer2->needs_painting(paint_context()));
225  EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform);
226  EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform);
227 
228  SkPaint filter_paint1, filter_paint2;
229  filter_paint1.setImageFilter(layer_filter1);
230  filter_paint2.setImageFilter(layer_filter2);
231  layer1->Paint(paint_context());
232  EXPECT_EQ(mock_canvas().draw_calls(),
233  std::vector({
235  0, MockCanvas::SaveLayerData{children_bounds, filter_paint1,
236  nullptr, 1}},
238  1, MockCanvas::DrawPathData{child_path1, child_paint1}},
240  1, MockCanvas::SaveLayerData{child_path2.getBounds(),
241  filter_paint2, nullptr, 2}},
243  2, MockCanvas::DrawPathData{child_path2, child_paint2}},
246  }));
247 }
248 
249 TEST_F(ImageFilterLayerTest, Readback) {
250  auto layer_filter = SkImageFilters::MatrixTransform(
251  SkMatrix(),
252  SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr);
253  auto initial_transform = SkMatrix();
254 
255  // ImageFilterLayer does not read from surface
256  auto layer = std::make_shared<ImageFilterLayer>(layer_filter);
257  preroll_context()->surface_needs_readback = false;
258  layer->Preroll(preroll_context(), initial_transform);
259  EXPECT_FALSE(preroll_context()->surface_needs_readback);
260 
261  // ImageFilterLayer blocks child with readback
262  auto mock_layer =
263  std::make_shared<MockLayer>(SkPath(), SkPaint(), false, true);
264  layer->Add(mock_layer);
265  preroll_context()->surface_needs_readback = false;
266  layer->Preroll(preroll_context(), initial_transform);
267  EXPECT_FALSE(preroll_context()->surface_needs_readback);
268 }
269 
270 TEST_F(ImageFilterLayerTest, ChildIsCached) {
271  auto layer_filter = SkImageFilters::MatrixTransform(
272  SkMatrix(),
273  SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr);
274  auto initial_transform = SkMatrix::Translate(50.0, 25.5);
275  auto other_transform = SkMatrix::Scale(1.0, 2.0);
276  const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f));
277  auto mock_layer = std::make_shared<MockLayer>(child_path);
278  auto layer = std::make_shared<ImageFilterLayer>(layer_filter);
279  layer->Add(mock_layer);
280 
281  SkMatrix cache_ctm = initial_transform;
282  SkCanvas cache_canvas;
283  cache_canvas.setMatrix(cache_ctm);
284  SkCanvas other_canvas;
285  other_canvas.setMatrix(other_transform);
286 
287  use_mock_raster_cache();
288 
289  EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
290  EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas));
291  EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
292 
293  layer->Preroll(preroll_context(), initial_transform);
294 
295  EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
296  EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas));
297  EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
298 }
299 
300 TEST_F(ImageFilterLayerTest, ChildrenNotCached) {
301  auto layer_filter = SkImageFilters::MatrixTransform(
302  SkMatrix(),
303  SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr);
304  auto initial_transform = SkMatrix::Translate(50.0, 25.5);
305  auto other_transform = SkMatrix::Scale(1.0, 2.0);
306  const SkPath child_path1 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f));
307  const SkPath child_path2 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f));
308  auto mock_layer1 = std::make_shared<MockLayer>(child_path1);
309  auto mock_layer2 = std::make_shared<MockLayer>(child_path2);
310  auto layer = std::make_shared<ImageFilterLayer>(layer_filter);
311  layer->Add(mock_layer1);
312  layer->Add(mock_layer2);
313 
314  SkMatrix cache_ctm = initial_transform;
315  SkCanvas cache_canvas;
316  cache_canvas.setMatrix(cache_ctm);
317  SkCanvas other_canvas;
318  other_canvas.setMatrix(other_transform);
319 
320  use_mock_raster_cache();
321 
322  EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
323  EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas));
324  EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas));
325  EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas));
326  EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas));
327 
328  layer->Preroll(preroll_context(), initial_transform);
329 
330  EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
331  EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas));
332  EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas));
333  EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas));
334  EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas));
335 }
336 
337 #ifdef FLUTTER_ENABLE_DIFF_CONTEXT
338 
339 using ImageFilterLayerDiffTest = DiffContextTest;
340 
341 TEST_F(ImageFilterLayerDiffTest, ImageFilterLayer) {
342  auto filter = SkImageFilters::Blur(10, 10, SkTileMode::kClamp, nullptr);
343 
344  {
345  // tests later assume 30px paint area, fail early if that's not the case
346  auto paint_rect =
347  filter->filterBounds(SkIRect::MakeWH(10, 10), SkMatrix::I(),
348  SkImageFilter::kForward_MapDirection);
349  EXPECT_EQ(paint_rect, SkIRect::MakeLTRB(-30, -30, 40, 40));
350  }
351 
352  MockLayerTree l1;
353  auto filter_layer = std::make_shared<ImageFilterLayer>(filter);
354  auto path = SkPath().addRect(SkRect::MakeLTRB(100, 100, 110, 110));
355  filter_layer->Add(std::make_shared<MockLayer>(path));
356  l1.root()->Add(filter_layer);
357 
358  auto damage = DiffLayerTree(l1, MockLayerTree());
359  EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(70, 70, 140, 140));
360 
361  MockLayerTree l2;
362  auto scale = std::make_shared<TransformLayer>(SkMatrix::Scale(2.0, 2.0));
363  scale->Add(filter_layer);
364  l2.root()->Add(scale);
365 
366  damage = DiffLayerTree(l2, MockLayerTree());
367  EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(140, 140, 280, 280));
368 
369  MockLayerTree l3;
370  l3.root()->Add(scale);
371 
372  // path outside of ImageFilterLayer
373  auto path1 = SkPath().addRect(SkRect::MakeLTRB(130, 130, 140, 140));
374  l3.root()->Add(std::make_shared<MockLayer>(path1));
375  damage = DiffLayerTree(l3, l2);
376  EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(130, 130, 140, 140));
377 
378  // path intersecting ImageFilterLayer, shouldn't trigger entire
379  // ImageFilterLayer repaint
380  MockLayerTree l4;
381  l4.root()->Add(scale);
382  auto path2 = SkPath().addRect(SkRect::MakeLTRB(130, 130, 141, 141));
383  l4.root()->Add(std::make_shared<MockLayer>(path2));
384  damage = DiffLayerTree(l4, l3);
385  EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(130, 130, 141, 141));
386 }
387 
388 #endif
389 
390 } // namespace testing
391 } // namespace flutter
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
LayerTestBase<::testing::Test > LayerTest
Definition: layer_test.h:151
TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies)
static constexpr SkRect kEmptyRect
Definition: mock_canvas.h:28