Flutter Engine
 
Loading...
Searching...
No Matches
clip_path_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 "gtest/gtest.h"
15
16// TODO(zanderso): https://github.com/flutter/flutter/issues/127701
17// NOLINTBEGIN(bugprone-unchecked-optional-access)
18
19namespace flutter {
20namespace testing {
21
23
24#ifndef NDEBUG
25TEST_F(ClipPathLayerTest, ClipNoneBehaviorDies) {
26 EXPECT_DEATH_IF_SUPPORTED(
27 auto clip = std::make_shared<ClipPathLayer>(DlPath(), Clip::kNone),
28 "clip_behavior != Clip::kNone");
29}
30
31TEST_F(ClipPathLayerTest, PaintingEmptyLayerDies) {
32 auto layer = std::make_shared<ClipPathLayer>(DlPath(), Clip::kHardEdge);
33
34 layer->Preroll(preroll_context());
35
36 // Untouched
37 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect);
38 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
39
40 EXPECT_EQ(layer->paint_bounds(), DlRect());
41 EXPECT_EQ(layer->child_paint_bounds(), DlRect());
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(ClipPathLayerTest, PaintBeforePrerollDies) {
49 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
50 const DlPath layer_path = DlPath::MakeRect(layer_bounds);
51 auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::kHardEdge);
52 EXPECT_EQ(layer->paint_bounds(), DlRect());
53 EXPECT_EQ(layer->child_paint_bounds(), DlRect());
54 EXPECT_FALSE(layer->needs_painting(paint_context()));
55
56 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
57 "needs_painting\\(context\\)");
58}
59
60TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) {
61 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
62 const DlRect child_bounds = DlRect::MakeXYWH(1.0, 2.0, 2.0, 2.0);
63 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
64 const DlRect distant_bounds = DlRect::MakeXYWH(100.0, 100.0, 10.0, 10.0);
65 const DlPath child_path = DlPath::MakeRect(child_bounds);
66 const DlPath layer_path = DlPath::MakeRect(layer_bounds) +
67 DlPath::MakeRect(layer_bounds.Expand(-0.1, -0.1));
68 auto mock_layer = std::make_shared<MockLayer>(child_path);
69 auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::kHardEdge);
70 layer->Add(mock_layer);
71
72 // Cull these children
73 preroll_context()->state_stack.set_preroll_delegate(distant_bounds,
74 initial_matrix);
75 layer->Preroll(preroll_context());
76
77 // Untouched
78 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), distant_bounds);
79 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
80
81 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
82 EXPECT_EQ(layer->paint_bounds(), child_bounds);
83 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
84 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
85 EXPECT_TRUE(layer->needs_painting(paint_context()));
86 EXPECT_EQ(mock_layer->parent_cull_rect(), DlRect());
87 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
88 EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)}));
89
90 auto mutator = paint_context().state_stack.save();
91 mutator.clipRect(distant_bounds, false);
92 EXPECT_FALSE(mock_layer->needs_painting(paint_context()));
93 EXPECT_FALSE(layer->needs_painting(paint_context()));
94 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
95 "needs_painting\\(context\\)");
96}
97#endif
98
99TEST_F(ClipPathLayerTest, ChildOutsideBounds) {
100 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
101 const DlRect local_cull_bounds = DlRect::MakeXYWH(0.0, 0.0, 2.0, 4.0);
102 const DlRect device_cull_bounds =
103 local_cull_bounds.TransformAndClipBounds(initial_matrix);
104 const DlRect child_bounds = DlRect::MakeXYWH(2.5, 5.0, 4.5, 4.0);
105 const DlRect clip_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
106 const DlPath child_path = DlPath::MakeRect(child_bounds);
107 const DlPath clip_path = DlPath::MakeRect(clip_bounds) +
108 DlPath::MakeRect(clip_bounds.Expand(-0.1f, -0.1f));
109 const DlPaint child_paint = DlPaint(DlColor::kYellow());
110 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
111 auto layer = std::make_shared<ClipPathLayer>(clip_path, Clip::kHardEdge);
112 layer->Add(mock_layer);
113
114 auto clip_cull_rect = local_cull_bounds.Intersection(clip_bounds);
115 ASSERT_TRUE(clip_cull_rect.has_value());
116 auto clip_layer_bounds = child_bounds.Intersection(clip_bounds);
117 ASSERT_TRUE(clip_layer_bounds.has_value());
118
119 // Set up both contexts to cull clipped child
120 preroll_context()->state_stack.set_preroll_delegate(device_cull_bounds,
121 initial_matrix);
122 paint_context().canvas->ClipRect(device_cull_bounds);
123 paint_context().canvas->Transform(initial_matrix);
124
125 layer->Preroll(preroll_context());
126 // Untouched
127 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(),
128 device_cull_bounds);
129 EXPECT_EQ(preroll_context()->state_stack.local_cull_rect(),
130 local_cull_bounds);
131 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
132
133 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
134 EXPECT_EQ(layer->paint_bounds(), clip_layer_bounds.value());
135 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
136 EXPECT_EQ(mock_layer->parent_cull_rect(), clip_cull_rect.value());
137 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
138 EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(clip_path)}));
139
140 EXPECT_FALSE(layer->needs_painting(paint_context()));
141 EXPECT_FALSE(mock_layer->needs_painting(paint_context()));
142 // Top level layer not visible so calling layer->Paint()
143 // would trip an FML_DCHECK
144}
145
146TEST_F(ClipPathLayerTest, RectPathReducesToClipRect) {
147 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
148 const DlRect child_bounds = DlRect::MakeXYWH(1.0, 2.0, 2.0, 2.0);
149 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
150 const DlPath child_path = DlPath::MakeRect(child_bounds) +
151 DlPath::MakeOval(child_bounds.Expand(-0.1f));
152 const DlPath layer_path = DlPath::MakeRect(layer_bounds);
153 const DlPaint child_paint = DlPaint(DlColor::kYellow());
154 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
155 auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::kHardEdge);
156 layer->Add(mock_layer);
157
158 preroll_context()->state_stack.set_preroll_delegate(initial_matrix);
159 layer->Preroll(preroll_context());
160
161 // Untouched
162 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect);
163 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
164
165 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
166 EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
167 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
168 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
169 EXPECT_TRUE(layer->needs_painting(paint_context()));
170 EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds);
171 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
172 EXPECT_EQ(mock_layer->parent_mutators(),
173 std::vector({Mutator(layer_bounds)}));
174
175 layer->Paint(display_list_paint_context());
176 DisplayListBuilder expected_builder;
177 /* (ClipPath)layer::Paint */ {
178 expected_builder.Save();
179 {
180 expected_builder.ClipRect(layer_bounds);
181 /* mock_layer::Paint */ {
182 expected_builder.DrawPath(child_path, child_paint);
183 }
184 }
185 expected_builder.Restore();
186 }
187 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
188}
189
190TEST_F(ClipPathLayerTest, OvalPathReducesToClipRRect) {
191 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
192 const DlRect child_bounds = DlRect::MakeXYWH(1.0, 2.0, 2.0, 2.0);
193 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
194 const DlPath child_path = DlPath::MakeRect(child_bounds) +
195 DlPath::MakeOval(child_bounds.Expand(-0.1f));
196 const DlPath layer_path = DlPath::MakeOval(layer_bounds);
197 const DlPaint child_paint = DlPaint(DlColor::kYellow());
198 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
199 auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::kHardEdge);
200 layer->Add(mock_layer);
201
202 preroll_context()->state_stack.set_preroll_delegate(initial_matrix);
203 layer->Preroll(preroll_context());
204
205 // Untouched
206 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect);
207 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
208
209 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
210 EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
211 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
212 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
213 EXPECT_TRUE(layer->needs_painting(paint_context()));
214 EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds);
215 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
216 EXPECT_EQ(mock_layer->parent_mutators(),
217 std::vector({Mutator(DlRoundRect::MakeOval(layer_bounds))}));
218
219 layer->Paint(display_list_paint_context());
220 DisplayListBuilder expected_builder;
221 /* (ClipPath)layer::Paint */ {
222 expected_builder.Save();
223 {
224 expected_builder.ClipRoundRect(DlRoundRect::MakeOval(layer_bounds));
225 /* mock_layer::Paint */ {
226 expected_builder.DrawPath(child_path, child_paint);
227 }
228 }
229 expected_builder.Restore();
230 }
231 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
232}
233
234TEST_F(ClipPathLayerTest, RRectPathReducesToClipRRect) {
235 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
236 const DlRect child_bounds = DlRect::MakeXYWH(1.0, 2.0, 2.0, 2.0);
237 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
238 const DlPath child_path = DlPath::MakeRect(child_bounds) +
239 DlPath::MakeOval(child_bounds.Expand(-0.1f));
240 const DlRoundRect layer_rrect =
241 DlRoundRect::MakeRectXY(layer_bounds, 0.1f, 0.1f);
242 const DlPath layer_path = DlPath::MakeRoundRect(layer_rrect);
243 const DlPaint child_paint = DlPaint(DlColor::kYellow());
244 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
245 auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::kHardEdge);
246 layer->Add(mock_layer);
247 DlRoundRect converted_rrect;
248
249 // The conversion back to an RRect by "IsRoundRect" is lossy, so we need
250 // to use the version that will be reduced by the clip_path_layer.
251 EXPECT_TRUE(layer_path.IsRoundRect(&converted_rrect));
252
253 preroll_context()->state_stack.set_preroll_delegate(initial_matrix);
254 layer->Preroll(preroll_context());
255
256 // Untouched
257 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect);
258 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
259
260 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
261 EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
262 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
263 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
264 EXPECT_TRUE(layer->needs_painting(paint_context()));
265 EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds);
266 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
267 EXPECT_EQ(mock_layer->parent_mutators(),
268 std::vector({Mutator(converted_rrect)}));
269
270 layer->Paint(display_list_paint_context());
271 EXPECT_EQ(display_list()->GetOpType(1u),
272 DisplayListOpType::kClipIntersectRoundRect);
273 DisplayListBuilder expected_builder;
274 /* (ClipPath)layer::Paint */ {
275 expected_builder.Save();
276 {
277 expected_builder.ClipRoundRect(converted_rrect);
278 /* mock_layer::Paint */ {
279 expected_builder.DrawPath(child_path, child_paint);
280 }
281 }
282 expected_builder.Restore();
283 }
284 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
285}
286
287TEST_F(ClipPathLayerTest, FullyContainedChild) {
288 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
289 const DlRect child_bounds = DlRect::MakeXYWH(1.0, 2.0, 2.0, 2.0);
290 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
291 const DlPath child_path = DlPath::MakeRect(child_bounds) +
292 DlPath::MakeOval(child_bounds.Expand(-0.1f));
293 const DlPath layer_path = DlPath::MakeRect(layer_bounds) +
294 DlPath::MakeOval(layer_bounds.Expand(-0.1f));
295 const DlPaint child_paint = DlPaint(DlColor::kYellow());
296 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
297 auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::kHardEdge);
298 layer->Add(mock_layer);
299
300 preroll_context()->state_stack.set_preroll_delegate(initial_matrix);
301 layer->Preroll(preroll_context());
302
303 // Untouched
304 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect);
305 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
306
307 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
308 EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
309 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
310 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
311 EXPECT_TRUE(layer->needs_painting(paint_context()));
312 EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds);
313 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
314 EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)}));
315
316 layer->Paint(display_list_paint_context());
317 DisplayListBuilder expected_builder;
318 /* (ClipPath)layer::Paint */ {
319 expected_builder.Save();
320 {
321 expected_builder.ClipPath(layer_path);
322 /* mock_layer::Paint */ {
323 expected_builder.DrawPath(child_path, child_paint);
324 }
325 }
326 expected_builder.Restore();
327 }
328 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
329}
330
331TEST_F(ClipPathLayerTest, PartiallyContainedChild) {
332 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
333 const DlRect local_cull_bounds = DlRect::MakeXYWH(0.0, 0.0, 4.0, 5.5);
334 const DlRect device_cull_bounds =
335 local_cull_bounds.TransformAndClipBounds(initial_matrix);
336 const DlRect child_bounds = DlRect::MakeXYWH(2.5, 5.0, 4.5, 4.0);
337 const DlRect clip_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
338 const DlPath child_path = DlPath::MakeRect(child_bounds) +
339 DlPath::MakeOval(child_bounds.Expand(-0.1f));
340 const DlPath clip_path = DlPath::MakeRect(clip_bounds) +
341 DlPath::MakeOval(clip_bounds.Expand(-0.1f));
342 const DlPaint child_paint = DlPaint(DlColor::kYellow());
343 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
344 auto layer = std::make_shared<ClipPathLayer>(clip_path, Clip::kHardEdge);
345 layer->Add(mock_layer);
346
347 auto clip_cull_rect = local_cull_bounds.Intersection(clip_bounds);
348 ASSERT_TRUE(clip_cull_rect.has_value());
349 auto clip_layer_bounds = child_bounds.Intersection(clip_bounds);
350 ASSERT_TRUE(clip_layer_bounds.has_value());
351
352 // Cull child
353 preroll_context()->state_stack.set_preroll_delegate(device_cull_bounds,
354 initial_matrix);
355 layer->Preroll(preroll_context());
356
357 // Untouched
358 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(),
359 device_cull_bounds);
360 EXPECT_EQ(preroll_context()->state_stack.local_cull_rect(),
361 local_cull_bounds);
362 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
363
364 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
365 EXPECT_EQ(layer->paint_bounds(), clip_layer_bounds.value());
366 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
367 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
368 EXPECT_TRUE(layer->needs_painting(paint_context()));
369 EXPECT_EQ(mock_layer->parent_cull_rect(), clip_cull_rect.value());
370 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
371 EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(clip_path)}));
372
373 layer->Paint(display_list_paint_context());
374 DisplayListBuilder expected_builder;
375 /* (ClipPath)layer::Paint */ {
376 expected_builder.Save();
377 {
378 expected_builder.ClipPath(clip_path);
379 /* mock_layer::Paint */ {
380 expected_builder.DrawPath(child_path, child_paint);
381 }
382 }
383 expected_builder.Restore();
384 }
385 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
386}
387
388static bool ReadbackResult(PrerollContext* context,
389 Clip clip_behavior,
390 const std::shared_ptr<Layer>& child,
391 bool before) {
392 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
393 const DlPath layer_path = DlPath::MakeRect(layer_bounds);
394 auto layer = std::make_shared<ClipPathLayer>(layer_path, clip_behavior);
395 if (child != nullptr) {
396 layer->Add(child);
397 }
398 context->surface_needs_readback = before;
399 layer->Preroll(context);
400 return context->surface_needs_readback;
401}
402
403TEST_F(ClipPathLayerTest, Readback) {
404 PrerollContext* context = preroll_context();
405 DlPath path;
406 DlPaint paint;
407
408 const Clip hard = Clip::kHardEdge;
409 const Clip soft = Clip::kAntiAlias;
410 const Clip save_layer = Clip::kAntiAliasWithSaveLayer;
411
412 std::shared_ptr<MockLayer> nochild;
413 auto reader = std::make_shared<MockLayer>(path, paint);
414 reader->set_fake_reads_surface(true);
415 auto nonreader = std::make_shared<MockLayer>(path, paint);
416
417 // No children, no prior readback -> no readback after
418 EXPECT_FALSE(ReadbackResult(context, hard, nochild, false));
419 EXPECT_FALSE(ReadbackResult(context, soft, nochild, false));
420 EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false));
421
422 // No children, prior readback -> readback after
423 EXPECT_TRUE(ReadbackResult(context, hard, nochild, true));
424 EXPECT_TRUE(ReadbackResult(context, soft, nochild, true));
425 EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true));
426
427 // Non readback child, no prior readback -> no readback after
428 EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false));
429 EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false));
430 EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false));
431
432 // Non readback child, prior readback -> readback after
433 EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true));
434 EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true));
435 EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true));
436
437 // Readback child, no prior readback -> readback after unless SaveLayer
438 EXPECT_TRUE(ReadbackResult(context, hard, reader, false));
439 EXPECT_TRUE(ReadbackResult(context, soft, reader, false));
440 EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false));
441
442 // Readback child, prior readback -> readback after
443 EXPECT_TRUE(ReadbackResult(context, hard, reader, true));
444 EXPECT_TRUE(ReadbackResult(context, soft, reader, true));
445 EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true));
446}
447
448TEST_F(ClipPathLayerTest, OpacityInheritance) {
449 auto path1 = DlPath::MakeRectLTRB(10, 10, 30, 30);
450 auto mock1 = MockLayer::MakeOpacityCompatible(path1);
451 auto layer_clip =
452 DlPath::MakeRectLTRB(5, 5, 25, 25) + DlPath::MakeOvalLTRB(20, 20, 40, 50);
453 auto clip_path_layer =
454 std::make_shared<ClipPathLayer>(layer_clip, Clip::kHardEdge);
455 clip_path_layer->Add(mock1);
456
457 // ClipRectLayer will pass through compatibility from a compatible child
458 PrerollContext* context = preroll_context();
459 clip_path_layer->Preroll(context);
460 EXPECT_EQ(context->renderable_state_flags,
462
463 auto path2 = DlPath::MakeRectLTRB(40, 40, 50, 50);
464 auto mock2 = MockLayer::MakeOpacityCompatible(path2);
465 clip_path_layer->Add(mock2);
466
467 // ClipRectLayer will pass through compatibility from multiple
468 // non-overlapping compatible children
469 clip_path_layer->Preroll(context);
470 EXPECT_EQ(context->renderable_state_flags,
472
473 auto path3 = DlPath::MakeRectLTRB(20, 20, 40, 40);
474 auto mock3 = MockLayer::MakeOpacityCompatible(path3);
475 clip_path_layer->Add(mock3);
476
477 // ClipRectLayer will not pass through compatibility from multiple
478 // overlapping children even if they are individually compatible
479 clip_path_layer->Preroll(context);
480 EXPECT_EQ(context->renderable_state_flags, 0);
481
482 {
483 // ClipRectLayer(aa with saveLayer) will always be compatible
484 auto clip_path_savelayer = std::make_shared<ClipPathLayer>(
486 clip_path_savelayer->Add(mock1);
487 clip_path_savelayer->Add(mock2);
488
489 // Double check first two children are compatible and non-overlapping
490 clip_path_savelayer->Preroll(context);
491 EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags);
492
493 // Now add the overlapping child and test again, should still be compatible
494 clip_path_savelayer->Add(mock3);
495 clip_path_savelayer->Preroll(context);
496 EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags);
497 }
498
499 // An incompatible, but non-overlapping child for the following tests
500 auto path4 = DlPath::MakeRectLTRB(60, 60, 70, 70);
501 auto mock4 = MockLayer::Make(path4);
502
503 {
504 // ClipRectLayer with incompatible child will not be compatible
505 auto clip_path_bad_child =
506 std::make_shared<ClipPathLayer>(layer_clip, Clip::kHardEdge);
507 clip_path_bad_child->Add(mock1);
508 clip_path_bad_child->Add(mock2);
509
510 // Double check first two children are compatible and non-overlapping
511 clip_path_bad_child->Preroll(context);
512 EXPECT_EQ(context->renderable_state_flags,
514
515 clip_path_bad_child->Add(mock4);
516
517 // The third child is non-overlapping, but not compatible so the
518 // TransformLayer should end up incompatible
519 clip_path_bad_child->Preroll(context);
520 EXPECT_EQ(context->renderable_state_flags, 0);
521 }
522
523 {
524 // ClipRectLayer(aa with saveLayer) will always be compatible
525 auto clip_path_savelayer_bad_child = std::make_shared<ClipPathLayer>(
527 clip_path_savelayer_bad_child->Add(mock1);
528 clip_path_savelayer_bad_child->Add(mock2);
529
530 // Double check first two children are compatible and non-overlapping
531 clip_path_savelayer_bad_child->Preroll(context);
532 EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags);
533
534 // Now add the incompatible child and test again, should still be compatible
535 clip_path_savelayer_bad_child->Add(mock4);
536 clip_path_savelayer_bad_child->Preroll(context);
537 EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags);
538 }
539}
540
541TEST_F(ClipPathLayerTest, OpacityInheritancePainting) {
542 auto path1 = DlPath::MakeRectLTRB(10, 10, 30, 30);
543 auto mock1 = MockLayer::MakeOpacityCompatible(path1);
544 auto path2 = DlPath::MakeRectLTRB(40, 40, 50, 50);
545 auto mock2 = MockLayer::MakeOpacityCompatible(path2);
546 auto layer_clip =
547 DlPath::MakeRectLTRB(5, 5, 25, 25) + DlPath::MakeOvalLTRB(45, 45, 55, 55);
548 auto clip_path_layer =
549 std::make_shared<ClipPathLayer>(layer_clip, Clip::kAntiAlias);
550 clip_path_layer->Add(mock1);
551 clip_path_layer->Add(mock2);
552
553 // ClipRectLayer will pass through compatibility from multiple
554 // non-overlapping compatible children
555 PrerollContext* context = preroll_context();
556 clip_path_layer->Preroll(context);
557 EXPECT_EQ(context->renderable_state_flags,
559
560 int opacity_alpha = 0x7F;
561 DlPoint offset = DlPoint(10, 10);
562 auto opacity_layer = std::make_shared<OpacityLayer>(opacity_alpha, offset);
563 opacity_layer->Add(clip_path_layer);
564 opacity_layer->Preroll(context);
565 EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
566
567 DisplayListBuilder expected_builder;
568 /* OpacityLayer::Paint() */ {
569 expected_builder.Save();
570 {
571 expected_builder.Translate(offset.x, offset.y);
572 /* ClipRectLayer::Paint() */ {
573 expected_builder.Save();
574 expected_builder.ClipPath(layer_clip, DlClipOp::kIntersect, true);
575 /* child layer1 paint */ {
576 expected_builder.DrawPath(path1, DlPaint().setAlpha(opacity_alpha));
577 }
578 /* child layer2 paint */ {
579 expected_builder.DrawPath(path2, DlPaint().setAlpha(opacity_alpha));
580 }
581 expected_builder.Restore();
582 }
583 }
584 expected_builder.Restore();
585 }
586
587 opacity_layer->Paint(display_list_paint_context());
588 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
589}
590
591TEST_F(ClipPathLayerTest, OpacityInheritanceSaveLayerPainting) {
592 auto path1 = DlPath::MakeRectLTRB(10, 10, 30, 30);
593 auto mock1 = MockLayer::MakeOpacityCompatible(path1);
594 auto path2 = DlPath::MakeRectLTRB(20, 20, 40, 40);
595 auto mock2 = MockLayer::MakeOpacityCompatible(path2);
596 auto children_bounds = path1.GetBounds().Union(path2.GetBounds());
597 auto layer_clip =
598 DlPath::MakeRectLTRB(5, 5, 25, 25) + DlPath::MakeOvalLTRB(20, 20, 40, 50);
599 auto clip_path_layer = std::make_shared<ClipPathLayer>(
601 clip_path_layer->Add(mock1);
602 clip_path_layer->Add(mock2);
603
604 // ClipRectLayer will pass through compatibility from multiple
605 // non-overlapping compatible children
606 PrerollContext* context = preroll_context();
607 clip_path_layer->Preroll(context);
609
610 int opacity_alpha = 0x7F;
611 DlPoint offset = DlPoint(10, 10);
612 auto opacity_layer = std::make_shared<OpacityLayer>(opacity_alpha, offset);
613 opacity_layer->Add(clip_path_layer);
614 opacity_layer->Preroll(context);
615 EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
616
617 DisplayListBuilder expected_builder;
618 /* OpacityLayer::Paint() */ {
619 expected_builder.Save();
620 {
621 expected_builder.Translate(offset.x, offset.y);
622 /* ClipRectLayer::Paint() */ {
623 expected_builder.Save();
624 expected_builder.ClipPath(layer_clip, DlClipOp::kIntersect, true);
625 expected_builder.SaveLayer(children_bounds,
626 &DlPaint().setAlpha(opacity_alpha));
627 /* child layer1 paint */ {
628 expected_builder.DrawPath(path1, DlPaint());
629 }
630 /* child layer2 paint */ { //
631 expected_builder.DrawPath(path2, DlPaint());
632 }
633 expected_builder.Restore();
634 }
635 }
636 expected_builder.Restore();
637 }
638
639 opacity_layer->Paint(display_list_paint_context());
640 EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list()));
641}
642
644 auto path1 = DlPath::MakeRectLTRB(10, 10, 30, 30);
645 auto mock1 = MockLayer::MakeOpacityCompatible(path1);
646 auto layer_clip =
647 DlPath::MakeRectLTRB(5, 5, 25, 25) + DlPath::MakeOvalLTRB(20, 20, 40, 50);
648 auto layer = std::make_shared<ClipPathLayer>(layer_clip,
650 layer->Add(mock1);
651
652 auto initial_transform = DlMatrix::MakeTranslation({50.0, 25.5});
653 DlMatrix cache_ctm = initial_transform;
654 DisplayListBuilder cache_canvas;
655 cache_canvas.Transform(cache_ctm);
656
657 use_mock_raster_cache();
658 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
659
660 const auto* clip_cache_item = layer->raster_cache_item();
661
662 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
663
664 layer->Preroll(preroll_context());
665 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
666
667 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
668 EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone);
669
670 layer->Preroll(preroll_context());
671 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
672 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
673 EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone);
674
675 layer->Preroll(preroll_context());
676 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
677 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
678 EXPECT_EQ(clip_cache_item->cache_state(),
680 DlPaint paint;
681 EXPECT_TRUE(raster_cache()->Draw(clip_cache_item->GetId().value(),
682 cache_canvas, &paint));
683}
684
685TEST_F(ClipPathLayerTest, EmptyClipDoesNotCullPlatformView) {
686 const DlPoint view_offset = DlPoint(0.0f, 0.0f);
687 const DlSize view_size = DlSize(8.0f, 8.0f);
688 const int64_t view_id = 42;
689 auto platform_view =
690 std::make_shared<PlatformViewLayer>(view_offset, view_size, view_id);
691
692 auto layer_clip = DlPath::MakeRect(DlRect());
693 auto clip = std::make_shared<ClipPathLayer>(layer_clip,
695 clip->Add(platform_view);
696
697 auto embedder = MockViewEmbedder();
698 DisplayListBuilder fake_overlay_builder;
699 embedder.AddCanvas(&fake_overlay_builder);
700 preroll_context()->view_embedder = &embedder;
701 paint_context().view_embedder = &embedder;
702
703 clip->Preroll(preroll_context());
704 EXPECT_EQ(embedder.prerolled_views(), std::vector<int64_t>({view_id}));
705
706 clip->Paint(paint_context());
707 EXPECT_EQ(embedder.painted_views(), std::vector<int64_t>({view_id}));
708}
709
710} // namespace testing
711} // namespace flutter
712
713// NOLINTEND(bugprone-unchecked-optional-access)
std::unique_ptr< flutter::PlatformViewIOS > platform_view
void ClipRect(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) 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 ClipRoundRect(const DlRoundRect &rrect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) 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 ClipPath(const DlPath &path, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void Transform(const DlMatrix &matrix) override
static DlPath MakeRectLTRB(DlScalar left, DlScalar top, DlScalar right, DlScalar bottom)
Definition dl_path.cc:43
static DlPath MakeRoundRect(const DlRoundRect &rrect)
Definition dl_path.cc:72
static DlPath MakeRect(const DlRect &rect)
Definition dl_path.cc:39
bool IsRoundRect(DlRoundRect *rrect=nullptr) const
Definition dl_path.cc:228
static DlPath MakeOvalLTRB(DlScalar left, DlScalar top, DlScalar right, DlScalar bottom)
Definition dl_path.cc:61
static DlPath MakeOval(const DlRect &bounds)
Definition dl_path.cc:57
static constexpr int kSaveLayerRenderFlags
Definition layer.h:118
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
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
bool DisplayListsEQ_Verbose(const DisplayList *a, const DisplayList *b)
static bool ReadbackResult(PrerollContext *context, Clip clip_behavior, const std::shared_ptr< Layer > &child, bool before)
@ kAntiAlias
Definition layer.h:43
@ kAntiAliasWithSaveLayer
Definition layer.h:43
@ kNone
Definition layer.h:43
@ kHardEdge
Definition layer.h:43
impeller::Rect DlRect
impeller::Size DlSize
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
static constexpr DlRect kGiantRect
Definition layer.h:40
impeller::Point DlPoint
static constexpr DlColor kYellow()
Definition dl_color.h:76
bool surface_needs_readback
Definition layer.h:51
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
static RoundRect MakeOval(const Rect &rect)
Definition round_rect.h:23
static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition round_rect.h:31
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition rect.h:528
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 > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition rect.h:618