Flutter Engine
 
Loading...
Searching...
No Matches
opacity_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
18#include "flutter/fml/macros.h"
20#include "gtest/gtest.h"
21
22// TODO(zanderso): https://github.com/flutter/flutter/issues/127701
23// NOLINTBEGIN(bugprone-unchecked-optional-access)
24
25namespace flutter {
26namespace testing {
27
29
30#ifndef NDEBUG
31TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) {
32 auto mock_layer = std::make_shared<MockLayer>(DlPath());
33 auto layer =
34 std::make_shared<OpacityLayer>(DlColor::toAlpha(1.0f), DlPoint());
35 layer->Add(mock_layer);
36
37 layer->Preroll(preroll_context());
38 EXPECT_EQ(mock_layer->paint_bounds(), DlPath().GetBounds());
39 EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
40 EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds());
41 EXPECT_FALSE(mock_layer->needs_painting(paint_context()));
42 EXPECT_FALSE(layer->needs_painting(paint_context()));
43
44 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
45 "needs_painting\\(context\\)");
46}
47
48TEST_F(OpacityLayerTest, PaintBeforePrerollDies) {
49 const DlPath child_path = DlPath::MakeRectLTRB(5.0f, 6.0f, 20.5f, 21.5f);
50 auto mock_layer = std::make_shared<MockLayer>(child_path);
51 auto layer =
52 std::make_shared<OpacityLayer>(DlColor::toAlpha(1.0f), DlPoint());
53 layer->Add(mock_layer);
54
55 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
56 "needs_painting\\(context\\)");
57}
58#endif
59
60TEST_F(OpacityLayerTest, TranslateChildren) {
61 const DlPath child_path1 = DlPath::MakeRectLTRB(10.0f, 10.0f, 20.0f, 20.f);
62 DlPaint child_paint1 = DlPaint(DlColor::kMidGrey());
63 auto layer =
64 std::make_shared<OpacityLayer>(DlColor::toAlpha(0.5f), DlPoint(10, 10));
65 auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
66 layer->Add(mock_layer1);
67
68 auto initial_transform = DlMatrix::MakeScale({2.0f, 2.0f, 1.0f});
69 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
70 layer->Preroll(preroll_context());
71
72 DlRect layer_bounds = mock_layer1->paint_bounds().TransformAndClipBounds(
73 mock_layer1->parent_matrix());
74
75 EXPECT_EQ(layer_bounds, DlRect::MakeXYWH(40, 40, 20, 20));
76}
77
78TEST_F(OpacityLayerTest, CacheChild) {
79 const uint8_t alpha_half = DlColor::toAlpha(0.5f);
80 auto initial_transform = DlMatrix::MakeTranslation({50.0f, 25.5f});
81 auto other_transform = DlMatrix::MakeScale({1.0f, 2.0f, 1.0f});
82 const DlPath child_path = DlPath::MakeRect(DlRect::MakeWH(5.0f, 5.0f));
83 auto mock_layer = std::make_shared<MockLayer>(child_path);
84 auto layer = std::make_shared<OpacityLayer>(alpha_half, DlPoint());
85 layer->Add(mock_layer);
86 DlPaint paint;
87
88 DlMatrix cache_ctm = initial_transform;
89 DisplayListBuilder cache_canvas;
90 cache_canvas.Transform(cache_ctm);
91 DisplayListBuilder other_canvas;
92 other_canvas.Transform(other_transform);
93
94 use_mock_raster_cache();
95
96 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
97
98 const auto* cacheable_opacity_item = layer->raster_cache_item();
99
100 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
101 EXPECT_EQ(cacheable_opacity_item->cache_state(),
103 EXPECT_FALSE(cacheable_opacity_item->GetId().has_value());
104
105 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
106 layer->Preroll(preroll_context());
107 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
108
109 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
110
111 EXPECT_EQ(cacheable_opacity_item->cache_state(),
113 EXPECT_EQ(
114 cacheable_opacity_item->GetId().value(),
115 RasterCacheKeyID(RasterCacheKeyID::LayerChildrenIds(layer.get()).value(),
117 EXPECT_FALSE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(),
118 other_canvas, &paint));
119 EXPECT_TRUE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(),
120 cache_canvas, &paint));
121}
122
123TEST_F(OpacityLayerTest, CacheChildren) {
124 const uint8_t alpha_half = DlColor::toAlpha(0.5f);
125 auto initial_transform = DlMatrix::MakeTranslation({50.0f, 25.5f});
126 auto other_transform = DlMatrix::MakeScale({1.0f, 2.0f, 1.0f});
127 const DlPath child_path1 = DlPath::MakeRect(DlRect::MakeWH(5.0f, 5.0f));
128 const DlPath child_path2 = DlPath::MakeRect(DlRect::MakeWH(5.0f, 5.0f));
129 DlPaint paint;
130 auto mock_layer1 = std::make_shared<MockLayer>(child_path1);
131 auto mock_layer2 = std::make_shared<MockLayer>(child_path2);
132 auto layer = std::make_shared<OpacityLayer>(alpha_half, DlPoint());
133 layer->Add(mock_layer1);
134 layer->Add(mock_layer2);
135
136 DlMatrix cache_ctm = initial_transform;
137 DisplayListBuilder cache_canvas;
138 cache_canvas.Transform(cache_ctm);
139 DisplayListBuilder other_canvas;
140 other_canvas.Transform(other_transform);
141
142 use_mock_raster_cache();
143
144 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
145
146 const auto* cacheable_opacity_item = layer->raster_cache_item();
147
148 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
149 EXPECT_EQ(cacheable_opacity_item->cache_state(),
151 EXPECT_FALSE(cacheable_opacity_item->GetId().has_value());
152
153 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
154 layer->Preroll(preroll_context());
155 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
156
157 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
158
159 EXPECT_EQ(cacheable_opacity_item->cache_state(),
161 EXPECT_EQ(
162 cacheable_opacity_item->GetId().value(),
163 RasterCacheKeyID(RasterCacheKeyID::LayerChildrenIds(layer.get()).value(),
165 EXPECT_FALSE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(),
166 other_canvas, &paint));
167 EXPECT_TRUE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(),
168 cache_canvas, &paint));
169}
170
171TEST_F(OpacityLayerTest, ShouldNotCacheChildren) {
172 DlPaint paint;
173 auto opacity_layer = std::make_shared<OpacityLayer>(128, DlPoint(20, 20));
174 auto mock_layer = MockLayer::MakeOpacityCompatible(DlPath());
175 opacity_layer->Add(mock_layer);
176
177 PrerollContext* context = preroll_context();
178
179 use_mock_raster_cache();
180
181 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
182
183 const auto* cacheable_opacity_item = opacity_layer->raster_cache_item();
184
185 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
186 EXPECT_EQ(cacheable_opacity_item->cache_state(),
188 EXPECT_FALSE(cacheable_opacity_item->GetId().has_value());
189
190 opacity_layer->Preroll(preroll_context());
191
192 EXPECT_EQ(context->renderable_state_flags,
194 EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
195 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
196 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
197 EXPECT_EQ(cacheable_opacity_item->cache_state(),
199 EXPECT_FALSE(cacheable_opacity_item->Draw(paint_context(), &paint));
200}
201
203 const DlPath child_path = DlPath::MakeRect(DlRect::MakeWH(5.0f, 5.0f));
204 const DlPoint layer_offset = DlPoint(0.5f, 1.5f);
205 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 0.5f});
206 const DlMatrix layer_transform = DlMatrix::MakeTranslation(layer_offset);
207 const DlPaint child_paint = DlPaint(DlColor::kGreen());
208 const DlRect expected_layer_bounds =
209 child_path.GetBounds().TransformAndClipBounds(layer_transform);
210 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
211 auto layer =
212 std::make_shared<OpacityLayer>(DlColor::toAlpha(1.0f), layer_offset);
213 layer->Add(mock_layer);
214
215 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
216 layer->Preroll(preroll_context());
217 EXPECT_EQ(mock_layer->paint_bounds(), child_path.GetBounds());
218 EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds);
219 EXPECT_EQ(layer->child_paint_bounds(), child_path.GetBounds());
220 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
221 EXPECT_TRUE(layer->needs_painting(paint_context()));
222 EXPECT_EQ(mock_layer->parent_matrix(), initial_transform * layer_transform);
223 EXPECT_EQ(mock_layer->parent_mutators(),
224 std::vector({Mutator(layer_transform)}));
225
226 DisplayListBuilder expected_builder;
227 /* (Opacity)layer::Paint */ {
228 expected_builder.Save();
229 {
230 expected_builder.Translate(layer_offset.x, layer_offset.y);
231 // Opaque alpha needs no SaveLayer, just recurse into painting mock_layer
232 /* mock_layer::Paint */ {
233 expected_builder.DrawPath(child_path, child_paint);
234 }
235 }
236 expected_builder.Restore();
237 }
238 layer->Paint(display_list_paint_context());
239 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
240}
241
242TEST_F(OpacityLayerTest, FullyTransparent) {
243 const DlRect child_bounds = DlRect::MakeWH(5.0f, 5.0f);
244 const DlPath child_path = DlPath::MakeRect(child_bounds);
245 const DlPoint layer_offset = DlPoint(0.5f, 1.5f);
246 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 0.5f});
247 const DlMatrix layer_transform = DlMatrix::MakeTranslation(layer_offset);
248 const DlPaint child_paint = DlPaint(DlColor::kGreen());
249 const DlRect expected_layer_bounds =
250 child_path.GetBounds().TransformAndClipBounds(layer_transform);
251 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
252 auto layer = std::make_shared<OpacityLayer>(0u, layer_offset);
253 layer->Add(mock_layer);
254
255 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
256 layer->Preroll(preroll_context());
257 EXPECT_EQ(mock_layer->paint_bounds(), child_path.GetBounds());
258 EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds);
259 EXPECT_EQ(layer->child_paint_bounds(), child_path.GetBounds());
260 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
261 EXPECT_TRUE(layer->needs_painting(paint_context()));
262 EXPECT_EQ(mock_layer->parent_matrix(), initial_transform * layer_transform);
263 EXPECT_EQ(mock_layer->parent_mutators(),
264 std::vector({Mutator(layer_transform), Mutator(0u)}));
265
266 DisplayListBuilder expected_builder;
267 /* (Opacity)layer::Paint */ {
268 expected_builder.Save();
269 {
270 expected_builder.Translate(layer_offset.x, layer_offset.y);
271 /* (Opacity)layer::PaintChildren */ {
272 DlPaint save_paint(DlPaint().setOpacity(layer->opacity()));
273 expected_builder.SaveLayer(child_bounds, &save_paint);
274 /* mock_layer::Paint */ {
275 expected_builder.DrawPath(child_path, child_paint);
276 }
277 expected_builder.Restore();
278 }
279 }
280 expected_builder.Restore();
281 }
282 layer->Paint(display_list_paint_context());
283 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
284}
285
286TEST_F(OpacityLayerTest, HalfTransparent) {
287 const DlPath child_path = DlPath::MakeRect(DlRect::MakeWH(5.0f, 5.0f));
288 const DlPoint layer_offset = DlPoint(0.5f, 1.5f);
289 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 0.5f});
290 const DlMatrix layer_transform = DlMatrix::MakeTranslation(layer_offset);
291 const DlPaint child_paint = DlPaint(DlColor::kGreen());
292 const DlRect expected_layer_bounds =
293 child_path.GetBounds().TransformAndClipBounds(layer_transform);
294 const uint8_t alpha_half = DlColor::toAlpha(0.5f);
295 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
296 auto layer = std::make_shared<OpacityLayer>(alpha_half, layer_offset);
297 layer->Add(mock_layer);
298
299 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
300 layer->Preroll(preroll_context());
301 EXPECT_EQ(mock_layer->paint_bounds(), child_path.GetBounds());
302 EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds);
303 EXPECT_EQ(layer->child_paint_bounds(), child_path.GetBounds());
304 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
305 EXPECT_TRUE(layer->needs_painting(paint_context()));
306 EXPECT_EQ(mock_layer->parent_matrix(), initial_transform * layer_transform);
307 EXPECT_EQ(mock_layer->parent_mutators(),
308 std::vector({Mutator(layer_transform), Mutator(alpha_half)}));
309
310 DlRect opacity_bounds =
311 DlRect::RoundOut(expected_layer_bounds.Shift(-layer_offset));
312 DlPaint save_paint = DlPaint().setAlpha(alpha_half);
313 DlPaint child_dl_paint = DlPaint(DlColor::kGreen());
314
315 auto expected_builder = DisplayListBuilder();
316 /* (Opacity)layer::Paint */ {
317 expected_builder.Save();
318 expected_builder.Translate(layer_offset.x, layer_offset.y);
319 /* (Opacity)layer::PaintChildren */ {
320 expected_builder.SaveLayer(opacity_bounds, &save_paint);
321 /* mock_layer::Paint */ {
322 expected_builder.DrawPath(child_path, child_dl_paint);
323 }
324 expected_builder.Restore();
325 }
326 expected_builder.Restore();
327 }
328 sk_sp<DisplayList> expected_display_list = expected_builder.Build();
329
330 layer->Paint(display_list_paint_context());
331 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_display_list));
332}
333
334TEST_F(OpacityLayerTest, Nested) {
335 const DlPath child1_path = DlPath::MakeRect(DlRect::MakeWH(5.0f, 6.0f));
336 const DlPath child2_path = DlPath::MakeRect(DlRect::MakeWH(2.0f, 7.0f));
337 const DlPath child3_path = DlPath::MakeRect(DlRect::MakeWH(6.0f, 6.0f));
338 const DlPoint layer1_offset = DlPoint(0.5f, 1.5f);
339 const DlPoint layer2_offset = DlPoint(2.5f, 0.5f);
340 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 0.5f});
341 const DlMatrix layer1_transform = DlMatrix::MakeTranslation(layer1_offset);
342 const DlMatrix layer2_transform = DlMatrix::MakeTranslation(layer2_offset);
343 const DlPaint child1_paint = DlPaint(DlColor::kRed());
344 const DlPaint child2_paint = DlPaint(DlColor::kBlue());
345 const DlPaint child3_paint = DlPaint(DlColor::kGreen());
346 const uint8_t alpha1 = 155u;
347 const uint8_t alpha2 = 224u;
348 auto mock_layer1 = std::make_shared<MockLayer>(child1_path, child1_paint);
349 auto mock_layer2 = std::make_shared<MockLayer>(child2_path, child2_paint);
350 auto mock_layer3 = std::make_shared<MockLayer>(child3_path, child3_paint);
351 auto layer1 = std::make_shared<OpacityLayer>(alpha1, layer1_offset);
352 auto layer2 = std::make_shared<OpacityLayer>(alpha2, layer2_offset);
353 layer2->Add(mock_layer2);
354 layer1->Add(mock_layer1);
355 layer1->Add(layer2);
356 layer1->Add(mock_layer3); // Ensure something is processed after recursion
357
358 const DlRect expected_layer2_bounds =
359 child2_path.GetBounds().TransformAndClipBounds(layer2_transform);
360 const DlRect layer1_child_bounds = //
361 expected_layer2_bounds //
362 .Union(child1_path.GetBounds()) //
363 .Union(child3_path.GetBounds());
364 const DlRect expected_layer1_bounds =
365 layer1_child_bounds.TransformAndClipBounds(layer1_transform);
366 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
367 layer1->Preroll(preroll_context());
368 EXPECT_EQ(mock_layer1->paint_bounds(), child1_path.GetBounds());
369 EXPECT_EQ(mock_layer2->paint_bounds(), child2_path.GetBounds());
370 EXPECT_EQ(mock_layer3->paint_bounds(), child3_path.GetBounds());
371 EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds);
372 EXPECT_EQ(layer1->child_paint_bounds(), layer1_child_bounds);
373 EXPECT_EQ(layer2->paint_bounds(), expected_layer2_bounds);
374 EXPECT_EQ(layer2->child_paint_bounds(), child2_path.GetBounds());
375 EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
376 EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
377 EXPECT_TRUE(mock_layer3->needs_painting(paint_context()));
378 EXPECT_TRUE(layer1->needs_painting(paint_context()));
379 EXPECT_TRUE(layer2->needs_painting(paint_context()));
380 EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform * layer1_transform);
381 EXPECT_EQ(mock_layer1->parent_mutators(),
382 std::vector({Mutator(layer1_transform), Mutator(alpha1)}));
383 EXPECT_EQ(mock_layer2->parent_matrix(),
384 (initial_transform * layer1_transform) * layer2_transform);
385 EXPECT_EQ(mock_layer2->parent_mutators(),
386 std::vector({Mutator(layer1_transform), Mutator(alpha1),
387 Mutator(layer2_transform), Mutator(alpha2)}));
388 EXPECT_EQ(mock_layer3->parent_matrix(), initial_transform * layer1_transform);
389 EXPECT_EQ(mock_layer3->parent_mutators(),
390 std::vector({Mutator(layer1_transform), Mutator(alpha1)}));
391
392 DlRect opacity1_bounds = expected_layer1_bounds.Shift(-layer1_offset);
393 DlRect opacity2_bounds = expected_layer2_bounds.Shift(-layer2_offset);
394 DlPaint opacity1_paint = DlPaint().setAlpha(alpha1);
395 DlPaint opacity2_paint = DlPaint().setAlpha(alpha2);
396
397 DisplayListBuilder expected_builder;
398 /* (Opacity)layer1::Paint */ {
399 expected_builder.Save();
400 {
401 expected_builder.Translate(layer1_offset.x, layer1_offset.y);
402 /* (Opacity)layer1::PaintChildren */ {
403 expected_builder.SaveLayer(opacity1_bounds, &opacity1_paint);
404 /* mock_layer1::Paint */ {
405 expected_builder.DrawPath(child1_path, child1_paint);
406 }
407 /* (Opacity)layer2::Paint */ {
408 expected_builder.Save();
409 {
410 expected_builder.Translate(layer2_offset.x, layer2_offset.y);
411 /* (Opacity)layer2::PaintChidren */ {
412 expected_builder.SaveLayer(opacity2_bounds, &opacity2_paint);
413 {
414 /* mock_layer2::Paint */ {
415 expected_builder.DrawPath(child2_path, child2_paint);
416 }
417 }
418 expected_builder.Restore();
419 }
420 }
421 expected_builder.Restore();
422 }
423 /* mock_layer3::Paint */ {
424 expected_builder.DrawPath(child3_path, child3_paint);
425 }
426 expected_builder.Restore();
427 }
428 }
429 expected_builder.Restore();
430 }
431 layer1->Paint(display_list_paint_context());
432 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
433}
434
435TEST_F(OpacityLayerTest, Readback) {
436 auto layer = std::make_shared<OpacityLayer>(0xff, DlPoint());
437 layer->Add(std::make_shared<MockLayer>(DlPath()));
438
439 // OpacityLayer does not read from surface
440 preroll_context()->surface_needs_readback = false;
441 layer->Preroll(preroll_context());
442 EXPECT_FALSE(preroll_context()->surface_needs_readback);
443
444 // OpacityLayer blocks child with readback
445 auto mock_layer = std::make_shared<MockLayer>(DlPath(), DlPaint());
446 mock_layer->set_fake_reads_surface(true);
447 layer->Add(mock_layer);
448 preroll_context()->surface_needs_readback = false;
449 layer->Preroll(preroll_context());
450 EXPECT_FALSE(preroll_context()->surface_needs_readback);
451}
452
453TEST_F(OpacityLayerTest, CullRectIsTransformed) {
454 auto clip_rect_layer = std::make_shared<ClipRectLayer>(
455 DlRect::MakeLTRB(0, 0, 10, 10), Clip::kHardEdge);
456 auto opacity_layer = std::make_shared<OpacityLayer>(128u, DlPoint(20, 20));
457 auto mock_layer = std::make_shared<MockLayer>(DlPath());
458 clip_rect_layer->Add(opacity_layer);
459 opacity_layer->Add(mock_layer);
460 clip_rect_layer->Preroll(preroll_context());
461 EXPECT_EQ(mock_layer->parent_cull_rect().GetLeft(), -20);
462 EXPECT_EQ(mock_layer->parent_cull_rect().GetTop(), -20);
463}
464
465TEST_F(OpacityLayerTest, OpacityInheritanceCompatibleChild) {
466 auto opacity_layer = std::make_shared<OpacityLayer>(128u, DlPoint(20, 20));
467 auto mock_layer = MockLayer::MakeOpacityCompatible(DlPath());
468 opacity_layer->Add(mock_layer);
469
470 PrerollContext* context = preroll_context();
471 opacity_layer->Preroll(context);
472 EXPECT_EQ(context->renderable_state_flags,
474 EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
475}
476
477TEST_F(OpacityLayerTest, OpacityInheritanceIncompatibleChild) {
478 auto opacity_layer = std::make_shared<OpacityLayer>(128u, DlPoint(20, 20));
479 auto mock_layer = MockLayer::Make(DlPath());
480 opacity_layer->Add(mock_layer);
481
482 PrerollContext* context = preroll_context();
483 opacity_layer->Preroll(context);
484 EXPECT_EQ(context->renderable_state_flags,
486 EXPECT_FALSE(opacity_layer->children_can_accept_opacity());
487}
488
489TEST_F(OpacityLayerTest, OpacityInheritanceThroughContainer) {
490 auto opacity_layer = std::make_shared<OpacityLayer>(128u, DlPoint(20, 20));
491 auto container_layer = std::make_shared<ContainerLayer>();
492 auto mock_layer = MockLayer::MakeOpacityCompatible(DlPath());
493 container_layer->Add(mock_layer);
494 opacity_layer->Add(container_layer);
495
496 PrerollContext* context = preroll_context();
497 opacity_layer->Preroll(context);
498 EXPECT_EQ(context->renderable_state_flags,
500 EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
501}
502
503TEST_F(OpacityLayerTest, OpacityInheritanceThroughTransform) {
504 auto opacity_layer = std::make_shared<OpacityLayer>(128u, DlPoint(20, 20));
505 auto transformLayer =
506 std::make_shared<TransformLayer>(DlMatrix::MakeScale({2.0f, 2.0f, 1.0f}));
507 auto mock_layer = MockLayer::MakeOpacityCompatible(DlPath());
508 transformLayer->Add(mock_layer);
509 opacity_layer->Add(transformLayer);
510
511 PrerollContext* context = preroll_context();
512 opacity_layer->Preroll(context);
513 EXPECT_EQ(context->renderable_state_flags,
515 EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
516}
517
518TEST_F(OpacityLayerTest, OpacityInheritanceThroughImageFilter) {
519 auto opacity_layer = std::make_shared<OpacityLayer>(128u, DlPoint(20, 20));
520 auto filter_layer = std::make_shared<ImageFilterLayer>(
522 auto mock_layer = MockLayer::MakeOpacityCompatible(DlPath());
523 filter_layer->Add(mock_layer);
524 opacity_layer->Add(filter_layer);
525
526 PrerollContext* context = preroll_context();
527 opacity_layer->Preroll(context);
528 EXPECT_EQ(context->renderable_state_flags,
530 EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
531}
532
533TEST_F(OpacityLayerTest, OpacityInheritanceNestedWithCompatibleChild) {
534 DlPoint offset1 = DlPoint(10, 20);
535 DlPoint offset2 = DlPoint(20, 10);
536 DlPath mock_path = DlPath::MakeRectLTRB(10, 10, 20, 20);
537 auto opacity_layer_1 = std::make_shared<OpacityLayer>(128u, offset1);
538 auto opacity_layer_2 = std::make_shared<OpacityLayer>(64u, offset2);
539 auto mock_layer = MockLayer::MakeOpacityCompatible(mock_path);
540 opacity_layer_2->Add(mock_layer);
541 opacity_layer_1->Add(opacity_layer_2);
542
543 PrerollContext* context = preroll_context();
544 opacity_layer_1->Preroll(context);
545 EXPECT_EQ(context->renderable_state_flags,
547 EXPECT_TRUE(opacity_layer_1->children_can_accept_opacity());
548 EXPECT_TRUE(opacity_layer_2->children_can_accept_opacity());
549
550 DlPaint savelayer_paint;
551 DlScalar inherited_opacity = DlColor::toOpacity(128u);
552 inherited_opacity *= DlColor::toOpacity(64u);
553 savelayer_paint.setOpacity(inherited_opacity);
554
555 DisplayListBuilder expected_builder;
556 /* opacity_layer_1::Paint */ {
557 expected_builder.Save();
558 {
559 expected_builder.Translate(offset1.x, offset1.y);
560 /* opacity_layer_2::Paint */ {
561 expected_builder.Save();
562 {
563 expected_builder.Translate(offset2.x, offset2.y);
564 /* mock_layer::Paint */ {
565 expected_builder.DrawPath(mock_path,
566 DlPaint().setOpacity(inherited_opacity));
567 }
568 }
569 expected_builder.Restore();
570 }
571 }
572 expected_builder.Restore();
573 }
574
575 opacity_layer_1->Paint(display_list_paint_context());
576 EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list()));
577}
578
579TEST_F(OpacityLayerTest, OpacityInheritanceNestedWithIncompatibleChild) {
580 DlPoint offset1 = DlPoint(10, 20);
581 DlPoint offset2 = DlPoint(20, 10);
582 DlPath mock_path = DlPath::MakeRectLTRB(10, 10, 20, 20);
583 auto opacity_layer_1 = std::make_shared<OpacityLayer>(128u, offset1);
584 auto opacity_layer_2 = std::make_shared<OpacityLayer>(64u, offset2);
585 auto mock_layer = MockLayer::Make(mock_path);
586 opacity_layer_2->Add(mock_layer);
587 opacity_layer_1->Add(opacity_layer_2);
588
589 PrerollContext* context = preroll_context();
590 opacity_layer_1->Preroll(context);
591 EXPECT_EQ(context->renderable_state_flags,
593 EXPECT_TRUE(opacity_layer_1->children_can_accept_opacity());
594 EXPECT_FALSE(opacity_layer_2->children_can_accept_opacity());
595
596 DlPaint savelayer_paint;
597 DlScalar inherited_opacity = DlColor::toOpacity(128);
598 inherited_opacity *= DlColor::toOpacity(64u);
599 savelayer_paint.setOpacity(inherited_opacity);
600
601 DisplayListBuilder expected_builder;
602 /* opacity_layer_1::Paint */ {
603 expected_builder.Save();
604 {
605 expected_builder.Translate(offset1.x, offset1.y);
606 /* opacity_layer_2::Paint */ {
607 expected_builder.Save();
608 {
609 expected_builder.Translate(offset2.x, offset2.y);
610 expected_builder.SaveLayer(mock_layer->paint_bounds(),
611 &savelayer_paint);
612 /* mock_layer::Paint */ {
613 expected_builder.DrawPath(mock_path, DlPaint());
614 }
615 }
616 expected_builder.Restore();
617 }
618 }
619 expected_builder.Restore();
620 }
621
622 opacity_layer_1->Paint(display_list_paint_context());
623 EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list()));
624}
625
627
628TEST_F(OpacityLayerDiffTest, FractionalTranslation) {
629 auto picture = CreateDisplayListLayer(
630 CreateDisplayList(DlRect::MakeLTRB(10, 10, 60, 60)));
631 auto layer = CreateOpacityLater({picture}, 128u, DlPoint(0.5f, 0.5f));
632
633 MockLayerTree tree1;
634 tree1.root()->Add(layer);
635
636 auto damage = DiffLayerTree(tree1, MockLayerTree(), DlIRect(), 0, 0,
637 /*use_raster_cache=*/false);
638 EXPECT_EQ(damage.frame_damage, DlIRect::MakeLTRB(10, 10, 61, 61));
639}
640
641TEST_F(OpacityLayerDiffTest, FractionalTranslationWithRasterCache) {
642 auto picture = CreateDisplayListLayer(
643 CreateDisplayList(DlRect::MakeLTRB(10, 10, 60, 60)));
644 auto layer = CreateOpacityLater({picture}, 128u, DlPoint(0.5f, 0.5f));
645
646 MockLayerTree tree1;
647 tree1.root()->Add(layer);
648
649 auto damage = DiffLayerTree(tree1, MockLayerTree(), DlIRect(), 0, 0,
650 /*use_raster_cache=*/true);
651 EXPECT_EQ(damage.frame_damage, DlIRect::MakeLTRB(11, 11, 61, 61));
652}
653
654TEST_F(OpacityLayerTest, FullyOpaqueWithFractionalValues) {
655 use_mock_raster_cache(); // Ensure pixel-snapped alignment.
656
657 const DlPath child_path = DlPath::MakeRect(DlRect::MakeWH(5.0f, 5.0f));
658 const DlPoint layer_offset = DlPoint(0.5f, 1.5f);
659 const DlMatrix initial_transform = DlMatrix::MakeTranslation({0.5f, 0.5f});
660 const DlPaint child_paint = DlPaint(DlColor::kGreen());
661 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
662 auto layer = std::make_shared<OpacityLayer>(0xff, layer_offset);
663 layer->Add(mock_layer);
664
665 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
666 layer->Preroll(preroll_context());
667
668 auto expected_builder = DisplayListBuilder();
669 /* (Opacity)layer::Paint */ {
670 expected_builder.Save();
671 expected_builder.Translate(layer_offset.x, layer_offset.y);
672 // Opaque alpha needs no SaveLayer, just recurse into painting mock_layer
673 // but since we use the mock raster cache we pixel snap the transform
674 expected_builder.TransformReset();
675 expected_builder.Transform2DAffine(1, 0, std::round(layer_offset.x), //
676 0, 1, std::round(layer_offset.y));
677 /* mock_layer::Paint */ {
678 expected_builder.DrawPath(child_path, child_paint);
679 }
680 expected_builder.Restore();
681 }
682 sk_sp<DisplayList> expected_display_list = expected_builder.Build();
683
684 layer->Paint(display_list_paint_context());
685 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_display_list));
686}
687
688TEST_F(OpacityLayerTest, FullyTransparentDoesNotCullPlatformView) {
689 const DlPoint opacity_offset = DlPoint(0.5f, 1.5f);
690 const DlPoint view_offset = DlPoint(0.0f, 0.0f);
691 const DlSize view_size = DlSize(8.0f, 8.0f);
692 const int64_t view_id = 42;
693 auto platform_view =
694 std::make_shared<PlatformViewLayer>(view_offset, view_size, view_id);
695
696 auto opacity = std::make_shared<OpacityLayer>(0u, opacity_offset);
697 opacity->Add(platform_view);
698
699 auto embedder = MockViewEmbedder();
700 DisplayListBuilder fake_overlay_builder;
701 embedder.AddCanvas(&fake_overlay_builder);
702 preroll_context()->view_embedder = &embedder;
703 paint_context().view_embedder = &embedder;
704
705 opacity->Preroll(preroll_context());
706 EXPECT_EQ(embedder.prerolled_views(), std::vector<int64_t>({view_id}));
707
708 opacity->Paint(paint_context());
709 EXPECT_EQ(embedder.painted_views(), std::vector<int64_t>({view_id}));
710}
711
712} // namespace testing
713} // namespace flutter
714
715// NOLINTEND(bugprone-unchecked-optional-access)
std::unique_ptr< flutter::PlatformViewIOS > platform_view
virtual void Add(std::shared_ptr< Layer > layer)
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
static std::shared_ptr< DlImageFilter > MakeBlur(DlScalar sigma_x, DlScalar sigma_y, DlTileMode tile_mode)
DlPaint & setAlpha(uint8_t alpha)
Definition dl_paint.h:76
DlPaint & setOpacity(DlScalar opacity)
Definition dl_paint.h:78
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
DlRect GetBounds() const override
Definition dl_path.cc:245
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::optional< std::vector< RasterCacheKeyID > > LayerChildrenIds(const Layer *layer)
static std::shared_ptr< MockLayer > Make(const DlPath &path, DlPaint paint=DlPaint())
Definition mock_layer.h:30
static std::shared_ptr< MockLayer > MakeOpacityCompatible(const DlPath &path)
Definition mock_layer.h:35
G_BEGIN_DECLS FlutterViewId view_id
TEST_F(DisplayListTest, Defaults)
LayerTestBase<::testing::Test > LayerTest
Definition layer_test.h:177
DiffContextTest OpacityLayerDiffTest
bool DisplayListsEQ_Verbose(const DisplayList *a, const DisplayList *b)
impeller::Scalar DlScalar
@ kHardEdge
Definition layer.h:43
impeller::Matrix DlMatrix
impeller::Rect DlRect
impeller::Size DlSize
impeller::IRect32 DlIRect
impeller::Point DlPoint
flutter::DlPath DlPath
flutter::DlPaint DlPaint
static constexpr DlColor kBlue()
Definition dl_color.h:73
static constexpr DlScalar toOpacity(uint8_t alpha)
Definition dl_color.h:65
static constexpr DlColor kMidGrey()
Definition dl_color.h:78
static uint8_t toAlpha(DlScalar opacity)
Definition dl_color.h:64
static constexpr DlColor kRed()
Definition dl_color.h:71
static constexpr DlColor kGreen()
Definition dl_color.h:72
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
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
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
constexpr TRect TransformAndClipBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle, clipped against the near clippin...
Definition rect.h:438
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