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