Flutter Engine
 
Loading...
Searching...
No Matches
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
6
14#include "flutter/fml/macros.h"
15#include "gtest/gtest.h"
16
17// TODO(zanderso): https://github.com/flutter/flutter/issues/127701
18// NOLINTBEGIN(bugprone-unchecked-optional-access)
19
20namespace flutter {
21namespace testing {
22
24
25#ifndef NDEBUG
26TEST_F(ImageFilterLayerTest, PaintingEmptyLayerDies) {
27 auto layer = std::make_shared<ImageFilterLayer>(nullptr);
28
29 layer->Preroll(preroll_context());
30 EXPECT_EQ(layer->paint_bounds(), DlRect());
31 EXPECT_FALSE(layer->needs_painting(paint_context()));
32
33 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
34 "needs_painting\\(context\\)");
35}
36
37TEST_F(ImageFilterLayerTest, PaintBeforePrerollDies) {
38 const DlRect child_bounds = DlRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
39 const DlPath child_path = DlPath::MakeRect(child_bounds);
40 auto mock_layer = std::make_shared<MockLayer>(child_path);
41 auto layer = std::make_shared<ImageFilterLayer>(nullptr);
42 layer->Add(mock_layer);
43
44 EXPECT_EQ(layer->paint_bounds(), DlRect());
45 EXPECT_EQ(layer->child_paint_bounds(), DlRect());
46 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
47 "needs_painting\\(context\\)");
48}
49#endif
50
51TEST_F(ImageFilterLayerTest, EmptyFilter) {
52 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 1.0f});
53 const DlRect child_bounds = DlRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
54 const DlPath child_path = DlPath::MakeRect(child_bounds);
55 const DlPaint child_paint = DlPaint(DlColor::kYellow());
56 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
57 auto layer = std::make_shared<ImageFilterLayer>(nullptr);
58 layer->Add(mock_layer);
59
60 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
61 layer->Preroll(preroll_context());
62 EXPECT_EQ(layer->paint_bounds(), child_bounds);
63 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
64 EXPECT_TRUE(layer->needs_painting(paint_context()));
65 EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
66
67 layer->Paint(display_list_paint_context());
68 DisplayListBuilder expected_builder;
69 /* (ImageFilter)layer::Paint */ {
70 expected_builder.Save();
71 /* mock_layer1::Paint */ {
72 expected_builder.DrawPath(child_path, child_paint);
73 }
74 expected_builder.Restore();
75 }
76 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
77}
78
79TEST_F(ImageFilterLayerTest, SimpleFilter) {
80 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 1.0f});
81 const DlRect child_bounds = DlRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
82 const DlPath child_path = DlPath::MakeRect(child_bounds);
83 const DlPaint child_paint = DlPaint(DlColor::kYellow());
84 auto dl_image_filter = DlImageFilter::MakeMatrix(
86 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
87 auto layer = std::make_shared<ImageFilterLayer>(dl_image_filter);
88 layer->Add(mock_layer);
89
90 const DlRect child_rounded_bounds =
91 DlRect::MakeLTRB(6.0f, 8.0f, 22.0f, 24.0f);
92
93 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
94 layer->Preroll(preroll_context());
95 EXPECT_EQ(layer->paint_bounds(), child_rounded_bounds);
96 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
97 EXPECT_TRUE(layer->needs_painting(paint_context()));
98 EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
99
100 DisplayListBuilder expected_builder;
101 /* ImageFilterLayer::Paint() */ {
102 DlPaint dl_paint;
103 dl_paint.setImageFilter(dl_image_filter.get());
104 expected_builder.SaveLayer(child_bounds, &dl_paint);
105 {
106 /* MockLayer::Paint() */ {
107 expected_builder.DrawPath(child_path, DlPaint(DlColor::kYellow()));
108 }
109 }
110 }
111 expected_builder.Restore();
112 auto expected_display_list = expected_builder.Build();
113
114 layer->Paint(display_list_paint_context());
115 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_display_list));
116}
117
118TEST_F(ImageFilterLayerTest, SimpleFilterWithOffset) {
119 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 1.0f});
120 const DlRect initial_cull_rect = DlRect::MakeLTRB(0, 0, 100, 100);
121 const DlRect child_bounds = DlRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
122 const DlPath child_path = DlPath::MakeRect(child_bounds);
123 const DlPaint child_paint = DlPaint(DlColor::kYellow());
124 const DlPoint layer_offset = DlPoint(5.5, 6.5);
125 auto dl_image_filter = DlImageFilter::MakeMatrix(
127 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
128 auto layer =
129 std::make_shared<ImageFilterLayer>(dl_image_filter, layer_offset);
130 layer->Add(mock_layer);
131
132 DlMatrix child_matrix =
133 DlMatrix::MakeTranslation(layer_offset) * initial_transform;
134 const DlRect child_rounded_bounds =
135 DlRect::MakeLTRB(11.5f, 14.5f, 27.5f, 30.5f);
136
137 preroll_context()->state_stack.set_preroll_delegate(initial_cull_rect,
138 initial_transform);
139 layer->Preroll(preroll_context());
140 EXPECT_EQ(layer->paint_bounds(), child_rounded_bounds);
141 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
142 EXPECT_TRUE(layer->needs_painting(paint_context()));
143 EXPECT_EQ(mock_layer->parent_matrix(), child_matrix);
144 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(),
145 initial_cull_rect);
146
147 DisplayListBuilder expected_builder;
148 /* ImageFilterLayer::Paint() */ {
149 expected_builder.Save();
150 {
151 expected_builder.Translate(layer_offset.x, layer_offset.y);
152 DlPaint dl_paint;
153 dl_paint.setImageFilter(dl_image_filter.get());
154 expected_builder.SaveLayer(child_bounds, &dl_paint);
155 {
156 /* MockLayer::Paint() */ {
157 expected_builder.DrawPath(child_path, DlPaint(DlColor::kYellow()));
158 }
159 }
160 expected_builder.Restore();
161 }
162 expected_builder.Restore();
163 }
164 auto expected_display_list = expected_builder.Build();
165
166 layer->Paint(display_list_paint_context());
167 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_display_list));
168}
169
170TEST_F(ImageFilterLayerTest, SimpleFilterBounds) {
171 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 1.0f});
172 const DlRect child_bounds = DlRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
173 const DlPath child_path = DlPath::MakeRect(child_bounds);
174 const DlPaint child_paint = DlPaint(DlColor::kYellow());
175 const DlMatrix filter_transform = DlMatrix::MakeScale({2.0, 2.0, 1});
176
177 auto dl_image_filter = DlImageFilter::MakeMatrix(
178 filter_transform, DlImageSampling::kMipmapLinear);
179 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
180 auto layer = std::make_shared<ImageFilterLayer>(dl_image_filter);
181 layer->Add(mock_layer);
182
183 const DlRect filter_bounds = DlRect::MakeLTRB(10.0f, 12.0f, 42.0f, 44.0f);
184
185 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
186 layer->Preroll(preroll_context());
187 EXPECT_EQ(layer->paint_bounds(), filter_bounds);
188 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
189 EXPECT_TRUE(layer->needs_painting(paint_context()));
190 EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
191
192 DisplayListBuilder expected_builder;
193 /* ImageFilterLayer::Paint() */ {
194 DlPaint dl_paint;
195 dl_paint.setImageFilter(dl_image_filter.get());
196 expected_builder.SaveLayer(child_bounds, &dl_paint);
197 {
198 /* MockLayer::Paint() */ {
199 expected_builder.DrawPath(child_path, DlPaint(DlColor::kYellow()));
200 }
201 }
202 }
203 expected_builder.Restore();
204 auto expected_display_list = expected_builder.Build();
205
206 layer->Paint(display_list_paint_context());
207 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_display_list));
208}
209
210TEST_F(ImageFilterLayerTest, MultipleChildren) {
211 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 1.0f});
212 const DlRect child_bounds = DlRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f);
213 const DlPath child_path1 = DlPath::MakeRect(child_bounds);
214 const DlPath child_path2 = DlPath::MakeRect(child_bounds.Shift(3.0f, 0.0f));
215 const DlPaint child_paint1 = DlPaint(DlColor::kYellow());
216 const DlPaint child_paint2 = DlPaint(DlColor::kCyan());
217 auto dl_image_filter = DlImageFilter::MakeMatrix(
219 auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
220 auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
221 auto layer = std::make_shared<ImageFilterLayer>(dl_image_filter);
222 layer->Add(mock_layer1);
223 layer->Add(mock_layer2);
224
225 const DlRect children_bounds =
226 child_path1.GetBounds().Union(child_path2.GetBounds());
227 DlRect children_rounded_bounds =
228 DlRect::RoundOut(children_bounds).Shift(1.0f, 2.0f);
229
230 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
231 layer->Preroll(preroll_context());
232 EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.GetBounds());
233 EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.GetBounds());
234 EXPECT_EQ(layer->paint_bounds(), children_rounded_bounds);
235 EXPECT_EQ(layer->child_paint_bounds(), children_bounds);
236 EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
237 EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
238 EXPECT_TRUE(layer->needs_painting(paint_context()));
239 EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform);
240 EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform);
241
242 DisplayListBuilder expected_builder;
243 /* ImageFilterLayer::Paint() */ {
244 DlPaint dl_paint;
245 dl_paint.setImageFilter(dl_image_filter.get());
246 expected_builder.SaveLayer(children_bounds, &dl_paint);
247 {
248 /* MockLayer::Paint() */ {
249 expected_builder.DrawPath(child_path1, DlPaint(DlColor::kYellow()));
250 }
251 /* MockLayer::Paint() */ {
252 expected_builder.DrawPath(child_path2, DlPaint(DlColor::kCyan()));
253 }
254 }
255 }
256 expected_builder.Restore();
257 auto expected_display_list = expected_builder.Build();
258
259 layer->Paint(display_list_paint_context());
260 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_display_list));
261}
262
264 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 1.0f});
265 const DlRect child_bounds = DlRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f);
266 const DlPath child_path1 = DlPath::MakeRect(child_bounds);
267 const DlPath child_path2 = DlPath::MakeRect(child_bounds.Shift(3.0f, 0.0f));
268 const DlPaint child_paint1 = DlPaint(DlColor::kYellow());
269 const DlPaint child_paint2 = DlPaint(DlColor::kCyan());
270 auto dl_image_filter1 = DlImageFilter::MakeMatrix(
272 auto dl_image_filter2 = DlImageFilter::MakeMatrix(
274 auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
275 auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
276 auto layer1 = std::make_shared<ImageFilterLayer>(dl_image_filter1);
277 auto layer2 = std::make_shared<ImageFilterLayer>(dl_image_filter2);
278 layer2->Add(mock_layer2);
279 layer1->Add(mock_layer1);
280 layer1->Add(layer2);
281
282 // Filter(translate by 1, 2)
283 // / |
284 // Mock(child_path1) Filter(translate by 3, 4)
285 // |
286 // Mock(child_path2 (shifted (3, 0)))
287
288 DlRect filter2_bounds = DlRect::RoundOut( //
289 child_path2
290 .GetBounds() // includes shift(3, 0) on child_path2
291 .Shift(3.0, 4.0) // filter2 translation
292 );
293 const DlRect filter1_child_bounds =
294 child_path1.GetBounds().Union(filter2_bounds);
295 DlRect filter1_bounds = DlRect::RoundOut( //
296 filter1_child_bounds // no shift on child_path1
297 .Shift(1.0, 2.0) // filter1 translation
298 );
299
300 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
301 layer1->Preroll(preroll_context());
302 EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.GetBounds());
303 EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.GetBounds());
304 EXPECT_EQ(layer1->paint_bounds(), filter1_bounds);
305 EXPECT_EQ(layer1->child_paint_bounds(), filter1_child_bounds);
306 EXPECT_EQ(layer2->paint_bounds(), filter2_bounds);
307 EXPECT_EQ(layer2->child_paint_bounds(), child_path2.GetBounds());
308 EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
309 EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
310 EXPECT_TRUE(layer1->needs_painting(paint_context()));
311 EXPECT_TRUE(layer2->needs_painting(paint_context()));
312 EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform);
313 EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform);
314
315 DisplayListBuilder expected_builder;
316 /* ImageFilterLayer::Paint() */ {
317 DlPaint dl_paint;
318 dl_paint.setImageFilter(dl_image_filter1.get());
319 expected_builder.SaveLayer(filter1_child_bounds, &dl_paint);
320 {
321 /* MockLayer::Paint() */ {
322 expected_builder.DrawPath(child_path1, DlPaint(DlColor::kYellow()));
323 }
324 /* ImageFilterLayer::Paint() */ {
325 DlPaint child_paint;
326 child_paint.setImageFilter(dl_image_filter2.get());
327 expected_builder.SaveLayer(child_path2.GetBounds(), &child_paint);
328 /* MockLayer::Paint() */ {
329 expected_builder.DrawPath(child_path2, DlPaint(DlColor::kCyan()));
330 }
331 expected_builder.Restore();
332 }
333 }
334 }
335 expected_builder.Restore();
336 auto expected_display_list = expected_builder.Build();
337
338 layer1->Paint(display_list_paint_context());
339 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_display_list));
340}
341
342TEST_F(ImageFilterLayerTest, Readback) {
343 auto dl_image_filter = DlImageFilter::MakeMatrix(
345
346 // ImageFilterLayer does not read from surface
347 auto layer = std::make_shared<ImageFilterLayer>(dl_image_filter);
348 preroll_context()->surface_needs_readback = false;
349 layer->Preroll(preroll_context());
350 EXPECT_FALSE(preroll_context()->surface_needs_readback);
351
352 // ImageFilterLayer blocks child with readback
353 auto mock_layer = std::make_shared<MockLayer>(DlPath(), DlPaint());
354 mock_layer->set_fake_reads_surface(true);
355 layer->Add(mock_layer);
356 preroll_context()->surface_needs_readback = false;
357 layer->Preroll(preroll_context());
358 EXPECT_FALSE(preroll_context()->surface_needs_readback);
359}
360
361TEST_F(ImageFilterLayerTest, CacheChild) {
362 auto dl_image_filter = DlImageFilter::MakeMatrix(
364 auto initial_transform = DlMatrix::MakeTranslation({50.0f, 25.5f});
365 auto other_transform = DlMatrix::MakeScale({1.0f, 2.0f, 1.0f});
366 const DlPath child_path = DlPath::MakeRect(DlRect::MakeWH(5.0f, 5.0f));
367 auto mock_layer = std::make_shared<MockLayer>(child_path);
368 auto layer = std::make_shared<ImageFilterLayer>(dl_image_filter);
369 layer->Add(mock_layer);
370
371 DlMatrix cache_ctm = initial_transform;
372 DisplayListBuilder cache_canvas;
373 cache_canvas.Transform(cache_ctm);
374 DisplayListBuilder other_canvas;
375 other_canvas.Transform(other_transform);
376 DlPaint paint;
377
378 use_mock_raster_cache();
379 const auto* cacheable_image_filter_item = layer->raster_cache_item();
380
381 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
382 // ImageFilterLayer default cache itself.
383 EXPECT_EQ(cacheable_image_filter_item->cache_state(),
385 EXPECT_FALSE(cacheable_image_filter_item->Draw(paint_context(), &paint));
386
387 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
388 layer->Preroll(preroll_context());
389 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
390
391 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
392 // The layer_cache_item's strategy is Children, mean we will must cache
393 // his children
394 EXPECT_EQ(cacheable_image_filter_item->cache_state(),
396 // We assert here because the lines after it will crash if this is not true
397 // (It will generally only fail if another EXPECT test above also fails)
398 ASSERT_TRUE(cacheable_image_filter_item->GetId().has_value());
399 EXPECT_TRUE(raster_cache()->Draw(cacheable_image_filter_item->GetId().value(),
400 cache_canvas, &paint));
401 EXPECT_FALSE(raster_cache()->Draw(
402 cacheable_image_filter_item->GetId().value(), other_canvas, &paint));
403}
404
405TEST_F(ImageFilterLayerTest, CacheChildren) {
406 auto dl_image_filter = DlImageFilter::MakeMatrix(
408 auto initial_transform = DlMatrix::MakeTranslation({50.0f, 25.5f});
409 auto other_transform = DlMatrix::MakeScale({1.0f, 2.0f, 1.0f});
410 DlPaint paint;
411 const DlPath child_path1 = DlPath::MakeRect(DlRect::MakeWH(5.0f, 5.0f));
412 const DlPath child_path2 = DlPath::MakeRect(DlRect::MakeWH(5.0f, 5.0f));
413 auto mock_layer1 = std::make_shared<MockLayer>(child_path1);
414 auto mock_layer2 = std::make_shared<MockLayer>(child_path2);
415 auto offset = DlPoint(54, 24);
416 auto layer = std::make_shared<ImageFilterLayer>(dl_image_filter, offset);
417 layer->Add(mock_layer1);
418 layer->Add(mock_layer2);
419
420 DlMatrix cache_ctm = initial_transform;
421 DisplayListBuilder cache_canvas;
422 cache_canvas.Transform(cache_ctm);
423 DisplayListBuilder other_canvas;
424 other_canvas.Transform(other_transform);
425
426 use_mock_raster_cache();
427
428 const auto* cacheable_image_filter_item = layer->raster_cache_item();
429 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
430
431 // ImageFilterLayer default cache itself.
432 EXPECT_EQ(cacheable_image_filter_item->cache_state(),
434 EXPECT_FALSE(cacheable_image_filter_item->Draw(paint_context(), &paint));
435
436 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
437 layer->Preroll(preroll_context());
438 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
439
440 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
441
442 // The layer_cache_item's strategy is Children, mean we will must cache his
443 // children
444 EXPECT_EQ(cacheable_image_filter_item->cache_state(),
446 // We assert here because the lines after it will crash if this is not true
447 // (It will generally only fail if another EXPECT test above also fails)
448 ASSERT_TRUE(cacheable_image_filter_item->GetId().has_value());
449 EXPECT_TRUE(raster_cache()->Draw(cacheable_image_filter_item->GetId().value(),
450 cache_canvas, &paint));
451 EXPECT_FALSE(raster_cache()->Draw(
452 cacheable_image_filter_item->GetId().value(), other_canvas, &paint));
453
454 layer->Preroll(preroll_context());
455
456 DlMatrix snapped_matrix = DlMatrix::MakeTranslation(offset.Round());
457 DlMatrix cache_matrix = snapped_matrix * initial_transform;
458 auto transformed_filter = dl_image_filter->makeWithLocalMatrix(cache_matrix);
459
460 layer->Paint(display_list_paint_context());
461 DisplayListBuilder expected_builder;
462 /* (ImageFilter)layer::Paint() */ {
463 expected_builder.Save();
464 {
465 expected_builder.Translate(offset.x, offset.y);
466 // translation components already snapped to pixels, intent to
467 // use raster cache won't change them
468 DlPaint dl_paint;
469 dl_paint.setImageFilter(transformed_filter.get());
470 raster_cache()->Draw(cacheable_image_filter_item->GetId().value(),
471 expected_builder, &dl_paint);
472 }
473 expected_builder.Restore();
474 }
475 expected_builder.Restore();
476 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
477}
478
479TEST_F(ImageFilterLayerTest, CacheImageFilterLayerSelf) {
480 auto dl_image_filter = DlImageFilter::MakeMatrix(
482
483 auto initial_transform = DlMatrix::MakeTranslation({50.0f, 25.5f});
484 auto other_transform = DlMatrix::MakeScale({1.0f, 2.0f, 1.0f});
485 auto child_rect = DlRect::MakeWH(5.0f, 5.0f);
486 const DlPath child_path = DlPath::MakeRect(child_rect);
487 auto mock_layer = std::make_shared<MockLayer>(child_path);
488 auto offset = DlPoint(53.8, 24.4);
489 auto layer = std::make_shared<ImageFilterLayer>(dl_image_filter, offset);
490 layer->Add(mock_layer);
491
492 DlMatrix cache_ctm = initial_transform;
493 DisplayListBuilder cache_canvas;
494 cache_canvas.Transform(cache_ctm);
495 DisplayListBuilder other_canvas;
496 other_canvas.Transform(other_transform);
497 DlPaint paint;
498
499 DlMatrix snapped_matrix = DlMatrix::MakeTranslation(offset.Round());
500
501 use_mock_raster_cache();
502 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
503 const auto* cacheable_image_filter_item = layer->raster_cache_item();
504 // frame 1.
505 layer->Preroll(preroll_context());
506
507 layer->Paint(display_list_paint_context());
508 {
509 DisplayListBuilder expected_builder;
510 /* (ImageFilter)layer::Paint */ {
511 expected_builder.Save();
512 {
513 expected_builder.Translate(offset.x, offset.y);
514 // Snap to pixel translation due to use of raster cache
515 expected_builder.TransformReset();
516 expected_builder.Transform(snapped_matrix);
517 DlPaint save_paint = DlPaint().setImageFilter(dl_image_filter);
518 expected_builder.SaveLayer(child_rect, &save_paint);
519 {
520 /* mock_layer::Paint */ {
521 expected_builder.DrawPath(child_path, DlPaint());
522 }
523 }
524 expected_builder.Restore();
525 }
526 expected_builder.Restore();
527 }
528 EXPECT_TRUE(
529 DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
530 }
531
532 // frame 2.
533 layer->Preroll(preroll_context());
534 layer->Paint(display_list_paint_context());
535 // frame 3.
536 layer->Preroll(preroll_context());
537 layer->Paint(display_list_paint_context());
538
539 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
540 // frame1,2 cache the ImageFilter's children layer, frame3 cache the
541 // ImageFilterLayer
542 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)2);
543
544 // ImageFilterLayer default cache itself.
545 EXPECT_EQ(cacheable_image_filter_item->cache_state(),
547 EXPECT_EQ(cacheable_image_filter_item->GetId(),
548 RasterCacheKeyID(layer->unique_id(), RasterCacheKeyType::kLayer));
549 EXPECT_TRUE(raster_cache()->Draw(cacheable_image_filter_item->GetId().value(),
550 cache_canvas, &paint));
551 EXPECT_FALSE(raster_cache()->Draw(
552 cacheable_image_filter_item->GetId().value(), other_canvas, &paint));
553
554 layer->Preroll(preroll_context());
555
556 reset_display_list();
557 layer->Paint(display_list_paint_context());
558 {
559 DisplayListBuilder expected_builder;
560 /* (ImageFilter)layer::Paint */ {
561 expected_builder.Save();
562 {
563 EXPECT_TRUE(
564 raster_cache()->Draw(cacheable_image_filter_item->GetId().value(),
565 expected_builder, nullptr));
566 }
567 expected_builder.Restore();
568 }
569 EXPECT_TRUE(
570 DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
571 }
572}
573
574TEST_F(ImageFilterLayerTest, OpacityInheritance) {
575 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 1.0f});
576 const DlRect child_bounds = DlRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
577 const DlPath child_path = DlPath::MakeRect(child_bounds);
578 const DlPaint child_paint = DlPaint(DlColor::kYellow());
579 auto dl_image_filter = DlImageFilter::MakeMatrix(
581
582 // The mock_layer child will not be compatible with opacity
583 auto mock_layer = MockLayer::Make(child_path, child_paint);
584 auto image_filter_layer = std::make_shared<ImageFilterLayer>(dl_image_filter);
585 image_filter_layer->Add(mock_layer);
586
587 PrerollContext* context = preroll_context();
588 context->state_stack.set_preroll_delegate(initial_transform);
589 image_filter_layer->Preroll(preroll_context());
590 // ImageFilterLayers can always inherit opacity whether or not their
591 // children are compatible.
592 EXPECT_EQ(context->renderable_state_flags,
595
596 int opacity_alpha = 0x7F;
597 DlPoint offset = DlPoint(10, 10);
598 auto opacity_layer = std::make_shared<OpacityLayer>(opacity_alpha, offset);
599 opacity_layer->Add(image_filter_layer);
600 context->state_stack.set_preroll_delegate(DlMatrix());
601 opacity_layer->Preroll(context);
602 EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
603
604 DisplayListBuilder expected_builder;
605 /* OpacityLayer::Paint() */ {
606 expected_builder.Save();
607 {
608 expected_builder.Translate(offset.x, offset.y);
609 /* ImageFilterLayer::Paint() */ {
610 DlPaint image_filter_paint;
611 image_filter_paint.setColor(DlColor(opacity_alpha << 24));
612 image_filter_paint.setImageFilter(dl_image_filter.get());
613 expected_builder.SaveLayer(child_path.GetBounds(), &image_filter_paint);
614 /* MockLayer::Paint() */ {
615 expected_builder.DrawPath(child_path,
616 DlPaint(child_paint.getColor()));
617 }
618 expected_builder.Restore();
619 }
620 }
621 expected_builder.Restore();
622 }
623
624 opacity_layer->Paint(display_list_paint_context());
625 EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list()));
626}
627
629
631 auto dl_blur_filter = DlImageFilter::MakeBlur(10, 10, DlTileMode::kClamp);
632 {
633 // tests later assume 30px paint area, fail early if that's not the case
634 DlIRect input_bounds;
635 dl_blur_filter->get_input_device_bounds(DlIRect::MakeWH(10, 10), DlMatrix(),
636 input_bounds);
637 EXPECT_EQ(input_bounds, DlIRect::MakeLTRB(-30, -30, 40, 40));
638 }
639
640 MockLayerTree l1;
641 auto filter_layer = std::make_shared<ImageFilterLayer>(dl_blur_filter);
642 auto path = DlPath::MakeRectLTRB(100, 100, 110, 110);
643 filter_layer->Add(std::make_shared<MockLayer>(path));
644 l1.root()->Add(filter_layer);
645
646 auto damage = DiffLayerTree(l1, MockLayerTree());
647 EXPECT_EQ(damage.frame_damage, DlIRect::MakeLTRB(70, 70, 140, 140));
648
649 MockLayerTree l2;
650 auto scale =
651 std::make_shared<TransformLayer>(DlMatrix::MakeScale({2.0f, 2.0f, 1.0f}));
652 scale->Add(filter_layer);
653 l2.root()->Add(scale);
654
655 damage = DiffLayerTree(l2, MockLayerTree());
656 EXPECT_EQ(damage.frame_damage, DlIRect::MakeLTRB(140, 140, 280, 280));
657
658 MockLayerTree l3;
659 l3.root()->Add(scale);
660
661 // path outside of ImageFilterLayer
662 auto path1 = DlPath::MakeRectLTRB(130, 130, 140, 140);
663 l3.root()->Add(std::make_shared<MockLayer>(path1));
664 damage = DiffLayerTree(l3, l2);
665 EXPECT_EQ(damage.frame_damage, DlIRect::MakeLTRB(130, 130, 140, 140));
666
667 // path intersecting ImageFilterLayer, shouldn't trigger entire
668 // ImageFilterLayer repaint
669 MockLayerTree l4;
670 l4.root()->Add(scale);
671 auto path2 = DlPath::MakeRectLTRB(130, 130, 141, 141);
672 l4.root()->Add(std::make_shared<MockLayer>(path2));
673 damage = DiffLayerTree(l4, l3);
674 EXPECT_EQ(damage.frame_damage, DlIRect::MakeLTRB(130, 130, 141, 141));
675}
676
677TEST_F(ImageFilterLayerDiffTest, ImageFilterLayerInflatestChildSize) {
678 auto dl_blur_filter = DlImageFilter::MakeBlur(10, 10, DlTileMode::kClamp);
679
680 {
681 // tests later assume 30px paint area, fail early if that's not the case
682 DlIRect input_bounds;
683 dl_blur_filter->get_input_device_bounds(DlIRect::MakeWH(10, 10), DlMatrix(),
684 input_bounds);
685 EXPECT_EQ(input_bounds, DlIRect::MakeLTRB(-30, -30, 40, 40));
686 }
687
688 MockLayerTree l1;
689
690 // Use nested filter layers to check if both contribute to child bounds
691 auto filter_layer_1_1 = std::make_shared<ImageFilterLayer>(dl_blur_filter);
692 auto filter_layer_1_2 = std::make_shared<ImageFilterLayer>(dl_blur_filter);
693 filter_layer_1_1->Add(filter_layer_1_2);
694 auto path = DlPath::MakeRectLTRB(100, 100, 110, 110);
695 filter_layer_1_2->Add(
696 std::make_shared<MockLayer>(path, DlPaint(DlColor::kYellow())));
697 l1.root()->Add(filter_layer_1_1);
698
699 // second layer tree with identical filter layers but different child layer
700 MockLayerTree l2;
701 auto filter_layer2_1 = std::make_shared<ImageFilterLayer>(dl_blur_filter);
702 filter_layer2_1->AssignOldLayer(filter_layer_1_1.get());
703 auto filter_layer2_2 = std::make_shared<ImageFilterLayer>(dl_blur_filter);
704 filter_layer2_2->AssignOldLayer(filter_layer_1_2.get());
705 filter_layer2_1->Add(filter_layer2_2);
706 filter_layer2_2->Add(
707 std::make_shared<MockLayer>(path, DlPaint(DlColor::kRed())));
708 l2.root()->Add(filter_layer2_1);
709
710 DiffLayerTree(l1, MockLayerTree());
711 auto damage = DiffLayerTree(l2, l1);
712
713 // ensure that filter properly inflated child size
714 EXPECT_EQ(damage.frame_damage, DlIRect::MakeLTRB(40, 40, 170, 170));
715}
716
717TEST_F(ImageFilterLayerTest, EmptyFilterWithOffset) {
718 const DlRect child_bounds = DlRect::MakeLTRB(10.0f, 11.0f, 19.0f, 20.0f);
719 const DlPath child_path = DlPath::MakeRect(child_bounds);
720 const DlPaint child_paint = DlPaint(DlColor::kYellow());
721 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
722 const DlPoint offset = DlPoint(5.0f, 6.0f);
723 auto layer = std::make_shared<ImageFilterLayer>(nullptr, offset);
724 layer->Add(mock_layer);
725
726 layer->Preroll(preroll_context());
727 EXPECT_EQ(layer->paint_bounds(), child_bounds.Shift(offset));
728}
729
730} // namespace testing
731} // namespace flutter
732
733// NOLINTEND(bugprone-unchecked-optional-access)
virtual void Add(std::shared_ptr< Layer > layer)
void TransformReset() override
void SaveLayer(const std::optional< DlRect > &bounds, const DlPaint *paint=nullptr, const DlImageFilter *backdrop=nullptr, std::optional< int64_t > backdrop_id=std::nullopt) override
void Translate(DlScalar tx, DlScalar ty) override
sk_sp< DisplayList > Build()
Definition dl_builder.cc:66
void DrawPath(const DlPath &path, const DlPaint &paint) override
void Transform(const DlMatrix &matrix) override
static std::shared_ptr< DlImageFilter > MakeBlur(DlScalar sigma_x, DlScalar sigma_y, DlTileMode tile_mode)
static std::shared_ptr< DlImageFilter > MakeMatrix(const DlMatrix &matrix, DlImageSampling sampling)
DlPaint & setColor(DlColor color)
Definition dl_paint.h:70
DlPaint & setImageFilter(std::nullptr_t filter)
Definition dl_paint.h:167
static DlPath MakeRectLTRB(DlScalar left, DlScalar top, DlScalar right, DlScalar bottom)
Definition dl_path.cc:43
static DlPath MakeRect(const DlRect &rect)
Definition dl_path.cc:39
static constexpr int kCallerCanApplyColorFilter
static constexpr int kCallerCanApplyOpacity
static void TryToRasterCache(const std::vector< RasterCacheItem * > &raster_cached_entries, const PaintContext *paint_context, bool ignore_raster_cache=false)
Definition layer_tree.cc:67
static std::shared_ptr< MockLayer > Make(const DlPath &path, DlPaint paint=DlPaint())
Definition mock_layer.h:30
TEST_F(DisplayListTest, Defaults)
LayerTestBase<::testing::Test > LayerTest
Definition layer_test.h:177
bool DisplayListsEQ_Verbose(const DisplayList *a, const DisplayList *b)
impeller::Matrix DlMatrix
impeller::Rect DlRect
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switch_defs.h:52
impeller::Point DlPoint
flutter::DlPath DlPath
flutter::DlColor DlColor
flutter::DlPaint DlPaint
static constexpr DlColor kYellow()
Definition dl_color.h:76
static constexpr DlColor kRed()
Definition dl_color.h:71
static constexpr DlColor kCyan()
Definition dl_color.h:74
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
constexpr Matrix Translate(const Vector3 &t) const
Definition matrix.h:263
constexpr Quad Transform(const Quad &quad) const
Definition matrix.h:623
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
static constexpr TRect MakeWH(Type width, Type height)
Definition rect.h:140
constexpr TRect Union(const TRect &o) const
Definition rect.h:513
RoundOut(const TRect< U > &r)
Definition rect.h:679
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition rect.h:602
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129