Flutter Engine
 
Loading...
Searching...
No Matches
display_list_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <memory>
6#include <string>
7#include <unordered_set>
8#include <utility>
9#include <vector>
10
22#include "flutter/fml/logging.h"
23#include "flutter/fml/math.h"
24
25#if IMPELLER_SUPPORTS_RENDERING
28#endif
29
33
34namespace flutter {
35
37 return builder.asReceiver();
38}
39
41 return builder.CurrentAttributes();
42}
43
45 return builder.LastOpIndex();
46}
47
48namespace testing {
49
50static std::vector<testing::DisplayListInvocationGroup> allGroups =
52
53template <typename BaseT>
54class DisplayListTestBase : public BaseT {
55 public:
57
61
62 static sk_sp<DisplayList> Build(DisplayListInvocation& invocation) {
63 DisplayListBuilder builder;
64 invocation.Invoke(ToReceiver(builder));
65 return builder.Build();
66 }
67
68 static sk_sp<DisplayList> Build(size_t g_index, size_t v_index) {
69 DisplayListBuilder builder;
70 DlOpReceiver& receiver =
72 uint32_t op_count = 0u;
73 size_t byte_count = 0u;
74 uint32_t depth = 0u;
75 uint32_t render_op_depth_cost = 1u;
76 for (size_t i = 0; i < allGroups.size(); i++) {
78 size_t j = (i == g_index ? v_index : 0);
79 if (j >= group.variants.size()) {
80 continue;
81 }
82 DisplayListInvocation& invocation = group.variants[j];
83 op_count += invocation.op_count();
84 byte_count += invocation.raw_byte_count();
85 depth += invocation.depth_accumulated(render_op_depth_cost);
86 invocation.Invoke(receiver);
87 render_op_depth_cost =
88 invocation.adjust_render_op_depth_cost(render_op_depth_cost);
89 }
90 sk_sp<DisplayList> dl = builder.Build();
91 std::string name;
92 if (g_index >= allGroups.size()) {
93 name = "Default";
94 } else {
95 name = allGroups[g_index].op_name;
96 if (v_index >= allGroups[g_index].variants.size()) {
97 name += " skipped";
98 } else {
99 name += " variant " + std::to_string(v_index + 1);
100 }
101 }
102 EXPECT_EQ(dl->op_count(false), op_count) << name;
103 EXPECT_EQ(dl->bytes(false), byte_count + sizeof(DisplayList)) << name;
104 EXPECT_EQ(dl->total_depth(), depth) << name;
105 return dl;
106 }
107
108 static void check_defaults(
109 DisplayListBuilder& builder,
110 const DlRect& cull_rect = DisplayListBuilder::kMaxCullRect) {
111 DlPaint builder_paint = DisplayListBuilderTestingAttributes(builder);
112 DlPaint defaults;
113
114 EXPECT_EQ(builder_paint.isAntiAlias(), defaults.isAntiAlias());
115 EXPECT_EQ(builder_paint.isInvertColors(), defaults.isInvertColors());
116 EXPECT_EQ(builder_paint.getColor(), defaults.getColor());
117 EXPECT_EQ(builder_paint.getBlendMode(), defaults.getBlendMode());
118 EXPECT_EQ(builder_paint.getDrawStyle(), defaults.getDrawStyle());
119 EXPECT_EQ(builder_paint.getStrokeWidth(), defaults.getStrokeWidth());
120 EXPECT_EQ(builder_paint.getStrokeMiter(), defaults.getStrokeMiter());
121 EXPECT_EQ(builder_paint.getStrokeCap(), defaults.getStrokeCap());
122 EXPECT_EQ(builder_paint.getStrokeJoin(), defaults.getStrokeJoin());
123 EXPECT_EQ(builder_paint.getColorSource(), defaults.getColorSource());
124 EXPECT_EQ(builder_paint.getColorFilter(), defaults.getColorFilter());
125 EXPECT_EQ(builder_paint.getImageFilter(), defaults.getImageFilter());
126 EXPECT_EQ(builder_paint.getMaskFilter(), defaults.getMaskFilter());
127 EXPECT_EQ(builder_paint, defaults);
128 EXPECT_TRUE(builder_paint.isDefault());
129
130 EXPECT_EQ(builder.GetMatrix(), DlMatrix());
131
132 EXPECT_EQ(builder.GetLocalClipCoverage(), cull_rect);
133 EXPECT_EQ(builder.GetDestinationClipCoverage(), cull_rect);
134
135 EXPECT_EQ(builder.GetSaveCount(), 1);
136 }
137
138 typedef const std::function<void(DlCanvas&)> DlSetup;
139 typedef const std::function<void(DlCanvas&, DlPaint&, DlRect&)> DlRenderer;
140
141 static void verify_inverted_bounds(DlSetup& setup,
142 DlRenderer& renderer,
143 DlPaint paint,
144 DlRect render_rect,
145 DlRect expected_bounds,
146 const std::string& desc) {
147 DisplayListBuilder builder;
148 setup(builder);
149 renderer(builder, paint, render_rect);
150 auto dl = builder.Build();
151 EXPECT_EQ(dl->op_count(), 1u) << desc;
152 EXPECT_EQ(dl->GetBounds(), expected_bounds) << desc;
153 }
154
155 static void check_inverted_bounds(DlRenderer& renderer,
156 const std::string& desc) {
157 DlRect rect = DlRect::MakeLTRB(0.0f, 0.0f, 10.0f, 10.0f);
158 DlRect invertedLR = DlRect::MakeLTRB(rect.GetRight(), rect.GetTop(),
159 rect.GetLeft(), rect.GetBottom());
160 DlRect invertedTB = DlRect::MakeLTRB(rect.GetLeft(), rect.GetBottom(),
161 rect.GetRight(), rect.GetTop());
162 DlRect invertedLTRB = DlRect::MakeLTRB(rect.GetRight(), rect.GetBottom(),
163 rect.GetLeft(), rect.GetTop());
164 auto empty_setup = [](DlCanvas&) {};
165
166 ASSERT_TRUE(rect.GetLeft() < rect.GetRight());
167 ASSERT_TRUE(rect.GetTop() < rect.GetBottom());
168 ASSERT_FALSE(rect.IsEmpty());
169 ASSERT_TRUE(invertedLR.GetLeft() > invertedLR.GetRight());
170 ASSERT_TRUE(invertedLR.IsEmpty());
171 ASSERT_TRUE(invertedTB.GetTop() > invertedTB.GetBottom());
172 ASSERT_TRUE(invertedTB.IsEmpty());
173 ASSERT_TRUE(invertedLTRB.GetLeft() > invertedLTRB.GetRight());
174 ASSERT_TRUE(invertedLTRB.GetTop() > invertedLTRB.GetBottom());
175 ASSERT_TRUE(invertedLTRB.IsEmpty());
176
177 DlPaint ref_paint = DlPaint();
178 DlRect ref_bounds = rect;
179 verify_inverted_bounds(empty_setup, renderer, ref_paint, invertedLR,
180 ref_bounds, desc + " LR swapped");
181 verify_inverted_bounds(empty_setup, renderer, ref_paint, invertedTB,
182 ref_bounds, desc + " TB swapped");
183 verify_inverted_bounds(empty_setup, renderer, ref_paint, invertedLTRB,
184 ref_bounds, desc + " LR&TB swapped");
185
186 // Round joins are used because miter joins greatly pad the bounds,
187 // but only on paths. So we use round joins for consistency there.
188 // We aren't fully testing all stroke-related bounds computations here,
189 // those are more fully tested in the render tests. We are simply
190 // checking that they are applied to the ordered bounds.
191 DlPaint stroke_paint = DlPaint() //
194 .setStrokeWidth(2.0f);
195 DlRect stroke_bounds = rect.Expand(1.0f, 1.0f);
196 verify_inverted_bounds(empty_setup, renderer, stroke_paint, invertedLR,
197 stroke_bounds, desc + " LR swapped, sw 2");
198 verify_inverted_bounds(empty_setup, renderer, stroke_paint, invertedTB,
199 stroke_bounds, desc + " TB swapped, sw 2");
200 verify_inverted_bounds(empty_setup, renderer, stroke_paint, invertedLTRB,
201 stroke_bounds, desc + " LR&TB swapped, sw 2");
202
203 DlBlurMaskFilter mask_filter(DlBlurStyle::kNormal, 2.0f);
204 DlPaint maskblur_paint = DlPaint() //
205 .setMaskFilter(&mask_filter);
206 DlRect maskblur_bounds = rect.Expand(6.0f, 6.0f);
207 verify_inverted_bounds(empty_setup, renderer, maskblur_paint, invertedLR,
208 maskblur_bounds, desc + " LR swapped, mask 2");
209 verify_inverted_bounds(empty_setup, renderer, maskblur_paint, invertedTB,
210 maskblur_bounds, desc + " TB swapped, mask 2");
211 verify_inverted_bounds(empty_setup, renderer, maskblur_paint, invertedLTRB,
212 maskblur_bounds, desc + " LR&TB swapped, mask 2");
213
214 DlErodeImageFilter erode_filter(2.0f, 2.0f);
215 DlPaint erode_paint = DlPaint() //
216 .setImageFilter(&erode_filter);
217 DlRect erode_bounds = rect.Expand(-2.0f, -2.0f);
218 verify_inverted_bounds(empty_setup, renderer, erode_paint, invertedLR,
219 erode_bounds, desc + " LR swapped, erode 2");
220 verify_inverted_bounds(empty_setup, renderer, erode_paint, invertedTB,
221 erode_bounds, desc + " TB swapped, erode 2");
222 verify_inverted_bounds(empty_setup, renderer, erode_paint, invertedLTRB,
223 erode_bounds, desc + " LR&TB swapped, erode 2");
224 }
225
226 private:
228};
230
232 DisplayListBuilder builder;
233 check_defaults(builder);
234}
235
237 DisplayListBuilder builder;
238 auto dl = builder.Build();
239 EXPECT_EQ(dl->op_count(), 0u);
240 EXPECT_EQ(dl->bytes(), sizeof(DisplayList));
241 EXPECT_EQ(dl->total_depth(), 0u);
242}
243
244TEST_F(DisplayListTest, EmptyRebuild) {
245 DisplayListBuilder builder;
246 auto dl1 = builder.Build();
247 auto dl2 = builder.Build();
248 auto dl3 = builder.Build();
249 ASSERT_TRUE(dl1->Equals(dl2));
250 ASSERT_TRUE(dl2->Equals(dl3));
251}
252
253TEST_F(DisplayListTest, NopReusedBuildIsReallyEmpty) {
254 DisplayListBuilder builder;
255 builder.DrawRect(DlRect::MakeLTRB(0.0f, 0.0f, 10.0f, 10.0f), DlPaint());
256
257 {
258 auto dl1 = builder.Build();
259 EXPECT_EQ(dl1->op_count(), 1u);
260 EXPECT_GT(dl1->bytes(), sizeof(DisplayList));
261 EXPECT_EQ(dl1->GetBounds(), DlRect::MakeLTRB(0.0f, 0.0f, 10.0f, 10.0f));
262 }
263
264 {
265 auto dl2 = builder.Build();
266 EXPECT_EQ(dl2->op_count(), 0u);
267 EXPECT_EQ(dl2->bytes(), sizeof(DisplayList));
268 EXPECT_EQ(dl2->GetBounds(), DlRect());
269 }
270}
271
272TEST_F(DisplayListTest, GeneralReceiverInitialValues) {
274
275 EXPECT_EQ(receiver.GetOpsReceived(), 0u);
276
277 auto max_type = static_cast<int>(DisplayListOpType::kMaxOp);
278 for (int i = 0; i <= max_type; i++) {
280 EXPECT_EQ(receiver.GetOpsReceived(type), 0u) << type;
281 }
282
283 auto max_category = static_cast<int>(DisplayListOpCategory::kMaxCategory);
284 for (int i = 0; i <= max_category; i++) {
285 DisplayListOpCategory category = static_cast<DisplayListOpCategory>(i);
286 EXPECT_EQ(receiver.GetOpsReceived(category), 0u) << category;
287 }
288}
289
291 DisplayListBuilder builder;
292 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
293 auto dl = builder.Build();
294 for (DlIndex i : *dl) {
295 EXPECT_EQ(dl->GetOpType(i), DisplayListOpType::kDrawRect) //
296 << "at " << i;
297 EXPECT_EQ(dl->GetOpCategory(i), DisplayListOpCategory::kRendering)
298 << "at " << i;
299 }
300}
301
302TEST_F(DisplayListTest, InvalidIndices) {
303 DisplayListBuilder builder;
304 builder.DrawRect(kTestBounds, DlPaint());
305 auto dl = builder.Build();
307
308 EXPECT_FALSE(dl->Dispatch(receiver, -1));
309 EXPECT_FALSE(dl->Dispatch(receiver, dl->GetRecordCount()));
310 EXPECT_EQ(dl->GetOpType(-1), DisplayListOpType::kInvalidOp);
311 EXPECT_EQ(dl->GetOpType(dl->GetRecordCount()), DisplayListOpType::kInvalidOp);
312 EXPECT_EQ(dl->GetOpCategory(-1), DisplayListOpCategory::kInvalidCategory);
313 EXPECT_EQ(dl->GetOpCategory(dl->GetRecordCount()),
315 EXPECT_EQ(dl->GetOpCategory(-1), DisplayListOpCategory::kInvalidCategory);
316 EXPECT_EQ(dl->GetOpCategory(DisplayListOpType::kInvalidOp),
318 EXPECT_EQ(dl->GetOpCategory(DisplayListOpType::kMaxOp),
320 EXPECT_EQ(receiver.GetOpsReceived(), 0u);
321}
322
323TEST_F(DisplayListTest, ValidIndices) {
324 DisplayListBuilder builder;
325 builder.DrawRect(kTestBounds, DlPaint());
326 auto dl = builder.Build();
328
329 EXPECT_EQ(dl->GetRecordCount(), 1u);
330 EXPECT_TRUE(dl->Dispatch(receiver, 0u));
331 EXPECT_EQ(dl->GetOpType(0u), DisplayListOpType::kDrawRect);
332 EXPECT_EQ(dl->GetOpCategory(0u), DisplayListOpCategory::kRendering);
333 EXPECT_EQ(receiver.GetOpsReceived(), 1u);
334}
335
336TEST_F(DisplayListTest, BuilderCanBeReused) {
338 builder.DrawRect(kTestBounds, DlPaint());
339 auto dl = builder.Build();
340 builder.DrawRect(kTestBounds, DlPaint());
341 auto dl2 = builder.Build();
342 ASSERT_TRUE(dl->Equals(dl2));
343}
344
345TEST_F(DisplayListTest, SaveRestoreRestoresTransform) {
346 DlRect cull_rect = DlRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f);
347 DisplayListBuilder builder(cull_rect);
348
349 builder.Save();
350 builder.Translate(10.0f, 10.0f);
351 builder.Restore();
352 check_defaults(builder, cull_rect);
353
354 builder.Save();
355 builder.Scale(10.0f, 10.0f);
356 builder.Restore();
357 check_defaults(builder, cull_rect);
358
359 builder.Save();
360 builder.Skew(0.1f, 0.1f);
361 builder.Restore();
362 check_defaults(builder, cull_rect);
363
364 builder.Save();
365 builder.Rotate(45.0f);
366 builder.Restore();
367 check_defaults(builder, cull_rect);
368
369 builder.Save();
370 builder.Transform(DlMatrix::MakeScale({10.0f, 10.0f, 1.0f}));
371 builder.Restore();
372 check_defaults(builder, cull_rect);
373
374 builder.Save();
375 builder.Transform2DAffine(1.0f, 0.0f, 12.0f, //
376 0.0f, 1.0f, 35.0f);
377 builder.Restore();
378 check_defaults(builder, cull_rect);
379
380 builder.Save();
381 builder.TransformFullPerspective(1.0f, 0.0f, 0.0f, 12.0f, //
382 0.0f, 1.0f, 0.0f, 35.0f, //
383 0.0f, 0.0f, 1.0f, 5.0f, //
384 0.0f, 0.0f, 0.0f, 1.0f);
385 builder.Restore();
386 check_defaults(builder, cull_rect);
387}
388
389TEST_F(DisplayListTest, BuildRestoresTransform) {
390 DlRect cull_rect = DlRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f);
391 DisplayListBuilder builder(cull_rect);
392
393 builder.Translate(10.0f, 10.0f);
394 builder.Build();
395 check_defaults(builder, cull_rect);
396
397 builder.Scale(10.0f, 10.0f);
398 builder.Build();
399 check_defaults(builder, cull_rect);
400
401 builder.Skew(0.1f, 0.1f);
402 builder.Build();
403 check_defaults(builder, cull_rect);
404
405 builder.Rotate(45.0f);
406 builder.Build();
407 check_defaults(builder, cull_rect);
408
409 builder.Transform(DlMatrix::MakeScale({10.0f, 10.0f, 1.0f}));
410 builder.Build();
411 check_defaults(builder, cull_rect);
412
413 builder.Transform2DAffine(1.0f, 0.0f, 12.0f, //
414 0.0f, 1.0f, 35.0f);
415 builder.Build();
416 check_defaults(builder, cull_rect);
417
418 builder.TransformFullPerspective(1.0f, 0.0f, 0.0f, 12.0f, //
419 0.0f, 1.0f, 0.0f, 35.0f, //
420 0.0f, 0.0f, 1.0f, 5.0f, //
421 0.0f, 0.0f, 0.0f, 1.0f);
422 builder.Build();
423 check_defaults(builder, cull_rect);
424}
425
426TEST_F(DisplayListTest, SaveRestoreRestoresClip) {
427 DlRect cull_rect = DlRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f);
428 DisplayListBuilder builder(cull_rect);
429
430 builder.Save();
431 builder.ClipRect(DlRect::MakeLTRB(0.0f, 0.0f, 10.0f, 10.0f));
432 builder.Restore();
433 check_defaults(builder, cull_rect);
434
435 builder.Save();
437 DlRect::MakeLTRB(0.0f, 0.0f, 5.0f, 5.0f), 2.0f, 2.0f));
438 builder.Restore();
439 check_defaults(builder, cull_rect);
440
441 builder.Save();
442 builder.ClipPath(DlPath::MakeOvalLTRB(0.0f, 0.0f, 10.0f, 10.0f));
443 builder.Restore();
444 check_defaults(builder, cull_rect);
445}
446
447TEST_F(DisplayListTest, BuildRestoresClip) {
448 DlRect cull_rect = DlRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f);
449 DisplayListBuilder builder(cull_rect);
450
451 builder.ClipRect(DlRect::MakeLTRB(0.0f, 0.0f, 10.0f, 10.0f));
452 builder.Build();
453 check_defaults(builder, cull_rect);
454
456 DlRect::MakeLTRB(0.0f, 0.0f, 5.0f, 5.0f), 2.0f, 2.0f));
457 builder.Build();
458 check_defaults(builder, cull_rect);
459
460 builder.ClipPath(DlPath::MakeOvalLTRB(0.0f, 0.0f, 10.0f, 10.0f));
461 builder.Build();
462 check_defaults(builder, cull_rect);
463}
464
465TEST_F(DisplayListTest, BuildRestoresAttributes) {
466 DlRect cull_rect = DlRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f);
467 DisplayListBuilder builder(cull_rect);
468 DlOpReceiver& receiver = ToReceiver(builder);
469
470 receiver.setAntiAlias(true);
471 builder.Build();
472 check_defaults(builder, cull_rect);
473
474 receiver.setInvertColors(true);
475 builder.Build();
476 check_defaults(builder, cull_rect);
477
478 receiver.setColor(DlColor::kRed());
479 builder.Build();
480 check_defaults(builder, cull_rect);
481
482 receiver.setBlendMode(DlBlendMode::kColorBurn);
483 builder.Build();
484 check_defaults(builder, cull_rect);
485
487 builder.Build();
488 check_defaults(builder, cull_rect);
489
490 receiver.setStrokeWidth(300.0f);
491 builder.Build();
492 check_defaults(builder, cull_rect);
493
494 receiver.setStrokeMiter(300.0f);
495 builder.Build();
496 check_defaults(builder, cull_rect);
497
499 builder.Build();
500 check_defaults(builder, cull_rect);
501
503 builder.Build();
504 check_defaults(builder, cull_rect);
505
506 receiver.setColorSource(kTestSource1.get());
507 builder.Build();
508 check_defaults(builder, cull_rect);
509
511 builder.Build();
512 check_defaults(builder, cull_rect);
513
515 builder.Build();
516 check_defaults(builder, cull_rect);
517
519 builder.Build();
520 check_defaults(builder, cull_rect);
521}
522
523TEST_F(DisplayListTest, BuilderBoundsTransformComparedToSkia) {
524 const DlRect frame_rect = DlRect::MakeLTRB(10, 10, 100, 100);
525 DisplayListBuilder builder(frame_rect);
526 ASSERT_EQ(builder.GetDestinationClipCoverage(), frame_rect);
527 ASSERT_EQ(builder.GetLocalClipCoverage(), frame_rect);
528 ASSERT_EQ(builder.GetMatrix(), DlMatrix());
529}
530
531TEST_F(DisplayListTest, BuilderInitialClipBounds) {
532 DlRect cull_rect = DlRect::MakeWH(100, 100);
533 DlRect clip_bounds = DlRect::MakeWH(100, 100);
534 DisplayListBuilder builder(cull_rect);
535 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds);
536}
537
538TEST_F(DisplayListTest, BuilderInitialClipBoundsNaN) {
539 auto NaN = std::numeric_limits<DlScalar>::quiet_NaN();
540 DlRect cull_rect = DlRect::MakeWH(NaN, NaN);
541 DlRect clip_bounds = DlRect();
542 DisplayListBuilder builder(cull_rect);
543 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds);
544}
545
546TEST_F(DisplayListTest, BuilderClipBoundsAfterClipRect) {
547 DlRect cull_rect = DlRect::MakeWH(100, 100);
548 DlRect clip_rect = DlRect::MakeLTRB(10, 10, 20, 20);
549 DlRect clip_bounds = DlRect::MakeLTRB(10, 10, 20, 20);
550 DisplayListBuilder builder(cull_rect);
551 builder.ClipRect(clip_rect, DlClipOp::kIntersect, false);
552 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds);
553}
554
555TEST_F(DisplayListTest, BuilderClipBoundsAfterClipRRect) {
556 DlRect cull_rect = DlRect::MakeWH(100, 100);
557 DlRect clip_rect = DlRect::MakeLTRB(10, 10, 20, 20);
558 DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(clip_rect, 2, 2);
559 DlRect clip_bounds = DlRect::MakeLTRB(10, 10, 20, 20);
560 DisplayListBuilder builder(cull_rect);
561 builder.ClipRoundRect(clip_rrect, DlClipOp::kIntersect, false);
562 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds);
563}
564
565TEST_F(DisplayListTest, BuilderClipBoundsAfterClipPath) {
566 DlRect cull_rect = DlRect::MakeWH(100, 100);
567 DlPath clip_path = DlPath::MakeRectLTRB(10, 10, 15, 15) +
568 DlPath::MakeRectLTRB(15, 15, 20, 20);
569 DlRect clip_bounds = DlRect::MakeLTRB(10, 10, 20, 20);
570 DisplayListBuilder builder(cull_rect);
571 builder.ClipPath(clip_path, DlClipOp::kIntersect, false);
572 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds);
573}
574
575TEST_F(DisplayListTest, BuilderInitialClipBoundsNonZero) {
576 DlRect cull_rect = DlRect::MakeLTRB(10, 10, 100, 100);
577 DlRect clip_bounds = DlRect::MakeLTRB(10, 10, 100, 100);
578 DisplayListBuilder builder(cull_rect);
579 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds);
580}
581
582TEST_F(DisplayListTest, UnclippedSaveLayerContentAccountsForFilter) {
583 DlRect cull_rect = DlRect::MakeLTRB(0.0f, 0.0f, 300.0f, 300.0f);
584 DlRect clip_rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
585 DlRect draw_rect = DlRect::MakeLTRB(50.0f, 140.0f, 101.0f, 160.0f);
586 auto filter = DlImageFilter::MakeBlur(10.0f, 10.0f, DlTileMode::kDecal);
587 DlPaint layer_paint = DlPaint().setImageFilter(filter);
588
589 ASSERT_TRUE(clip_rect.IntersectsWithRect(draw_rect));
590 ASSERT_TRUE(cull_rect.Contains(clip_rect));
591 ASSERT_TRUE(cull_rect.Contains(draw_rect));
592
593 DisplayListBuilder builder;
594 builder.Save();
595 {
596 builder.ClipRect(clip_rect, DlClipOp::kIntersect, false);
597 builder.SaveLayer(cull_rect, &layer_paint);
598 { //
599 builder.DrawRect(draw_rect, DlPaint());
600 }
601 builder.Restore();
602 }
603 builder.Restore();
604 auto display_list = builder.Build();
605
606 EXPECT_EQ(display_list->op_count(), 6u);
607 EXPECT_EQ(display_list->total_depth(), 2u);
608
609 DlRect result_rect = draw_rect.Expand(30.0f, 30.0f);
610 ASSERT_TRUE(result_rect.IntersectsWithRect(clip_rect));
611 result_rect = result_rect.IntersectionOrEmpty(clip_rect);
612 ASSERT_EQ(result_rect, DlRect::MakeLTRB(100.0f, 110.0f, 131.0f, 190.0f));
613 EXPECT_EQ(display_list->GetBounds(), result_rect);
614}
615
616TEST_F(DisplayListTest, ClippedSaveLayerContentAccountsForFilter) {
617 DlRect cull_rect = DlRect::MakeLTRB(0.0f, 0.0f, 300.0f, 300.0f);
618 DlRect clip_rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
619 DlRect draw_rect = DlRect::MakeLTRB(50.0f, 140.0f, 99.0f, 160.0f);
620 auto filter = DlImageFilter::MakeBlur(10.0f, 10.0f, DlTileMode::kDecal);
621 DlPaint layer_paint = DlPaint().setImageFilter(filter);
622
623 ASSERT_FALSE(clip_rect.IntersectsWithRect(draw_rect));
624 ASSERT_TRUE(cull_rect.Contains(clip_rect));
625 ASSERT_TRUE(cull_rect.Contains(draw_rect));
626
627 DisplayListBuilder builder;
628 builder.Save();
629 {
630 builder.ClipRect(clip_rect, DlClipOp::kIntersect, false);
631 builder.SaveLayer(cull_rect, &layer_paint);
632 { //
633 builder.DrawRect(draw_rect, DlPaint());
634 }
635 builder.Restore();
636 }
637 builder.Restore();
638 auto display_list = builder.Build();
639
640 EXPECT_EQ(display_list->op_count(), 6u);
641 EXPECT_EQ(display_list->total_depth(), 2u);
642
643 DlRect result_rect = draw_rect.Expand(30.0f, 30.0f);
644 ASSERT_TRUE(result_rect.IntersectsWithRect(clip_rect));
645 result_rect = result_rect.IntersectionOrEmpty(clip_rect);
646 ASSERT_EQ(result_rect, DlRect::MakeLTRB(100.0f, 110.0f, 129.0f, 190.0f));
647 EXPECT_EQ(display_list->GetBounds(), result_rect);
648}
649
650TEST_F(DisplayListTest, OOBSaveLayerContentCulledWithBlurFilter) {
651 DlRect cull_rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
652 DlRect draw_rect = DlRect::MakeLTRB(25.0f, 25.0f, 99.0f, 75.0f);
653 auto filter = DlImageFilter::MakeBlur(10.0f, 10.0f, DlTileMode::kDecal);
654 DlPaint layer_paint = DlPaint().setImageFilter(filter);
655
656 // We want a draw rect that is outside the layer bounds even though its
657 // filtered output might be inside. The drawn rect should be culled by
658 // the expectations of the layer bounds even though it is close enough
659 // to be visible due to filtering.
660 ASSERT_FALSE(cull_rect.IntersectsWithRect(draw_rect));
661 DlRect mapped_rect;
662 ASSERT_TRUE(filter->map_local_bounds(draw_rect, mapped_rect));
663 ASSERT_TRUE(mapped_rect.IntersectsWithRect(cull_rect));
664
665 DisplayListBuilder builder;
666 builder.SaveLayer(cull_rect, &layer_paint);
667 { //
668 builder.DrawRect(draw_rect, DlPaint());
669 }
670 builder.Restore();
671 auto display_list = builder.Build();
672
673 EXPECT_EQ(display_list->op_count(), 2u);
674 EXPECT_EQ(display_list->total_depth(), 1u);
675
676 EXPECT_TRUE(display_list->GetBounds().IsEmpty()) << display_list->GetBounds();
677}
678
679TEST_F(DisplayListTest, OOBSaveLayerContentCulledWithMatrixFilter) {
680 DlRect cull_rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
681 DlRect draw_rect = DlRect::MakeLTRB(25.0f, 125.0f, 75.0f, 175.0f);
682 auto filter = DlImageFilter::MakeMatrix(
684 DlPaint layer_paint = DlPaint().setImageFilter(filter);
685
686 // We want a draw rect that is outside the layer bounds even though its
687 // filtered output might be inside. The drawn rect should be culled by
688 // the expectations of the layer bounds even though it is close enough
689 // to be visible due to filtering.
690 ASSERT_FALSE(cull_rect.IntersectsWithRect(draw_rect));
691 DlRect mapped_rect;
692 ASSERT_TRUE(filter->map_local_bounds(draw_rect, mapped_rect));
693 ASSERT_TRUE(mapped_rect.IntersectsWithRect(cull_rect));
694
695 DisplayListBuilder builder;
696 builder.SaveLayer(cull_rect, &layer_paint);
697 { //
698 builder.DrawRect(draw_rect, DlPaint());
699 }
700 builder.Restore();
701 auto display_list = builder.Build();
702
703 EXPECT_EQ(display_list->op_count(), 2u);
704 EXPECT_EQ(display_list->total_depth(), 1u);
705
706 EXPECT_TRUE(display_list->GetBounds().IsEmpty()) << display_list->GetBounds();
707}
708
709TEST_F(DisplayListTest, SingleOpSizes) {
710 for (auto& group : allGroups) {
711 for (size_t i = 0; i < group.variants.size(); i++) {
712 auto& invocation = group.variants[i];
713 sk_sp<DisplayList> dl = Build(invocation);
714 auto desc = group.op_name + "(variant " + std::to_string(i + 1) + ")";
715 EXPECT_EQ(dl->op_count(false), invocation.op_count()) << desc;
716 EXPECT_EQ(dl->bytes(false), invocation.byte_count()) << desc;
717 EXPECT_EQ(dl->total_depth(), invocation.depth_accumulated()) << desc;
718 }
719 }
720}
721
722TEST_F(DisplayListTest, SingleOpDisplayListsNotEqualEmpty) {
723 sk_sp<DisplayList> empty = DisplayListBuilder().Build();
724 for (auto& group : allGroups) {
725 for (size_t i = 0; i < group.variants.size(); i++) {
726 sk_sp<DisplayList> dl = Build(group.variants[i]);
727 auto desc =
728 group.op_name + "(variant " + std::to_string(i + 1) + " != empty)";
729 if (group.variants[i].is_empty()) {
730 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, empty));
731 ASSERT_TRUE(empty->Equals(*dl)) << desc;
732 } else {
733 ASSERT_TRUE(DisplayListsNE_Verbose(dl, empty));
734 ASSERT_FALSE(empty->Equals(*dl)) << desc;
735 }
736 }
737 }
738}
739
740TEST_F(DisplayListTest, SingleOpDisplayListsRecapturedAreEqual) {
741 for (auto& group : allGroups) {
742 for (size_t i = 0; i < group.variants.size(); i++) {
743 sk_sp<DisplayList> dl = Build(group.variants[i]);
744 // Verify recapturing the replay of the display list is Equals()
745 // when dispatching directly from the DL to another builder
746 DisplayListBuilder copy_builder;
747 DlOpReceiver& r = ToReceiver(copy_builder);
748 dl->Dispatch(r);
749 sk_sp<DisplayList> copy = copy_builder.Build();
750 auto desc =
751 group.op_name + "(variant " + std::to_string(i + 1) + " == copy)";
752 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, copy));
753 ASSERT_EQ(copy->op_count(false), dl->op_count(false)) << desc;
754 ASSERT_EQ(copy->GetRecordCount(), dl->GetRecordCount());
755 ASSERT_EQ(copy->bytes(false), dl->bytes(false)) << desc;
756 ASSERT_EQ(copy->op_count(true), dl->op_count(true)) << desc;
757 ASSERT_EQ(copy->bytes(true), dl->bytes(true)) << desc;
758 ASSERT_EQ(copy->total_depth(), dl->total_depth()) << desc;
759 ASSERT_EQ(copy->GetBounds(), dl->GetBounds()) << desc;
760 ASSERT_TRUE(copy->Equals(*dl)) << desc;
761 ASSERT_TRUE(dl->Equals(*copy)) << desc;
762 }
763 }
764}
765
766TEST_F(DisplayListTest, SingleOpDisplayListsRecapturedByIndexAreEqual) {
767 for (auto& group : allGroups) {
768 for (size_t i = 0; i < group.variants.size(); i++) {
769 sk_sp<DisplayList> dl = Build(group.variants[i]);
770 // Verify recapturing the replay of the display list is Equals()
771 // when dispatching directly from the DL to another builder
772 DisplayListBuilder copy_builder;
773 DlOpReceiver& r = ToReceiver(copy_builder);
774 for (DlIndex i = 0; i < dl->GetRecordCount(); i++) {
775 EXPECT_NE(dl->GetOpType(i), DisplayListOpType::kInvalidOp);
776 EXPECT_NE(dl->GetOpCategory(i),
778 EXPECT_TRUE(dl->Dispatch(r, i));
779 }
780 sk_sp<DisplayList> copy = copy_builder.Build();
781 auto desc =
782 group.op_name + "(variant " + std::to_string(i + 1) + " == copy)";
783 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, copy));
784 ASSERT_EQ(copy->op_count(false), dl->op_count(false)) << desc;
785 ASSERT_EQ(copy->GetRecordCount(), dl->GetRecordCount());
786 ASSERT_EQ(copy->bytes(false), dl->bytes(false)) << desc;
787 ASSERT_EQ(copy->op_count(true), dl->op_count(true)) << desc;
788 ASSERT_EQ(copy->bytes(true), dl->bytes(true)) << desc;
789 ASSERT_EQ(copy->total_depth(), dl->total_depth()) << desc;
790 ASSERT_EQ(copy->GetBounds(), dl->GetBounds()) << desc;
791 ASSERT_TRUE(copy->Equals(*dl)) << desc;
792 ASSERT_TRUE(dl->Equals(*copy)) << desc;
793 }
794 }
795}
796
797TEST_F(DisplayListTest, SingleOpDisplayListsCompareToEachOther) {
798 for (auto& group : allGroups) {
799 std::vector<sk_sp<DisplayList>> lists_a;
800 std::vector<sk_sp<DisplayList>> lists_b;
801 for (size_t i = 0; i < group.variants.size(); i++) {
802 lists_a.push_back(Build(group.variants[i]));
803 lists_b.push_back(Build(group.variants[i]));
804 }
805
806 for (size_t i = 0; i < lists_a.size(); i++) {
807 const sk_sp<DisplayList>& listA = lists_a[i];
808 for (size_t j = 0; j < lists_b.size(); j++) {
809 const sk_sp<DisplayList>& listB = lists_b[j];
810 auto desc = group.op_name + "(variant " + std::to_string(i + 1) +
811 " ==? variant " + std::to_string(j + 1) + ")";
812 if (i == j ||
813 (group.variants[i].is_empty() && group.variants[j].is_empty())) {
814 // They are the same variant, or both variants are NOPs
815 ASSERT_EQ(listA->op_count(false), listB->op_count(false)) << desc;
816 ASSERT_EQ(listA->bytes(false), listB->bytes(false)) << desc;
817 ASSERT_EQ(listA->op_count(true), listB->op_count(true)) << desc;
818 ASSERT_EQ(listA->bytes(true), listB->bytes(true)) << desc;
819 EXPECT_EQ(listA->total_depth(), listB->total_depth()) << desc;
820 ASSERT_EQ(listA->GetBounds(), listB->GetBounds()) << desc;
821 ASSERT_TRUE(listA->Equals(*listB)) << desc;
822 ASSERT_TRUE(listB->Equals(*listA)) << desc;
823 } else {
824 // No assertion on op/byte counts or bounds
825 // they may or may not be equal between variants
826 ASSERT_FALSE(listA->Equals(*listB)) << desc;
827 ASSERT_FALSE(listB->Equals(*listA)) << desc;
828 }
829 }
830 }
831 }
832}
833
834TEST_F(DisplayListTest, SingleOpDisplayListsAreEqualWithOrWithoutRtree) {
835 for (auto& group : allGroups) {
836 for (size_t i = 0; i < group.variants.size(); i++) {
837 DisplayListBuilder builder1(/*prepare_rtree=*/false);
838 DisplayListBuilder builder2(/*prepare_rtree=*/true);
839 group.variants[i].Invoke(ToReceiver(builder1));
840 group.variants[i].Invoke(ToReceiver(builder2));
841 sk_sp<DisplayList> dl1 = builder1.Build();
842 sk_sp<DisplayList> dl2 = builder2.Build();
843
844 auto desc = group.op_name + "(variant " + std::to_string(i + 1) + " )";
845 ASSERT_EQ(dl1->op_count(false), dl2->op_count(false)) << desc;
846 ASSERT_EQ(dl1->bytes(false), dl2->bytes(false)) << desc;
847 ASSERT_EQ(dl1->op_count(true), dl2->op_count(true)) << desc;
848 ASSERT_EQ(dl1->bytes(true), dl2->bytes(true)) << desc;
849 EXPECT_EQ(dl1->total_depth(), dl2->total_depth()) << desc;
850 ASSERT_EQ(dl1->GetBounds(), dl2->GetBounds()) << desc;
851 ASSERT_EQ(dl1->total_depth(), dl2->total_depth()) << desc;
852 ASSERT_TRUE(DisplayListsEQ_Verbose(dl1, dl2)) << desc;
853 ASSERT_TRUE(DisplayListsEQ_Verbose(dl2, dl2)) << desc;
854 ASSERT_EQ(dl1->rtree().get(), nullptr) << desc;
855 ASSERT_NE(dl2->rtree().get(), nullptr) << desc;
856 }
857 }
858}
859
860TEST_F(DisplayListTest, FullRotationsAreNop) {
861 DisplayListBuilder builder;
862 builder.Rotate(0);
863 builder.Rotate(360);
864 builder.Rotate(720);
865 builder.Rotate(1080);
866 builder.Rotate(1440);
867 sk_sp<DisplayList> dl = builder.Build();
868 ASSERT_EQ(dl->bytes(false), sizeof(DisplayList));
869 ASSERT_EQ(dl->bytes(true), sizeof(DisplayList));
870 ASSERT_EQ(dl->op_count(false), 0u);
871 ASSERT_EQ(dl->op_count(true), 0u);
872 EXPECT_EQ(dl->total_depth(), 0u);
873}
874
875TEST_F(DisplayListTest, AllBlendModeNops) {
876 DisplayListBuilder builder;
877 DlOpReceiver& receiver = ToReceiver(builder);
878 receiver.setBlendMode(DlBlendMode::kSrcOver);
879 sk_sp<DisplayList> dl = builder.Build();
880 ASSERT_EQ(dl->bytes(false), sizeof(DisplayList));
881 ASSERT_EQ(dl->bytes(true), sizeof(DisplayList));
882 ASSERT_EQ(dl->op_count(false), 0u);
883 ASSERT_EQ(dl->op_count(true), 0u);
884 EXPECT_EQ(dl->total_depth(), 0u);
885}
886
887TEST_F(DisplayListTest, DisplayListsWithVaryingOpComparisons) {
888 sk_sp<DisplayList> default_dl = Build(allGroups.size(), 0);
889 ASSERT_TRUE(default_dl->Equals(*default_dl)) << "Default == itself";
890 for (size_t gi = 0; gi < allGroups.size(); gi++) {
892 sk_sp<DisplayList> missing_dl = Build(gi, group.variants.size());
893 auto desc = "[Group " + group.op_name + " omitted]";
894 ASSERT_TRUE(missing_dl->Equals(*missing_dl)) << desc << " == itself";
895 ASSERT_FALSE(missing_dl->Equals(*default_dl)) << desc << " != Default";
896 ASSERT_FALSE(default_dl->Equals(*missing_dl)) << "Default != " << desc;
897 for (size_t vi = 0; vi < group.variants.size(); vi++) {
898 auto desc = "[Group " + group.op_name + " variant " +
899 std::to_string(vi + 1) + "]";
900 sk_sp<DisplayList> variant_dl = Build(gi, vi);
901 ASSERT_TRUE(variant_dl->Equals(*variant_dl)) << desc << " == itself";
902 if (vi == 0) {
903 ASSERT_TRUE(variant_dl->Equals(*default_dl)) << desc << " == Default";
904 ASSERT_TRUE(default_dl->Equals(*variant_dl)) << "Default == " << desc;
905 } else {
906 ASSERT_FALSE(variant_dl->Equals(*default_dl)) << desc << " != Default";
907 ASSERT_FALSE(default_dl->Equals(*variant_dl)) << "Default != " << desc;
908 }
909 if (group.variants[vi].is_empty()) {
910 ASSERT_TRUE(variant_dl->Equals(*missing_dl)) << desc << " != omitted";
911 ASSERT_TRUE(missing_dl->Equals(*variant_dl)) << "omitted != " << desc;
912 } else {
913 ASSERT_FALSE(variant_dl->Equals(*missing_dl)) << desc << " != omitted";
914 ASSERT_FALSE(missing_dl->Equals(*variant_dl)) << "omitted != " << desc;
915 }
916 }
917 }
918}
919
920TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) {
921 DlRect build_bounds = DlRect::MakeLTRB(-100, -100, 200, 200);
922 DlRect save_bounds = DlRect::MakeWH(100, 100);
923 DlRect rect = DlRect::MakeLTRB(30, 30, 70, 70);
924 // clang-format off
925 const float color_matrix[] = {
926 0, 0, 0, 0, 0,
927 0, 1, 0, 0, 0,
928 0, 0, 1, 0, 0,
929 0, 0, 0, 1, 0,
930 };
931 // clang-format on
932 auto base_color_filter = DlColorFilter::MakeMatrix(color_matrix);
933 // clang-format off
934 const float alpha_matrix[] = {
935 0, 0, 0, 0, 0,
936 0, 1, 0, 0, 0,
937 0, 0, 1, 0, 0,
938 0, 0, 0, 0, 1,
939 };
940 // clang-format on
941 auto alpha_color_filter = DlColorFilter::MakeMatrix(alpha_matrix);
942
943 {
944 // No tricky stuff, just verifying drawing a rect produces rect bounds
945 DisplayListBuilder builder(build_bounds);
946 builder.SaveLayer(save_bounds, nullptr);
947 builder.DrawRect(rect, DlPaint());
948 builder.Restore();
949 sk_sp<DisplayList> display_list = builder.Build();
950 ASSERT_EQ(display_list->GetBounds(), rect);
951 }
952
953 {
954 // Now checking that a normal color filter still produces rect bounds
955 DisplayListBuilder builder(build_bounds);
956 DlPaint save_paint;
957 save_paint.setColorFilter(base_color_filter);
958 builder.SaveLayer(save_bounds, &save_paint);
959 builder.DrawRect(rect, DlPaint());
960 builder.Restore();
961 sk_sp<DisplayList> display_list = builder.Build();
962 ASSERT_EQ(display_list->GetBounds(), rect);
963 }
964
965 {
966 // Now checking that DisplayList returns the cull rect of the
967 // DisplayListBuilder when it encounters a save layer that modifies
968 // an unbounded region
969 DisplayListBuilder builder(build_bounds);
970 DlPaint save_paint;
971 save_paint.setColorFilter(alpha_color_filter);
972 builder.SaveLayer(save_bounds, &save_paint);
973 builder.DrawRect(rect, DlPaint());
974 builder.Restore();
975 sk_sp<DisplayList> display_list = builder.Build();
976 ASSERT_EQ(display_list->GetBounds(), build_bounds);
977 }
978
979 {
980 // Verifying that the save layer bounds are not relevant
981 // to the behavior in the previous example
982 DisplayListBuilder builder(build_bounds);
983 DlPaint save_paint;
984 save_paint.setColorFilter(alpha_color_filter);
985 builder.SaveLayer(std::nullopt, &save_paint);
986 builder.DrawRect(rect, DlPaint());
987 builder.Restore();
988 sk_sp<DisplayList> display_list = builder.Build();
989 ASSERT_EQ(display_list->GetBounds(), build_bounds);
990 }
991
992 {
993 // Making sure hiding a ColorFilter as an ImageFilter will
994 // generate the same behavior as setting it as a ColorFilter
995 DisplayListBuilder builder(build_bounds);
996 DlColorFilterImageFilter color_filter_image_filter(base_color_filter);
997 DlPaint save_paint;
998 save_paint.setImageFilter(&color_filter_image_filter);
999 builder.SaveLayer(save_bounds, &save_paint);
1000 builder.DrawRect(rect, DlPaint());
1001 builder.Restore();
1002 sk_sp<DisplayList> display_list = builder.Build();
1003 ASSERT_EQ(display_list->GetBounds(), rect);
1004 }
1005
1006 {
1007 // Making sure hiding a problematic ColorFilter as an ImageFilter
1008 // will generate the same behavior as setting it as a ColorFilter
1009 DisplayListBuilder builder(build_bounds);
1010 DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter);
1011 DlPaint save_paint;
1012 save_paint.setImageFilter(&color_filter_image_filter);
1013 builder.SaveLayer(save_bounds, &save_paint);
1014 builder.DrawRect(rect, DlPaint());
1015 builder.Restore();
1016 sk_sp<DisplayList> display_list = builder.Build();
1017 ASSERT_EQ(display_list->GetBounds(), build_bounds);
1018 }
1019
1020 {
1021 // Same as above (ImageFilter hiding ColorFilter) with no save bounds
1022 DisplayListBuilder builder(build_bounds);
1023 DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter);
1024 DlPaint save_paint;
1025 save_paint.setImageFilter(&color_filter_image_filter);
1026 builder.SaveLayer(std::nullopt, &save_paint);
1027 builder.DrawRect(rect, DlPaint());
1028 builder.Restore();
1029 sk_sp<DisplayList> display_list = builder.Build();
1030 ASSERT_EQ(display_list->GetBounds(), build_bounds);
1031 }
1032
1033 {
1034 // Testing behavior with an unboundable blend mode
1035 DisplayListBuilder builder(build_bounds);
1036 DlPaint save_paint;
1037 save_paint.setBlendMode(DlBlendMode::kClear);
1038 builder.SaveLayer(save_bounds, &save_paint);
1039 builder.DrawRect(rect, DlPaint());
1040 builder.Restore();
1041 sk_sp<DisplayList> display_list = builder.Build();
1042 ASSERT_EQ(display_list->GetBounds(), build_bounds);
1043 }
1044
1045 {
1046 // Same as previous with no save bounds
1047 DisplayListBuilder builder(build_bounds);
1048 DlPaint save_paint;
1049 save_paint.setBlendMode(DlBlendMode::kClear);
1050 builder.SaveLayer(std::nullopt, &save_paint);
1051 builder.DrawRect(rect, DlPaint());
1052 builder.Restore();
1053 sk_sp<DisplayList> display_list = builder.Build();
1054 ASSERT_EQ(display_list->GetBounds(), build_bounds);
1055 }
1056}
1057
1058TEST_F(DisplayListTest, NestedOpCountMetrics) {
1059 DisplayListBuilder builder(DlRect::MakeWH(150, 100));
1060 DlPaint dl_paint;
1061 for (int y = 10; y <= 60; y += 10) {
1062 for (int x = 10; x <= 60; x += 10) {
1063 dl_paint.setColor(((x + y) % 20) == 10 ? DlColor::kRed()
1064 : DlColor::kBlue());
1065 builder.DrawRect(DlRect::MakeXYWH(x, y, 80, 80), dl_paint);
1066 }
1067 }
1068
1069 DisplayListBuilder outer_builder(DlRect::MakeWH(150, 100));
1070 outer_builder.DrawDisplayList(builder.Build());
1071 auto display_list = outer_builder.Build();
1072
1073 ASSERT_EQ(display_list->op_count(), 1u);
1074 ASSERT_EQ(display_list->op_count(true), 36u);
1075 EXPECT_EQ(display_list->total_depth(), 37u);
1076}
1077
1078TEST_F(DisplayListTest, DisplayListFullPerspectiveTransformHandling) {
1079 auto matrix = DlMatrix::MakeRow(
1080 // clang-format off
1081 1, 2, 3, 4,
1082 5, 6, 7, 8,
1083 9, 10, 11, 12,
1084 13, 14, 15, 16
1085 // clang-format on
1086 );
1087
1088 { // First test ==
1089 DisplayListBuilder builder;
1090 // builder.TransformFullPerspective takes row-major order
1092 // clang-format off
1093 1, 2, 3, 4,
1094 5, 6, 7, 8,
1095 9, 10, 11, 12,
1096 13, 14, 15, 16
1097 // clang-format on
1098 );
1099 DlMatrix dl_matrix = builder.GetMatrix();
1100 ASSERT_EQ(dl_matrix, matrix);
1101 }
1102 { // Next test !=
1103 DisplayListBuilder builder;
1104 // builder.TransformFullPerspective takes row-major order
1106 // clang-format off
1107 1, 5, 9, 13,
1108 2, 6, 7, 11,
1109 3, 7, 11, 15,
1110 4, 8, 12, 16
1111 // clang-format on
1112 );
1113 DlMatrix dl_matrix = builder.GetMatrix();
1114 ASSERT_NE(dl_matrix, matrix);
1115 }
1116}
1117
1118TEST_F(DisplayListTest, DisplayListTransformResetHandling) {
1119 DisplayListBuilder builder;
1120 builder.Scale(20.0, 20.0);
1121 builder.TransformReset();
1122 ASSERT_TRUE(builder.GetMatrix().IsIdentity());
1123}
1124
1125TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) {
1126 auto run_tests = [](const std::string& name,
1127 void build(DlCanvas & canvas, const DlPaint& paint),
1128 bool expect_for_op, bool expect_with_kSrc) {
1129 {
1130 // First test is the draw op, by itself
1131 // (usually supports group opacity)
1132 DisplayListBuilder builder;
1133 DlPaint paint;
1134 build(builder, paint);
1135 auto display_list = builder.Build();
1136 EXPECT_EQ(display_list->can_apply_group_opacity(), expect_for_op)
1137 << "{" << std::endl
1138 << " " << name << std::endl
1139 << "}";
1140 }
1141 {
1142 // Second test is the draw op with kSrc,
1143 // (usually fails group opacity)
1144 DisplayListBuilder builder;
1145 DlPaint paint;
1146 paint.setBlendMode(DlBlendMode::kSrc);
1147 build(builder, paint);
1148 auto display_list = builder.Build();
1149 EXPECT_EQ(display_list->can_apply_group_opacity(), expect_with_kSrc)
1150 << "{" << std::endl
1151 << " receiver.setBlendMode(kSrc);" << std::endl
1152 << " " << name << std::endl
1153 << "}";
1154 }
1155 };
1156
1157#define RUN_TESTS(body) \
1158 run_tests( \
1159 #body, [](DlCanvas& canvas, const DlPaint& paint) { body }, true, false)
1160
1161#define RUN_TESTS2(body, expect) \
1162 run_tests( \
1163 #body, [](DlCanvas& canvas, const DlPaint& paint) { body }, expect, \
1164 expect)
1165
1166 RUN_TESTS(canvas.DrawPaint(paint););
1167 RUN_TESTS2(canvas.DrawColor(DlColor(SK_ColorRED), DlBlendMode::kSrcOver);
1168 , true);
1169 RUN_TESTS2(canvas.DrawColor(DlColor(SK_ColorRED), DlBlendMode::kSrc);, false);
1170 RUN_TESTS(canvas.DrawLine(DlPoint(0, 0), DlPoint(10, 10), paint););
1171 RUN_TESTS(canvas.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10), paint););
1172 RUN_TESTS(canvas.DrawOval(DlRect::MakeLTRB(0, 0, 10, 10), paint););
1173 RUN_TESTS(canvas.DrawCircle(DlPoint(10, 10), 5, paint););
1174 RUN_TESTS(canvas.DrawRoundRect(
1175 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(0, 0, 10, 10), 2, 2), paint););
1177 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(0, 0, 10, 10), 2, 2),
1178 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(2, 2, 8, 8), 2, 2), paint););
1179 RUN_TESTS(canvas.DrawPath(
1180 DlPath::MakeOvalLTRB(0, 0, 10, 10) + DlPath::MakeOvalLTRB(5, 5, 15, 15),
1181 paint););
1182 RUN_TESTS(canvas.DrawArc(DlRect::MakeLTRB(0, 0, 10, 10), 0, math::kPi, true,
1183 paint););
1185 kTestPoints, paint);
1186 , false);
1187 RUN_TESTS2(canvas.DrawVertices(kTestVertices1, DlBlendMode::kSrc, paint);
1188 , false);
1191 , true);
1192 RUN_TESTS(canvas.DrawImageRect(kTestImage1, DlIRect::MakeLTRB(10, 10, 20, 20),
1193 DlRect::MakeLTRB(0, 0, 10, 10),
1194 kNearestSampling, &paint,
1196 RUN_TESTS2(
1197 canvas.DrawImageRect(kTestImage1, DlIRect::MakeLTRB(10, 10, 20, 20),
1198 DlRect::MakeLTRB(0, 0, 10, 10), kNearestSampling,
1200 , true);
1201 RUN_TESTS(canvas.DrawImageNine(kTestImage2, DlIRect::MakeLTRB(20, 20, 30, 30),
1202 DlRect::MakeLTRB(0, 0, 20, 20),
1203 DlFilterMode::kLinear, &paint););
1205 kTestImage2, DlIRect::MakeLTRB(20, 20, 30, 30),
1206 DlRect::MakeLTRB(0, 0, 20, 20), DlFilterMode::kLinear, nullptr);
1207 , true);
1208 static DlRSTransform xforms[] = {
1209 DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
1210 DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
1211 };
1212 static DlRect texs[] = {
1213 DlRect::MakeLTRB(10, 10, 20, 20),
1214 DlRect::MakeLTRB(20, 20, 30, 30),
1215 };
1216 RUN_TESTS2(
1217 canvas.DrawAtlas(kTestImage1, xforms, texs, nullptr, 2,
1218 DlBlendMode::kSrcIn, kNearestSampling, nullptr, &paint);
1219 , false);
1220 RUN_TESTS2(
1221 canvas.DrawAtlas(kTestImage1, xforms, texs, nullptr, 2,
1222 DlBlendMode::kSrcIn, kNearestSampling, nullptr, nullptr);
1223 , false);
1224 EXPECT_TRUE(TestDisplayList1->can_apply_group_opacity());
1226 {
1227 static DisplayListBuilder builder;
1228 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10), DlPaint());
1229 builder.DrawRect(DlRect::MakeLTRB(5, 5, 15, 15), DlPaint());
1230 static auto display_list = builder.Build();
1231 RUN_TESTS2(canvas.DrawDisplayList(display_list);, false);
1232 }
1233 RUN_TESTS2(canvas.DrawText(DlTextSkia::Make(GetTestTextBlob(1)), 0, 0, paint);
1234 , false);
1235#if IMPELLER_SUPPORTS_RENDERING
1236 RUN_TESTS2(
1237 canvas.DrawText(DlTextImpeller::Make(GetTestTextFrame(1)), 0, 0, paint);
1238 , false);
1239#endif
1240 RUN_TESTS2(canvas.DrawShadow(kTestPath1, DlColor::kBlack(), 1.0, false, 1.0);
1241 , false);
1242
1243#undef RUN_TESTS2
1244#undef RUN_TESTS
1245}
1246
1247TEST_F(DisplayListTest, OverlappingOpsDoNotSupportGroupOpacity) {
1248 DisplayListBuilder builder;
1249 for (int i = 0; i < 10; i++) {
1250 builder.DrawRect(DlRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint());
1251 }
1252 auto display_list = builder.Build();
1253 EXPECT_FALSE(display_list->can_apply_group_opacity());
1254}
1255
1256TEST_F(DisplayListTest, LineOfNonOverlappingOpsSupportGroupOpacity) {
1257 DisplayListBuilder builder;
1258 for (int i = 0; i < 10; i++) {
1259 builder.DrawRect(DlRect::MakeXYWH(i * 30, 0, 30, 30), DlPaint());
1260 }
1261 auto display_list = builder.Build();
1262 EXPECT_TRUE(display_list->can_apply_group_opacity());
1263}
1264
1265TEST_F(DisplayListTest, CrossOfNonOverlappingOpsSupportGroupOpacity) {
1266 DisplayListBuilder builder;
1267 builder.DrawRect(DlRect::MakeLTRB(200, 200, 300, 300), DlPaint()); // center
1268 builder.DrawRect(DlRect::MakeLTRB(100, 200, 200, 300), DlPaint()); // left
1269 builder.DrawRect(DlRect::MakeLTRB(200, 100, 300, 200), DlPaint()); // above
1270 builder.DrawRect(DlRect::MakeLTRB(300, 200, 400, 300), DlPaint()); // right
1271 builder.DrawRect(DlRect::MakeLTRB(200, 300, 300, 400), DlPaint()); // below
1272 auto display_list = builder.Build();
1273 EXPECT_TRUE(display_list->can_apply_group_opacity());
1274}
1275
1276TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityOverlappingChidren) {
1277 DisplayListBuilder builder;
1278 builder.SaveLayer(std::nullopt, nullptr);
1279 for (int i = 0; i < 10; i++) {
1280 builder.DrawRect(DlRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint());
1281 }
1282 builder.Restore();
1283 auto display_list = builder.Build();
1284 EXPECT_TRUE(display_list->can_apply_group_opacity());
1285}
1286
1287TEST_F(DisplayListTest, SaveLayerTrueSupportsGroupOpacityOverlappingChidren) {
1288 DisplayListBuilder builder;
1289 DlPaint save_paint;
1290 builder.SaveLayer(std::nullopt, &save_paint);
1291 for (int i = 0; i < 10; i++) {
1292 builder.DrawRect(DlRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint());
1293 }
1294 builder.Restore();
1295 auto display_list = builder.Build();
1296 EXPECT_TRUE(display_list->can_apply_group_opacity());
1297}
1298
1299TEST_F(DisplayListTest, SaveLayerFalseWithSrcBlendSupportsGroupOpacity) {
1300 DisplayListBuilder builder;
1301 // This empty draw rect will not actually be inserted into the stream,
1302 // but the Src blend mode will be synchronized as an attribute. The
1303 // SaveLayer following it should not use that attribute to base its
1304 // decisions about group opacity and the draw rect after that comes
1305 // with its own compatible blend mode.
1306 builder.DrawRect(DlRect(), DlPaint().setBlendMode(DlBlendMode::kSrc));
1307 builder.SaveLayer(std::nullopt, nullptr);
1308 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10), DlPaint());
1309 builder.Restore();
1310 auto display_list = builder.Build();
1311 EXPECT_TRUE(display_list->can_apply_group_opacity());
1312}
1313
1314TEST_F(DisplayListTest, SaveLayerTrueWithSrcBlendDoesNotSupportGroupOpacity) {
1315 DisplayListBuilder builder;
1316 DlPaint save_paint;
1317 save_paint.setBlendMode(DlBlendMode::kSrc);
1318 builder.SaveLayer(std::nullopt, &save_paint);
1319 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10), DlPaint());
1320 builder.Restore();
1321 auto display_list = builder.Build();
1322 EXPECT_FALSE(display_list->can_apply_group_opacity());
1323}
1324
1325TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityWithChildSrcBlend) {
1326 DisplayListBuilder builder;
1327 builder.SaveLayer(std::nullopt, nullptr);
1328 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
1329 DlPaint().setBlendMode(DlBlendMode::kSrc));
1330 builder.Restore();
1331 auto display_list = builder.Build();
1332 EXPECT_TRUE(display_list->can_apply_group_opacity());
1333}
1334
1335TEST_F(DisplayListTest, SaveLayerTrueSupportsGroupOpacityWithChildSrcBlend) {
1336 DisplayListBuilder builder;
1337 DlPaint save_paint;
1338 builder.SaveLayer(std::nullopt, &save_paint);
1339 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
1340 DlPaint().setBlendMode(DlBlendMode::kSrc));
1341 builder.Restore();
1342 auto display_list = builder.Build();
1343 EXPECT_TRUE(display_list->can_apply_group_opacity());
1344}
1345
1346TEST_F(DisplayListTest, SaveLayerBoundsSnapshotsImageFilter) {
1347 DisplayListBuilder builder;
1348 DlPaint save_paint;
1349 builder.SaveLayer(std::nullopt, &save_paint);
1350 builder.DrawRect(DlRect::MakeLTRB(50, 50, 100, 100), DlPaint());
1351 // This image filter should be ignored since it was not set before SaveLayer
1352 // And the rect drawn with it will not contribute any more area to the bounds
1353 DlPaint draw_paint;
1355 builder.DrawRect(DlRect::MakeLTRB(70, 70, 80, 80), draw_paint);
1356 builder.Restore();
1357 EXPECT_EQ(builder.Build()->GetBounds(), DlRect::MakeLTRB(50, 50, 100, 100));
1358}
1359
1360#define SAVE_LAYER_EXPECTOR(name) SaveLayerExpector name(__FILE__, __LINE__)
1361
1363 std::function<bool(const SaveLayerOptions& options)>;
1364
1367
1368 // NOLINTNEXTLINE(google-explicit-constructor)
1370 // NOLINTNEXTLINE(google-explicit-constructor)
1372 // NOLINTNEXTLINE(google-explicit-constructor)
1374
1375 std::optional<SaveLayerOptions> options;
1376 std::optional<SaveLayerOptionsTester> tester;
1377 std::optional<DlBlendMode> max_blend_mode;
1379
1380::std::ostream& operator<<(::std::ostream& os,
1381 const SaveLayerExpectations& expect) {
1382 os << "SaveLayerExpectation(";
1383 if (expect.options.has_value()) {
1384 os << "options: " << expect.options.value();
1385 }
1386 if (expect.tester.has_value()) {
1387 os << "option tester: " << &expect.tester.value();
1388 }
1389 if (expect.max_blend_mode.has_value()) {
1390 os << "max_blend: " << expect.max_blend_mode.value();
1391 }
1392 os << ")";
1393 return os;
1394}
1395
1396class SaveLayerExpector : public virtual DlOpReceiver,
1401 public:
1402 SaveLayerExpector(const std::string& file, int line)
1403 : file_(file), line_(line), detail_("") {}
1404
1406 EXPECT_EQ(save_layer_count_, expected_.size()) << label();
1407 while (save_layer_count_ < expected_.size()) {
1408 auto expect = expected_[save_layer_count_];
1409 FML_LOG(ERROR) << "leftover expectation[" << save_layer_count_
1410 << "] = " << expect;
1411 save_layer_count_++;
1412 }
1413 }
1414
1415 SaveLayerExpector& addDetail(const std::string& detail) {
1416 detail_ = detail;
1417 return *this;
1418 }
1419
1421 expected_.push_back(expected);
1422 return *this;
1423 }
1424
1426 expected_.push_back(SaveLayerExpectations(tester));
1427 return *this;
1428 }
1429
1431 expected_.push_back(SaveLayerExpectations());
1432 return *this;
1433 }
1434
1435 void saveLayer(const DlRect& bounds,
1436 const SaveLayerOptions options,
1437 const DlImageFilter* backdrop,
1438 std::optional<int64_t> backdrop_id) override {
1440 }
1441
1442 virtual void saveLayer(const DlRect& bounds,
1443 const SaveLayerOptions& options,
1444 uint32_t total_content_depth,
1445 DlBlendMode max_content_blend_mode,
1446 const DlImageFilter* backdrop = nullptr,
1447 std::optional<int64_t> backdrop_id = std::nullopt) {
1448 ASSERT_LT(save_layer_count_, expected_.size()) << label();
1449 auto expect = expected_[save_layer_count_];
1450 if (expect.options.has_value()) {
1451 EXPECT_EQ(options, expect.options.value()) << label();
1452 }
1453 if (expect.tester.has_value()) {
1454 EXPECT_TRUE(expect.tester.value()(options)) << label();
1455 }
1456 if (expect.max_blend_mode.has_value()) {
1457 EXPECT_EQ(max_content_blend_mode, expect.max_blend_mode.value())
1458 << label();
1459 }
1460 save_layer_count_++;
1461 }
1462
1464 return save_layer_count_ == expected_.size();
1465 }
1466
1467 private:
1468 // mutable allows the copy constructor to leave no expectations behind
1469 mutable std::vector<SaveLayerExpectations> expected_;
1470 size_t save_layer_count_ = 0;
1471
1472 const std::string file_;
1473 const int line_;
1474 std::string detail_;
1475
1476 std::string label() {
1477 std::string label = "at index " + std::to_string(save_layer_count_) + //
1478 ", from " + file_ + //
1479 ":" + std::to_string(line_);
1480 if (detail_.length() > 0) {
1481 label = label + " (" + detail_ + ")";
1482 }
1483 return label;
1484 }
1485};
1486
1487TEST_F(DisplayListTest, SaveLayerOneSimpleOpInheritsOpacity) {
1488 SAVE_LAYER_EXPECTOR(expector);
1489 expector.addExpectation(
1490 SaveLayerOptions::kWithAttributes.with_can_distribute_opacity());
1491
1492 DisplayListBuilder builder;
1493 DlPaint save_paint;
1494 save_paint.setColor(DlColor::kWhite().withAlphaF(0.5f));
1495 builder.SaveLayer(std::nullopt, &save_paint);
1496 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
1497 builder.Restore();
1498
1499 builder.Build()->Dispatch(expector);
1500 EXPECT_TRUE(expector.all_expectations_checked());
1501}
1502
1503TEST_F(DisplayListTest, SaveLayerNoAttributesInheritsOpacity) {
1504 SAVE_LAYER_EXPECTOR(expector);
1505 expector.addExpectation(
1506 SaveLayerOptions::kNoAttributes.with_can_distribute_opacity());
1507
1508 DisplayListBuilder builder;
1509 builder.SaveLayer(std::nullopt, nullptr);
1510 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
1511 builder.Restore();
1512
1513 builder.Build()->Dispatch(expector);
1514 EXPECT_TRUE(expector.all_expectations_checked());
1515}
1516
1517TEST_F(DisplayListTest, SaveLayerTwoOverlappingOpsDoesNotInheritOpacity) {
1518 SAVE_LAYER_EXPECTOR(expector);
1519 expector.addExpectation(SaveLayerOptions::kWithAttributes);
1520
1521 DisplayListBuilder builder;
1522 DlPaint save_paint;
1523 save_paint.setColor(DlColor::kWhite().withAlphaF(0.5f));
1524 builder.SaveLayer(std::nullopt, &save_paint);
1525 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
1526 builder.DrawRect(DlRect::MakeLTRB(15, 15, 25, 25), DlPaint());
1527 builder.Restore();
1528
1529 builder.Build()->Dispatch(expector);
1530 EXPECT_TRUE(expector.all_expectations_checked());
1531}
1532
1533TEST_F(DisplayListTest, NestedSaveLayersMightInheritOpacity) {
1534 SAVE_LAYER_EXPECTOR(expector);
1535 expector //
1536 .addExpectation(
1537 SaveLayerOptions::kWithAttributes.with_can_distribute_opacity())
1538 .addExpectation(SaveLayerOptions::kWithAttributes)
1539 .addExpectation(
1540 SaveLayerOptions::kWithAttributes.with_can_distribute_opacity());
1541
1542 DisplayListBuilder builder;
1543 DlPaint save_paint;
1544 save_paint.setColor(DlColor::kWhite().withAlphaF(0.5f));
1545 builder.SaveLayer(std::nullopt, &save_paint);
1546 builder.SaveLayer(std::nullopt, &save_paint);
1547 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
1548 builder.SaveLayer(std::nullopt, &save_paint);
1549 builder.DrawRect(DlRect::MakeLTRB(15, 15, 25, 25), DlPaint());
1550 builder.Restore();
1551 builder.Restore();
1552 builder.Restore();
1553
1554 builder.Build()->Dispatch(expector);
1555 EXPECT_TRUE(expector.all_expectations_checked());
1556}
1557
1558TEST_F(DisplayListTest, NestedSaveLayersCanBothSupportOpacityOptimization) {
1559 SAVE_LAYER_EXPECTOR(expector);
1560 expector //
1561 .addExpectation(
1562 SaveLayerOptions::kWithAttributes.with_can_distribute_opacity())
1563 .addExpectation(
1564 SaveLayerOptions::kNoAttributes.with_can_distribute_opacity());
1565
1566 DisplayListBuilder builder;
1567 DlPaint save_paint;
1568 save_paint.setColor(DlColor::kWhite().withAlphaF(0.5f));
1569 builder.SaveLayer(std::nullopt, &save_paint);
1570 builder.SaveLayer(std::nullopt, nullptr);
1571 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
1572 builder.Restore();
1573 builder.Restore();
1574
1575 builder.Build()->Dispatch(expector);
1576 EXPECT_TRUE(expector.all_expectations_checked());
1577}
1578
1579TEST_F(DisplayListTest, SaveLayerImageFilterDoesNotInheritOpacity) {
1580 SAVE_LAYER_EXPECTOR(expector);
1581 expector.addExpectation(SaveLayerOptions::kWithAttributes);
1582
1583 DisplayListBuilder builder;
1584 DlPaint save_paint;
1585 save_paint.setColor(DlColor::kWhite().withAlphaF(0.5f));
1587 builder.SaveLayer(std::nullopt, &save_paint);
1588 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
1589 builder.Restore();
1590
1591 builder.Build()->Dispatch(expector);
1592 EXPECT_TRUE(expector.all_expectations_checked());
1593}
1594
1595TEST_F(DisplayListTest, SaveLayerColorFilterDoesNotInheritOpacity) {
1596 SAVE_LAYER_EXPECTOR(expector);
1597 expector.addExpectation(SaveLayerOptions::kWithAttributes);
1598
1599 DisplayListBuilder builder;
1600 DlPaint save_paint;
1601 save_paint.setColor(DlColor::kWhite().withAlphaF(0.5f));
1603 builder.SaveLayer(std::nullopt, &save_paint);
1604 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
1605 builder.Restore();
1606
1607 builder.Build()->Dispatch(expector);
1608 EXPECT_TRUE(expector.all_expectations_checked());
1609}
1610
1611TEST_F(DisplayListTest, SaveLayerSrcBlendDoesNotInheritOpacity) {
1612 SAVE_LAYER_EXPECTOR(expector);
1613 expector.addExpectation(SaveLayerOptions::kWithAttributes);
1614
1615 DisplayListBuilder builder;
1616 DlPaint save_paint;
1617 save_paint.setColor(DlColor::kWhite().withAlphaF(0.5f));
1618 save_paint.setBlendMode(DlBlendMode::kSrc);
1619 builder.SaveLayer(std::nullopt, &save_paint);
1620 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
1621 builder.Restore();
1622
1623 builder.Build()->Dispatch(expector);
1624 EXPECT_TRUE(expector.all_expectations_checked());
1625}
1626
1627TEST_F(DisplayListTest, SaveLayerImageFilterOnChildInheritsOpacity) {
1628 SAVE_LAYER_EXPECTOR(expector);
1629 expector.addExpectation(
1630 SaveLayerOptions::kWithAttributes.with_can_distribute_opacity());
1631
1632 DisplayListBuilder builder;
1633 DlPaint save_paint;
1634 save_paint.setColor(DlColor::kWhite().withAlphaF(0.5f));
1635 builder.SaveLayer(std::nullopt, &save_paint);
1636 DlPaint draw_paint = save_paint;
1638 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), draw_paint);
1639 builder.Restore();
1640
1641 builder.Build()->Dispatch(expector);
1642 EXPECT_TRUE(expector.all_expectations_checked());
1643}
1644
1645TEST_F(DisplayListTest, SaveLayerColorFilterOnChildDoesNotInheritOpacity) {
1646 SAVE_LAYER_EXPECTOR(expector);
1647 expector.addExpectation(SaveLayerOptions::kWithAttributes);
1648
1649 DisplayListBuilder builder;
1650 DlPaint save_paint;
1651 save_paint.setColor(DlColor::kWhite().withAlphaF(0.5f));
1652 builder.SaveLayer(std::nullopt, &save_paint);
1653 DlPaint draw_paint = save_paint;
1655 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), draw_paint);
1656 builder.Restore();
1657
1658 builder.Build()->Dispatch(expector);
1659 EXPECT_TRUE(expector.all_expectations_checked());
1660}
1661
1662TEST_F(DisplayListTest, SaveLayerSrcBlendOnChildDoesNotInheritOpacity) {
1663 SAVE_LAYER_EXPECTOR(expector);
1664 expector.addExpectation(SaveLayerOptions::kWithAttributes);
1665
1666 DisplayListBuilder builder;
1667 DlPaint save_paint;
1668 save_paint.setColor(DlColor::kWhite().withAlphaF(0.5f));
1669 builder.SaveLayer(std::nullopt, &save_paint);
1670 DlPaint draw_paint = save_paint;
1671 draw_paint.setBlendMode(DlBlendMode::kSrc);
1672 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), draw_paint);
1673 builder.Restore();
1674
1675 builder.Build()->Dispatch(expector);
1676 EXPECT_TRUE(expector.all_expectations_checked());
1677}
1678
1679TEST_F(DisplayListTest, FlutterSvgIssue661BoundsWereEmpty) {
1680 // See https://github.com/dnfield/flutter_svg/issues/661
1681
1682 DlPathBuilder path_builder1;
1683 path_builder1.MoveTo({25.54f, 37.52f});
1684 path_builder1.CubicCurveTo({20.91f, 37.52f}, //
1685 {16.54f, 33.39f}, //
1686 {13.62f, 30.58f});
1687 path_builder1.LineTo({13, 30});
1688 path_builder1.LineTo({12.45f, 29.42f});
1689 path_builder1.CubicCurveTo({8.39f, 25.15f}, //
1690 {1.61f, 18}, //
1691 {8.37f, 11.27f});
1692 path_builder1.CubicCurveTo({10.18f, 9.46f}, //
1693 {12.37f, 9.58f}, //
1694 {14.49f, 11.58f});
1695 path_builder1.CubicCurveTo({15.67f, 12.71f}, //
1696 {17.05f, 14.69f}, //
1697 {17.07f, 16.58f});
1698 path_builder1.CubicCurveTo({17.0968f, 17.458f}, //
1699 {16.7603f, 18.3081f}, //
1700 {16.14f, 18.93f});
1701 path_builder1.CubicCurveTo({15.8168f, 19.239f}, //
1702 {15.4653f, 19.5169f}, //
1703 {15.09f, 19.76f});
1704 path_builder1.CubicCurveTo({14.27f, 20.33f}, //
1705 {14.21f, 20.44f}, //
1706 {14.27f, 20.62f});
1707 path_builder1.CubicCurveTo({15.1672f, 22.3493f}, //
1708 {16.3239f, 23.9309f}, //
1709 {17.7f, 25.31f});
1710 path_builder1.CubicCurveTo({19.0791f, 26.6861f}, //
1711 {20.6607f, 27.8428f}, //
1712 {22.39f, 28.74f});
1713 path_builder1.CubicCurveTo({22.57f, 28.8f}, //
1714 {22.69f, 28.74f}, //
1715 {23.25f, 27.92f});
1716 path_builder1.CubicCurveTo({23.5f, 27.566f}, //
1717 {23.778f, 27.231f}, //
1718 {24.08f, 26.92f});
1719 path_builder1.CubicCurveTo({24.7045f, 26.3048f}, //
1720 {25.5538f, 25.9723f}, //
1721 {26.43f, 26});
1722 path_builder1.CubicCurveTo({28.29f, 26}, //
1723 {30.27f, 27.4f}, //
1724 {31.43f, 28.58f});
1725 path_builder1.CubicCurveTo({33.43f, 30.67f}, //
1726 {33.55f, 32.9f}, //
1727 {31.74f, 34.7f});
1728 path_builder1.CubicCurveTo({30.1477f, 36.4508f}, //
1729 {27.906f, 37.4704f}, //
1730 {25.54f, 37.52f});
1731 path_builder1.Close();
1732 path_builder1.MoveTo({11.17f, 12.23f});
1733 path_builder1.CubicCurveTo({10.6946f, 12.2571f}, //
1734 {10.2522f, 12.4819f}, //
1735 {9.95f, 12.85f});
1736 path_builder1.CubicCurveTo({5.12f, 17.67f}, //
1737 {8.95f, 22.5f}, //
1738 {14.05f, 27.85f});
1739 path_builder1.LineTo({14.62f, 28.45f});
1740 path_builder1.LineTo({15.16f, 28.96f});
1741 path_builder1.CubicCurveTo({20.52f, 34.06f}, //
1742 {25.35f, 37.89f}, //
1743 {30.16f, 33.06f});
1744 path_builder1.CubicCurveTo({30.83f, 32.39f}, //
1745 {31.25f, 31.56f}, //
1746 {29.81f, 30.06f});
1747 path_builder1.CubicCurveTo({28.9247f, 29.07f}, //
1748 {27.7359f, 28.4018f}, //
1749 {26.43f, 28.16f});
1750 path_builder1.CubicCurveTo({26.1476f, 28.1284f}, //
1751 {25.8676f, 28.2367f}, //
1752 {25.68f, 28.45f});
1753 path_builder1.CubicCurveTo({25.4633f, 28.6774f}, //
1754 {25.269f, 28.9252f}, //
1755 {25.1f, 29.19f});
1756 path_builder1.CubicCurveTo({24.53f, 30.01f}, //
1757 {23.47f, 31.54f}, //
1758 {21.54f, 30.79f});
1759 path_builder1.LineTo({21.41f, 30.72f});
1760 path_builder1.CubicCurveTo({19.4601f, 29.7156f}, //
1761 {17.6787f, 28.4133f}, //
1762 {16.13f, 26.86f});
1763 path_builder1.CubicCurveTo({14.5748f, 25.3106f}, //
1764 {13.2693f, 23.5295f}, //
1765 {12.26f, 21.58f});
1766 path_builder1.LineTo({12.2f, 21.44f});
1767 path_builder1.CubicCurveTo({11.45f, 19.51f}, //
1768 {12.97f, 18.44f}, //
1769 {13.8f, 17.88f});
1770 path_builder1.CubicCurveTo({14.061f, 17.706f}, //
1771 {14.308f, 17.512f}, //
1772 {14.54f, 17.3f});
1773 path_builder1.CubicCurveTo({14.7379f, 17.1067f}, //
1774 {14.8404f, 16.8359f}, //
1775 {14.82f, 16.56f});
1776 path_builder1.CubicCurveTo({14.5978f, 15.268f}, //
1777 {13.9585f, 14.0843f}, //
1778 {13, 13.19f});
1779 path_builder1.CubicCurveTo({12.5398f, 12.642f}, //
1780 {11.8824f, 12.2971f}, //
1781 {11.17f, 12.23f});
1782 path_builder1.LineTo({11.17f, 12.23f});
1783 path_builder1.Close();
1784 path_builder1.MoveTo({27, 19.34f});
1785 path_builder1.LineTo({24.74f, 19.34f});
1786 path_builder1.CubicCurveTo({24.7319f, 18.758f}, //
1787 {24.262f, 18.2881f}, //
1788 {23.68f, 18.28f});
1789 path_builder1.LineTo({23.68f, 16.05f});
1790 path_builder1.LineTo({23.7f, 16.05f});
1791 path_builder1.CubicCurveTo({25.5153f, 16.0582f}, //
1792 {26.9863f, 17.5248f}, //
1793 {27, 19.34f});
1794 path_builder1.LineTo({27, 19.34f});
1795 path_builder1.Close();
1796 path_builder1.MoveTo({32.3f, 19.34f});
1797 path_builder1.LineTo({30.07f, 19.34f});
1798 path_builder1.CubicCurveTo({30.037f, 15.859f}, //
1799 {27.171f, 13.011f}, //
1800 {23.69f, 13});
1801 path_builder1.LineTo({23.69f, 10.72f});
1802 path_builder1.CubicCurveTo({28.415f, 10.725f}, //
1803 {32.3f, 14.615f}, //
1804 {32.3f, 19.34f});
1805 path_builder1.Close();
1806 path_builder1.SetFillType(DlPathFillType::kNonZero);
1807 DlPath dl_path1 = path_builder1.TakePath();
1808
1809 DlPathBuilder path_builder2;
1810 path_builder2.MoveTo({37.5f, 19.33f});
1811 path_builder2.LineTo({35.27f, 19.33f});
1812 path_builder2.CubicCurveTo({35.265f, 12.979f}, //
1813 {30.041f, 7.755f}, //
1814 {23.69f, 7.75f});
1815 path_builder2.LineTo({23.69f, 5.52f});
1816 path_builder2.CubicCurveTo({31.264f, 5.525f}, //
1817 {37.495f, 11.756f}, //
1818 {37.5f, 19.33f});
1819 path_builder2.Close();
1820 path_builder2.SetFillType(DlPathFillType::kNonZero);
1821 DlPath dl_path2 = path_builder2.TakePath();
1822
1823 DisplayListBuilder builder;
1825 {
1826 builder.Save();
1827 builder.ClipRect(DlRect::MakeLTRB(0, 0, 100, 100), DlClipOp::kIntersect,
1828 true);
1829 {
1830 builder.Save();
1831 builder.Transform2DAffine(2.17391, 0, -2547.83, //
1832 0, 2.04082, -500);
1833 {
1834 builder.Save();
1835 builder.ClipRect(DlRect::MakeLTRB(1172, 245, 1218, 294),
1836 DlClipOp::kIntersect, true);
1837 {
1838 builder.SaveLayer(std::nullopt, nullptr, nullptr);
1839 {
1840 builder.Save();
1841 builder.Transform2DAffine(1.4375, 0, 1164.09, //
1842 0, 1.53125, 236.548);
1843 builder.DrawPath(DlPath(dl_path1), paint);
1844 builder.Restore();
1845 }
1846 {
1847 builder.Save();
1848 builder.Transform2DAffine(1.4375, 0, 1164.09, //
1849 0, 1.53125, 236.548);
1850 builder.DrawPath(DlPath(dl_path2), paint);
1851 builder.Restore();
1852 }
1853 builder.Restore();
1854 }
1855 builder.Restore();
1856 }
1857 builder.Restore();
1858 }
1859 builder.Restore();
1860 }
1861 sk_sp<DisplayList> display_list = builder.Build();
1862 // Prior to the fix, the bounds were empty.
1863 EXPECT_FALSE(display_list->GetBounds().IsEmpty());
1864 // These are just inside and outside of the expected bounds, but
1865 // testing float values can be flaky wrt minor changes in the bounds
1866 // calculations. If these lines have to be revised too often as the DL
1867 // implementation is improved and maintained, then we can eliminate
1868 // this test and just rely on the "rounded out" bounds test that follows.
1869 DlRect min_bounds = DlRect::MakeLTRB(0, 0.00191, 99.983, 100);
1870 DlRect max_bounds = DlRect::MakeLTRB(0, 0.00189, 99.985, 100);
1871 ASSERT_TRUE(max_bounds.Contains(min_bounds));
1872 EXPECT_TRUE(max_bounds.Contains(display_list->GetBounds()));
1873 EXPECT_TRUE(display_list->GetBounds().Contains(min_bounds));
1874
1875 // This is the more practical result. The bounds are "almost" 0,0,100x100
1876 EXPECT_EQ(DlIRect::RoundOut(display_list->GetBounds()),
1877 DlIRect::MakeWH(100, 100));
1878 EXPECT_EQ(display_list->op_count(), 19u);
1879 EXPECT_EQ(display_list->bytes(), sizeof(DisplayList) + 408u);
1880 EXPECT_EQ(display_list->total_depth(), 3u);
1881}
1882
1883TEST_F(DisplayListTest, TranslateAffectsCurrentTransform) {
1884 DisplayListBuilder builder;
1885 builder.Translate(12.3f, 14.5f);
1886 DlMatrix matrix = DlMatrix::MakeTranslation({12.3f, 14.5f});
1887 DlMatrix cur_matrix = builder.GetMatrix();
1888 ASSERT_EQ(cur_matrix, matrix);
1889 builder.Translate(10, 10);
1890 // CurrentTransform has changed
1891 ASSERT_NE(builder.GetMatrix(), cur_matrix);
1892 // Previous return values have not
1893 ASSERT_EQ(cur_matrix, matrix);
1894}
1895
1896TEST_F(DisplayListTest, ScaleAffectsCurrentTransform) {
1897 DisplayListBuilder builder;
1898 builder.Scale(12.3, 14.5);
1899 DlMatrix matrix = DlMatrix::MakeScale({12.3, 14.5, 1.0f});
1900 DlMatrix cur_matrix = builder.GetMatrix();
1901 ASSERT_EQ(cur_matrix, matrix);
1902 builder.Translate(10, 10);
1903 // CurrentTransform has changed
1904 ASSERT_NE(builder.GetMatrix(), cur_matrix);
1905 // Previous return values have not
1906 ASSERT_EQ(cur_matrix, matrix);
1907}
1908
1909TEST_F(DisplayListTest, RotateAffectsCurrentTransform) {
1910 DisplayListBuilder builder;
1911 builder.Rotate(12.3f);
1913 DlMatrix cur_matrix = builder.GetMatrix();
1914 ASSERT_EQ(cur_matrix, matrix);
1915 builder.Translate(10, 10);
1916 // CurrentTransform has changed
1917 ASSERT_NE(builder.GetMatrix(), cur_matrix);
1918 // Previous return values have not
1919 ASSERT_EQ(cur_matrix, matrix);
1920}
1921
1922TEST_F(DisplayListTest, SkewAffectsCurrentTransform) {
1923 DisplayListBuilder builder;
1924 builder.Skew(12.3f, 14.5f);
1925 DlMatrix matrix = DlMatrix::MakeSkew(12.3f, 14.5f);
1926 DlMatrix cur_matrix = builder.GetMatrix();
1927 ASSERT_EQ(cur_matrix, matrix);
1928 builder.Translate(10, 10);
1929 // CurrentTransform has changed
1930 ASSERT_NE(builder.GetMatrix(), cur_matrix);
1931 // Previous return values have not
1932 ASSERT_EQ(cur_matrix, matrix);
1933}
1934
1935TEST_F(DisplayListTest, TransformAffectsCurrentTransform) {
1936 DisplayListBuilder builder;
1937 builder.Transform2DAffine(3.0f, 0.0f, 12.3f, //
1938 1.0f, 5.0f, 14.5f);
1939 DlMatrix matrix = DlMatrix::MakeRow(3.0f, 0.0f, 0.0f, 12.3, //
1940 1.0f, 5.0f, 0.0f, 14.5, //
1941 0.0f, 0.0f, 1.0f, 0.0f, //
1942 0.0f, 0.0f, 0.0f, 1.0f //
1943 );
1944 DlMatrix cur_matrix = builder.GetMatrix();
1945 ASSERT_EQ(cur_matrix, matrix);
1946 builder.Translate(10, 10);
1947 // CurrentTransform has changed
1948 ASSERT_NE(builder.GetMatrix(), cur_matrix);
1949 // Previous return values have not
1950 ASSERT_EQ(cur_matrix, matrix);
1951}
1952
1953TEST_F(DisplayListTest, FullTransformAffectsCurrentTransform) {
1954 DisplayListBuilder builder;
1955 builder.TransformFullPerspective(3.0f, 0.0f, 4.0f, 12.3f, //
1956 1.0f, 5.0f, 3.0f, 14.5f, //
1957 0.0f, 0.0f, 7.0f, 16.2f, //
1958 0.0f, 0.0f, 0.0f, 1.0f);
1959 DlMatrix matrix = DlMatrix::MakeRow(3.0f, 0.0f, 4.0f, 12.3f, //
1960 1.0f, 5.0f, 3.0f, 14.5f, //
1961 0.0f, 0.0f, 7.0f, 16.2f, //
1962 0.0f, 0.0f, 0.0f, 1.0f);
1963 DlMatrix cur_matrix = builder.GetMatrix();
1964 ASSERT_EQ(cur_matrix, matrix);
1965 builder.Translate(10, 10);
1966 // CurrentTransform has changed
1967 ASSERT_NE(builder.GetMatrix(), cur_matrix);
1968 // Previous return values have not
1969 ASSERT_EQ(cur_matrix, matrix);
1970}
1971
1972TEST_F(DisplayListTest, ClipRectAffectsClipBounds) {
1973 DisplayListBuilder builder;
1974 DlRect clip_bounds = DlRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
1975 builder.ClipRect(clip_bounds, DlClipOp::kIntersect, false);
1976
1977 // Save initial return values for testing restored values
1978 DlRect initial_local_bounds = builder.GetLocalClipCoverage();
1979 DlRect initial_destination_bounds = builder.GetDestinationClipCoverage();
1980 ASSERT_EQ(initial_local_bounds, clip_bounds);
1981 ASSERT_EQ(initial_destination_bounds, clip_bounds);
1982
1983 builder.Save();
1984 builder.ClipRect(DlRect::MakeLTRB(0, 0, 15, 15), DlClipOp::kIntersect, false);
1985 // Both clip bounds have changed
1986 ASSERT_NE(builder.GetLocalClipCoverage(), clip_bounds);
1987 ASSERT_NE(builder.GetDestinationClipCoverage(), clip_bounds);
1988 // Previous return values have not changed
1989 ASSERT_EQ(initial_local_bounds, clip_bounds);
1990 ASSERT_EQ(initial_destination_bounds, clip_bounds);
1991 builder.Restore();
1992
1993 // save/restore returned the values to their original values
1994 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
1995 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
1996
1997 builder.Save();
1998 builder.Scale(2.0f, 2.0f);
1999 DlRect scaled_clip_bounds = DlRect::MakeLTRB(5.1f, 5.65f, 10.2f, 12.85f);
2000 ASSERT_EQ(builder.GetLocalClipCoverage(), scaled_clip_bounds);
2001 // Destination bounds are unaffected by transform
2002 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds);
2003 builder.Restore();
2004
2005 // save/restore returned the values to their original values
2006 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2007 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2008}
2009
2010TEST_F(DisplayListTest, ClipRectDoAAAffectsClipBounds) {
2011 DisplayListBuilder builder;
2012 DlRect clip_bounds = DlRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
2013 DlRect clip_expanded_bounds = DlRect::MakeLTRB(10, 11, 21, 26);
2014 builder.ClipRect(clip_bounds, DlClipOp::kIntersect, true);
2015
2016 // Save initial return values for testing restored values
2017 DlRect initial_local_bounds = builder.GetLocalClipCoverage();
2018 DlRect initial_destination_bounds = builder.GetDestinationClipCoverage();
2019 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
2020 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
2021
2022 builder.Save();
2023 builder.ClipRect(DlRect::MakeLTRB(0, 0, 15, 15), DlClipOp::kIntersect, true);
2024 // Both clip bounds have changed
2025 ASSERT_NE(builder.GetLocalClipCoverage(), clip_expanded_bounds);
2026 ASSERT_NE(builder.GetDestinationClipCoverage(), clip_expanded_bounds);
2027 // Previous return values have not changed
2028 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
2029 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
2030 builder.Restore();
2031
2032 // save/restore returned the values to their original values
2033 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2034 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2035
2036 builder.Save();
2037 builder.Scale(2, 2);
2038 DlRect scaled_expanded_bounds = DlRect::MakeLTRB(5.0f, 5.5f, 10.5f, 13.0f);
2039 ASSERT_EQ(builder.GetLocalClipCoverage(), scaled_expanded_bounds);
2040 // Destination bounds are unaffected by transform
2041 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_expanded_bounds);
2042 builder.Restore();
2043
2044 // save/restore returned the values to their original values
2045 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2046 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2047}
2048
2049TEST_F(DisplayListTest, ClipRectAffectsClipBoundsWithMatrix) {
2050 DisplayListBuilder builder;
2051 DlRect clip_bounds_1 = DlRect::MakeLTRB(0, 0, 10, 10);
2052 DlRect clip_bounds_2 = DlRect::MakeLTRB(10, 10, 20, 20);
2053 builder.Save();
2054 builder.ClipRect(clip_bounds_1, DlClipOp::kIntersect, false);
2055 builder.Translate(10, 0);
2056 builder.ClipRect(clip_bounds_1, DlClipOp::kIntersect, false);
2057 ASSERT_TRUE(builder.GetDestinationClipCoverage().IsEmpty());
2058 builder.Restore();
2059
2060 builder.Save();
2061 builder.ClipRect(clip_bounds_1, DlClipOp::kIntersect, false);
2062 builder.Translate(-10, -10);
2063 builder.ClipRect(clip_bounds_2, DlClipOp::kIntersect, false);
2064 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds_1);
2065 builder.Restore();
2066}
2067
2068TEST_F(DisplayListTest, ClipRRectAffectsClipBounds) {
2069 DisplayListBuilder builder;
2070 DlRect clip_bounds = DlRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
2071 DlRoundRect clip = DlRoundRect::MakeRectXY(clip_bounds, 3, 2);
2072 builder.ClipRoundRect(clip, DlClipOp::kIntersect, false);
2073
2074 // Save initial return values for testing restored values
2075 DlRect initial_local_bounds = builder.GetLocalClipCoverage();
2076 DlRect initial_destination_bounds = builder.GetDestinationClipCoverage();
2077 ASSERT_EQ(initial_local_bounds, clip_bounds);
2078 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2079
2080 builder.Save();
2081 builder.ClipRect(DlRect::MakeLTRB(0, 0, 15, 15), DlClipOp::kIntersect, false);
2082 // Both clip bounds have changed
2083 ASSERT_NE(builder.GetLocalClipCoverage(), clip_bounds);
2084 ASSERT_NE(builder.GetDestinationClipCoverage(), clip_bounds);
2085 // Previous return values have not changed
2086 ASSERT_EQ(initial_local_bounds, clip_bounds);
2087 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2088 builder.Restore();
2089
2090 // save/restore returned the values to their original values
2091 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2092 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2093
2094 builder.Save();
2095 builder.Scale(2, 2);
2096 DlRect scaled_clip_bounds = DlRect::MakeLTRB(5.1f, 5.65f, 10.2f, 12.85f);
2097 ASSERT_EQ(builder.GetLocalClipCoverage(), scaled_clip_bounds);
2098 // Destination bounds are unaffected by transform
2099 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds);
2100 builder.Restore();
2101
2102 // save/restore returned the values to their original values
2103 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2104 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2105}
2106
2107TEST_F(DisplayListTest, ClipRRectDoAAAffectsClipBounds) {
2108 DisplayListBuilder builder;
2109 DlRect clip_bounds = DlRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
2110 DlRect clip_expanded_bounds = DlRect::MakeLTRB(10, 11, 21, 26);
2111 DlRoundRect clip = DlRoundRect::MakeRectXY(clip_bounds, 3, 2);
2112 builder.ClipRoundRect(clip, DlClipOp::kIntersect, true);
2113
2114 // Save initial return values for testing restored values
2115 DlRect initial_local_bounds = builder.GetLocalClipCoverage();
2116 DlRect initial_destination_bounds = builder.GetDestinationClipCoverage();
2117 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
2118 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
2119
2120 builder.Save();
2121 builder.ClipRect(DlRect::MakeLTRB(0, 0, 15, 15), DlClipOp::kIntersect, true);
2122 // Both clip bounds have changed
2123 ASSERT_NE(builder.GetLocalClipCoverage(), clip_expanded_bounds);
2124 ASSERT_NE(builder.GetDestinationClipCoverage(), clip_expanded_bounds);
2125 // Previous return values have not changed
2126 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
2127 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
2128 builder.Restore();
2129
2130 // save/restore returned the values to their original values
2131 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2132 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2133
2134 builder.Save();
2135 builder.Scale(2, 2);
2136 DlRect scaled_expanded_bounds = DlRect::MakeLTRB(5.0f, 5.5f, 10.5f, 13.0f);
2137 ASSERT_EQ(builder.GetLocalClipCoverage(), scaled_expanded_bounds);
2138 // Destination bounds are unaffected by transform
2139 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_expanded_bounds);
2140 builder.Restore();
2141
2142 // save/restore returned the values to their original values
2143 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2144 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2145}
2146
2147TEST_F(DisplayListTest, ClipRRectAffectsClipBoundsWithMatrix) {
2148 DisplayListBuilder builder;
2149 DlRect clip_bounds_1 = DlRect::MakeLTRB(0, 0, 10, 10);
2150 DlRect clip_bounds_2 = DlRect::MakeLTRB(10, 10, 20, 20);
2151 DlRoundRect clip1 = DlRoundRect::MakeRectXY(clip_bounds_1, 3, 2);
2152 DlRoundRect clip2 = DlRoundRect::MakeRectXY(clip_bounds_2, 3, 2);
2153
2154 builder.Save();
2155 builder.ClipRoundRect(clip1, DlClipOp::kIntersect, false);
2156 builder.Translate(10, 0);
2157 builder.ClipRoundRect(clip1, DlClipOp::kIntersect, false);
2158 ASSERT_TRUE(builder.GetDestinationClipCoverage().IsEmpty());
2159 builder.Restore();
2160
2161 builder.Save();
2162 builder.ClipRoundRect(clip1, DlClipOp::kIntersect, false);
2163 builder.Translate(-10, -10);
2164 builder.ClipRoundRect(clip2, DlClipOp::kIntersect, false);
2165 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds_1);
2166 builder.Restore();
2167}
2168
2169TEST_F(DisplayListTest, ClipPathAffectsClipBounds) {
2170 DisplayListBuilder builder;
2171 DlPath clip = DlPath::MakeCircle(DlPoint(10.2f, 11.3f), 2.0f) +
2172 DlPath::MakeCircle(DlPoint(20.4f, 25.7f), 2.0f);
2173 DlRect clip_bounds = DlRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
2174 builder.ClipPath(clip, DlClipOp::kIntersect, false);
2175
2176 // Save initial return values for testing restored values
2177 DlRect initial_local_bounds = builder.GetLocalClipCoverage();
2178 DlRect initial_destination_bounds = builder.GetDestinationClipCoverage();
2179 ASSERT_EQ(initial_local_bounds, clip_bounds);
2180 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2181
2182 builder.Save();
2183 builder.ClipRect(DlRect::MakeLTRB(0, 0, 15, 15), DlClipOp::kIntersect, false);
2184 // Both clip bounds have changed
2185 ASSERT_NE(builder.GetLocalClipCoverage(), clip_bounds);
2186 ASSERT_NE(builder.GetDestinationClipCoverage(), clip_bounds);
2187 // Previous return values have not changed
2188 ASSERT_EQ(initial_local_bounds, clip_bounds);
2189 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2190 builder.Restore();
2191
2192 // save/restore returned the values to their original values
2193 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2194 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2195
2196 builder.Save();
2197 builder.Scale(2, 2);
2198 DlRect scaled_clip_bounds = DlRect::MakeLTRB(4.1, 4.65, 11.2, 13.85);
2199 ASSERT_EQ(builder.GetLocalClipCoverage(), scaled_clip_bounds);
2200 // Destination bounds are unaffected by transform
2201 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds);
2202 builder.Restore();
2203
2204 // save/restore returned the values to their original values
2205 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2206 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2207}
2208
2209TEST_F(DisplayListTest, ClipPathDoAAAffectsClipBounds) {
2210 DisplayListBuilder builder;
2211 DlPath clip = DlPath::MakeCircle(DlPoint(10.2f, 11.3f), 2.0f) +
2212 DlPath::MakeCircle(DlPoint(20.4f, 25.7f), 2.0f);
2213 DlRect clip_expanded_bounds = DlRect::MakeLTRB(8, 9, 23, 28);
2214 builder.ClipPath(clip, DlClipOp::kIntersect, true);
2215
2216 // Save initial return values for testing restored values
2217 DlRect initial_local_bounds = builder.GetLocalClipCoverage();
2218 DlRect initial_destination_bounds = builder.GetDestinationClipCoverage();
2219 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
2220 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
2221
2222 builder.Save();
2223 builder.ClipRect(DlRect::MakeLTRB(0, 0, 15, 15), DlClipOp::kIntersect, true);
2224 // Both clip bounds have changed
2225 ASSERT_NE(builder.GetLocalClipCoverage(), clip_expanded_bounds);
2226 ASSERT_NE(builder.GetDestinationClipCoverage(), clip_expanded_bounds);
2227 // Previous return values have not changed
2228 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
2229 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
2230 builder.Restore();
2231
2232 // save/restore returned the values to their original values
2233 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2234 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2235
2236 builder.Save();
2237 builder.Scale(2, 2);
2238 DlRect scaled_expanded_bounds = DlRect::MakeLTRB(4.0f, 4.5f, 11.5f, 14.0f);
2239 ASSERT_EQ(builder.GetLocalClipCoverage(), scaled_expanded_bounds);
2240 // Destination bounds are unaffected by transform
2241 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_expanded_bounds);
2242 builder.Restore();
2243
2244 // save/restore returned the values to their original values
2245 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2246 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2247}
2248
2249TEST_F(DisplayListTest, ClipPathAffectsClipBoundsWithMatrix) {
2250 DisplayListBuilder builder;
2251 DlRect clip_bounds = DlRect::MakeLTRB(0, 0, 10, 10);
2252 DlPath clip1 = DlPath::MakeCircle(DlPoint(2.5f, 2.5f), 2.5) +
2253 DlPath::MakeCircle(DlPoint(7.5f, 7.5f), 2.5);
2254 DlPath clip2 = DlPath::MakeCircle(DlPoint(12.5f, 12.5f), 2.5) +
2255 DlPath::MakeCircle(DlPoint(17.5f, 17.5f), 2.5);
2256
2257 builder.Save();
2258 builder.ClipPath(clip1, DlClipOp::kIntersect, false);
2259 builder.Translate(10, 0);
2260 builder.ClipPath(clip1, DlClipOp::kIntersect, false);
2261 ASSERT_TRUE(builder.GetDestinationClipCoverage().IsEmpty());
2262 builder.Restore();
2263
2264 builder.Save();
2265 builder.ClipPath(clip1, DlClipOp::kIntersect, false);
2266 builder.Translate(-10, -10);
2267 builder.ClipPath(clip2, DlClipOp::kIntersect, false);
2268 ASSERT_EQ(builder.GetDestinationClipCoverage(), clip_bounds);
2269 builder.Restore();
2270}
2271
2272TEST_F(DisplayListTest, DiffClipRectDoesNotAffectClipBounds) {
2273 DisplayListBuilder builder;
2274 DlRect diff_clip = DlRect::MakeLTRB(0, 0, 15, 15);
2275 DlRect clip_bounds = DlRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
2276 builder.ClipRect(clip_bounds, DlClipOp::kIntersect, false);
2277
2278 // Save initial return values for testing after kDifference clip
2279 DlRect initial_local_bounds = builder.GetLocalClipCoverage();
2280 DlRect initial_destination_bounds = builder.GetDestinationClipCoverage();
2281 ASSERT_EQ(initial_local_bounds, clip_bounds);
2282 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2283
2284 builder.ClipRect(diff_clip, DlClipOp::kDifference, false);
2285 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2286 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2287}
2288
2289TEST_F(DisplayListTest, DiffClipRRectDoesNotAffectClipBounds) {
2290 DisplayListBuilder builder;
2291 DlRoundRect diff_clip =
2292 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(0, 0, 15, 15), 1, 1);
2293 DlRect clip_bounds = DlRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
2294 DlRoundRect clip =
2295 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(10.2, 11.3, 20.4, 25.7), 3, 2);
2296 builder.ClipRoundRect(clip, DlClipOp::kIntersect, false);
2297
2298 // Save initial return values for testing after kDifference clip
2299 DlRect initial_local_bounds = builder.GetLocalClipCoverage();
2300 DlRect initial_destination_bounds = builder.GetDestinationClipCoverage();
2301 ASSERT_EQ(initial_local_bounds, clip_bounds);
2302 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2303
2304 builder.ClipRoundRect(diff_clip, DlClipOp::kDifference, false);
2305 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2306 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2307}
2308
2309TEST_F(DisplayListTest, DiffClipPathDoesNotAffectClipBounds) {
2310 DisplayListBuilder builder;
2311 DlPath diff_clip = DlPath::MakeRectLTRB(0, 0, 15, 15);
2312 DlPath clip = DlPath::MakeCircle(DlPoint(10.2, 11.3), 2) +
2313 DlPath::MakeCircle(DlPoint(20.4, 25.7), 2);
2314 DlRect clip_bounds = DlRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
2315 builder.ClipPath(clip, DlClipOp::kIntersect, false);
2316
2317 // Save initial return values for testing after kDifference clip
2318 DlRect initial_local_bounds = builder.GetLocalClipCoverage();
2319 DlRect initial_destination_bounds = builder.GetDestinationClipCoverage();
2320 ASSERT_EQ(initial_local_bounds, clip_bounds);
2321 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2322
2323 builder.ClipPath(diff_clip, DlClipOp::kDifference, false);
2324 ASSERT_EQ(builder.GetLocalClipCoverage(), initial_local_bounds);
2325 ASSERT_EQ(builder.GetDestinationClipCoverage(), initial_destination_bounds);
2326}
2327
2328TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) {
2329 DlPoint horizontal_points[2] = {DlPoint(10, 10), DlPoint(20, 10)};
2330 DlPoint vertical_points[2] = {DlPoint(10, 10), DlPoint(10, 20)};
2331 {
2332 DisplayListBuilder builder;
2333 builder.DrawPoints(DlPointMode::kPolygon, 2, horizontal_points, DlPaint());
2334 DlRect bounds = builder.Build()->GetBounds();
2335 EXPECT_TRUE(bounds.Contains(DlPoint(10, 10)));
2336 EXPECT_TRUE(bounds.Contains(DlPoint(20, 10)));
2337 EXPECT_GE(bounds.GetWidth(), 10);
2338 }
2339 {
2340 DisplayListBuilder builder;
2341 builder.DrawPoints(DlPointMode::kPolygon, 2, vertical_points, DlPaint());
2342 DlRect bounds = builder.Build()->GetBounds();
2343 EXPECT_TRUE(bounds.Contains(DlPoint(10, 10)));
2344 EXPECT_TRUE(bounds.Contains(DlPoint(10, 20)));
2345 EXPECT_GE(bounds.GetHeight(), 10);
2346 }
2347 {
2348 DisplayListBuilder builder;
2349 builder.DrawPoints(DlPointMode::kPoints, 1, horizontal_points, DlPaint());
2350 DlRect bounds = builder.Build()->GetBounds();
2351 EXPECT_TRUE(bounds.Contains(DlPoint(10, 10)));
2352 }
2353 {
2354 DisplayListBuilder builder;
2355 DlPaint paint;
2356 paint.setStrokeWidth(2);
2357 builder.DrawPoints(DlPointMode::kPolygon, 2, horizontal_points, paint);
2358 DlRect bounds = builder.Build()->GetBounds();
2359 EXPECT_TRUE(bounds.Contains(DlPoint(10, 10)));
2360 EXPECT_TRUE(bounds.Contains(DlPoint(20, 10)));
2361 EXPECT_EQ(bounds, DlRect::MakeLTRB(9, 9, 21, 11));
2362 }
2363 {
2364 DisplayListBuilder builder;
2365 DlPaint paint;
2366 paint.setStrokeWidth(2);
2367 builder.DrawPoints(DlPointMode::kPolygon, 2, vertical_points, paint);
2368 DlRect bounds = builder.Build()->GetBounds();
2369 EXPECT_TRUE(bounds.Contains(DlPoint(10, 10)));
2370 EXPECT_TRUE(bounds.Contains(DlPoint(10, 20)));
2371 EXPECT_EQ(bounds, DlRect::MakeLTRB(9, 9, 11, 21));
2372 }
2373 {
2374 DisplayListBuilder builder;
2375 DlPaint paint;
2376 paint.setStrokeWidth(2);
2377 builder.DrawPoints(DlPointMode::kPoints, 1, horizontal_points, paint);
2378 DlRect bounds = builder.Build()->GetBounds();
2379 EXPECT_TRUE(bounds.Contains(DlPoint(10, 10)));
2380 EXPECT_EQ(bounds, DlRect::MakeLTRB(9, 9, 11, 11));
2381 }
2382}
2383
2384#define TEST_RTREE(rtree, query, expected_rects, expected_indices) \
2385 test_rtree(rtree, query, expected_rects, expected_indices, __FILE__, __LINE__)
2386
2387static void test_rtree(const sk_sp<const DlRTree>& rtree,
2388 const DlRect& query,
2389 std::vector<DlRect> expected_rects,
2390 const std::vector<int>& expected_indices,
2391 const std::string& file,
2392 int line) {
2393 std::vector<int> indices;
2394 auto label = "from " + file + ":" + std::to_string(line);
2395 rtree->search(query, &indices);
2396 EXPECT_EQ(indices, expected_indices) << label;
2397 EXPECT_EQ(indices.size(), expected_indices.size()) << label;
2398 std::list<DlRect> rects = rtree->searchAndConsolidateRects(query, false);
2399 // ASSERT_EQ(rects.size(), expected_indices.size());
2400 auto iterator = rects.cbegin();
2401 for (int i : expected_indices) {
2402 ASSERT_TRUE(iterator != rects.cend()) << label;
2403 EXPECT_EQ(*iterator++, expected_rects[i]) << label;
2404 }
2405}
2406
2407TEST_F(DisplayListTest, RTreeOfSimpleScene) {
2408 DisplayListBuilder builder(/*prepare_rtree=*/true);
2409 std::vector<DlRect> rects = {
2410 DlRect::MakeLTRB(10, 10, 20, 20),
2411 DlRect::MakeLTRB(50, 50, 60, 60),
2412 };
2413 builder.DrawRect(rects[0], DlPaint());
2414 builder.DrawRect(rects[1], DlPaint());
2415 auto display_list = builder.Build();
2416 auto rtree = display_list->rtree();
2417
2418 // Missing all drawRect calls
2419 TEST_RTREE(rtree, DlRect::MakeLTRB(5, 5, 10, 10), rects, {});
2420 TEST_RTREE(rtree, DlRect::MakeLTRB(20, 20, 25, 25), rects, {});
2421 TEST_RTREE(rtree, DlRect::MakeLTRB(45, 45, 50, 50), rects, {});
2422 TEST_RTREE(rtree, DlRect::MakeLTRB(60, 60, 65, 65), rects, {});
2423
2424 // Hitting just 1 of the drawRects
2425 TEST_RTREE(rtree, DlRect::MakeLTRB(5, 5, 11, 11), rects, {0});
2426 TEST_RTREE(rtree, DlRect::MakeLTRB(19, 19, 25, 25), rects, {0});
2427 TEST_RTREE(rtree, DlRect::MakeLTRB(45, 45, 51, 51), rects, {1});
2428 TEST_RTREE(rtree, DlRect::MakeLTRB(59, 59, 65, 65), rects, {1});
2429
2430 // Hitting both drawRect calls
2431 TEST_RTREE(rtree, DlRect::MakeLTRB(19, 19, 51, 51), rects,
2432 std::vector<int>({0, 1}));
2433}
2434
2435TEST_F(DisplayListTest, RTreeOfSaveRestoreScene) {
2436 DisplayListBuilder builder(/*prepare_rtree=*/true);
2437 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
2438 builder.Save();
2439 builder.DrawRect(DlRect::MakeLTRB(50, 50, 60, 60), DlPaint());
2440 builder.Restore();
2441 auto display_list = builder.Build();
2442 auto rtree = display_list->rtree();
2443 std::vector<DlRect> rects = {
2444 DlRect::MakeLTRB(10, 10, 20, 20),
2445 DlRect::MakeLTRB(50, 50, 60, 60),
2446 };
2447
2448 // Missing all drawRect calls
2449 TEST_RTREE(rtree, DlRect::MakeLTRB(5, 5, 10, 10), rects, {});
2450 TEST_RTREE(rtree, DlRect::MakeLTRB(20, 20, 25, 25), rects, {});
2451 TEST_RTREE(rtree, DlRect::MakeLTRB(45, 45, 50, 50), rects, {});
2452 TEST_RTREE(rtree, DlRect::MakeLTRB(60, 60, 65, 65), rects, {});
2453
2454 // Hitting just 1 of the drawRects
2455 TEST_RTREE(rtree, DlRect::MakeLTRB(5, 5, 11, 11), rects, {0});
2456 TEST_RTREE(rtree, DlRect::MakeLTRB(19, 19, 25, 25), rects, {0});
2457 TEST_RTREE(rtree, DlRect::MakeLTRB(45, 45, 51, 51), rects, {1});
2458 TEST_RTREE(rtree, DlRect::MakeLTRB(59, 59, 65, 65), rects, {1});
2459
2460 // Hitting both drawRect calls
2461 TEST_RTREE(rtree, DlRect::MakeLTRB(19, 19, 51, 51), rects,
2462 std::vector<int>({0, 1}));
2463}
2464
2465TEST_F(DisplayListTest, RTreeOfSaveLayerFilterScene) {
2466 DisplayListBuilder builder(/*prepare_rtree=*/true);
2467 // blur filter with sigma=1 expands by 3 on all sides
2468 auto filter = DlBlurImageFilter(1.0, 1.0, DlTileMode::kClamp);
2469 DlPaint default_paint = DlPaint();
2470 DlPaint filter_paint = DlPaint().setImageFilter(&filter);
2471 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), default_paint);
2472 builder.SaveLayer(std::nullopt, &filter_paint);
2473 // the following rectangle will be expanded to 50,50,60,60
2474 // by the SaveLayer filter during the restore operation
2475 builder.DrawRect(DlRect::MakeLTRB(53, 53, 57, 57), default_paint);
2476 builder.Restore();
2477 auto display_list = builder.Build();
2478 auto rtree = display_list->rtree();
2479 std::vector<DlRect> rects = {
2480 DlRect::MakeLTRB(10, 10, 20, 20),
2481 DlRect::MakeLTRB(50, 50, 60, 60),
2482 };
2483
2484 // Missing all drawRect calls
2485 TEST_RTREE(rtree, DlRect::MakeLTRB(5, 5, 10, 10), rects, {});
2486 TEST_RTREE(rtree, DlRect::MakeLTRB(20, 20, 25, 25), rects, {});
2487 TEST_RTREE(rtree, DlRect::MakeLTRB(45, 45, 50, 50), rects, {});
2488 TEST_RTREE(rtree, DlRect::MakeLTRB(60, 60, 65, 65), rects, {});
2489
2490 // Hitting just 1 of the drawRects
2491 TEST_RTREE(rtree, DlRect::MakeLTRB(5, 5, 11, 11), rects, {0});
2492 TEST_RTREE(rtree, DlRect::MakeLTRB(19, 19, 25, 25), rects, {0});
2493 TEST_RTREE(rtree, DlRect::MakeLTRB(45, 45, 51, 51), rects, {1});
2494 TEST_RTREE(rtree, DlRect::MakeLTRB(59, 59, 65, 65), rects, {1});
2495
2496 // Hitting both drawRect calls
2497 auto expected_indices = std::vector<int>{0, 1};
2498 TEST_RTREE(rtree, DlRect::MakeLTRB(19, 19, 51, 51), rects, expected_indices);
2499}
2500
2501TEST_F(DisplayListTest, NestedDisplayListRTreesAreSparse) {
2502 DisplayListBuilder nested_dl_builder(/**prepare_rtree=*/true);
2503 nested_dl_builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
2504 nested_dl_builder.DrawRect(DlRect::MakeLTRB(50, 50, 60, 60), DlPaint());
2505 auto nested_display_list = nested_dl_builder.Build();
2506
2507 DisplayListBuilder builder(/**prepare_rtree=*/true);
2508 builder.DrawDisplayList(nested_display_list);
2509 auto display_list = builder.Build();
2510
2511 auto rtree = display_list->rtree();
2512 std::vector<DlRect> rects = {
2513 DlRect::MakeLTRB(10, 10, 20, 20),
2514 DlRect::MakeLTRB(50, 50, 60, 60),
2515 };
2516
2517 // Hitting both sub-dl drawRect calls
2518 TEST_RTREE(rtree, DlRect::MakeLTRB(19, 19, 51, 51), rects,
2519 std::vector<int>({0, 1}));
2520}
2521
2522TEST_F(DisplayListTest, RemoveUnnecessarySaveRestorePairs) {
2523 {
2524 DisplayListBuilder builder;
2525 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
2526 builder.Save(); // This save op is unnecessary
2527 builder.DrawRect(DlRect::MakeLTRB(50, 50, 60, 60), DlPaint());
2528 builder.Restore();
2529
2530 DisplayListBuilder builder2;
2531 builder2.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
2532 builder2.DrawRect(DlRect::MakeLTRB(50, 50, 60, 60), DlPaint());
2533 ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build()));
2534 }
2535
2536 {
2537 DisplayListBuilder builder;
2538 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
2539 builder.Save();
2540 {
2541 builder.Translate(1.0, 1.0);
2542 builder.Save();
2543 { //
2544 builder.DrawRect(DlRect::MakeLTRB(50, 50, 60, 60), DlPaint());
2545 }
2546 builder.Restore();
2547 }
2548 builder.Restore();
2549
2550 DisplayListBuilder builder2;
2551 builder2.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
2552 builder2.Save();
2553 { //
2554 builder2.Translate(1.0, 1.0);
2555 { //
2556 builder2.DrawRect(DlRect::MakeLTRB(50, 50, 60, 60), DlPaint());
2557 }
2558 }
2559 builder2.Restore();
2560 ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build()));
2561 }
2562}
2563
2564TEST_F(DisplayListTest, CollapseMultipleNestedSaveRestore) {
2565 DisplayListBuilder builder1;
2566 builder1.Save();
2567 {
2568 builder1.Save();
2569 {
2570 builder1.Save();
2571 {
2572 builder1.Translate(10, 10);
2573 builder1.Scale(2, 2);
2574 builder1.ClipRect(DlRect::MakeLTRB(10, 10, 20, 20),
2575 DlClipOp::kIntersect, false);
2576 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2577 }
2578 builder1.Restore();
2579 }
2580 builder1.Restore();
2581 }
2582 builder1.Restore();
2583 auto display_list1 = builder1.Build();
2584
2585 DisplayListBuilder builder2;
2586 builder2.Save();
2587 {
2588 builder2.Translate(10, 10);
2589 builder2.Scale(2, 2);
2590 builder2.ClipRect(DlRect::MakeLTRB(10, 10, 20, 20), DlClipOp::kIntersect,
2591 false);
2592 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2593 }
2594 builder2.Restore();
2595 auto display_list2 = builder2.Build();
2596
2597 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2598}
2599
2600TEST_F(DisplayListTest, CollapseNestedSaveAndSaveLayerRestore) {
2601 DisplayListBuilder builder1;
2602 builder1.Save();
2603 {
2604 builder1.SaveLayer(std::nullopt, nullptr);
2605 {
2606 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2607 builder1.Scale(2, 2);
2608 }
2609 builder1.Restore();
2610 }
2611 builder1.Restore();
2612 auto display_list1 = builder1.Build();
2613
2614 DisplayListBuilder builder2;
2615 builder2.SaveLayer(std::nullopt, nullptr);
2616 {
2617 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2618 builder2.Scale(2, 2);
2619 }
2620 builder2.Restore();
2621 auto display_list2 = builder2.Build();
2622
2623 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2624}
2625
2626TEST_F(DisplayListTest, RemoveUnnecessarySaveRestorePairsInSetPaint) {
2627 DlRect build_bounds = DlRect::MakeLTRB(-100, -100, 200, 200);
2628 DlRect rect = DlRect::MakeLTRB(30, 30, 70, 70);
2629 // clang-format off
2630 const float alpha_matrix[] = {
2631 0, 0, 0, 0, 0,
2632 0, 1, 0, 0, 0,
2633 0, 0, 1, 0, 0,
2634 0, 0, 0, 0, 1,
2635 };
2636 // clang-format on
2637 auto alpha_color_filter = DlColorFilter::MakeMatrix(alpha_matrix);
2638 // Making sure hiding a problematic ColorFilter as an ImageFilter
2639 // will generate the same behavior as setting it as a ColorFilter
2640
2641 DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter);
2642 {
2643 DisplayListBuilder builder(build_bounds);
2644 builder.Save();
2645 DlPaint paint;
2646 paint.setImageFilter(&color_filter_image_filter);
2647 builder.DrawRect(rect, paint);
2648 builder.Restore();
2649 sk_sp<DisplayList> display_list1 = builder.Build();
2650
2651 DisplayListBuilder builder2(build_bounds);
2652 DlPaint paint2;
2653 paint2.setImageFilter(&color_filter_image_filter);
2654 builder2.DrawRect(rect, paint2);
2655 sk_sp<DisplayList> display_list2 = builder2.Build();
2656 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2657 }
2658
2659 {
2660 DisplayListBuilder builder(build_bounds);
2661 builder.Save();
2662 builder.SaveLayer(build_bounds);
2663 DlPaint paint;
2664 paint.setImageFilter(&color_filter_image_filter);
2665 builder.DrawRect(rect, paint);
2666 builder.Restore();
2667 builder.Restore();
2668 sk_sp<DisplayList> display_list1 = builder.Build();
2669
2670 DisplayListBuilder builder2(build_bounds);
2671 builder2.SaveLayer(build_bounds);
2672 DlPaint paint2;
2673 paint2.setImageFilter(&color_filter_image_filter);
2674 builder2.DrawRect(rect, paint2);
2675 builder2.Restore();
2676 sk_sp<DisplayList> display_list2 = builder2.Build();
2677 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2678 }
2679}
2680
2681TEST_F(DisplayListTest, TransformTriggersDeferredSave) {
2682 DisplayListBuilder builder1;
2683 builder1.Save();
2684 {
2685 builder1.Save();
2686 {
2687 builder1.TransformFullPerspective(1, 0, 0, 10, //
2688 0, 1, 0, 100, //
2689 0, 0, 1, 0, //
2690 0, 0, 0, 1);
2691 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2692 }
2693 builder1.Restore();
2694 builder1.TransformFullPerspective(1, 0, 0, 10, //
2695 0, 1, 0, 100, //
2696 0, 0, 1, 0, //
2697 0, 0, 0, 1);
2698 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2699 }
2700 builder1.Restore();
2701 auto display_list1 = builder1.Build();
2702
2703 DisplayListBuilder builder2;
2704 builder2.Save();
2705 {
2706 builder2.TransformFullPerspective(1, 0, 0, 10, //
2707 0, 1, 0, 100, //
2708 0, 0, 1, 0, //
2709 0, 0, 0, 1);
2710 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2711 }
2712 builder2.Restore();
2713 builder2.Save();
2714 {
2715 builder2.TransformFullPerspective(1, 0, 0, 10, //
2716 0, 1, 0, 100, //
2717 0, 0, 1, 0, //
2718 0, 0, 0, 1);
2719 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2720 }
2721 builder2.Restore();
2722 auto display_list2 = builder2.Build();
2723
2724 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2725}
2726
2727TEST_F(DisplayListTest, Transform2DTriggersDeferredSave) {
2728 DisplayListBuilder builder1;
2729 builder1.Save();
2730 {
2731 builder1.Save();
2732 {
2733 builder1.Transform2DAffine(0, 1, 12, 1, 0, 33);
2734 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2735 }
2736 builder1.Restore();
2737 }
2738 builder1.Restore();
2739 auto display_list1 = builder1.Build();
2740
2741 DisplayListBuilder builder2;
2742 builder2.Save();
2743 {
2744 builder2.Transform2DAffine(0, 1, 12, 1, 0, 33);
2745 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2746 }
2747 builder2.Restore();
2748 auto display_list2 = builder2.Build();
2749
2750 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2751}
2752
2753TEST_F(DisplayListTest, TransformPerspectiveTriggersDeferredSave) {
2754 DisplayListBuilder builder1;
2755 builder1.Save();
2756 {
2757 builder1.Save();
2758 {
2759 builder1.TransformFullPerspective(0, 1, 0, 12, //
2760 1, 0, 0, 33, //
2761 3, 2, 5, 29, //
2762 0, 0, 0, 12);
2763 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2764 }
2765 builder1.Restore();
2766 }
2767 builder1.Restore();
2768 auto display_list1 = builder1.Build();
2769
2770 DisplayListBuilder builder2;
2771 builder2.Save();
2772 {
2773 builder2.TransformFullPerspective(0, 1, 0, 12, //
2774 1, 0, 0, 33, //
2775 3, 2, 5, 29, //
2776 0, 0, 0, 12);
2777 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2778 }
2779 builder2.Restore();
2780 auto display_list2 = builder2.Build();
2781
2782 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2783}
2784
2785TEST_F(DisplayListTest, ResetTransformTriggersDeferredSave) {
2786 DisplayListBuilder builder1;
2787 builder1.Save();
2788 {
2789 builder1.Save();
2790 {
2791 builder1.TransformReset();
2792 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2793 }
2794 builder1.Restore();
2795 }
2796 builder1.Restore();
2797 auto display_list1 = builder1.Build();
2798
2799 DisplayListBuilder builder2;
2800 builder2.Save();
2801 {
2802 builder2.TransformReset();
2803 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2804 }
2805 builder2.Restore();
2806 auto display_list2 = builder2.Build();
2807
2808 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2809}
2810
2811TEST_F(DisplayListTest, SkewTriggersDeferredSave) {
2812 DisplayListBuilder builder1;
2813 builder1.Save();
2814 {
2815 builder1.Save();
2816 {
2817 builder1.Skew(10, 10);
2818 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2819 }
2820 builder1.Restore();
2821 }
2822 builder1.Restore();
2823 auto display_list1 = builder1.Build();
2824
2825 DisplayListBuilder builder2;
2826 builder2.Save();
2827 {
2828 builder2.Skew(10, 10);
2829 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2830 }
2831 builder2.Restore();
2832 auto display_list2 = builder2.Build();
2833
2834 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2835}
2836
2837TEST_F(DisplayListTest, TranslateTriggersDeferredSave) {
2838 DisplayListBuilder builder1;
2839 builder1.Save();
2840 {
2841 builder1.Save();
2842 {
2843 builder1.Translate(10, 10);
2844 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2845 }
2846 builder1.Restore();
2847 }
2848 builder1.Restore();
2849 auto display_list1 = builder1.Build();
2850
2851 DisplayListBuilder builder2;
2852 builder2.Save();
2853 {
2854 builder2.Translate(10, 10);
2855 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2856 }
2857 builder2.Restore();
2858 auto display_list2 = builder2.Build();
2859
2860 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2861}
2862
2863TEST_F(DisplayListTest, ScaleTriggersDeferredSave) {
2864 DisplayListBuilder builder1;
2865 builder1.Save();
2866 {
2867 builder1.Save();
2868 {
2869 builder1.Scale(0.5, 0.5);
2870 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2871 }
2872 builder1.Restore();
2873 }
2874 builder1.Restore();
2875 auto display_list1 = builder1.Build();
2876
2877 DisplayListBuilder builder2;
2878 builder2.Save();
2879 {
2880 builder2.Scale(0.5, 0.5);
2881 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2882 }
2883 builder2.Restore();
2884 auto display_list2 = builder2.Build();
2885
2886 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2887}
2888
2889TEST_F(DisplayListTest, ClipRectTriggersDeferredSave) {
2890 DisplayListBuilder builder1;
2891 builder1.Save();
2892 {
2893 builder1.Save();
2894 {
2895 builder1.ClipRect(DlRect::MakeLTRB(0, 0, 100, 100), DlClipOp::kIntersect,
2896 true);
2897 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2898 }
2899 builder1.Restore();
2900 builder1.TransformFullPerspective(1, 0, 0, 0, //
2901 0, 1, 0, 0, //
2902 0, 0, 1, 0, //
2903 0, 0, 0, 1);
2904 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2905 }
2906 builder1.Restore();
2907 auto display_list1 = builder1.Build();
2908
2909 DisplayListBuilder builder2;
2910 builder2.Save();
2911 {
2912 builder2.ClipRect(DlRect::MakeLTRB(0, 0, 100, 100), DlClipOp::kIntersect,
2913 true);
2914 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2915 }
2916 builder2.Restore();
2917 builder2.TransformFullPerspective(1, 0, 0, 0, //
2918 0, 1, 0, 0, //
2919 0, 0, 1, 0, //
2920 0, 0, 0, 1);
2921 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2922 auto display_list2 = builder2.Build();
2923
2924 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2925}
2926
2927TEST_F(DisplayListTest, ClipRRectTriggersDeferredSave) {
2928 DisplayListBuilder builder1;
2929 builder1.Save();
2930 {
2931 builder1.Save();
2932 {
2934
2935 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2936 }
2937 builder1.Restore();
2938 builder1.TransformFullPerspective(1, 0, 0, 0, //
2939 0, 1, 0, 0, //
2940 0, 0, 1, 0, //
2941 0, 0, 0, 1);
2942 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2943 }
2944 builder1.Restore();
2945 auto display_list1 = builder1.Build();
2946
2947 DisplayListBuilder builder2;
2948 builder2.Save();
2950
2951 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2952 builder2.Restore();
2953 builder2.TransformFullPerspective(1, 0, 0, 0, //
2954 0, 1, 0, 0, //
2955 0, 0, 1, 0, //
2956 0, 0, 0, 1);
2957 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2958 auto display_list2 = builder2.Build();
2959
2960 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2961}
2962
2963TEST_F(DisplayListTest, ClipPathTriggersDeferredSave) {
2964 DisplayListBuilder builder1;
2965 builder1.Save();
2966 {
2967 builder1.Save();
2968 {
2969 builder1.ClipPath(kTestPath1, DlClipOp::kIntersect, true);
2970 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2971 }
2972 builder1.Restore();
2973 builder1.TransformFullPerspective(1, 0, 0, 0, //
2974 0, 1, 0, 0, //
2975 0, 0, 1, 0, //
2976 0, 0, 0, 1);
2977 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2978 }
2979 builder1.Restore();
2980 auto display_list1 = builder1.Build();
2981
2982 DisplayListBuilder builder2;
2983 builder2.Save();
2984 {
2985 builder2.ClipPath(kTestPath1, DlClipOp::kIntersect, true);
2986 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2987 }
2988 builder2.Restore();
2989 builder2.TransformFullPerspective(1, 0, 0, 0, //
2990 0, 1, 0, 0, //
2991 0, 0, 1, 0, //
2992 0, 0, 0, 1);
2993 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
2994 auto display_list2 = builder2.Build();
2995
2996 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2997}
2998
2999TEST_F(DisplayListTest, NOPTranslateDoesNotTriggerDeferredSave) {
3000 DisplayListBuilder builder1;
3001 builder1.Save();
3002 {
3003 builder1.Save();
3004 {
3005 builder1.Translate(0, 0);
3006 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3007 }
3008 builder1.Restore();
3009 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3010 }
3011 builder1.Restore();
3012 auto display_list1 = builder1.Build();
3013
3014 DisplayListBuilder builder2;
3015 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3016 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3017 auto display_list2 = builder2.Build();
3018
3019 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3020}
3021
3022TEST_F(DisplayListTest, NOPScaleDoesNotTriggerDeferredSave) {
3023 DisplayListBuilder builder1;
3024 builder1.Save();
3025 {
3026 builder1.Save();
3027 {
3028 builder1.Scale(1.0, 1.0);
3029 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3030 }
3031 builder1.Restore();
3032 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3033 }
3034 builder1.Restore();
3035 auto display_list1 = builder1.Build();
3036
3037 DisplayListBuilder builder2;
3038 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3039 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3040 auto display_list2 = builder2.Build();
3041
3042 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3043}
3044
3045TEST_F(DisplayListTest, NOPRotationDoesNotTriggerDeferredSave) {
3046 DisplayListBuilder builder1;
3047 builder1.Save();
3048 {
3049 builder1.Save();
3050 {
3051 builder1.Rotate(360);
3052 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3053 }
3054 builder1.Restore();
3055 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3056 }
3057 builder1.Restore();
3058 auto display_list1 = builder1.Build();
3059
3060 DisplayListBuilder builder2;
3061 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3062 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3063 auto display_list2 = builder2.Build();
3064
3065 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3066}
3067
3068TEST_F(DisplayListTest, NOPSkewDoesNotTriggerDeferredSave) {
3069 DisplayListBuilder builder1;
3070 builder1.Save();
3071 {
3072 builder1.Save();
3073 {
3074 builder1.Skew(0, 0);
3075 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3076 }
3077 builder1.Restore();
3078 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3079 }
3080 builder1.Restore();
3081 auto display_list1 = builder1.Build();
3082
3083 DisplayListBuilder builder2;
3084 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3085 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3086 auto display_list2 = builder2.Build();
3087
3088 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3089}
3090
3091TEST_F(DisplayListTest, NOPTransformDoesNotTriggerDeferredSave) {
3092 DisplayListBuilder builder1;
3093 builder1.Save();
3094 {
3095 builder1.Save();
3096 {
3097 builder1.TransformFullPerspective(1, 0, 0, 0, //
3098 0, 1, 0, 0, //
3099 0, 0, 1, 0, //
3100 0, 0, 0, 1);
3101 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3102 }
3103 builder1.Restore();
3104 builder1.TransformFullPerspective(1, 0, 0, 0, //
3105 0, 1, 0, 0, //
3106 0, 0, 1, 0, //
3107 0, 0, 0, 1);
3108 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3109 }
3110 builder1.Restore();
3111 auto display_list1 = builder1.Build();
3112
3113 DisplayListBuilder builder2;
3114 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3115 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3116 auto display_list2 = builder2.Build();
3117
3118 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3119}
3120
3121TEST_F(DisplayListTest, NOPTransform2DDoesNotTriggerDeferredSave) {
3122 DisplayListBuilder builder1;
3123 builder1.Save();
3124 {
3125 builder1.Save();
3126 {
3127 builder1.Transform2DAffine(1, 0, 0, 0, 1, 0);
3128 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3129 }
3130 builder1.Restore();
3131 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3132 }
3133 builder1.Restore();
3134 auto display_list1 = builder1.Build();
3135
3136 DisplayListBuilder builder2;
3137 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3138 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3139 auto display_list2 = builder2.Build();
3140
3141 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3142}
3143
3144TEST_F(DisplayListTest, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) {
3145 {
3146 DisplayListBuilder builder1;
3147 builder1.Save();
3148 {
3149 builder1.Save();
3150 {
3151 builder1.TransformFullPerspective(1, 0, 0, 0, //
3152 0, 1, 0, 0, //
3153 0, 0, 1, 0, //
3154 0, 0, 0, 1);
3155 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3156 }
3157 builder1.Restore();
3158 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3159 }
3160 builder1.Restore();
3161 auto display_list1 = builder1.Build();
3162
3163 DisplayListBuilder builder2;
3164 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3165 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3166 auto display_list2 = builder2.Build();
3167
3168 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3169 }
3170
3171 {
3172 DisplayListBuilder builder1;
3173 builder1.Save();
3174 {
3175 builder1.Save();
3176 {
3177 builder1.TransformFullPerspective(1, 0, 0, 0, //
3178 0, 1, 0, 0, //
3179 0, 0, 1, 0, //
3180 0, 0, 0, 1);
3181 builder1.TransformReset();
3182 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3183 }
3184 builder1.Restore();
3185 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3186 }
3187 builder1.Restore();
3188 auto display_list1 = builder1.Build();
3189
3190 DisplayListBuilder builder2;
3191 builder2.Save();
3192 {
3193 builder2.TransformReset();
3194 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3195 }
3196 builder2.Restore();
3197 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3198 auto display_list2 = builder2.Build();
3199
3200 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3201 }
3202}
3203
3204TEST_F(DisplayListTest, NOPClipDoesNotTriggerDeferredSave) {
3205 DlScalar NaN = std::numeric_limits<DlScalar>::quiet_NaN();
3206 DisplayListBuilder builder1;
3207 builder1.Save();
3208 {
3209 builder1.Save();
3210 {
3211 builder1.ClipRect(DlRect::MakeLTRB(0, NaN, NaN, 0), DlClipOp::kIntersect,
3212 true);
3213 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3214 }
3215 builder1.Restore();
3216 builder1.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3217 }
3218 builder1.Restore();
3219 auto display_list1 = builder1.Build();
3220
3221 DisplayListBuilder builder2;
3222 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3223 builder2.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), DlPaint());
3224 auto display_list2 = builder2.Build();
3225
3226 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3227}
3228
3229TEST_F(DisplayListTest, RTreeOfClippedSaveLayerFilterScene) {
3230 DisplayListBuilder builder(/*prepare_rtree=*/true);
3231 // blur filter with sigma=1 expands by 30 on all sides
3232 auto filter = DlBlurImageFilter(10.0, 10.0, DlTileMode::kClamp);
3233 DlPaint default_paint = DlPaint();
3234 DlPaint filter_paint = DlPaint().setImageFilter(&filter);
3235 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), default_paint);
3236 builder.ClipRect(DlRect::MakeLTRB(50, 50, 60, 60), DlClipOp::kIntersect,
3237 false);
3238 builder.SaveLayer(std::nullopt, &filter_paint);
3239 // the following rectangle will be expanded to 23,23,87,87
3240 // by the SaveLayer filter during the restore operation
3241 // but it will then be clipped to 50,50,60,60
3242 builder.DrawRect(DlRect::MakeLTRB(53, 53, 57, 57), default_paint);
3243 builder.Restore();
3244 auto display_list = builder.Build();
3245 auto rtree = display_list->rtree();
3246 std::vector<DlRect> rects = {
3247 DlRect::MakeLTRB(10, 10, 20, 20),
3248 DlRect::MakeLTRB(50, 50, 60, 60),
3249 };
3250
3251 // Missing all drawRect calls
3252 TEST_RTREE(rtree, DlRect::MakeLTRB(5, 5, 10, 10), rects, {});
3253 TEST_RTREE(rtree, DlRect::MakeLTRB(20, 20, 25, 25), rects, {});
3254 TEST_RTREE(rtree, DlRect::MakeLTRB(45, 45, 50, 50), rects, {});
3255 TEST_RTREE(rtree, DlRect::MakeLTRB(60, 60, 65, 65), rects, {});
3256
3257 // Hitting just 1 of the drawRects
3258 TEST_RTREE(rtree, DlRect::MakeLTRB(5, 5, 11, 11), rects, {0});
3259 TEST_RTREE(rtree, DlRect::MakeLTRB(19, 19, 25, 25), rects, {0});
3260 TEST_RTREE(rtree, DlRect::MakeLTRB(45, 45, 51, 51), rects, {1});
3261 TEST_RTREE(rtree, DlRect::MakeLTRB(59, 59, 65, 65), rects, {1});
3262
3263 // Hitting both drawRect calls
3264 TEST_RTREE(rtree, DlRect::MakeLTRB(19, 19, 51, 51), rects,
3265 std::vector<int>({0, 1}));
3266}
3267
3268TEST_F(DisplayListTest, RTreeRenderCulling) {
3269 DlRect rect1 = DlRect::MakeLTRB(0, 0, 10, 10);
3270 DlRect rect2 = DlRect::MakeLTRB(20, 0, 30, 10);
3271 DlRect rect3 = DlRect::MakeLTRB(0, 20, 10, 30);
3272 DlRect rect4 = DlRect::MakeLTRB(20, 20, 30, 30);
3273 DlPaint paint1 = DlPaint().setColor(DlColor::kRed());
3275 DlPaint paint3 = DlPaint().setColor(DlColor::kBlue());
3277
3278 DisplayListBuilder main_builder(true);
3279 main_builder.DrawRect(rect1, paint1);
3280 main_builder.DrawRect(rect2, paint2);
3281 main_builder.DrawRect(rect3, paint3);
3282 main_builder.DrawRect(rect4, paint4);
3283 auto main = main_builder.Build();
3284
3285 auto test = [main](DlIRect cull_rect, const sk_sp<DisplayList>& expected,
3286 const std::string& label) {
3287 DlRect cull_rectf = DlRect::Make(cull_rect);
3288
3289 { // Test DlIRect culling
3290 DisplayListBuilder culling_builder;
3291 main->Dispatch(ToReceiver(culling_builder), cull_rect);
3292
3293 EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected))
3294 << "using cull rect " << cull_rect //
3295 << " where " << label;
3296 }
3297
3298 { // Test DlRect culling
3299 DisplayListBuilder culling_builder;
3300 main->Dispatch(ToReceiver(culling_builder), cull_rectf);
3301
3302 EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected))
3303 << "using cull rect " << cull_rectf //
3304 << " where " << label;
3305 }
3306
3307 { // Test using vector of culled indices
3308 DisplayListBuilder culling_builder;
3309 DlOpReceiver& receiver = ToReceiver(culling_builder);
3310 auto indices = main->GetCulledIndices(cull_rectf);
3311 for (DlIndex i : indices) {
3312 EXPECT_TRUE(main->Dispatch(receiver, i));
3313 }
3314
3315 EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected))
3316 << "using culled indices on cull rect " << cull_rectf //
3317 << " where " << label;
3318 }
3319 };
3320
3321 { // No rects
3322 DlIRect cull_rect = DlIRect::MakeLTRB(11, 11, 19, 19);
3323
3324 DisplayListBuilder expected_builder;
3325 auto expected = expected_builder.Build();
3326
3327 test(cull_rect, expected, "no rects intersect");
3328 }
3329
3330 { // Rect 1
3331 DlIRect cull_rect = DlIRect::MakeLTRB(9, 9, 19, 19);
3332
3333 DisplayListBuilder expected_builder;
3334 expected_builder.DrawRect(rect1, paint1);
3335 auto expected = expected_builder.Build();
3336
3337 test(cull_rect, expected, "rect 1 intersects");
3338 }
3339
3340 { // Rect 2
3341 DlIRect cull_rect = DlIRect::MakeLTRB(11, 9, 21, 19);
3342
3343 DisplayListBuilder expected_builder;
3344 // Unfortunately we don't cull attribute records (yet?), so we forcibly
3345 // record all attributes for the un-culled operations
3346 ToReceiver(expected_builder).setColor(paint1.getColor());
3347 expected_builder.DrawRect(rect2, paint2);
3348 auto expected = expected_builder.Build();
3349
3350 test(cull_rect, expected, "rect 2 intersects");
3351 }
3352
3353 { // Rect 3
3354 DlIRect cull_rect = DlIRect::MakeLTRB(9, 11, 19, 21);
3355
3356 DisplayListBuilder expected_builder;
3357 // Unfortunately we don't cull attribute records (yet?), so we forcibly
3358 // record all attributes for the un-culled operations
3359 ToReceiver(expected_builder).setColor(paint1.getColor());
3360 ToReceiver(expected_builder).setColor(paint2.getColor());
3361 expected_builder.DrawRect(rect3, paint3);
3362 auto expected = expected_builder.Build();
3363
3364 test(cull_rect, expected, "rect 3 intersects");
3365 }
3366
3367 { // Rect 4
3368 DlIRect cull_rect = DlIRect::MakeLTRB(11, 11, 21, 21);
3369
3370 DisplayListBuilder expected_builder;
3371 // Unfortunately we don't cull attribute records (yet?), so we forcibly
3372 // record all attributes for the un-culled operations
3373 ToReceiver(expected_builder).setColor(paint1.getColor());
3374 ToReceiver(expected_builder).setColor(paint2.getColor());
3375 ToReceiver(expected_builder).setColor(paint3.getColor());
3376 expected_builder.DrawRect(rect4, paint4);
3377 auto expected = expected_builder.Build();
3378
3379 test(cull_rect, expected, "rect 4 intersects");
3380 }
3381
3382 { // All 4 rects
3383 DlIRect cull_rect = DlIRect::MakeLTRB(9, 9, 21, 21);
3384
3385 test(cull_rect, main, "all rects intersect");
3386 }
3387}
3388
3389TEST_F(DisplayListTest, DrawSaveDrawCannotInheritOpacity) {
3390 DisplayListBuilder builder;
3391 builder.DrawCircle(DlPoint(10, 10), 5, DlPaint());
3392 builder.Save();
3393 builder.ClipRect(DlRect::MakeLTRB(0, 0, 20, 20), DlClipOp::kIntersect, false);
3394 builder.DrawRect(DlRect::MakeLTRB(5, 5, 15, 15), DlPaint());
3395 builder.Restore();
3396 auto display_list = builder.Build();
3397
3398 ASSERT_FALSE(display_list->can_apply_group_opacity());
3399}
3400
3401TEST_F(DisplayListTest, DrawUnorderedRect) {
3402 auto renderer = [](DlCanvas& canvas, DlPaint& paint, DlRect& rect) {
3403 canvas.DrawRect(rect, paint);
3404 };
3405 check_inverted_bounds(renderer, "DrawRect");
3406}
3407
3408TEST_F(DisplayListTest, DrawUnorderedRoundRect) {
3409 auto renderer = [](DlCanvas& canvas, DlPaint& paint, DlRect& rect) {
3410 canvas.DrawRoundRect(DlRoundRect::MakeRectXY(rect, 2.0f, 2.0f), paint);
3411 };
3412 check_inverted_bounds(renderer, "DrawRoundRect");
3413}
3414
3415TEST_F(DisplayListTest, DrawUnorderedOval) {
3416 auto renderer = [](DlCanvas& canvas, DlPaint& paint, DlRect& rect) {
3417 canvas.DrawOval(rect, paint);
3418 };
3419 check_inverted_bounds(renderer, "DrawOval");
3420}
3421
3422TEST_F(DisplayListTest, DrawUnorderedRectangularPath) {
3423 auto renderer = [](DlCanvas& canvas, DlPaint& paint, DlRect& rect) {
3424 canvas.DrawPath(DlPath::MakeRect(rect), paint);
3425 };
3426 check_inverted_bounds(renderer, "DrawRectangularPath");
3427}
3428
3429TEST_F(DisplayListTest, DrawUnorderedOvalPath) {
3430 auto renderer = [](DlCanvas& canvas, DlPaint& paint, DlRect& rect) {
3431 canvas.DrawPath(DlPath::MakeOval(rect), paint);
3432 };
3433 check_inverted_bounds(renderer, "DrawOvalPath");
3434}
3435
3436TEST_F(DisplayListTest, DrawUnorderedRoundRectPathCW) {
3437 auto renderer = [](DlCanvas& canvas, DlPaint& paint, DlRect& rect) {
3438 DlPath path = DlPath::MakeRoundRectXY(rect, 2.0f, 2.0f, false);
3439 canvas.DrawPath(path, paint);
3440 };
3441 check_inverted_bounds(renderer, "DrawRoundRectPath Clockwise");
3442}
3443
3444TEST_F(DisplayListTest, DrawUnorderedRoundRectPathCCW) {
3445 auto renderer = [](DlCanvas& canvas, DlPaint& paint, DlRect& rect) {
3446 DlPath path = DlPath::MakeRoundRectXY(rect, 2.0f, 2.0f, true);
3447 canvas.DrawPath(path, paint);
3448 };
3449 check_inverted_bounds(renderer, "DrawRoundRectPath Counter-Clockwise");
3450}
3451
3452TEST_F(DisplayListTest, NopOperationsOmittedFromRecords) {
3453 auto run_tests = [](const std::string& name,
3454 void init(DisplayListBuilder & builder, DlPaint & paint),
3455 uint32_t expected_op_count = 0u,
3456 uint32_t expected_total_depth = 0u) {
3457 auto run_one_test =
3458 [init](const std::string& name,
3459 void build(DisplayListBuilder & builder, DlPaint & paint),
3460 uint32_t expected_op_count = 0u,
3461 uint32_t expected_total_depth = 0u) {
3462 DisplayListBuilder builder;
3463 DlPaint paint;
3464 init(builder, paint);
3465 build(builder, paint);
3466 auto list = builder.Build();
3467 if (list->op_count() != expected_op_count) {
3468 FML_LOG(ERROR) << *list;
3469 }
3470 ASSERT_EQ(list->op_count(), expected_op_count) << name;
3471 EXPECT_EQ(list->total_depth(), expected_total_depth) << name;
3472 ASSERT_TRUE(list->GetBounds().IsEmpty()) << name;
3473 };
3474 run_one_test(
3475 name + " DrawColor",
3476 [](DisplayListBuilder& builder, DlPaint& paint) {
3477 builder.DrawColor(paint.getColor(), paint.getBlendMode());
3478 },
3479 expected_op_count, expected_total_depth);
3480 run_one_test(
3481 name + " DrawPaint",
3482 [](DisplayListBuilder& builder, DlPaint& paint) {
3483 builder.DrawPaint(paint);
3484 },
3485 expected_op_count, expected_total_depth);
3486 run_one_test(
3487 name + " DrawRect",
3488 [](DisplayListBuilder& builder, DlPaint& paint) {
3489 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), paint);
3490 },
3491 expected_op_count, expected_total_depth);
3492 run_one_test(
3493 name + " Other Draw Ops",
3494 [](DisplayListBuilder& builder, DlPaint& paint) {
3495 builder.DrawLine(DlPoint(10, 10), DlPoint(20, 20), paint);
3496 builder.DrawOval(DlRect::MakeLTRB(10, 10, 20, 20), paint);
3497 builder.DrawCircle(DlPoint(50, 50), 20, paint);
3498 builder.DrawRoundRect(
3499 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(10, 10, 20, 20), 5, 5),
3500 paint);
3501 builder.DrawDiffRoundRect(
3502 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(5, 5, 100, 100), 5, 5),
3503 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(10, 10, 20, 20), 5, 5),
3504 paint);
3505 builder.DrawPath(kTestPath1, paint);
3506 builder.DrawArc(DlRect::MakeLTRB(10, 10, 20, 20), 45, 90, true,
3507 paint);
3508 DlPoint pts[] = {DlPoint(10, 10), DlPoint(20, 20)};
3509 builder.DrawPoints(DlPointMode::kLines, 2, pts, paint);
3510 builder.DrawVertices(kTestVertices1, DlBlendMode::kSrcOver, paint);
3511 builder.DrawImage(kTestImage1, DlPoint(10, 10),
3512 DlImageSampling::kLinear, &paint);
3513 builder.DrawImageRect(kTestImage1,
3514 DlRect::MakeLTRB(0.0f, 0.0f, 10.0f, 10.0f),
3515 DlRect::MakeLTRB(10.0f, 10.0f, 25.0f, 25.0f),
3516 DlImageSampling::kLinear, &paint);
3517 builder.DrawImageNine(kTestImage1, DlIRect::MakeLTRB(10, 10, 20, 20),
3518 DlRect::MakeLTRB(10, 10, 100, 100),
3519 DlFilterMode::kLinear, &paint);
3520 DlRSTransform xforms[] = {
3521 DlRSTransform::Make({10.0f, 10.0f}, 1.0f, DlDegrees(0)),
3522 DlRSTransform::Make({10.0f, 10.0f}, 1.0f, DlDegrees(90)),
3523 };
3524 DlRect rects[] = {
3525 DlRect::MakeLTRB(10, 10, 20, 20),
3526 DlRect::MakeLTRB(10, 20, 30, 20),
3527 };
3528 builder.DrawAtlas(kTestImage1, xforms, rects, nullptr, 2,
3529 DlBlendMode::kSrcOver, DlImageSampling::kLinear,
3530 nullptr, &paint);
3531 builder.DrawText(DlTextSkia::Make(GetTestTextBlob(1)), 10, 10, paint);
3532#if IMPELLER_SUPPORTS_RENDERING
3533 builder.DrawText(DlTextImpeller::Make(GetTestTextFrame(1)), 10, 10,
3534 paint);
3535#endif
3536
3537 // Dst mode eliminates most rendering ops except for
3538 // the following two, so we'll prune those manually...
3539 if (paint.getBlendMode() != DlBlendMode::kDst) {
3541 builder.DrawShadow(kTestPath1, paint.getColor(), 1, true, 1);
3542 }
3543 },
3544 expected_op_count, expected_total_depth);
3545 run_one_test(
3546 name + " SaveLayer",
3547 [](DisplayListBuilder& builder, DlPaint& paint) {
3548 builder.SaveLayer(std::nullopt, &paint, nullptr);
3549 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
3550 builder.Restore();
3551 },
3552 expected_op_count, expected_total_depth);
3553 run_one_test(
3554 name + " inside Save",
3555 [](DisplayListBuilder& builder, DlPaint& paint) {
3556 builder.Save();
3557 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), paint);
3558 builder.Restore();
3559 },
3560 expected_op_count, expected_total_depth);
3561 };
3562 run_tests("transparent color", //
3563 [](DisplayListBuilder& builder, DlPaint& paint) {
3565 });
3566 run_tests("0 alpha", //
3567 [](DisplayListBuilder& builder, DlPaint& paint) {
3568 // The transparent test above already tested transparent
3569 // black (all 0s), we set White color here so we can test
3570 // the case of all 1s with a 0 alpha
3571 paint.setColor(DlColor::kWhite());
3572 paint.setAlpha(0);
3573 });
3574 run_tests("BlendMode::kDst", //
3575 [](DisplayListBuilder& builder, DlPaint& paint) {
3576 paint.setBlendMode(DlBlendMode::kDst);
3577 });
3578 run_tests("Empty rect clip", //
3579 [](DisplayListBuilder& builder, DlPaint& paint) {
3580 builder.ClipRect(DlRect(), DlClipOp::kIntersect, false);
3581 });
3582 run_tests("Empty rrect clip", //
3583 [](DisplayListBuilder& builder, DlPaint& paint) {
3585 });
3586 run_tests("Empty path clip", //
3587 [](DisplayListBuilder& builder, DlPaint& paint) {
3588 builder.ClipPath(DlPath(), DlClipOp::kIntersect, false);
3589 });
3590 run_tests("Transparent SaveLayer", //
3591 [](DisplayListBuilder& builder, DlPaint& paint) {
3592 DlPaint save_paint;
3593 save_paint.setColor(DlColor::kTransparent());
3594 builder.SaveLayer(std::nullopt, &save_paint);
3595 });
3596 run_tests("0 alpha SaveLayer", //
3597 [](DisplayListBuilder& builder, DlPaint& paint) {
3598 DlPaint save_paint;
3599 // The transparent test above already tested transparent
3600 // black (all 0s), we set White color here so we can test
3601 // the case of all 1s with a 0 alpha
3602 save_paint.setColor(DlColor::kWhite());
3603 save_paint.setAlpha(0);
3604 builder.SaveLayer(std::nullopt, &save_paint);
3605 });
3606 run_tests("Dst blended SaveLayer", //
3607 [](DisplayListBuilder& builder, DlPaint& paint) {
3608 DlPaint save_paint;
3609 save_paint.setBlendMode(DlBlendMode::kDst);
3610 builder.SaveLayer(std::nullopt, &save_paint);
3611 });
3612 run_tests(
3613 "Nop inside SaveLayer",
3614 [](DisplayListBuilder& builder, DlPaint& paint) {
3615 builder.SaveLayer(std::nullopt, nullptr);
3616 paint.setBlendMode(DlBlendMode::kDst);
3617 },
3618 2u, 1u);
3619 run_tests("DrawImage inside Culled SaveLayer", //
3620 [](DisplayListBuilder& builder, DlPaint& paint) {
3621 DlPaint save_paint;
3622 save_paint.setColor(DlColor::kTransparent());
3623 builder.SaveLayer(std::nullopt, &save_paint);
3624 builder.DrawImage(kTestImage1, DlPoint(10, 10),
3626 });
3627}
3628
3634 public:
3636
3638 expected_.emplace_back(BoundsExpectation{
3639 .bounds = bounds,
3640 .options = SaveLayerOptions(),
3641 });
3642 return *this;
3643 }
3644
3646 bool clipped = false) {
3647 SaveLayerOptions options;
3648 options = options.with_bounds_from_caller();
3649 if (clipped) {
3650 options = options.with_content_is_clipped();
3651 }
3652 expected_.emplace_back(BoundsExpectation{
3653 .bounds = bounds,
3654 .options = options,
3655 });
3656 return *this;
3657 }
3658
3659 void saveLayer(const DlRect& bounds,
3660 const SaveLayerOptions options,
3661 const DlImageFilter* backdrop,
3662 std::optional<int64_t> backdrop_id) override {
3663 ASSERT_LT(save_layer_count_, expected_.size());
3664 auto expected = expected_[save_layer_count_];
3665 EXPECT_EQ(options.bounds_from_caller(),
3666 expected.options.bounds_from_caller())
3667 << "expected bounds index " << save_layer_count_;
3668 EXPECT_EQ(options.content_is_clipped(),
3669 expected.options.content_is_clipped())
3670 << "expected bounds index " << save_layer_count_;
3671 if (!DlScalarNearlyEqual(bounds.GetLeft(), expected.bounds.GetLeft()) ||
3672 !DlScalarNearlyEqual(bounds.GetTop(), expected.bounds.GetTop()) ||
3673 !DlScalarNearlyEqual(bounds.GetRight(), expected.bounds.GetRight()) ||
3674 !DlScalarNearlyEqual(bounds.GetBottom(), expected.bounds.GetBottom())) {
3675 EXPECT_EQ(bounds, expected.bounds)
3676 << "expected bounds index " << save_layer_count_;
3677 }
3678 save_layer_count_++;
3679 }
3680
3681 bool all_bounds_checked() const {
3682 return save_layer_count_ == expected_.size();
3683 }
3684
3685 private:
3686 struct BoundsExpectation {
3687 const DlRect bounds;
3688 const SaveLayerOptions options;
3689 };
3690
3691 std::vector<BoundsExpectation> expected_;
3692 size_t save_layer_count_ = 0;
3693};
3694
3695TEST_F(DisplayListTest, SaveLayerBoundsComputationOfSimpleRect) {
3696 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3697
3698 DisplayListBuilder builder;
3699 builder.SaveLayer(std::nullopt, nullptr);
3700 { //
3701 builder.DrawRect(rect, DlPaint());
3702 }
3703 builder.Restore();
3704 auto display_list = builder.Build();
3705
3706 SaveLayerBoundsExpector expector;
3707 expector.addComputedExpectation(rect);
3708 display_list->Dispatch(expector);
3709 EXPECT_TRUE(expector.all_bounds_checked());
3710}
3711
3712TEST_F(DisplayListTest, SaveLayerBoundsComputationOfMaskBlurredRect) {
3713 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3714 DlPaint draw_paint;
3715 auto mask_filter = DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 2.0f);
3716 draw_paint.setMaskFilter(mask_filter);
3717
3718 DisplayListBuilder builder;
3719 builder.SaveLayer(std::nullopt, nullptr);
3720 { //
3721 builder.DrawRect(rect, draw_paint);
3722 }
3723 builder.Restore();
3724 auto display_list = builder.Build();
3725
3726 SaveLayerBoundsExpector expector;
3727 expector.addComputedExpectation(rect.Expand(6.0f, 6.0f));
3728 display_list->Dispatch(expector);
3729 EXPECT_TRUE(expector.all_bounds_checked());
3730}
3731
3732TEST_F(DisplayListTest, SaveLayerBoundsComputationOfImageBlurredRect) {
3733 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3734 DlPaint draw_paint;
3735 auto image_filter = DlImageFilter::MakeBlur(2.0f, 3.0f, DlTileMode::kDecal);
3736 draw_paint.setImageFilter(image_filter);
3737
3738 DisplayListBuilder builder;
3739 builder.SaveLayer(std::nullopt, nullptr);
3740 { //
3741 builder.DrawRect(rect, draw_paint);
3742 }
3743 builder.Restore();
3744 auto display_list = builder.Build();
3745
3746 SaveLayerBoundsExpector expector;
3747 expector.addComputedExpectation(rect.Expand(6.0f, 9.0f));
3748 display_list->Dispatch(expector);
3749 EXPECT_TRUE(expector.all_bounds_checked());
3750}
3751
3752TEST_F(DisplayListTest, SaveLayerBoundsComputationOfStrokedRect) {
3753 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3754 DlPaint draw_paint;
3755 draw_paint.setStrokeWidth(5.0f);
3757
3758 DisplayListBuilder builder;
3759 builder.SaveLayer(std::nullopt, nullptr);
3760 { //
3761 builder.DrawRect(rect, draw_paint);
3762 }
3763 builder.Restore();
3764 auto display_list = builder.Build();
3765
3766 SaveLayerBoundsExpector expector;
3767 expector.addComputedExpectation(rect.Expand(2.5f, 2.5f));
3768 display_list->Dispatch(expector);
3769 EXPECT_TRUE(expector.all_bounds_checked());
3770}
3771
3772TEST_F(DisplayListTest, TranslatedSaveLayerBoundsComputationOfSimpleRect) {
3773 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3774
3775 DisplayListBuilder builder;
3776 builder.Translate(10.0f, 10.0f);
3777 builder.SaveLayer(std::nullopt, nullptr);
3778 { //
3779 builder.DrawRect(rect, DlPaint());
3780 }
3781 builder.Restore();
3782 auto display_list = builder.Build();
3783
3784 SaveLayerBoundsExpector expector;
3785 expector.addComputedExpectation(rect);
3786 display_list->Dispatch(expector);
3787 EXPECT_TRUE(expector.all_bounds_checked());
3788}
3789
3790TEST_F(DisplayListTest, ScaledSaveLayerBoundsComputationOfSimpleRect) {
3791 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3792
3793 DisplayListBuilder builder;
3794 builder.Scale(10.0f, 10.0f);
3795 builder.SaveLayer(std::nullopt, nullptr);
3796 { //
3797 builder.DrawRect(rect, DlPaint());
3798 }
3799 builder.Restore();
3800 auto display_list = builder.Build();
3801
3802 SaveLayerBoundsExpector expector;
3803 expector.addComputedExpectation(rect);
3804 display_list->Dispatch(expector);
3805 EXPECT_TRUE(expector.all_bounds_checked());
3806}
3807
3808TEST_F(DisplayListTest, RotatedSaveLayerBoundsComputationOfSimpleRect) {
3809 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3810
3811 DisplayListBuilder builder;
3812 builder.Rotate(45.0f);
3813 builder.SaveLayer(std::nullopt, nullptr);
3814 { //
3815 builder.DrawRect(rect, DlPaint());
3816 }
3817 builder.Restore();
3818 auto display_list = builder.Build();
3819
3820 SaveLayerBoundsExpector expector;
3821 expector.addComputedExpectation(rect);
3822 display_list->Dispatch(expector);
3823 EXPECT_TRUE(expector.all_bounds_checked());
3824}
3825
3826TEST_F(DisplayListTest, TransformResetSaveLayerBoundsComputationOfSimpleRect) {
3827 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3828 DlRect rect_doubled = DlRect::MakeLTRB(200.0f, 200.0f, 400.0f, 400.0f);
3829
3830 DisplayListBuilder builder;
3831 builder.Scale(10.0f, 10.0f);
3832 builder.SaveLayer(std::nullopt, nullptr);
3833 builder.TransformReset();
3834 builder.Scale(20.0f, 20.0f);
3835 // Net local transform for SaveLayer is Scale(2, 2)
3836 { //
3837 builder.DrawRect(rect, DlPaint());
3838 }
3839 builder.Restore();
3840 auto display_list = builder.Build();
3841
3842 SaveLayerBoundsExpector expector;
3843 expector.addComputedExpectation(rect_doubled);
3844 display_list->Dispatch(expector);
3845 EXPECT_TRUE(expector.all_bounds_checked());
3846}
3847
3848TEST_F(DisplayListTest, SaveLayerBoundsComputationOfTranslatedSimpleRect) {
3849 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3850
3851 DisplayListBuilder builder;
3852 builder.SaveLayer(std::nullopt, nullptr);
3853 { //
3854 builder.Translate(10.0f, 10.0f);
3855 builder.DrawRect(rect, DlPaint());
3856 }
3857 builder.Restore();
3858 auto display_list = builder.Build();
3859
3860 SaveLayerBoundsExpector expector;
3861 expector.addComputedExpectation(rect.Shift(10.0f, 10.0f));
3862 display_list->Dispatch(expector);
3863 EXPECT_TRUE(expector.all_bounds_checked());
3864}
3865
3866TEST_F(DisplayListTest, SaveLayerBoundsComputationOfScaledSimpleRect) {
3867 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3868
3869 DisplayListBuilder builder;
3870 builder.SaveLayer(std::nullopt, nullptr);
3871 { //
3872 builder.Scale(10.0f, 10.0f);
3873 builder.DrawRect(rect, DlPaint());
3874 }
3875 builder.Restore();
3876 auto display_list = builder.Build();
3877
3878 SaveLayerBoundsExpector expector;
3879 expector.addComputedExpectation(
3880 DlRect::MakeLTRB(1000.0f, 1000.0f, 2000.0f, 2000.0f));
3881 display_list->Dispatch(expector);
3882 EXPECT_TRUE(expector.all_bounds_checked());
3883}
3884
3885TEST_F(DisplayListTest, SaveLayerBoundsComputationOfRotatedSimpleRect) {
3886 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3887
3888 DisplayListBuilder builder;
3889 builder.SaveLayer(std::nullopt, nullptr);
3890 { //
3891 builder.Rotate(45.0f);
3892 builder.DrawRect(rect, DlPaint());
3893 }
3894 builder.Restore();
3895 auto display_list = builder.Build();
3896
3898 SaveLayerBoundsExpector expector;
3899 expector.addComputedExpectation(rect.TransformAndClipBounds(matrix));
3900 display_list->Dispatch(expector);
3901 EXPECT_TRUE(expector.all_bounds_checked());
3902}
3903
3904TEST_F(DisplayListTest, SaveLayerBoundsComputationOfNestedSimpleRect) {
3905 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3906
3907 DisplayListBuilder builder;
3908 builder.SaveLayer(std::nullopt, nullptr);
3909 { //
3910 builder.SaveLayer(std::nullopt, nullptr);
3911 { //
3912 builder.DrawRect(rect, DlPaint());
3913 }
3914 builder.Restore();
3915 }
3916 builder.Restore();
3917 auto display_list = builder.Build();
3918
3919 SaveLayerBoundsExpector expector;
3920 expector.addComputedExpectation(rect);
3921 expector.addComputedExpectation(rect);
3922 display_list->Dispatch(expector);
3923 EXPECT_TRUE(expector.all_bounds_checked());
3924}
3925
3926TEST_F(DisplayListTest, FloodingSaveLayerBoundsComputationOfSimpleRect) {
3927 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3928 DlPaint save_paint;
3929 auto color_filter =
3930 DlColorFilter::MakeBlend(DlColor::kRed(), DlBlendMode::kSrc);
3931 ASSERT_TRUE(color_filter->modifies_transparent_black());
3932 save_paint.setColorFilter(color_filter);
3933 DlRect clip_rect = rect.Expand(100.0f, 100.0f);
3934 ASSERT_NE(clip_rect, rect);
3935 ASSERT_TRUE(clip_rect.Contains(rect));
3936
3937 DisplayListBuilder builder;
3938 builder.ClipRect(clip_rect);
3939 builder.SaveLayer(std::nullopt, &save_paint);
3940 { //
3941 builder.DrawRect(rect, DlPaint());
3942 }
3943 builder.Restore();
3944 auto display_list = builder.Build();
3945
3946 SaveLayerBoundsExpector expector;
3947 expector.addComputedExpectation(rect);
3948 display_list->Dispatch(expector);
3949 EXPECT_TRUE(expector.all_bounds_checked());
3950}
3951
3952TEST_F(DisplayListTest, NestedFloodingSaveLayerBoundsComputationOfSimpleRect) {
3953 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3954 DlPaint save_paint;
3955 auto color_filter =
3956 DlColorFilter::MakeBlend(DlColor::kRed(), DlBlendMode::kSrc);
3957 ASSERT_TRUE(color_filter->modifies_transparent_black());
3958 save_paint.setColorFilter(color_filter);
3959 DlRect clip_rect = rect.Expand(100.0f, 100.0f);
3960 ASSERT_NE(clip_rect, rect);
3961 ASSERT_TRUE(clip_rect.Contains(rect));
3962
3963 DisplayListBuilder builder;
3964 builder.ClipRect(clip_rect);
3965 builder.SaveLayer(std::nullopt, nullptr);
3966 {
3967 builder.SaveLayer(std::nullopt, &save_paint);
3968 { //
3969 builder.DrawRect(rect, DlPaint());
3970 }
3971 builder.Restore();
3972 }
3973 builder.Restore();
3974 auto display_list = builder.Build();
3975
3976 EXPECT_EQ(display_list->GetBounds(), clip_rect);
3977
3978 SaveLayerBoundsExpector expector;
3979 expector.addComputedExpectation(clip_rect);
3980 expector.addComputedExpectation(rect);
3981 display_list->Dispatch(expector);
3982 EXPECT_TRUE(expector.all_bounds_checked());
3983}
3984
3985TEST_F(DisplayListTest, SaveLayerBoundsComputationOfFloodingImageFilter) {
3986 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3987 DlPaint draw_paint;
3988 auto color_filter =
3989 DlColorFilter::MakeBlend(DlColor::kRed(), DlBlendMode::kSrc);
3990 ASSERT_TRUE(color_filter->modifies_transparent_black());
3991 auto image_filter = DlImageFilter::MakeColorFilter(color_filter);
3992 draw_paint.setImageFilter(image_filter);
3993 DlRect clip_rect = rect.Expand(100.0f, 100.0f);
3994 ASSERT_NE(clip_rect, rect);
3995 ASSERT_TRUE(clip_rect.Contains(rect));
3996
3997 DisplayListBuilder builder;
3998 builder.ClipRect(clip_rect);
3999 builder.SaveLayer(std::nullopt, nullptr);
4000 { //
4001 builder.DrawRect(rect, draw_paint);
4002 }
4003 builder.Restore();
4004 auto display_list = builder.Build();
4005
4006 SaveLayerBoundsExpector expector;
4007 expector.addComputedExpectation(clip_rect);
4008 display_list->Dispatch(expector);
4009 EXPECT_TRUE(expector.all_bounds_checked());
4010}
4011
4012TEST_F(DisplayListTest, SaveLayerBoundsComputationOfFloodingColorFilter) {
4013 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
4014 DlPaint draw_paint;
4015 auto color_filter =
4016 DlColorFilter::MakeBlend(DlColor::kRed(), DlBlendMode::kSrc);
4017 ASSERT_TRUE(color_filter->modifies_transparent_black());
4018 draw_paint.setColorFilter(color_filter);
4019 DlRect clip_rect = rect.Expand(100.0f, 100.0f);
4020 ASSERT_NE(clip_rect, rect);
4021 ASSERT_TRUE(clip_rect.Contains(rect));
4022
4023 DisplayListBuilder builder;
4024 builder.ClipRect(clip_rect);
4025 builder.SaveLayer(std::nullopt, nullptr);
4026 { //
4027 builder.DrawRect(rect, draw_paint);
4028 }
4029 builder.Restore();
4030 auto display_list = builder.Build();
4031
4032 // A color filter is implicitly clipped to the draw bounds so the layer
4033 // bounds will be the same as the draw bounds.
4034 SaveLayerBoundsExpector expector;
4035 expector.addComputedExpectation(rect);
4036 display_list->Dispatch(expector);
4037 EXPECT_TRUE(expector.all_bounds_checked());
4038}
4039
4040TEST_F(DisplayListTest, SaveLayerBoundsClipDetectionSimpleUnclippedRect) {
4041 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
4042 DlRect save_rect = DlRect::MakeLTRB(50.0f, 50.0f, 250.0f, 250.0f);
4043
4044 DisplayListBuilder builder;
4045 builder.SaveLayer(save_rect, nullptr);
4046 { //
4047 builder.DrawRect(rect, DlPaint());
4048 }
4049 builder.Restore();
4050 auto display_list = builder.Build();
4051
4052 SaveLayerBoundsExpector expector;
4053 expector.addSuppliedExpectation(rect);
4054 display_list->Dispatch(expector);
4055 EXPECT_TRUE(expector.all_bounds_checked());
4056}
4057
4058TEST_F(DisplayListTest, SaveLayerBoundsClipDetectionSimpleClippedRect) {
4059 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
4060 DlRect save_rect = DlRect::MakeLTRB(50.0f, 50.0f, 110.0f, 110.0f);
4061 DlRect content_rect = DlRect::MakeLTRB(100.0f, 100.0f, 110.0f, 110.0f);
4062
4063 DisplayListBuilder builder;
4064 builder.SaveLayer(save_rect, nullptr);
4065 { //
4066 builder.DrawRect(rect, DlPaint());
4067 }
4068 builder.Restore();
4069 auto display_list = builder.Build();
4070
4071 SaveLayerBoundsExpector expector;
4072 expector.addSuppliedExpectation(content_rect, true);
4073 display_list->Dispatch(expector);
4074 EXPECT_TRUE(expector.all_bounds_checked());
4075}
4076
4077TEST_F(DisplayListTest, DisjointSaveLayerBoundsProduceEmptySuppliedBounds) {
4078 // This test was added when we fixed the Builder code to check the
4079 // return value of the Skia Rect intersect method, but it turns out
4080 // that the indicated case never happens in practice due to the
4081 // internal culling during the recording process. It actually passes
4082 // both before and after the fix, but is here to ensure the right
4083 // behavior does not regress.
4084
4085 DlRect layer_bounds = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4086 DlRect draw_rect = DlRect::MakeLTRB(50.0f, 50.0f, 100.0f, 100.0f);
4087 ASSERT_FALSE(layer_bounds.IntersectsWithRect(draw_rect));
4088 ASSERT_FALSE(layer_bounds.IsEmpty());
4089 ASSERT_FALSE(draw_rect.IsEmpty());
4090
4091 DisplayListBuilder builder;
4092 builder.SaveLayer(layer_bounds, nullptr);
4093 builder.DrawRect(draw_rect, DlPaint());
4094 builder.Restore();
4095 auto display_list = builder.Build();
4096
4097 SaveLayerBoundsExpector expector;
4098 expector.addSuppliedExpectation(DlRect(), false);
4099 display_list->Dispatch(expector);
4100 EXPECT_TRUE(expector.all_bounds_checked());
4101}
4102
4103class DepthExpector : public virtual DlOpReceiver,
4107 virtual IgnoreDrawDispatchHelper {
4108 public:
4109 explicit DepthExpector(std::vector<uint32_t> expectations)
4110 : depth_expectations_(std::move(expectations)) {}
4111
4112 void save() override {
4113 // This method should not be called since we override the variant with
4114 // the total_content_depth parameter.
4115 FAIL() << "save(no depth parameter) method should not be called";
4116 }
4117
4118 void save(uint32_t total_content_depth) override {
4119 ASSERT_LT(index_, depth_expectations_.size());
4120 EXPECT_EQ(depth_expectations_[index_], total_content_depth)
4121 << "at index " << index_;
4122 index_++;
4123 }
4124
4125 void saveLayer(const DlRect& bounds,
4126 SaveLayerOptions options,
4127 const DlImageFilter* backdrop,
4128 std::optional<int64_t> backdrop_id) override {
4129 // This method should not be called since we override the variant with
4130 // the total_content_depth parameter.
4131 FAIL() << "saveLayer(no depth parameter) method should not be called";
4132 }
4133
4134 void saveLayer(const DlRect& bounds,
4135 const SaveLayerOptions& options,
4136 uint32_t total_content_depth,
4137 DlBlendMode max_content_mode,
4138 const DlImageFilter* backdrop,
4139 std::optional<int64_t> backdrop_id) override {
4140 ASSERT_LT(index_, depth_expectations_.size());
4141 EXPECT_EQ(depth_expectations_[index_], total_content_depth)
4142 << "at index " << index_;
4143 index_++;
4144 }
4145
4146 bool all_depths_checked() const {
4147 return index_ == depth_expectations_.size();
4148 }
4149
4150 private:
4151 size_t index_ = 0;
4152 std::vector<uint32_t> depth_expectations_;
4153};
4154
4155TEST_F(DisplayListTest, SaveContentDepthTest) {
4156 DisplayListBuilder child_builder;
4157 child_builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20),
4158 DlPaint()); // depth 1
4159 auto child = child_builder.Build();
4160
4161 DisplayListBuilder builder;
4162 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint()); // depth 1
4163
4164 builder.Save(); // covers depth 1->9
4165 {
4166 builder.Translate(5, 5); // triggers deferred save at depth 1
4167 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint()); // depth 2
4168
4169 builder.DrawDisplayList(child, 1.0f); // depth 3 (content) + 4 (self)
4170
4171 builder.SaveLayer(std::nullopt, nullptr); // covers depth 5->6
4172 {
4173 builder.DrawRect(DlRect::MakeLTRB(12, 12, 22, 22), DlPaint()); // depth 5
4174 builder.DrawRect(DlRect::MakeLTRB(14, 14, 24, 24), DlPaint()); // depth 6
4175 }
4176 builder.Restore(); // layer is restored with depth 6
4177
4178 builder.DrawRect(DlRect::MakeLTRB(16, 16, 26, 26), DlPaint()); // depth 8
4179 builder.DrawRect(DlRect::MakeLTRB(18, 18, 28, 28), DlPaint()); // depth 9
4180 }
4181 builder.Restore(); // save is restored with depth 9
4182
4183 builder.DrawRect(DlRect::MakeLTRB(16, 16, 26, 26), DlPaint()); // depth 10
4184 builder.DrawRect(DlRect::MakeLTRB(18, 18, 28, 28), DlPaint()); // depth 11
4185 auto display_list = builder.Build();
4186
4187 EXPECT_EQ(display_list->total_depth(), 11u);
4188
4189 DepthExpector expector({8, 2});
4190 display_list->Dispatch(expector);
4191}
4192
4193TEST_F(DisplayListTest, FloodingFilteredLayerPushesRestoreOpIndex) {
4194 DisplayListBuilder builder(true);
4195 builder.ClipRect(DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f));
4196 // ClipRect does not contribute to rtree rects, no id needed
4197
4198 DlPaint save_paint;
4199 // clang-format off
4200 const float matrix[] = {
4201 0.5f, 0.0f, 0.0f, 0.0f, 0.5f,
4202 0.5f, 0.0f, 0.0f, 0.0f, 0.5f,
4203 0.5f, 0.0f, 0.0f, 0.0f, 0.5f,
4204 0.5f, 0.0f, 0.0f, 0.0f, 0.5f
4205 };
4206 // clang-format on
4207 auto color_filter = DlColorFilter::MakeMatrix(matrix);
4208 save_paint.setImageFilter(DlImageFilter::MakeColorFilter(color_filter));
4209 builder.SaveLayer(std::nullopt, &save_paint);
4210 int save_layer_id = DisplayListBuilderTestingLastOpIndex(builder);
4211
4212 builder.DrawRect(DlRect::MakeLTRB(120.0f, 120.0f, 125.0f, 125.0f), DlPaint());
4213 int draw_rect_id = DisplayListBuilderTestingLastOpIndex(builder);
4214
4215 builder.Restore();
4216 int restore_id = DisplayListBuilderTestingLastOpIndex(builder);
4217
4218 auto dl = builder.Build();
4219 std::vector<int> indices;
4220 dl->rtree()->search(DlRect::MakeLTRB(0.0f, 0.0f, 500.0f, 500.0f), &indices);
4221 ASSERT_EQ(indices.size(), 3u);
4222 EXPECT_EQ(dl->rtree()->id(indices[0]), save_layer_id);
4223 EXPECT_EQ(dl->rtree()->id(indices[1]), draw_rect_id);
4224 EXPECT_EQ(dl->rtree()->id(indices[2]), restore_id);
4225}
4226
4227TEST_F(DisplayListTest, TransformingFilterSaveLayerSimpleContentBounds) {
4228 DisplayListBuilder builder;
4229 builder.ClipRect(DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f));
4230
4231 DlPaint save_paint;
4232 auto image_filter =
4235 save_paint.setImageFilter(image_filter);
4236 builder.SaveLayer(std::nullopt, &save_paint);
4237
4238 builder.DrawRect(DlRect::MakeLTRB(20.0f, 20.0f, 25.0f, 25.0f), DlPaint());
4239
4240 builder.Restore();
4241
4242 auto dl = builder.Build();
4243 EXPECT_EQ(dl->GetBounds(), DlRect::MakeLTRB(120.0f, 120.0f, 125.0f, 125.0f));
4244}
4245
4246TEST_F(DisplayListTest, TransformingFilterSaveLayerFloodedContentBounds) {
4247 DisplayListBuilder builder;
4248 builder.ClipRect(DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f));
4249
4250 DlPaint save_paint;
4251 auto image_filter =
4254 save_paint.setImageFilter(image_filter);
4255 builder.SaveLayer(std::nullopt, &save_paint);
4256
4257 builder.DrawColor(DlColor::kBlue(), DlBlendMode::kSrcOver);
4258
4259 builder.Restore();
4260
4261 auto dl = builder.Build();
4262 EXPECT_EQ(dl->GetBounds(), DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f));
4263}
4264
4265TEST_F(DisplayListTest, OpacityIncompatibleRenderOpInsideDeferredSave) {
4266 {
4267 // Without deferred save
4268 DisplayListBuilder builder;
4269 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4270 DlPaint().setBlendMode(DlBlendMode::kClear));
4271 EXPECT_FALSE(builder.Build()->can_apply_group_opacity());
4272 }
4273
4274 {
4275 // With deferred save
4276 DisplayListBuilder builder;
4277 builder.Save();
4278 {
4279 // Nothing to trigger the deferred save...
4280 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4281 DlPaint().setBlendMode(DlBlendMode::kClear));
4282 }
4283 // Deferred save was not triggered, did it forward the incompatibility
4284 // flags?
4285 builder.Restore();
4286 EXPECT_FALSE(builder.Build()->can_apply_group_opacity());
4287 }
4288}
4289
4290TEST_F(DisplayListTest, MaxBlendModeEmptyDisplayList) {
4291 DisplayListBuilder builder;
4292 EXPECT_EQ(builder.Build()->max_root_blend_mode(), DlBlendMode::kClear);
4293}
4294
4295TEST_F(DisplayListTest, MaxBlendModeSimpleRect) {
4296 auto test = [](DlBlendMode mode) {
4297 DisplayListBuilder builder;
4298 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4300 DlBlendMode expect =
4301 (mode == DlBlendMode::kDst) ? DlBlendMode::kClear : mode;
4302 EXPECT_EQ(builder.Build()->max_root_blend_mode(), expect) //
4303 << "testing " << mode;
4304 };
4305
4306 for (int i = 0; i < static_cast<int>(DlBlendMode::kLastMode); i++) {
4307 test(static_cast<DlBlendMode>(i));
4308 }
4309}
4310
4311TEST_F(DisplayListTest, MaxBlendModeInsideNonDeferredSave) {
4312 DisplayListBuilder builder;
4313 builder.Save();
4314 {
4315 // Trigger the deferred save
4316 builder.Scale(2.0f, 2.0f);
4317 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4318 DlPaint().setBlendMode(DlBlendMode::kModulate));
4319 }
4320 // Save was triggered, did it forward the max blend mode?
4321 builder.Restore();
4322 EXPECT_EQ(builder.Build()->max_root_blend_mode(), DlBlendMode::kModulate);
4323}
4324
4325TEST_F(DisplayListTest, MaxBlendModeInsideDeferredSave) {
4326 DisplayListBuilder builder;
4327 builder.Save();
4328 {
4329 // Nothing to trigger the deferred save...
4330 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4331 DlPaint().setBlendMode(DlBlendMode::kModulate));
4332 }
4333 // Deferred save was not triggered, did it forward the max blend mode?
4334 builder.Restore();
4335 EXPECT_EQ(builder.Build()->max_root_blend_mode(), DlBlendMode::kModulate);
4336}
4337
4338TEST_F(DisplayListTest, MaxBlendModeInsideSaveLayer) {
4339 DisplayListBuilder builder;
4340 builder.SaveLayer(std::nullopt, nullptr);
4341 {
4342 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4343 DlPaint().setBlendMode(DlBlendMode::kModulate));
4344 }
4345 builder.Restore();
4346 auto dl = builder.Build();
4347 EXPECT_EQ(dl->max_root_blend_mode(), DlBlendMode::kSrcOver);
4348 SAVE_LAYER_EXPECTOR(expector);
4349 expector.addExpectation(DlBlendMode::kModulate);
4350 dl->Dispatch(expector);
4351 EXPECT_TRUE(expector.all_expectations_checked());
4352}
4353
4354TEST_F(DisplayListTest, MaxBlendModeInsideNonDefaultBlendedSaveLayer) {
4355 DisplayListBuilder builder;
4356 DlPaint save_paint;
4357 save_paint.setBlendMode(DlBlendMode::kScreen);
4358 builder.SaveLayer(std::nullopt, &save_paint);
4359 {
4360 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4361 DlPaint().setBlendMode(DlBlendMode::kModulate));
4362 }
4363 builder.Restore();
4364 auto dl = builder.Build();
4365 EXPECT_EQ(dl->max_root_blend_mode(), DlBlendMode::kScreen);
4366 SAVE_LAYER_EXPECTOR(expector);
4367 expector.addExpectation(DlBlendMode::kModulate);
4368 dl->Dispatch(expector);
4369 EXPECT_TRUE(expector.all_expectations_checked());
4370}
4371
4372TEST_F(DisplayListTest, MaxBlendModeInsideComplexDeferredSaves) {
4373 DisplayListBuilder builder;
4374 builder.Save();
4375 {
4376 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4377 DlPaint().setBlendMode(DlBlendMode::kModulate));
4378 builder.Save();
4379 {
4380 // We want to use a blend mode that is greater than modulate here
4381 ASSERT_GT(DlBlendMode::kScreen, DlBlendMode::kModulate);
4382 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4383 DlPaint().setBlendMode(DlBlendMode::kScreen));
4384 }
4385 builder.Restore();
4386
4387 // We want to use a blend mode that is smaller than modulate here
4388 ASSERT_LT(DlBlendMode::kSrc, DlBlendMode::kModulate);
4389 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4390 DlPaint().setBlendMode(DlBlendMode::kSrc));
4391 }
4392 builder.Restore();
4393
4394 // Double check that kScreen is the max blend mode
4395 auto expect = std::max(DlBlendMode::kModulate, DlBlendMode::kScreen);
4396 expect = std::max(expect, DlBlendMode::kSrc);
4397 ASSERT_EQ(expect, DlBlendMode::kScreen);
4398
4399 EXPECT_EQ(builder.Build()->max_root_blend_mode(), DlBlendMode::kScreen);
4400}
4401
4402TEST_F(DisplayListTest, MaxBlendModeInsideComplexSaveLayers) {
4403 DisplayListBuilder builder;
4404 builder.SaveLayer(std::nullopt, nullptr);
4405 {
4406 // outer save layer has Modulate now and Src later - Modulate is larger
4407 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4408 DlPaint().setBlendMode(DlBlendMode::kModulate));
4409 builder.SaveLayer(std::nullopt, nullptr);
4410 {
4411 // inner save layer only has a Screen blend
4412 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4413 DlPaint().setBlendMode(DlBlendMode::kScreen));
4414 }
4415 builder.Restore();
4416
4417 // We want to use a blend mode that is smaller than modulate here
4418 ASSERT_LT(DlBlendMode::kSrc, DlBlendMode::kModulate);
4419 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4420 DlPaint().setBlendMode(DlBlendMode::kSrc));
4421 }
4422 builder.Restore();
4423
4424 // Double check that kModulate is the max blend mode for the first
4425 // SaveLayer operations
4426 auto expect = std::max(DlBlendMode::kModulate, DlBlendMode::kSrc);
4427 ASSERT_EQ(expect, DlBlendMode::kModulate);
4428
4429 auto dl = builder.Build();
4430 EXPECT_EQ(dl->max_root_blend_mode(), DlBlendMode::kSrcOver);
4431 SAVE_LAYER_EXPECTOR(expector);
4432 expector //
4433 .addExpectation(DlBlendMode::kModulate)
4434 .addExpectation(DlBlendMode::kScreen);
4435 dl->Dispatch(expector);
4436 EXPECT_TRUE(expector.all_expectations_checked());
4437}
4438
4439TEST_F(DisplayListTest, BackdropDetectionEmptyDisplayList) {
4440 DisplayListBuilder builder;
4441 EXPECT_FALSE(builder.Build()->root_has_backdrop_filter());
4442}
4443
4444TEST_F(DisplayListTest, BackdropDetectionSimpleRect) {
4445 DisplayListBuilder builder;
4446 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10), DlPaint());
4447 EXPECT_FALSE(builder.Build()->root_has_backdrop_filter());
4448}
4449
4450TEST_F(DisplayListTest, BackdropDetectionSimpleSaveLayer) {
4451 DisplayListBuilder builder;
4452 builder.SaveLayer(std::nullopt, nullptr, &kTestBlurImageFilter1);
4453 {
4454 // inner content has no backdrop filter
4455 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10), DlPaint());
4456 }
4457 builder.Restore();
4458 auto dl = builder.Build();
4459
4460 EXPECT_TRUE(dl->root_has_backdrop_filter());
4461 // The SaveLayer itself, though, does not have the contains backdrop
4462 // flag set because its content does not contain a SaveLayer with backdrop
4463 SAVE_LAYER_EXPECTOR(expector);
4464 expector.addExpectation(
4465 SaveLayerOptions::kNoAttributes.with_can_distribute_opacity());
4466 dl->Dispatch(expector);
4467 EXPECT_TRUE(expector.all_expectations_checked());
4468}
4469
4470TEST_F(DisplayListTest, BackdropDetectionNestedSaveLayer) {
4471 DisplayListBuilder builder;
4472 builder.SaveLayer(std::nullopt, nullptr);
4473 {
4474 // first inner content does have backdrop filter
4475 builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10), DlPaint());
4476 builder.SaveLayer(std::nullopt, nullptr, &kTestBlurImageFilter1);
4477 {
4478 // second inner content has no backdrop filter
4479 builder.DrawRect(DlRect::MakeLTRB(10, 10, 20, 20), DlPaint());
4480 }
4481 builder.Restore();
4482 }
4483 builder.Restore();
4484 auto dl = builder.Build();
4485
4486 EXPECT_FALSE(dl->root_has_backdrop_filter());
4487 SAVE_LAYER_EXPECTOR(expector);
4488 expector //
4489 .addExpectation(SaveLayerOptions::kNoAttributes //
4490 .with_contains_backdrop_filter()
4491 .with_content_is_unbounded())
4492 .addExpectation(
4493 SaveLayerOptions::kNoAttributes.with_can_distribute_opacity());
4494 dl->Dispatch(expector);
4495 EXPECT_TRUE(expector.all_expectations_checked());
4496}
4497
4498TEST_F(DisplayListTest, DrawDisplayListForwardsMaxBlend) {
4499 DisplayListBuilder child_builder;
4500 child_builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4501 DlPaint().setBlendMode(DlBlendMode::kMultiply));
4502 auto child_dl = child_builder.Build();
4503 EXPECT_EQ(child_dl->max_root_blend_mode(), DlBlendMode::kMultiply);
4504 EXPECT_FALSE(child_dl->root_has_backdrop_filter());
4505
4506 DisplayListBuilder parent_builder;
4507 parent_builder.DrawDisplayList(child_dl);
4508 auto parent_dl = parent_builder.Build();
4509 EXPECT_EQ(parent_dl->max_root_blend_mode(), DlBlendMode::kMultiply);
4510 EXPECT_FALSE(parent_dl->root_has_backdrop_filter());
4511}
4512
4513TEST_F(DisplayListTest, DrawDisplayListForwardsBackdropFlag) {
4514 DisplayListBuilder child_builder;
4515 DlBlurImageFilter backdrop(2.0f, 2.0f, DlTileMode::kDecal);
4516 child_builder.SaveLayer(std::nullopt, nullptr, &backdrop);
4517 child_builder.DrawRect(DlRect::MakeLTRB(0, 0, 10, 10),
4518 DlPaint().setBlendMode(DlBlendMode::kMultiply));
4519 child_builder.Restore();
4520 auto child_dl = child_builder.Build();
4521 EXPECT_EQ(child_dl->max_root_blend_mode(), DlBlendMode::kSrcOver);
4522 EXPECT_TRUE(child_dl->root_has_backdrop_filter());
4523
4524 DisplayListBuilder parent_builder;
4525 parent_builder.DrawDisplayList(child_dl);
4526 auto parent_dl = parent_builder.Build();
4527 EXPECT_EQ(parent_dl->max_root_blend_mode(), DlBlendMode::kSrcOver);
4528 EXPECT_TRUE(parent_dl->root_has_backdrop_filter());
4529}
4530
4531#define CLIP_EXPECTOR(name) ClipExpector name(__FILE__, __LINE__)
4532
4534 std::variant<DlRect, DlRoundRect, DlRoundSuperellipse, DlPath> shape;
4537 bool is_aa;
4538
4539 std::string shape_name() {
4540 switch (shape.index()) {
4541 case 0:
4542 return is_oval ? "Oval" : "Rect";
4543 case 1:
4544 return "DlRoundRect";
4545 case 2:
4546 return "DlRoundSuperellipse";
4547 case 3:
4548 return "DlPath";
4549 default:
4550 return "Unknown";
4551 }
4552 }
4554
4555::std::ostream& operator<<(::std::ostream& os, const ClipExpectation& expect) {
4556 os << "Expectation(";
4557 switch (expect.shape.index()) {
4558 case 0:
4559 os << std::get<DlRect>(expect.shape);
4560 if (expect.is_oval) {
4561 os << " (oval)";
4562 }
4563 break;
4564 case 1:
4565 os << std::get<DlRoundRect>(expect.shape);
4566 break;
4567 case 2:
4568 os << std::get<DlPath>(expect.shape).GetSkPath();
4569 break;
4570 case 3:
4571 os << "Unknown";
4572 }
4573 os << ", " << expect.clip_op;
4574 os << ", " << expect.is_aa;
4575 os << ")";
4576 return os;
4577}
4578
4579class ClipExpector : public virtual DlOpReceiver,
4582 virtual IgnoreDrawDispatchHelper {
4583 public:
4584 // file and line supplied automatically from CLIP_EXPECTOR macro
4585 explicit ClipExpector(const std::string& file, int line)
4586 : file_(file), line_(line) {}
4587
4589 EXPECT_EQ(index_, clip_expectations_.size()) << label();
4590 while (index_ < clip_expectations_.size()) {
4591 auto expect = clip_expectations_[index_];
4592 FML_LOG(ERROR) << "leftover clip shape[" << index_ << "] = " << expect;
4593 index_++;
4594 }
4595 }
4596
4599 bool is_aa = false) {
4600 clip_expectations_.push_back({
4601 .shape = rect,
4602 .is_oval = false,
4603 .clip_op = clip_op,
4604 .is_aa = is_aa,
4605 });
4606 return *this;
4607 }
4608
4611 bool is_aa = false) {
4612 clip_expectations_.push_back({
4613 .shape = rect,
4614 .is_oval = true,
4615 .clip_op = clip_op,
4616 .is_aa = is_aa,
4617 });
4618 return *this;
4619 }
4620
4623 bool is_aa = false) {
4624 clip_expectations_.push_back({
4625 .shape = rrect,
4626 .is_oval = false,
4627 .clip_op = clip_op,
4628 .is_aa = is_aa,
4629 });
4630 return *this;
4631 }
4632
4635 bool is_aa = false) {
4636 clip_expectations_.push_back({
4637 .shape = path,
4638 .is_oval = false,
4639 .clip_op = clip_op,
4640 .is_aa = is_aa,
4641 });
4642 return *this;
4643 }
4644
4645 void clipRect(const DlRect& rect, DlClipOp clip_op, bool is_aa) override {
4646 check(rect, clip_op, is_aa);
4647 }
4648 void clipOval(const DlRect& bounds, DlClipOp clip_op, bool is_aa) override {
4649 check(bounds, clip_op, is_aa, true);
4650 }
4651 void clipRoundRect(const DlRoundRect& rrect,
4652 DlClipOp clip_op,
4653 bool is_aa) override {
4654 check(rrect, clip_op, is_aa);
4655 }
4657 DlClipOp clip_op,
4658 bool is_aa) override {
4659 check(rse, clip_op, is_aa);
4660 }
4661 void clipPath(const DlPath& path, DlClipOp clip_op, bool is_aa) override {
4662 check(path, clip_op, is_aa);
4663 }
4664
4665 private:
4666 size_t index_ = 0;
4667 std::vector<ClipExpectation> clip_expectations_;
4668
4669 template <typename T>
4670 void check(const T& shape,
4671 DlClipOp clip_op,
4672 bool is_aa,
4673 bool is_oval = false) {
4674 ASSERT_LT(index_, clip_expectations_.size())
4675 << label() << std::endl
4676 << "extra clip shape = " << shape << (is_oval ? " (oval)" : "");
4677 auto expected = clip_expectations_[index_];
4678 if (!std::holds_alternative<T>(expected.shape)) {
4679 EXPECT_TRUE(std::holds_alternative<T>(expected.shape))
4680 << label() << ", expected type: " << expected.shape_name();
4681 } else {
4682 EXPECT_EQ(std::get<T>(expected.shape), shape) << label();
4683 }
4684 EXPECT_EQ(expected.is_oval, is_oval) << label();
4685 EXPECT_EQ(expected.clip_op, clip_op) << label();
4686 EXPECT_EQ(expected.is_aa, is_aa) << label();
4687 index_++;
4688 }
4689
4690 const std::string file_;
4691 const int line_;
4692
4693 std::string label() {
4694 return "at index " + std::to_string(index_) + //
4695 ", from " + file_ + //
4696 ":" + std::to_string(line_);
4697 }
4698};
4699
4700TEST_F(DisplayListTest, ClipRectCullingPixel6a) {
4701 // These particular values create bit errors if we use the path that
4702 // tests for inclusion in local space, but work OK if we use a forward
4703 // path that tests for inclusion in device space, due to the fact that
4704 // the extra matrix inversion is just enough math to cause the transform
4705 // to place the local space cull corners just outside the original rect.
4706 // The test in device space only works under a simple scale, such as we
4707 // use for DPR adjustments (and which are not always inversion friendly).
4708
4709 auto frame = DlRect::MakeLTRB(0.0f, 0.0f, 1080.0f, 2400.0f);
4710 DlScalar DPR = 2.625f;
4711 auto clip = DlRect::MakeLTRB(0.0f, 0.0f, 1080.0f / DPR, 2400.0f / DPR);
4712
4713 DisplayListBuilder cull_builder;
4714 cull_builder.ClipRect(frame, DlClipOp::kIntersect, false);
4715 cull_builder.Scale(DPR, DPR);
4716 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4717 auto cull_dl = cull_builder.Build();
4718
4719 CLIP_EXPECTOR(expector);
4720 expector.addExpectation(frame, DlClipOp::kIntersect, false);
4721 cull_dl->Dispatch(expector);
4722}
4723
4724TEST_F(DisplayListTest, ClipRectCulling) {
4725 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4726
4727 DisplayListBuilder cull_builder;
4728 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4729 cull_builder.ClipRect(clip.Expand(1.0f, 1.0f), DlClipOp::kIntersect, false);
4730 auto cull_dl = cull_builder.Build();
4731
4732 CLIP_EXPECTOR(expector);
4733 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4734 cull_dl->Dispatch(expector);
4735}
4736
4737TEST_F(DisplayListTest, ClipRectNonCulling) {
4738 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4739 auto smaller_clip = clip.Expand(-1.0f, -1.0f);
4740
4741 DisplayListBuilder cull_builder;
4742 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4743 cull_builder.ClipRect(smaller_clip, DlClipOp::kIntersect, false);
4744 auto cull_dl = cull_builder.Build();
4745
4746 CLIP_EXPECTOR(expector);
4747 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4748 expector.addExpectation(smaller_clip, DlClipOp::kIntersect, false);
4749 cull_dl->Dispatch(expector);
4750}
4751
4752TEST_F(DisplayListTest, ClipRectNestedCulling) {
4753 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4754 auto larger_clip = clip.Expand(1.0f, 1.0f);
4755
4756 DisplayListBuilder cull_builder;
4757 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4758 cull_builder.Save();
4759 cull_builder.ClipRect(larger_clip, DlClipOp::kIntersect, false);
4760 cull_builder.Restore();
4761 auto cull_dl = cull_builder.Build();
4762
4763 CLIP_EXPECTOR(expector);
4764 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4765 cull_dl->Dispatch(expector);
4766}
4767
4768TEST_F(DisplayListTest, ClipRectNestedNonCulling) {
4769 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4770 auto larger_clip = clip.Expand(1.0f, 1.0f);
4771
4772 DisplayListBuilder cull_builder;
4773 cull_builder.Save();
4774 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4775 cull_builder.Restore();
4776 // Should not be culled because we have restored the prior clip
4777 cull_builder.ClipRect(larger_clip, DlClipOp::kIntersect, false);
4778 auto cull_dl = cull_builder.Build();
4779
4780 CLIP_EXPECTOR(expector);
4781 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4782 expector.addExpectation(larger_clip, DlClipOp::kIntersect, false);
4783 cull_dl->Dispatch(expector);
4784}
4785
4786TEST_F(DisplayListTest, ClipRectNestedCullingComplex) {
4787 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4788 auto smaller_clip = clip.Expand(-1.0f, -1.0f);
4789 auto smallest_clip = clip.Expand(-2.0f, -2.0f);
4790
4791 DisplayListBuilder cull_builder;
4792 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4793 cull_builder.Save();
4794 cull_builder.ClipRect(smallest_clip, DlClipOp::kIntersect, false);
4795 cull_builder.ClipRect(smaller_clip, DlClipOp::kIntersect, false);
4796 cull_builder.Restore();
4797 auto cull_dl = cull_builder.Build();
4798
4799 CLIP_EXPECTOR(expector);
4800 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4801 expector.addExpectation(smallest_clip, DlClipOp::kIntersect, false);
4802 cull_dl->Dispatch(expector);
4803}
4804
4805TEST_F(DisplayListTest, ClipRectNestedNonCullingComplex) {
4806 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4807 auto smaller_clip = clip.Expand(-1.0f, -1.0f);
4808 auto smallest_clip = clip.Expand(-2.0f, -2.0f);
4809
4810 DisplayListBuilder cull_builder;
4811 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4812 cull_builder.Save();
4813 cull_builder.ClipRect(smallest_clip, DlClipOp::kIntersect, false);
4814 cull_builder.Restore();
4815 // Would not be culled if it was inside the clip
4816 cull_builder.ClipRect(smaller_clip, DlClipOp::kIntersect, false);
4817 auto cull_dl = cull_builder.Build();
4818
4819 CLIP_EXPECTOR(expector);
4820 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4821 expector.addExpectation(smallest_clip, DlClipOp::kIntersect, false);
4822 expector.addExpectation(smaller_clip, DlClipOp::kIntersect, false);
4823 cull_dl->Dispatch(expector);
4824}
4825
4826TEST_F(DisplayListTest, ClipOvalCulling) {
4827 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4828 // A 10x10 rectangle extends 5x5 from the center to each corner. To have
4829 // an oval that encompasses that rectangle, the radius must be at least
4830 // length(5, 5), or 7.071+ so we expand the radius 5 square clip by 2.072
4831 // on each side to barely contain the corners of the square.
4832 auto encompassing_oval = clip.Expand(2.072f, 2.072f);
4833
4834 DisplayListBuilder cull_builder;
4835 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4836 cull_builder.ClipOval(encompassing_oval, DlClipOp::kIntersect, false);
4837 auto cull_dl = cull_builder.Build();
4838
4839 CLIP_EXPECTOR(expector);
4840 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4841 cull_dl->Dispatch(expector);
4842}
4843
4844TEST_F(DisplayListTest, ClipOvalNonCulling) {
4845 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4846 // A 10x10 rectangle extends 5x5 from the center to each corner. To have
4847 // an oval that encompasses that rectangle, the radius must be at least
4848 // length(5, 5), or 7.071+ so we expand the radius 5 square clip by 2.072
4849 // on each side to barely exclude the corners of the square.
4850 auto non_encompassing_oval = clip.Expand(2.071f, 2.071f);
4851
4852 DisplayListBuilder cull_builder;
4853 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4854 cull_builder.ClipOval(non_encompassing_oval, DlClipOp::kIntersect, false);
4855 auto cull_dl = cull_builder.Build();
4856
4857 CLIP_EXPECTOR(expector);
4858 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4859 expector.addOvalExpectation(non_encompassing_oval, DlClipOp::kIntersect,
4860 false);
4861 cull_dl->Dispatch(expector);
4862}
4863
4864TEST_F(DisplayListTest, ClipRRectCulling) {
4865 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4866 auto rrect = DlRoundRect::MakeRectXY(clip.Expand(2.0f, 2.0f), 2.0f, 2.0f);
4867 ASSERT_FALSE(rrect.IsOval());
4868
4869 DisplayListBuilder cull_builder;
4870 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4871 cull_builder.ClipRoundRect(rrect, DlClipOp::kIntersect, false);
4872 auto cull_dl = cull_builder.Build();
4873
4874 CLIP_EXPECTOR(expector);
4875 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4876 cull_dl->Dispatch(expector);
4877}
4878
4879TEST_F(DisplayListTest, ClipRRectNonCulling) {
4880 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4881 auto rrect = DlRoundRect::MakeRectXY(clip.Expand(1.0f, 1.0f), 4.0f, 4.0f);
4882 ASSERT_FALSE(rrect.IsOval());
4883
4884 DisplayListBuilder cull_builder;
4885 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4886 cull_builder.ClipRoundRect(rrect, DlClipOp::kIntersect, false);
4887 auto cull_dl = cull_builder.Build();
4888
4889 CLIP_EXPECTOR(expector);
4890 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4891 expector.addExpectation(rrect, DlClipOp::kIntersect, false);
4892 cull_dl->Dispatch(expector);
4893}
4894
4895TEST_F(DisplayListTest, ClipPathNonCulling) {
4896 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4897 DlPathBuilder path_builder;
4898 path_builder.MoveTo({0.0f, 0.0f});
4899 path_builder.LineTo({1000.0f, 0.0f});
4900 path_builder.LineTo({0.0f, 1000.0f});
4901 path_builder.Close();
4902 DlPath path = path_builder.TakePath();
4903
4904 // Double checking that the path does indeed contain the clip. But,
4905 // sadly, the Builder will not check paths for coverage to this level
4906 // of detail. (In particular, path containment of the corners is not
4907 // authoritative of true containment, but we know in this case that
4908 // a triangle contains a rect if it contains all 4 corners...)
4909 ASSERT_TRUE(path.Contains(clip.GetLeftTop()));
4910 ASSERT_TRUE(path.Contains(clip.GetRightTop()));
4911 ASSERT_TRUE(path.Contains(clip.GetRightBottom()));
4912 ASSERT_TRUE(path.Contains(clip.GetLeftBottom()));
4913
4914 DisplayListBuilder cull_builder;
4915 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4916 cull_builder.ClipPath(path, DlClipOp::kIntersect, false);
4917 auto cull_dl = cull_builder.Build();
4918
4919 CLIP_EXPECTOR(expector);
4920 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4921 expector.addExpectation(path, DlClipOp::kIntersect, false);
4922 cull_dl->Dispatch(expector);
4923}
4924
4925TEST_F(DisplayListTest, ClipPathRectCulling) {
4926 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4927 DlPath path = DlPath::MakeRect(clip.Expand(1.0f, 1.0f));
4928
4929 DisplayListBuilder cull_builder;
4930 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4931 cull_builder.ClipPath(path, DlClipOp::kIntersect, false);
4932 auto cull_dl = cull_builder.Build();
4933
4934 CLIP_EXPECTOR(expector);
4935 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4936 cull_dl->Dispatch(expector);
4937}
4938
4939TEST_F(DisplayListTest, ClipPathRectNonCulling) {
4940 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4941 auto smaller_clip = clip.Expand(-1.0f, -1.0f);
4942 DlPath path = DlPath::MakeRect(smaller_clip);
4943
4944 DisplayListBuilder cull_builder;
4945 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4946 cull_builder.ClipPath(path, DlClipOp::kIntersect, false);
4947 auto cull_dl = cull_builder.Build();
4948
4949 CLIP_EXPECTOR(expector);
4950 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4951 // Builder will not cull this clip, but it will turn it into a ClipRect
4952 expector.addExpectation(smaller_clip, DlClipOp::kIntersect, false);
4953 cull_dl->Dispatch(expector);
4954}
4955
4956TEST_F(DisplayListTest, ClipPathOvalCulling) {
4957 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4958 // A 10x10 rectangle extends 5x5 from the center to each corner. To have
4959 // an oval that encompasses that rectangle, the radius must be at least
4960 // length(5, 5), or 7.071+ so we expand the radius 5 square clip by 2.072
4961 // on each side to barely contain the corners of the square.
4962 auto encompassing_oval = clip.Expand(2.072f, 2.072f);
4963 DlPath path = DlPath::MakeOval(encompassing_oval);
4964
4965 DisplayListBuilder cull_builder;
4966 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4967 cull_builder.ClipPath(path, DlClipOp::kIntersect, false);
4968 auto cull_dl = cull_builder.Build();
4969
4970 CLIP_EXPECTOR(expector);
4971 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4972 cull_dl->Dispatch(expector);
4973}
4974
4975TEST_F(DisplayListTest, ClipPathOvalNonCulling) {
4976 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4977 // A 10x10 rectangle extends 5x5 from the center to each corner. To have
4978 // an oval that encompasses that rectangle, the radius must be at least
4979 // length(5, 5), or 7.071+ so we expand the radius 5 square clip by 2.072
4980 // on each side to barely exclude the corners of the square.
4981 auto non_encompassing_oval = clip.Expand(2.071f, 2.071f);
4982 DlPath path = DlPath::MakeOval(non_encompassing_oval);
4983
4984 DisplayListBuilder cull_builder;
4985 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
4986 cull_builder.ClipPath(path, DlClipOp::kIntersect, false);
4987 auto cull_dl = cull_builder.Build();
4988
4989 CLIP_EXPECTOR(expector);
4990 expector.addExpectation(clip, DlClipOp::kIntersect, false);
4991 // Builder will not cull this clip, but it will turn it into a ClipOval
4992 expector.addOvalExpectation(non_encompassing_oval, DlClipOp::kIntersect,
4993 false);
4994 cull_dl->Dispatch(expector);
4995}
4996
4997TEST_F(DisplayListTest, ClipPathRRectCulling) {
4998 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4999 DlPath path = DlPath::MakeRoundRectXY(clip.Expand(2.0f, 2.0f), 2.0f, 2.0f);
5000 ASSERT_FALSE(path.IsOval());
5001
5002 DisplayListBuilder cull_builder;
5003 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
5004 cull_builder.ClipPath(path, DlClipOp::kIntersect, false);
5005 auto cull_dl = cull_builder.Build();
5006
5007 CLIP_EXPECTOR(expector);
5008 expector.addExpectation(clip, DlClipOp::kIntersect, false);
5009 cull_dl->Dispatch(expector);
5010}
5011
5012TEST_F(DisplayListTest, ClipPathRRectNonCulling) {
5013 auto clip = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5014 auto rrect = DlRoundRect::MakeRectXY(clip.Expand(1.0f, 1.0f), 4.0f, 4.0f);
5015 ASSERT_FALSE(rrect.IsOval());
5017
5018 DisplayListBuilder cull_builder;
5019 cull_builder.ClipRect(clip, DlClipOp::kIntersect, false);
5020 cull_builder.ClipPath(path, DlClipOp::kIntersect, false);
5021 auto cull_dl = cull_builder.Build();
5022
5023 CLIP_EXPECTOR(expector);
5024 expector.addExpectation(clip, DlClipOp::kIntersect, false);
5025 // Builder will not cull this clip, but it will turn it into a ClipRRect
5026 expector.addExpectation(rrect, DlClipOp::kIntersect, false);
5027 cull_dl->Dispatch(expector);
5028}
5029
5030TEST_F(DisplayListTest, RecordLargeVertices) {
5031 constexpr size_t vertex_count = 2000000;
5032 auto points = std::vector<DlPoint>();
5033 points.reserve(vertex_count);
5034 auto colors = std::vector<DlColor>();
5035 colors.reserve(vertex_count);
5036 for (size_t i = 0; i < vertex_count; i++) {
5037 colors.emplace_back(DlColor(-i));
5038 points.emplace_back(((i & 1) == 0) ? DlPoint(-i, i) : DlPoint(i, i));
5039 }
5040 ASSERT_EQ(points.size(), vertex_count);
5041 ASSERT_EQ(colors.size(), vertex_count);
5042 auto vertices = DlVertices::Make(DlVertexMode::kTriangleStrip, vertex_count,
5043 points.data(), points.data(), colors.data());
5044 ASSERT_GT(vertices->size(), 1u << 24);
5045 auto backdrop = DlImageFilter::MakeBlur(5.0f, 5.0f, DlTileMode::kDecal);
5046
5047 for (int i = 0; i < 1000; i++) {
5048 DisplayListBuilder builder;
5049 for (int j = 0; j < 16; j++) {
5050 builder.SaveLayer(std::nullopt, nullptr, backdrop.get());
5051 builder.DrawVertices(vertices, DlBlendMode::kSrcOver, DlPaint());
5052 builder.Restore();
5053 }
5054 auto dl = builder.Build();
5055 }
5056}
5057
5058TEST_F(DisplayListTest, DrawRectRRectPromoteToDrawRect) {
5059 DlRect rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5060
5061 DisplayListBuilder builder;
5063 auto dl = builder.Build();
5064
5065 DisplayListBuilder expected;
5066 expected.DrawRect(rect, DlPaint());
5067 auto expect_dl = expected.Build();
5068
5069 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, expect_dl));
5070}
5071
5072TEST_F(DisplayListTest, DrawOvalRRectPromoteToDrawOval) {
5073 DlRect rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5074
5075 DisplayListBuilder builder;
5077 auto dl = builder.Build();
5078
5079 DisplayListBuilder expected;
5080 expected.DrawOval(rect, DlPaint());
5081 auto expect_dl = expected.Build();
5082
5083 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, expect_dl));
5084}
5085
5086TEST_F(DisplayListTest, DrawRectPathPromoteToDrawRect) {
5087 DlRect rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5088
5089 DisplayListBuilder builder;
5090 builder.DrawPath(DlPath::MakeRect(rect), DlPaint());
5091 auto dl = builder.Build();
5092
5093 DisplayListBuilder expected;
5094 expected.DrawRect(rect, DlPaint());
5095 auto expect_dl = expected.Build();
5096
5097 // Support for this will be re-added soon, until then verify that we
5098 // do not promote.
5099 ASSERT_TRUE(DisplayListsNE_Verbose(dl, expect_dl));
5100}
5101
5102TEST_F(DisplayListTest, DrawOvalPathPromoteToDrawOval) {
5103 DlRect rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5104
5105 DisplayListBuilder builder;
5106 builder.DrawPath(DlPath::MakeOval(rect), DlPaint());
5107 auto dl = builder.Build();
5108
5109 DisplayListBuilder expected;
5110 expected.DrawOval(rect, DlPaint());
5111 auto expect_dl = expected.Build();
5112
5113 // Support for this will be re-added soon, until then verify that we
5114 // do not promote.
5115 ASSERT_TRUE(DisplayListsNE_Verbose(dl, expect_dl));
5116}
5117
5118TEST_F(DisplayListTest, DrawRRectPathPromoteToDrawRoundRect) {
5119 DlRect rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5120 DlRoundRect rrect = DlRoundRect::MakeRectXY(rect, 2.0f, 2.0f);
5121
5122 DisplayListBuilder builder;
5123 builder.DrawPath(DlPath::MakeRoundRect(rrect), DlPaint());
5124 auto dl = builder.Build();
5125
5126 DisplayListBuilder expected;
5127 expected.DrawRoundRect(rrect, DlPaint());
5128 auto expect_dl = expected.Build();
5129
5130 // Support for this will be re-added soon, until then verify that we
5131 // do not promote.
5132 ASSERT_TRUE(DisplayListsNE_Verbose(dl, expect_dl));
5133}
5134
5135TEST_F(DisplayListTest, DrawRectRoundRectPathPromoteToDrawRect) {
5136 DlRect rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5137 DlRoundRect rrect = DlRoundRect::MakeRect(rect);
5138
5139 DisplayListBuilder builder;
5140 builder.DrawPath(DlPath::MakeRoundRect(rrect), DlPaint());
5141 auto dl = builder.Build();
5142
5143 DisplayListBuilder expected;
5144 expected.DrawRect(rect, DlPaint());
5145 auto expect_dl = expected.Build();
5146
5147 // Support for this will be re-added soon, until then verify that we
5148 // do not promote.
5149 ASSERT_TRUE(DisplayListsNE_Verbose(dl, expect_dl));
5150}
5151
5152TEST_F(DisplayListTest, DrawOvalRRectPathPromoteToDrawOval) {
5153 DlRect rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5154 DlRoundRect rrect = DlRoundRect::MakeOval(rect);
5155
5156 DisplayListBuilder builder;
5157 builder.DrawPath(DlPath::MakeRoundRect(rrect), DlPaint());
5158 auto dl = builder.Build();
5159
5160 DisplayListBuilder expected;
5161 expected.DrawOval(rect, DlPaint());
5162 auto expect_dl = expected.Build();
5163
5164 // Support for this will be re-added soon, until then verify that we
5165 // do not promote.
5166 ASSERT_TRUE(DisplayListsNE_Verbose(dl, expect_dl));
5167}
5168
5169TEST_F(DisplayListTest, ClipRectRRectPromoteToClipRect) {
5170 DlRect clip_rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5171 DlRect draw_rect = clip_rect.Expand(2.0f, 2.0f);
5172
5173 DisplayListBuilder builder;
5175 false);
5176 // Include a rendering op in case DlBuilder ever removes unneeded clips
5177 builder.DrawRect(draw_rect, DlPaint());
5178 auto dl = builder.Build();
5179
5180 DisplayListBuilder expected;
5181 expected.ClipRect(clip_rect, DlClipOp::kIntersect, false);
5182 expected.DrawRect(draw_rect, DlPaint());
5183 auto expect_dl = expected.Build();
5184
5185 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, expect_dl));
5186}
5187
5188TEST_F(DisplayListTest, ClipOvalRRectPromoteToClipOval) {
5189 DlRect clip_rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5190 DlRect draw_rect = clip_rect.Expand(2.0f, 2.0f);
5191
5192 DisplayListBuilder builder;
5194 false);
5195 // Include a rendering op in case DlBuilder ever removes unneeded clips
5196 builder.DrawRect(draw_rect, DlPaint());
5197 auto dl = builder.Build();
5198
5199 DisplayListBuilder expected;
5200 expected.ClipOval(clip_rect, DlClipOp::kIntersect, false);
5201 expected.DrawRect(draw_rect, DlPaint());
5202 auto expect_dl = expected.Build();
5203
5204 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, expect_dl));
5205}
5206
5207TEST_F(DisplayListTest, ClipRectPathPromoteToClipRect) {
5208 DlRect clip_rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5209 DlRect draw_rect = clip_rect.Expand(2.0f, 2.0f);
5210 DlPath clip_path = DlPath::MakeRect(clip_rect);
5211 ASSERT_TRUE(clip_path.IsRect(nullptr));
5212
5213 DisplayListBuilder builder;
5214 builder.ClipPath(clip_path, DlClipOp::kIntersect, false);
5215 // Include a rendering op in case DlBuilder ever removes unneeded clips
5216 builder.DrawRect(draw_rect, DlPaint());
5217 auto dl = builder.Build();
5218
5219 DisplayListBuilder expected;
5220 expected.ClipRect(clip_rect, DlClipOp::kIntersect, false);
5221 expected.DrawRect(draw_rect, DlPaint());
5222 auto expect_dl = expected.Build();
5223
5224 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, expect_dl));
5225}
5226
5227TEST_F(DisplayListTest, ClipOvalPathPromoteToClipOval) {
5228 DlRect clip_rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5229 DlRect draw_rect = clip_rect.Expand(2.0f, 2.0f);
5230 DlPath clip_path = DlPath::MakeOval(clip_rect);
5231 ASSERT_TRUE(clip_path.IsOval(nullptr));
5232
5233 DisplayListBuilder builder;
5234 builder.ClipPath(clip_path, DlClipOp::kIntersect, false);
5235 // Include a rendering op in case DlBuilder ever removes unneeded clips
5236 builder.DrawRect(draw_rect, DlPaint());
5237 auto dl = builder.Build();
5238
5239 DisplayListBuilder expected;
5240 expected.ClipOval(clip_rect, DlClipOp::kIntersect, false);
5241 expected.DrawRect(draw_rect, DlPaint());
5242 auto expect_dl = expected.Build();
5243
5244 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, expect_dl));
5245}
5246
5247TEST_F(DisplayListTest, ClipRRectPathPromoteToClipRRect) {
5248 DlRect clip_rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5249 DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(clip_rect, 2.0f, 2.0f);
5250 DlRect draw_rect = clip_rect.Expand(2.0f, 2.0f);
5251 DlPath clip_path = DlPath::MakeRoundRect(clip_rrect);
5252 ASSERT_TRUE(clip_path.IsRoundRect());
5253
5254 DisplayListBuilder builder;
5255 builder.ClipPath(clip_path, DlClipOp::kIntersect, false);
5256 // Include a rendering op in case DlBuilder ever removes unneeded clips
5257 builder.DrawRect(draw_rect, DlPaint());
5258 auto dl = builder.Build();
5259
5260 DisplayListBuilder expected;
5261 expected.ClipRoundRect(clip_rrect, DlClipOp::kIntersect, false);
5262 expected.DrawRect(draw_rect, DlPaint());
5263 auto expect_dl = expected.Build();
5264
5265 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, expect_dl));
5266}
5267
5268TEST_F(DisplayListTest, ClipRectRRectPathPromoteToClipRect) {
5269 DlRect clip_rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5270 DlRoundRect clip_rrect = DlRoundRect::MakeRect(clip_rect);
5271 DlRect draw_rect = clip_rect.Expand(2.0f, 2.0f);
5272 DlPath clip_path = DlPath::MakeRoundRect(clip_rrect);
5273
5274 DisplayListBuilder builder;
5275 builder.ClipPath(clip_path, DlClipOp::kIntersect, false);
5276 // Include a rendering op in case DlBuilder ever removes unneeded clips
5277 builder.DrawRect(draw_rect, DlPaint());
5278 auto dl = builder.Build();
5279
5280 DisplayListBuilder expected;
5281 expected.ClipRect(clip_rect, DlClipOp::kIntersect, false);
5282 expected.DrawRect(draw_rect, DlPaint());
5283 auto expect_dl = expected.Build();
5284
5285 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, expect_dl));
5286}
5287
5288TEST_F(DisplayListTest, ClipOvalRRectPathPromoteToClipOval) {
5289 DlRect clip_rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5290 DlRoundRect clip_rrect = DlRoundRect::MakeOval(clip_rect);
5291 DlRect draw_rect = clip_rect.Expand(2.0f, 2.0f);
5292 DlPath clip_path = DlPath::MakeRoundRect(clip_rrect);
5293
5294 DisplayListBuilder builder;
5295 builder.ClipPath(clip_path, DlClipOp::kIntersect, false);
5296 // Include a rendering op in case DlBuilder ever removes unneeded clips
5297 builder.DrawRect(draw_rect, DlPaint());
5298 auto dl = builder.Build();
5299
5300 DisplayListBuilder expected;
5301 expected.ClipOval(clip_rect, DlClipOp::kIntersect, false);
5302 expected.DrawRect(draw_rect, DlPaint());
5303 auto expect_dl = expected.Build();
5304
5305 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, expect_dl));
5306}
5307
5308TEST_F(DisplayListTest, BoundedRenderOpsDoNotReportUnbounded) {
5309 static const DlRect root_cull = DlRect::MakeLTRB(100, 100, 200, 200);
5310 static const DlRect draw_rect = DlRect::MakeLTRB(110, 110, 190, 190);
5311
5312 using Renderer = const std::function<void(DlCanvas&)>;
5313 auto test_bounded = [](const std::string& label, const Renderer& renderer) {
5314 {
5315 DisplayListBuilder builder(root_cull);
5316 renderer(builder);
5317 auto display_list = builder.Build();
5318
5319 EXPECT_EQ(display_list->GetBounds(), draw_rect) << label;
5320 EXPECT_FALSE(display_list->root_is_unbounded()) << label;
5321 }
5322
5323 {
5324 DisplayListBuilder builder(root_cull);
5325 builder.SaveLayer(std::nullopt, nullptr);
5326 renderer(builder);
5327 builder.Restore();
5328 auto display_list = builder.Build();
5329
5330 EXPECT_EQ(display_list->GetBounds(), draw_rect) << label;
5331 EXPECT_FALSE(display_list->root_is_unbounded()) << label;
5332
5333 SAVE_LAYER_EXPECTOR(expector);
5334 expector //
5335 .addDetail(label)
5336 .addExpectation([](const SaveLayerOptions& options) {
5337 return !options.content_is_unbounded();
5338 });
5339 display_list->Dispatch(expector);
5340 }
5341 };
5342
5343 test_bounded("DrawLine", [](DlCanvas& builder) {
5344 builder.DrawLine(
5345 DlPoint(draw_rect.GetLeft() + 1.0f, draw_rect.GetTop() + 1.0f),
5346 DlPoint(draw_rect.GetRight() - 1.0f, draw_rect.GetTop() + 1.0f),
5347 DlPaint().setStrokeWidth(2.0f).setStrokeCap(DlStrokeCap::kSquare));
5348 builder.DrawLine(
5349 DlPoint(draw_rect.GetLeft() + 1.0f, draw_rect.GetBottom() - 1.0f),
5350 DlPoint(draw_rect.GetRight() - 1.0f, draw_rect.GetBottom() - 1.0f),
5351 DlPaint().setStrokeWidth(2.0f).setStrokeCap(DlStrokeCap::kSquare));
5352 });
5353
5354 test_bounded("DrawDashedLine", [](DlCanvas& builder) {
5355 builder.DrawDashedLine(
5356 DlPoint(draw_rect.GetLeft() + 1.0f, draw_rect.GetTop() + 1.0f),
5357 DlPoint(draw_rect.GetRight() - 1.0f, draw_rect.GetTop() + 1.0f),
5358 // must fill 80 x 80 square with on dashes at both
5359 // ends - 40 + 25 + 40 == 105 so it will be on
5360 // at both ends
5361 40.0f, 25.0f,
5362 DlPaint().setStrokeWidth(2.0f).setStrokeCap(DlStrokeCap::kSquare));
5363 builder.DrawDashedLine(
5364 DlPoint(draw_rect.GetLeft() + 1.0f, draw_rect.GetBottom() - 1.0f),
5365 DlPoint(draw_rect.GetRight() - 1.0f, draw_rect.GetBottom() - 1.0f),
5366 // must fill 80 x 80 square with on dashes at both
5367 // ends - 40 + 25 + 40 == 105 so it will be on
5368 // at both ends
5369 40.0f, 25.0f,
5370 DlPaint().setStrokeWidth(2.0f).setStrokeCap(DlStrokeCap::kSquare));
5371 });
5372
5373 test_bounded("DrawRect", [](DlCanvas& builder) {
5374 builder.DrawRect(draw_rect, DlPaint());
5375 });
5376
5377 test_bounded("DrawOval", [](DlCanvas& builder) {
5378 builder.DrawOval(draw_rect, DlPaint());
5379 });
5380
5381 test_bounded("DrawCircle", [](DlCanvas& builder) {
5382 builder.DrawCircle(draw_rect.GetCenter(), draw_rect.GetWidth() * 0.5f,
5383 DlPaint());
5384 });
5385
5386 test_bounded("DrawRoundRect", [](DlCanvas& builder) {
5387 builder.DrawRoundRect(DlRoundRect::MakeRectXY(draw_rect, 5.0f, 5.0f),
5388 DlPaint());
5389 });
5390
5391 test_bounded("DrawDiffRoundRect", [](DlCanvas& builder) {
5392 builder.DrawDiffRoundRect(
5393 DlRoundRect::MakeRectXY(draw_rect, 5.0f, 5.0f),
5394 DlRoundRect::MakeRectXY(draw_rect.Expand(-10.0f, -10.0f), 5.0f, 5.0f),
5395 DlPaint());
5396 });
5397
5398 test_bounded("DrawArc", [](DlCanvas& builder) {
5399 builder.DrawArc(draw_rect, 45.0f, 355.0f, false, DlPaint());
5400 });
5401
5402 test_bounded("DrawPathEvenOdd", [](DlCanvas& builder) {
5403 DlPath path =
5404 DlPath::MakeRect(draw_rect).WithFillType(DlPathFillType::kOdd);
5405 builder.DrawPath(path, DlPaint());
5406 });
5407
5408 test_bounded("DrawPathWinding", [](DlCanvas& builder) {
5409 DlPath path =
5410 DlPath::MakeRect(draw_rect).WithFillType(DlPathFillType::kNonZero);
5411 builder.DrawPath(path, DlPaint());
5412 });
5413
5414 auto test_draw_points = [&test_bounded](DlPointMode mode) {
5415 std::stringstream ss;
5416 ss << "DrawPoints(" << mode << ")";
5417 test_bounded(ss.str(), [mode](DlCanvas& builder) {
5418 DlPoint points[4] = {
5419 DlPoint(draw_rect.GetLeft() + 1.0f, draw_rect.GetTop() + 1.0f),
5420 DlPoint(draw_rect.GetRight() - 1.0f, draw_rect.GetTop() + 1.0f),
5421 DlPoint(draw_rect.GetRight() - 1.0f, draw_rect.GetBottom() - 1.0f),
5422 DlPoint(draw_rect.GetLeft() + 1.0f, draw_rect.GetBottom() - 1.0f),
5423 };
5424 DlPaint paint;
5425 paint.setStrokeWidth(2.0f);
5427 // bounds accumulation doesn't examine the points to see if they
5428 // have diagonals so Square caps may have their corners accumulated
5429 // but Round caps will always pad by only half the stroke width.
5431
5432 builder.DrawPoints(mode, 4, points, paint);
5433 });
5434 };
5435
5436 test_draw_points(DlPointMode::kPoints);
5437 test_draw_points(DlPointMode::kLines);
5438 test_draw_points(DlPointMode::kPolygon);
5439
5440 test_bounded("DrawVerticesTriangles", [](DlCanvas& builder) {
5441 DlPoint points[6] = {
5442 DlPoint(draw_rect.GetLeft(), draw_rect.GetTop()),
5443 DlPoint(draw_rect.GetRight(), draw_rect.GetTop()),
5444 DlPoint(draw_rect.GetRight(), draw_rect.GetBottom()),
5445 DlPoint(draw_rect.GetRight(), draw_rect.GetBottom()),
5446 DlPoint(draw_rect.GetLeft(), draw_rect.GetBottom()),
5447 DlPoint(draw_rect.GetLeft(), draw_rect.GetTop()),
5448 };
5449 DlVertices::Builder vertices(DlVertexMode::kTriangles, 6,
5451 vertices.store_vertices(points);
5452 builder.DrawVertices(vertices.build(), DlBlendMode::kSrcOver, DlPaint());
5453 });
5454
5455 test_bounded("DrawVerticesTriangleStrip", [](DlCanvas& builder) {
5456 DlPoint points[6] = {
5457 DlPoint(draw_rect.GetLeft(), draw_rect.GetTop()),
5458 DlPoint(draw_rect.GetRight(), draw_rect.GetTop()),
5459 DlPoint(draw_rect.GetRight(), draw_rect.GetBottom()),
5460 DlPoint(draw_rect.GetLeft(), draw_rect.GetBottom()),
5461 DlPoint(draw_rect.GetLeft(), draw_rect.GetTop()),
5462 DlPoint(draw_rect.GetRight(), draw_rect.GetTop()),
5463 };
5464 DlVertices::Builder vertices(DlVertexMode::kTriangleStrip, 6,
5466 vertices.store_vertices(points);
5467 builder.DrawVertices(vertices.build(), DlBlendMode::kSrcOver, DlPaint());
5468 });
5469
5470 test_bounded("DrawVerticesTriangleFan", [](DlCanvas& builder) {
5471 DlPoint points[6] = {
5472 draw_rect.GetCenter(),
5473 DlPoint(draw_rect.GetLeft(), draw_rect.GetTop()),
5474 DlPoint(draw_rect.GetRight(), draw_rect.GetTop()),
5475 DlPoint(draw_rect.GetRight(), draw_rect.GetBottom()),
5476 DlPoint(draw_rect.GetLeft(), draw_rect.GetBottom()),
5477 };
5478 DlVertices::Builder vertices(DlVertexMode::kTriangleFan, 5,
5480 vertices.store_vertices(points);
5481 builder.DrawVertices(vertices.build(), DlBlendMode::kSrcOver, DlPaint());
5482 });
5483
5484 test_bounded("DrawImage", [](DlCanvas& builder) {
5485 auto image = MakeTestImage(draw_rect.GetWidth(), draw_rect.GetHeight(), 5);
5486 builder.DrawImage(image, DlPoint(draw_rect.GetLeft(), draw_rect.GetTop()),
5488 });
5489
5490 test_bounded("DrawImageRect", [](DlCanvas& builder) {
5491 auto image = MakeTestImage(root_cull.GetWidth(), root_cull.GetHeight(), 5);
5492 builder.DrawImageRect(image, draw_rect, DlImageSampling::kLinear);
5493 });
5494
5495 test_bounded("DrawImageNine", [](DlCanvas& builder) {
5496 auto image = MakeTestImage(root_cull.GetWidth(), root_cull.GetHeight(), 5);
5497 DlIRect center = image->GetBounds().Expand(-10, -10);
5498 builder.DrawImageNine(image, center, draw_rect, DlFilterMode::kLinear);
5499 });
5500
5501 test_bounded("DrawTextBlob", [](DlCanvas& builder) {
5502 auto blob = GetTestTextBlob("Hello");
5503
5504 // Make sure the blob fits within the draw_rect bounds.
5505 ASSERT_LT(blob->bounds().width(), draw_rect.GetWidth());
5506 ASSERT_LT(blob->bounds().height(), draw_rect.GetHeight());
5507
5508 auto text = DlTextSkia::Make(blob);
5509 // Draw once at upper left and again at lower right to fill the bounds.
5510 builder.DrawText(text, draw_rect.GetLeft() - blob->bounds().left(),
5511 draw_rect.GetTop() - blob->bounds().top(), DlPaint());
5512 builder.DrawText(text, draw_rect.GetRight() - blob->bounds().right(),
5513 draw_rect.GetBottom() - blob->bounds().bottom(),
5514 DlPaint());
5515 });
5516
5517#if IMPELLER_SUPPORTS_RENDERING
5518 test_bounded("DrawTextFrame", [](DlCanvas& builder) {
5519 auto blob = GetTestTextBlob("Hello");
5520
5521 // Make sure the blob fits within the draw_rect bounds.
5522 ASSERT_LT(blob->bounds().width(), draw_rect.GetWidth());
5523 ASSERT_LT(blob->bounds().height(), draw_rect.GetHeight());
5524
5526
5527 // Draw once at upper left and again at lower right to fill the bounds.
5528 builder.DrawText(text, draw_rect.GetLeft() - blob->bounds().left(),
5529 draw_rect.GetTop() - blob->bounds().top(), DlPaint());
5530 builder.DrawText(text, draw_rect.GetRight() - blob->bounds().right(),
5531 draw_rect.GetBottom() - blob->bounds().bottom(),
5532 DlPaint());
5533 });
5534#endif
5535
5536 test_bounded("DrawBoundedDisplayList", [](DlCanvas& builder) {
5537 DisplayListBuilder nested_builder(root_cull);
5538 nested_builder.DrawRect(draw_rect, DlPaint());
5539 auto nested_display_list = nested_builder.Build();
5540
5541 EXPECT_EQ(nested_display_list->GetBounds(), draw_rect);
5542 ASSERT_FALSE(nested_display_list->root_is_unbounded());
5543
5544 builder.DrawDisplayList(nested_display_list);
5545 });
5546
5547 test_bounded("DrawShadow", [](DlCanvas& builder) {
5548 DlPath path = DlPath::MakeRect(draw_rect.Expand(-20, -20));
5549 DlScalar elevation = 2.0f;
5550 DlScalar dpr = 1.0f;
5551 auto shadow_bounds =
5552 DlCanvas::ComputeShadowBounds(path, elevation, dpr, DlMatrix());
5553
5554 // Make sure the shadow fits within the draw_rect bounds.
5555 ASSERT_LT(shadow_bounds.GetWidth(), draw_rect.GetWidth());
5556 ASSERT_LT(shadow_bounds.GetHeight(), draw_rect.GetHeight());
5557
5558 // Draw once at upper left and again at lower right to fill the bounds.
5559 DlPath pathUL =
5560 path.WithOffset(draw_rect.GetLeftTop() - shadow_bounds.GetLeftTop());
5561 builder.DrawShadow(pathUL, DlColor::kMagenta(), elevation, true, dpr);
5562 DlPath pathLR = path.WithOffset(draw_rect.GetRightBottom() -
5563 shadow_bounds.GetRightBottom());
5564 builder.DrawShadow(pathLR, DlColor::kMagenta(), elevation, true, dpr);
5565 });
5566
5567 for (int i = 0; i <= static_cast<int>(DlBlendMode::kLastMode); i++) {
5568 DlBlendMode mode = static_cast<DlBlendMode>(i);
5569 if (mode == DlBlendMode::kDst) {
5570 // No way to make kDst a non-nop
5571 continue;
5572 }
5573 std::stringstream ss;
5574 ss << "DrawRectWith" << mode;
5575 test_bounded(ss.str(), [mode](DlCanvas& builder) {
5576 // alpha of 0x7f prevents kDstIn from being a nop
5577 builder.DrawRect(draw_rect, DlPaint().setBlendMode(mode).setAlpha(0x7f));
5578 });
5579 }
5580}
5581
5582TEST_F(DisplayListTest, UnboundedRenderOpsAreReportedUnlessClipped) {
5583 static const DlRect root_cull = DlRect::MakeLTRB(100, 100, 200, 200);
5584 static const DlRect clip_rect = DlRect::MakeLTRB(120, 120, 180, 180);
5585 static const DlRect draw_rect = DlRect::MakeLTRB(110, 110, 190, 190);
5586
5587 using Renderer = const std::function<void(DlCanvas&)>;
5588 auto test_unbounded = [](const std::string& label, //
5589 const Renderer& renderer,
5590 int extra_save_layers = 0) {
5591 {
5592 DisplayListBuilder builder(root_cull);
5593 renderer(builder);
5594 auto display_list = builder.Build();
5595
5596 EXPECT_EQ(display_list->GetBounds(), root_cull) << label;
5597 EXPECT_TRUE(display_list->root_is_unbounded()) << label;
5598 }
5599
5600 {
5601 DisplayListBuilder builder(root_cull);
5602 builder.ClipRect(clip_rect);
5603 renderer(builder);
5604 auto display_list = builder.Build();
5605
5606 EXPECT_EQ(display_list->GetBounds(), clip_rect) << label;
5607 EXPECT_FALSE(display_list->root_is_unbounded()) << label;
5608 }
5609
5610 {
5611 DisplayListBuilder builder(root_cull);
5612 builder.SaveLayer(std::nullopt, nullptr);
5613 renderer(builder);
5614 builder.Restore();
5615 auto display_list = builder.Build();
5616
5617 EXPECT_EQ(display_list->GetBounds(), root_cull) << label;
5618 EXPECT_FALSE(display_list->root_is_unbounded()) << label;
5619
5620 SAVE_LAYER_EXPECTOR(expector);
5621 expector //
5622 .addDetail(label)
5623 .addExpectation([](const SaveLayerOptions& options) {
5624 return options.content_is_unbounded();
5625 });
5626 for (int i = 0; i < extra_save_layers; i++) {
5627 expector.addOpenExpectation();
5628 }
5629 display_list->Dispatch(expector);
5630 }
5631
5632 {
5633 DisplayListBuilder builder(root_cull);
5634 builder.SaveLayer(std::nullopt, nullptr);
5635 builder.ClipRect(clip_rect);
5636 renderer(builder);
5637 builder.Restore();
5638 auto display_list = builder.Build();
5639
5640 EXPECT_EQ(display_list->GetBounds(), clip_rect) << label;
5641 EXPECT_FALSE(display_list->root_is_unbounded()) << label;
5642
5643 SAVE_LAYER_EXPECTOR(expector);
5644 expector //
5645 .addDetail(label)
5646 .addExpectation([](const SaveLayerOptions& options) {
5647 return !options.content_is_unbounded();
5648 });
5649 for (int i = 0; i < extra_save_layers; i++) {
5650 expector.addOpenExpectation();
5651 }
5652 display_list->Dispatch(expector);
5653 }
5654 };
5655
5656 test_unbounded("DrawPaint", [](DlCanvas& builder) { //
5657 builder.DrawPaint(DlPaint());
5658 });
5659
5660 test_unbounded("DrawColor", [](DlCanvas& builder) {
5661 builder.DrawColor(DlColor::kMagenta(), DlBlendMode::kSrc);
5662 });
5663
5664 test_unbounded("Clear", [](DlCanvas& builder) { //
5665 builder.Clear(DlColor::kMagenta());
5666 });
5667
5668 test_unbounded("DrawUnboundedDisplayList", [](DlCanvas& builder) {
5669 DisplayListBuilder nested_builder(root_cull);
5670 nested_builder.DrawPaint(DlPaint());
5671 auto nested_display_list = nested_builder.Build();
5672
5673 EXPECT_EQ(nested_display_list->GetBounds(), root_cull);
5674 ASSERT_TRUE(nested_display_list->root_is_unbounded());
5675
5676 builder.DrawDisplayList(nested_display_list);
5677 });
5678
5679 test_unbounded("DrawRectWithUnboundedImageFilter", [](DlCanvas& builder) {
5680 // clang-format off
5681 const DlScalar matrix[20] = {
5682 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
5683 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
5684 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
5685 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
5686 };
5687 // clang-format on
5688 auto unbounded_cf = DlColorFilter::MakeMatrix(matrix);
5689 // ColorFilter must modify transparent black to be "unbounded"
5690 ASSERT_TRUE(unbounded_cf->modifies_transparent_black());
5691 auto unbounded_if = DlImageFilter::MakeColorFilter(unbounded_cf);
5692 DlRect output_bounds;
5693 // ImageFilter returns null from bounds queries if it is "unbounded"
5694 ASSERT_EQ(unbounded_if->map_local_bounds(draw_rect, output_bounds),
5695 nullptr);
5696
5697 builder.DrawRect(draw_rect, DlPaint().setImageFilter(unbounded_if));
5698 });
5699
5700 test_unbounded(
5701 "SaveLayerWithBackdropFilter",
5702 [](DlCanvas& builder) {
5703 auto filter = DlImageFilter::MakeBlur(3.0f, 3.0f, DlTileMode::kMirror);
5704 builder.SaveLayer(std::nullopt, nullptr, filter.get());
5705 builder.Restore();
5706 },
5707 1);
5708}
5709
5710TEST_F(DisplayListTest, BackdropFilterCulledAlongsideClipAndTransform) {
5711 DlRect frame_bounds = DlRect::MakeWH(100.0f, 100.0f);
5712 DlRect frame_clip = frame_bounds.Expand(-0.5f, -0.5f);
5713
5714 DlRect clip_rect = DlRect::MakeLTRB(40.0f, 40.0f, 60.0f, 60.0f);
5715 DlRect draw_rect1 = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
5716 DlRect draw_rect2 = DlRect::MakeLTRB(45.0f, 20.0f, 55.0f, 55.0f);
5717 DlRect cull_rect = DlRect::MakeLTRB(1.0f, 1.0f, 99.0f, 30.0f);
5718 auto bdf_filter = DlImageFilter::MakeBlur(5.0f, 5.0f, DlTileMode::kClamp);
5719
5720 ASSERT_TRUE(frame_bounds.Contains(clip_rect));
5721 ASSERT_TRUE(frame_bounds.Contains(draw_rect1));
5722 ASSERT_TRUE(frame_bounds.Contains(draw_rect2));
5723 ASSERT_TRUE(frame_bounds.Contains(cull_rect));
5724
5725 ASSERT_TRUE(frame_clip.Contains(clip_rect));
5726 ASSERT_TRUE(frame_clip.Contains(draw_rect1));
5727 ASSERT_TRUE(frame_clip.Contains(draw_rect2));
5728 ASSERT_TRUE(frame_clip.Contains(cull_rect));
5729
5730 ASSERT_FALSE(clip_rect.IntersectsWithRect(draw_rect1));
5731 ASSERT_TRUE(clip_rect.IntersectsWithRect(draw_rect2));
5732
5733 ASSERT_FALSE(cull_rect.IntersectsWithRect(clip_rect));
5734 ASSERT_TRUE(cull_rect.IntersectsWithRect(draw_rect1));
5735 ASSERT_TRUE(cull_rect.IntersectsWithRect(draw_rect2));
5736
5737 DisplayListBuilder builder(frame_bounds, true);
5738 builder.Save();
5739 {
5740 builder.Translate(0.1f, 0.1f);
5741 builder.ClipRect(frame_clip);
5742 builder.DrawRect(draw_rect1, DlPaint());
5743 // Should all be culled below
5744 builder.ClipRect(clip_rect);
5745 builder.Translate(0.1f, 0.1f);
5746 builder.SaveLayer(std::nullopt, nullptr, bdf_filter.get());
5747 { //
5748 builder.DrawRect(clip_rect, DlPaint());
5749 }
5750 builder.Restore();
5751 // End of culling
5752 }
5753 builder.Restore();
5754 builder.DrawRect(draw_rect2, DlPaint());
5755 auto display_list = builder.Build();
5756
5757 {
5758 DisplayListBuilder unculled(frame_bounds);
5759 display_list->Dispatch(ToReceiver(unculled), frame_bounds);
5760 auto unculled_dl = unculled.Build();
5761
5762 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list, unculled_dl));
5763 }
5764
5765 {
5766 DisplayListBuilder culled(frame_bounds);
5767 display_list->Dispatch(ToReceiver(culled), cull_rect);
5768 auto culled_dl = culled.Build();
5769
5770 EXPECT_TRUE(DisplayListsNE_Verbose(display_list, culled_dl));
5771
5772 DisplayListBuilder expected(frame_bounds);
5773 expected.Save();
5774 {
5775 expected.Translate(0.1f, 0.1f);
5776 expected.ClipRect(frame_clip);
5777 expected.DrawRect(draw_rect1, DlPaint());
5778 }
5779 expected.Restore();
5780 expected.DrawRect(draw_rect2, DlPaint());
5781 auto expected_dl = expected.Build();
5782
5783 EXPECT_TRUE(DisplayListsEQ_Verbose(culled_dl, expected_dl));
5784 }
5785}
5786
5787TEST_F(DisplayListTest, RecordManyLargeDisplayListOperations) {
5788 DisplayListBuilder builder;
5789
5790 // 2050 points is sizeof(DlPoint) * 2050 = 16400 bytes, this is more
5791 // than the page size of 16384 bytes.
5792 std::vector<DlPoint> points(2050);
5793 builder.DrawPoints(DlPointMode::kPoints, points.size(), points.data(),
5794 DlPaint{});
5795 builder.DrawPoints(DlPointMode::kPoints, points.size(), points.data(),
5796 DlPaint{});
5797 builder.DrawPoints(DlPointMode::kPoints, points.size(), points.data(),
5798 DlPaint{});
5799 builder.DrawPoints(DlPointMode::kPoints, points.size(), points.data(),
5800 DlPaint{});
5801 builder.DrawPoints(DlPointMode::kPoints, points.size(), points.data(),
5802 DlPaint{});
5803 builder.DrawPoints(DlPointMode::kPoints, points.size(), points.data(),
5804 DlPaint{});
5805
5806 EXPECT_TRUE(!!builder.Build());
5807}
5808
5809TEST_F(DisplayListTest, RecordSingleLargeDisplayListOperation) {
5810 DisplayListBuilder builder;
5811
5812 std::vector<DlPoint> points(40000);
5813 builder.DrawPoints(DlPointMode::kPoints, points.size(), points.data(),
5814 DlPaint{});
5815
5816 EXPECT_TRUE(!!builder.Build());
5817}
5818
5819TEST_F(DisplayListTest, DisplayListDetectsRuntimeEffect) {
5820 auto color_source = DlColorSource::MakeRuntimeEffect(
5821 kTestRuntimeEffect1, {}, std::make_shared<std::vector<uint8_t>>());
5822 auto image_filter = DlImageFilter::MakeRuntimeEffect(
5823 kTestRuntimeEffect1, {}, std::make_shared<std::vector<uint8_t>>());
5824
5825 {
5826 // Default - no runtime effects, supports group opacity
5827 DisplayListBuilder builder;
5828 DlPaint paint;
5829
5830 builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
5831 EXPECT_TRUE(builder.Build()->can_apply_group_opacity());
5832 }
5833
5834 {
5835 // Draw with RTE color source does not support group opacity
5836 DisplayListBuilder builder;
5837 DlPaint paint;
5838
5839 paint.setColorSource(color_source);
5840 builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
5841
5842 EXPECT_FALSE(builder.Build()->can_apply_group_opacity());
5843 }
5844
5845 {
5846 // Draw with RTE image filter does not support group opacity
5847 DisplayListBuilder builder;
5848 DlPaint paint;
5849
5850 paint.setImageFilter(image_filter);
5851 builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
5852
5853 EXPECT_FALSE(builder.Build()->can_apply_group_opacity());
5854 }
5855
5856 {
5857 // Draw with RTE color source inside SaveLayer does not support group
5858 // opacity on the SaveLayer, but does support it on the DisplayList
5859 DisplayListBuilder builder;
5860 DlPaint paint;
5861
5862 builder.SaveLayer(std::nullopt, nullptr);
5863 paint.setColorSource(color_source);
5864 builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
5865 builder.Restore();
5866
5867 auto display_list = builder.Build();
5868 EXPECT_TRUE(display_list->can_apply_group_opacity());
5869
5870 SAVE_LAYER_EXPECTOR(expector);
5871 expector.addExpectation([](const SaveLayerOptions& options) {
5872 return !options.can_distribute_opacity();
5873 });
5874 display_list->Dispatch(expector);
5875 }
5876
5877 {
5878 // Draw with RTE image filter inside SaveLayer does not support group
5879 // opacity on the SaveLayer, but does support it on the DisplayList
5880 DisplayListBuilder builder;
5881 DlPaint paint;
5882
5883 builder.SaveLayer(std::nullopt, nullptr);
5884 paint.setImageFilter(image_filter);
5885 builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
5886 builder.Restore();
5887
5888 auto display_list = builder.Build();
5889 EXPECT_TRUE(display_list->can_apply_group_opacity());
5890
5891 SAVE_LAYER_EXPECTOR(expector);
5892 expector.addExpectation([](const SaveLayerOptions& options) {
5893 return !options.can_distribute_opacity();
5894 });
5895 display_list->Dispatch(expector);
5896 }
5897
5898 {
5899 // Draw with RTE color source inside nested saveLayers does not support
5900 // group opacity on the inner SaveLayer, but does support it on the
5901 // outer SaveLayer and the DisplayList
5902 DisplayListBuilder builder;
5903 DlPaint paint;
5904
5905 builder.SaveLayer(std::nullopt, nullptr);
5906
5907 builder.SaveLayer(std::nullopt, nullptr);
5908 paint.setColorSource(color_source);
5909 builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
5910 paint.setColorSource(nullptr);
5911 builder.Restore();
5912
5913 builder.SaveLayer(std::nullopt, nullptr);
5914 paint.setImageFilter(image_filter);
5915 // Make sure these DrawRects are non-overlapping otherwise the outer
5916 // SaveLayer and DisplayList will be incompatible due to overlaps
5917 builder.DrawRect(DlRect::MakeLTRB(60, 60, 100, 100), paint);
5918 paint.setImageFilter(nullptr);
5919 builder.Restore();
5920
5921 builder.Restore();
5922 auto display_list = builder.Build();
5923 EXPECT_TRUE(display_list->can_apply_group_opacity());
5924
5925 SAVE_LAYER_EXPECTOR(expector);
5926 expector.addExpectation([](const SaveLayerOptions& options) {
5927 // outer SaveLayer supports group opacity
5928 return options.can_distribute_opacity();
5929 });
5930 expector.addExpectation([](const SaveLayerOptions& options) {
5931 // first inner SaveLayer does not support group opacity
5932 return !options.can_distribute_opacity();
5933 });
5934 expector.addExpectation([](const SaveLayerOptions& options) {
5935 // second inner SaveLayer does not support group opacity
5936 return !options.can_distribute_opacity();
5937 });
5938 display_list->Dispatch(expector);
5939 }
5940}
5941
5942} // namespace testing
5943} // namespace flutter
int main(int argc, char **argv)
GLenum type
void DrawOval(const DlRect &bounds, const DlPaint &paint) override
static constexpr DlRect kMaxCullRect
Definition dl_builder.h:32
void ClipRect(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void DrawImageRect(const sk_sp< DlImage > &image, const DlRect &src, const DlRect &dst, DlImageSampling sampling, const DlPaint *paint=nullptr, DlSrcRectConstraint constraint=DlSrcRectConstraint::kFast) override
void DrawVertices(const std::shared_ptr< DlVertices > &vertices, DlBlendMode mode, const DlPaint &paint) override
void DrawImageNine(const sk_sp< DlImage > &image, const DlIRect &center, const DlRect &dst, DlFilterMode filter, const DlPaint *paint=nullptr) override
void DrawAtlas(const sk_sp< DlImage > &atlas, const DlRSTransform xform[], const DlRect tex[], const DlColor colors[], int count, DlBlendMode mode, DlImageSampling sampling, const DlRect *cullRect, const DlPaint *paint=nullptr) override
void DrawRoundRect(const DlRoundRect &rrect, const DlPaint &paint) override
void DrawArc(const DlRect &bounds, DlScalar start, DlScalar sweep, bool useCenter, const DlPaint &paint) override
void DrawShadow(const DlPath &path, const DlColor color, const DlScalar elevation, bool transparent_occluder, DlScalar dpr) override
Draws the shadow of the given |path| rendered in the provided |color| (which is only consulted for it...
void DrawImage(const sk_sp< DlImage > &image, const DlPoint &point, DlImageSampling sampling, const DlPaint *paint=nullptr) override
void DrawColor(DlColor color, DlBlendMode mode) override
DlMatrix GetMatrix() const override
Definition dl_builder.h:102
void DrawCircle(const DlPoint &center, DlScalar radius, const DlPaint &paint) override
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 DrawLine(const DlPoint &p0, const DlPoint &p1, const DlPaint &paint) override
void ClipRoundRect(const DlRoundRect &rrect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void Rotate(DlScalar degrees) override
void DrawText(const std::shared_ptr< DlText > &text, DlScalar x, DlScalar y, const DlPaint &paint) override
void Scale(DlScalar sx, DlScalar sy) override
void DrawDisplayList(const sk_sp< DisplayList > display_list, DlScalar opacity=SK_Scalar1) override
void Skew(DlScalar sx, DlScalar sy) override
void Translate(DlScalar tx, DlScalar ty) override
void Transform2DAffine(DlScalar mxx, DlScalar mxy, DlScalar mxt, DlScalar myx, DlScalar myy, DlScalar myt) override
void DrawPaint(const DlPaint &paint) override
sk_sp< DisplayList > Build()
Definition dl_builder.cc:66
void DrawPath(const DlPath &path, const DlPaint &paint) override
DlRect GetDestinationClipCoverage() const override
Definition dl_builder.h:129
void ClipPath(const DlPath &path, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void DrawPoints(DlPointMode mode, uint32_t count, const DlPoint pts[], const DlPaint &paint) override
void ClipOval(const DlRect &bounds, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
DlRect GetLocalClipCoverage() const override
Definition dl_builder.h:136
int GetSaveCount() const override
Definition dl_builder.h:62
void TransformFullPerspective(DlScalar mxx, DlScalar mxy, DlScalar mxz, DlScalar mxt, DlScalar myx, DlScalar myy, DlScalar myz, DlScalar myt, DlScalar mzx, DlScalar mzy, DlScalar mzz, DlScalar mzt, DlScalar mwx, DlScalar mwy, DlScalar mwz, DlScalar mwt) override
void DrawDiffRoundRect(const DlRoundRect &outer, const DlRoundRect &inner, const DlPaint &paint) override
void Transform(const DlMatrix &matrix) override
void DrawRect(const DlRect &rect, const DlPaint &paint) override
static std::shared_ptr< DlMaskFilter > Make(DlBlurStyle style, SkScalar sigma, bool respect_ctm=true)
Developer-facing API for rendering anything within the engine.
Definition dl_canvas.h:32
virtual void DrawPaint(const DlPaint &paint)=0
virtual void DrawCircle(const DlPoint &center, DlScalar radius, const DlPaint &paint)=0
virtual void DrawRoundRect(const DlRoundRect &rrect, const DlPaint &paint)=0
virtual void DrawDisplayList(const sk_sp< DisplayList > display_list, DlScalar opacity=SK_Scalar1)=0
virtual void DrawImageNine(const sk_sp< DlImage > &image, const DlIRect &center, const DlRect &dst, DlFilterMode filter, const DlPaint *paint=nullptr)=0
virtual void SaveLayer(const std::optional< DlRect > &bounds, const DlPaint *paint=nullptr, const DlImageFilter *backdrop=nullptr, std::optional< int64_t > backdrop_id=std::nullopt)=0
virtual void DrawLine(const DlPoint &p0, const DlPoint &p1, const DlPaint &paint)=0
virtual void DrawDiffRoundRect(const DlRoundRect &outer, const DlRoundRect &inner, const DlPaint &paint)=0
virtual void DrawRect(const DlRect &rect, const DlPaint &paint)=0
virtual void DrawVertices(const std::shared_ptr< DlVertices > &vertices, DlBlendMode mode, const DlPaint &paint)=0
virtual void DrawText(const std::shared_ptr< DlText > &text, DlScalar x, DlScalar y, const DlPaint &paint)=0
virtual void DrawImage(const sk_sp< DlImage > &image, const DlPoint &point, DlImageSampling sampling, const DlPaint *paint=nullptr)=0
virtual void DrawOval(const DlRect &bounds, const DlPaint &paint)=0
virtual void DrawColor(DlColor color, DlBlendMode mode=DlBlendMode::kSrcOver)=0
virtual void DrawDashedLine(const DlPoint &p0, const DlPoint &p1, DlScalar on_length, DlScalar off_length, const DlPaint &paint)=0
virtual void DrawPath(const DlPath &path, const DlPaint &paint)=0
virtual void Restore()=0
virtual void DrawPoints(DlPointMode mode, uint32_t count, const DlPoint pts[], const DlPaint &paint)=0
void Clear(DlColor color)
Definition dl_canvas.h:104
virtual void DrawArc(const DlRect &bounds, DlScalar start, DlScalar sweep, bool useCenter, const DlPaint &paint)=0
virtual void DrawAtlas(const sk_sp< DlImage > &atlas, const DlRSTransform xform[], const DlRect tex[], const DlColor colors[], int count, DlBlendMode mode, DlImageSampling sampling, const DlRect *cullRect, const DlPaint *paint=nullptr)=0
virtual void DrawImageRect(const sk_sp< DlImage > &image, const DlRect &src, const DlRect &dst, DlImageSampling sampling, const DlPaint *paint=nullptr, DlSrcRectConstraint constraint=DlSrcRectConstraint::kFast)=0
static DlRect ComputeShadowBounds(const DlPath &path, float elevation, DlScalar dpr, const DlMatrix &ctm)
Compute the local coverage for a |DrawShadow| operation using the given parameters (excluding the col...
Definition dl_canvas.cc:125
virtual void DrawShadow(const DlPath &path, const DlColor color, const DlScalar elevation, bool transparent_occluder, DlScalar dpr)=0
Draws the shadow of the given |path| rendered in the provided |color| (which is only consulted for it...
static std::shared_ptr< const DlColorFilter > MakeBlend(DlColor color, DlBlendMode mode)
static std::shared_ptr< const DlColorFilter > MakeMatrix(const float matrix[20])
static std::shared_ptr< DlColorSource > MakeRuntimeEffect(sk_sp< DlRuntimeEffect > runtime_effect, std::vector< std::shared_ptr< DlColorSource > > samplers, std::shared_ptr< std::vector< uint8_t > > uniform_data)
static std::shared_ptr< DlImageFilter > MakeBlur(DlScalar sigma_x, DlScalar sigma_y, DlTileMode tile_mode)
static std::shared_ptr< DlImageFilter > MakeRuntimeEffect(sk_sp< DlRuntimeEffect > runtime_effect, std::vector< std::shared_ptr< DlColorSource > > samplers, std::shared_ptr< std::vector< uint8_t > > uniform_data)
static std::shared_ptr< DlImageFilter > MakeColorFilter(const std::shared_ptr< const DlColorFilter > &filter)
static std::shared_ptr< DlImageFilter > MakeMatrix(const DlMatrix &matrix, DlImageSampling sampling)
Internal API for rendering recorded display lists to backends.
virtual void setStrokeMiter(float limit)=0
virtual void setStrokeWidth(float width)=0
virtual void setMaskFilter(const DlMaskFilter *filter)=0
virtual void setColorFilter(const DlColorFilter *filter)=0
virtual void setAntiAlias(bool aa)=0
virtual void setStrokeJoin(DlStrokeJoin join)=0
virtual void setImageFilter(const DlImageFilter *filter)=0
virtual void setColorSource(const DlColorSource *source)=0
virtual void setDrawStyle(DlDrawStyle style)=0
virtual void setBlendMode(DlBlendMode mode)=0
virtual void setColor(DlColor color)=0
virtual void setInvertColors(bool invert)=0
virtual void setStrokeCap(DlStrokeCap cap)=0
bool isAntiAlias() const
Definition dl_paint.h:57
DlStrokeCap getStrokeCap() const
Definition dl_paint.h:98
bool isDefault() const
Definition dl_paint.h:198
DlColor getColor() const
Definition dl_paint.h:69
DlPaint & setColor(DlColor color)
Definition dl_paint.h:70
DlPaint & setAntiAlias(bool isAntiAlias)
Definition dl_paint.h:58
DlBlendMode getBlendMode() const
Definition dl_paint.h:82
float getStrokeMiter() const
Definition dl_paint.h:120
DlStrokeJoin getStrokeJoin() const
Definition dl_paint.h:106
DlPaint & setStrokeCap(DlStrokeCap cap)
Definition dl_paint.h:101
DlPaint & setStrokeWidth(float width)
Definition dl_paint.h:115
DlPaint & setAlpha(uint8_t alpha)
Definition dl_paint.h:76
DlPaint & setBlendMode(DlBlendMode mode)
Definition dl_paint.h:85
const std::shared_ptr< const DlMaskFilter > & getMaskFilter() const
Definition dl_paint.h:180
DlScalar getOpacity() const
Definition dl_paint.h:77
DlDrawStyle getDrawStyle() const
Definition dl_paint.h:90
DlPaint & setImageFilter(std::nullptr_t filter)
Definition dl_paint.h:167
const std::shared_ptr< const DlColorSource > & getColorSource() const
Definition dl_paint.h:126
const std::shared_ptr< DlImageFilter > & getImageFilter() const
Definition dl_paint.h:162
float getStrokeWidth() const
Definition dl_paint.h:114
const std::shared_ptr< const DlColorFilter > & getColorFilter() const
Definition dl_paint.h:144
DlPaint & setMaskFilter(std::nullptr_t filter)
Definition dl_paint.h:185
DlPaint & setDrawStyle(DlDrawStyle style)
Definition dl_paint.h:93
DlPaint & setStrokeJoin(DlStrokeJoin join)
Definition dl_paint.h:109
DlPaint & setColorFilter(std::nullptr_t filter)
Definition dl_paint.h:149
DlPaint & setColorSource(std::nullptr_t source)
Definition dl_paint.h:131
bool isInvertColors() const
Definition dl_paint.h:63
DlPathBuilder & LineTo(DlPoint p2)
Draw a line from the current point to the indicated point p2.
DlPathBuilder & MoveTo(DlPoint p2)
Start a new contour that will originate at the indicated point p2.
DlPathBuilder & SetFillType(DlPathFillType fill_type)
Set the fill type that should be used to determine the interior of this path to the indicated |fill_t...
const DlPath TakePath()
Returns the path constructed by this path builder and resets its internal state to the default state ...
DlPathBuilder & Close()
The path is closed back to the location of the most recent MoveTo call. Contours that are filled are ...
DlPathBuilder & CubicCurveTo(DlPoint cp1, DlPoint cp2, DlPoint p2)
Draw a cubic bezier curve from the current point to the indicated point p2, using the indicated point...
bool IsRect(DlRect *rect=nullptr, bool *is_closed=nullptr) const
Definition dl_path.cc:210
static DlPath MakeRectLTRB(DlScalar left, DlScalar top, DlScalar right, DlScalar bottom)
Definition dl_path.cc:43
DlPath WithOffset(const DlPoint offset) const
Definition dl_path.cc:184
static DlPath MakeRoundRect(const DlRoundRect &rrect)
Definition dl_path.cc:72
static DlPath MakeCircle(const DlPoint center, DlScalar radius)
Definition dl_path.cc:68
static DlPath MakeRect(const DlRect &rect)
Definition dl_path.cc:39
static DlPath MakeRoundRectXY(const DlRect &rect, DlScalar x_radius, DlScalar y_radius, bool counter_clock_wise=false)
Definition dl_path.cc:76
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
DlPath WithFillType(DlPathFillType type) const
Definition dl_path.cc:196
static DlPath MakeOval(const DlRect &bounds)
Definition dl_path.cc:57
bool IsOval(DlRect *bounds=nullptr) const
Definition dl_path.cc:214
static std::shared_ptr< DlTextImpeller > MakeFromBlob(const sk_sp< SkTextBlob > &blob)
static std::shared_ptr< DlTextImpeller > Make(const std::shared_ptr< impeller::TextFrame > &frame)
static std::shared_ptr< DlTextSkia > Make(const sk_sp< SkTextBlob > &blob)
static constexpr Flags kNone
Definition dl_vertices.h:94
static std::shared_ptr< DlVertices > Make(DlVertexMode mode, int vertex_count, const DlPoint vertices[], const DlPoint texture_coordinates[], const DlColor colors[], int index_count=0, const uint16_t indices[]=nullptr, const DlRect *bounds=nullptr)
Constructs a DlVector with compact inline storage for all of its required and optional lists of data.
bool content_is_clipped() const
static const SaveLayerOptions kNoAttributes
SaveLayerOptions with_bounds_from_caller() const
bool bounds_from_caller() const
bool content_is_unbounded() const
static const SaveLayerOptions kWithAttributes
SaveLayerOptions with_content_is_clipped() const
bool can_distribute_opacity() const
void clipOval(const DlRect &bounds, DlClipOp clip_op, bool is_aa) override
void clipRect(const DlRect &rect, DlClipOp clip_op, bool is_aa) override
ClipExpector & addExpectation(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false)
void clipPath(const DlPath &path, DlClipOp clip_op, bool is_aa) override
ClipExpector & addExpectation(const DlRoundRect &rrect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false)
ClipExpector(const std::string &file, int line)
void clipRoundRect(const DlRoundRect &rrect, DlClipOp clip_op, bool is_aa) override
void clipRoundSuperellipse(const DlRoundSuperellipse &rse, DlClipOp clip_op, bool is_aa) override
ClipExpector & addExpectation(const DlPath &path, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false)
ClipExpector & addOvalExpectation(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false)
void saveLayer(const DlRect &bounds, const SaveLayerOptions &options, uint32_t total_content_depth, DlBlendMode max_content_mode, const DlImageFilter *backdrop, std::optional< int64_t > backdrop_id) override
void saveLayer(const DlRect &bounds, SaveLayerOptions options, const DlImageFilter *backdrop, std::optional< int64_t > backdrop_id) override
void save(uint32_t total_content_depth) override
DepthExpector(std::vector< uint32_t > expectations)
static DlOpReceiver & ToReceiver(DisplayListBuilder &builder)
static void check_inverted_bounds(DlRenderer &renderer, const std::string &desc)
const std::function< void(DlCanvas &)> DlSetup
static void check_defaults(DisplayListBuilder &builder, const DlRect &cull_rect=DisplayListBuilder::kMaxCullRect)
static sk_sp< DisplayList > Build(size_t g_index, size_t v_index)
static void verify_inverted_bounds(DlSetup &setup, DlRenderer &renderer, DlPaint paint, DlRect render_rect, DlRect expected_bounds, const std::string &desc)
static sk_sp< DisplayList > Build(DisplayListInvocation &invocation)
const std::function< void(DlCanvas &, DlPaint &, DlRect &)> DlRenderer
SaveLayerBoundsExpector & addComputedExpectation(const DlRect &bounds)
void saveLayer(const DlRect &bounds, const SaveLayerOptions options, const DlImageFilter *backdrop, std::optional< int64_t > backdrop_id) override
SaveLayerBoundsExpector & addSuppliedExpectation(const DlRect &bounds, bool clipped=false)
void saveLayer(const DlRect &bounds, const SaveLayerOptions options, const DlImageFilter *backdrop, std::optional< int64_t > backdrop_id) override
SaveLayerExpector(const std::string &file, int line)
SaveLayerExpector & addExpectation(const SaveLayerExpectations &expected)
SaveLayerExpector & addDetail(const std::string &detail)
virtual void saveLayer(const DlRect &bounds, const SaveLayerOptions &options, uint32_t total_content_depth, DlBlendMode max_content_blend_mode, const DlImageFilter *backdrop=nullptr, std::optional< int64_t > backdrop_id=std::nullopt)
SaveLayerExpector & addExpectation(const SaveLayerOptionsTester &tester)
#define CLIP_EXPECTOR(name)
#define SAVE_LAYER_EXPECTOR(name)
#define RUN_TESTS2(body, expect)
#define RUN_TESTS(body)
#define TEST_RTREE(rtree, query, expected_rects, expected_indices)
int32_t x
#define TestPointCount
FlutterVulkanImage * image
#define FML_LOG(severity)
Definition logging.h:101
#define FML_UNREACHABLE()
Definition logging.h:128
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
std::u16string text
double y
constexpr float kPi
Definition math.h:27
TEST_F(DisplayListTest, Defaults)
static const DlBlurMaskFilter kTestMaskFilter1(DlBlurStyle::kNormal, 3.0)
bool DisplayListsNE_Verbose(const DisplayList *a, const DisplayList *b)
constexpr DlPoint kTestPoints[]
static const auto kTestMatrixColorFilter1
static std::vector< testing::DisplayListInvocationGroup > allGroups
static const std::shared_ptr< DlColorSource > kTestSource1
constexpr DlRect kTestBounds
static const DlPath kTestPath1
sk_sp< DlImage > MakeTestImage(int w, int h, int checker_size)
static void test_rtree(const sk_sp< const DlRTree > &rtree, const DlRect &query, std::vector< DlRect > expected_rects, const std::vector< int > &expected_indices, const std::string &file, int line)
static auto kTestImage1
static DlImageSampling kLinearSampling
::std::ostream & operator<<(::std::ostream &os, const SaveLayerExpectations &expect)
static auto kTestImage2
bool DisplayListsEQ_Verbose(const DisplayList *a, const DisplayList *b)
static DlImageSampling kNearestSampling
sk_sp< SkTextBlob > GetTestTextBlob(int index)
std::function< bool(const SaveLayerOptions &options)> SaveLayerOptionsTester
static const DlBlurImageFilter kTestBlurImageFilter1(5.0, 5.0, DlTileMode::kClamp)
std::vector< DisplayListInvocationGroup > CreateAllGroups()
static const std::shared_ptr< DlVertices > kTestVertices1
static const DlRoundRect kTestRRect
static sk_sp< DisplayList > TestDisplayList1
impeller::Scalar DlScalar
DlPaint DisplayListBuilderTestingAttributes(DisplayListBuilder &builder)
uint32_t DlIndex
constexpr bool DlScalarNearlyEqual(DlScalar x, DlScalar y, DlScalar tolerance=kEhCloseEnough)
DisplayListOpCategory
impeller::RoundRect DlRoundRect
@ kRound
adds circle
@ kSquare
adds square
impeller::Matrix DlMatrix
impeller::Rect DlRect
impeller::Degrees DlDegrees
DlOpReceiver & DisplayListBuilderTestingAccessor(DisplayListBuilder &builder)
DlPointMode
Definition dl_types.h:15
@ kLines
draw each separate pair of points as a line segment
@ kPolygon
draw each pair of overlapping points as a line segment
@ kPoints
draw each point separately
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
DEF_SWITCHES_START aot vmservice shared library name
Definition switch_defs.h:27
@ kTriangles
The vertices are taken 3 at a time to form a triangle.
@ kStrokeAndFill
both strokes and fills shapes
@ kStroke
strokes boundary of shapes
impeller::IRect32 DlIRect
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
@ kNormal
fuzzy inside and outside
impeller::BlendMode DlBlendMode
TEST_F(EngineAnimatorTest, AnimatorAcceptsMultipleRenders)
impeller::Point DlPoint
int DisplayListBuilderTestingLastOpIndex(DisplayListBuilder &builder)
flutter::DlPath DlPath
BlendMode
Definition color.h:58
Definition ref_ptr.h:261
flutter::DlPaint DlPaint
static constexpr DlColor kMagenta()
Definition dl_color.h:75
static constexpr DlColor kWhite()
Definition dl_color.h:70
static constexpr DlColor kBlue()
Definition dl_color.h:73
static constexpr DlColor kBlack()
Definition dl_color.h:69
static constexpr DlColor kTransparent()
Definition dl_color.h:68
static constexpr DlColor kRed()
Definition dl_color.h:71
static constexpr DlColor kGreen()
Definition dl_color.h:72
std::variant< DlRect, DlRoundRect, DlRoundSuperellipse, DlPath > shape
std::vector< DisplayListInvocation > variants
uint32_t adjust_render_op_depth_cost(uint32_t previous_cost)
uint32_t depth_accumulated(uint32_t depth_scale=1u)
SaveLayerExpectations(const SaveLayerOptionsTester &t)
std::optional< SaveLayerOptions > options
std::optional< SaveLayerOptionsTester > tester
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
constexpr bool IsIdentity() const
Definition matrix.h:467
static constexpr Matrix MakeRow(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition matrix.h:83
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition matrix.h:127
static Matrix MakeRotationZ(Radians r)
Definition matrix.h:223
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
static RSTransform Make(Point origin, Scalar scale, Radians radians)
Definition rstransform.h:38
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
static RoundRect MakeRect(const Rect &rect)
Definition round_rect.h:19
constexpr auto GetBottom() const
Definition rect.h:357
static constexpr TRect MakeWH(Type width, Type height)
Definition rect.h:140
constexpr auto GetTop() const
Definition rect.h:353
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition rect.h:347
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition rect.h:297
constexpr bool Contains(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the half-open interior of this rectangle.
Definition rect.h:231
static constexpr std::enable_if_t< std::is_floating_point_v< FT >, TRect > Make(const TRect< U > &rect)
Definition rect.h:157
constexpr bool IntersectsWithRect(const TRect &o) const
Definition rect.h:546
constexpr auto GetLeft() const
Definition rect.h:351
RoundOut(const TRect< U > &r)
Definition rect.h:679
constexpr auto GetRight() const
Definition rect.h:355
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 Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition rect.h:341
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
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition rect.h:382
constexpr TRect IntersectionOrEmpty(const TRect &o) const
Definition rect.h:542
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
std::vector< Point > points