Flutter Engine
The Flutter Engine
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
11#include "flutter/display_list/display_list.h"
12#include "flutter/display_list/dl_blend_mode.h"
13#include "flutter/display_list/dl_builder.h"
14#include "flutter/display_list/dl_paint.h"
15#include "flutter/display_list/geometry/dl_rtree.h"
16#include "flutter/display_list/skia/dl_sk_dispatcher.h"
17#include "flutter/display_list/testing/dl_test_snippets.h"
18#include "flutter/display_list/utils/dl_receiver_utils.h"
19#include "flutter/fml/logging.h"
20#include "flutter/fml/math.h"
21#include "flutter/testing/assertions_skia.h"
22#include "flutter/testing/display_list_testing.h"
23#include "flutter/testing/testing.h"
24
30
31namespace flutter {
32
34 return builder.asReceiver();
35}
36
38 return builder.CurrentAttributes();
39}
40
42 return builder.LastOpIndex();
43}
44
45namespace testing {
46
47static std::vector<testing::DisplayListInvocationGroup> allGroups =
49
52
53template <typename BaseT>
54class DisplayListTestBase : public BaseT {
55 public:
57
60 }
61
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) {
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(
110 const SkRect& cull_rect = DisplayListBuilder::kMaxCullRect) {
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.GetTransform(), SkMatrix());
131 EXPECT_EQ(builder.GetTransformFullPerspective(), SkM44());
132
133 EXPECT_EQ(builder.GetLocalClipBounds(), cull_rect);
134 EXPECT_EQ(builder.GetDestinationClipBounds(), cull_rect);
135
136 EXPECT_EQ(builder.GetSaveCount(), 1);
137 }
138
139 typedef const std::function<void(DlCanvas&)> DlSetup;
140 typedef const std::function<void(DlCanvas&, DlPaint&, SkRect& rect)>
142
146 SkRect render_rect,
147 SkRect expected_bounds,
148 const std::string& desc) {
150 setup(builder);
151 renderer(builder, paint, render_rect);
152 auto dl = builder.Build();
153 EXPECT_EQ(dl->op_count(), 1u) << desc;
154 EXPECT_EQ(dl->bounds(), expected_bounds) << desc;
155 }
156
158 const std::string& desc) {
159 SkRect rect = SkRect::MakeLTRB(0.0f, 0.0f, 10.0f, 10.0f);
160 SkRect invertedLR = SkRect::MakeLTRB(rect.fRight, rect.fTop, //
161 rect.fLeft, rect.fBottom);
162 SkRect invertedTB = SkRect::MakeLTRB(rect.fLeft, rect.fBottom, //
163 rect.fRight, rect.fTop);
164 SkRect invertedLTRB = SkRect::MakeLTRB(rect.fRight, rect.fBottom, //
165 rect.fLeft, rect.fTop);
166 auto empty_setup = [](DlCanvas&) {};
167
168 ASSERT_TRUE(rect.fLeft < rect.fRight);
169 ASSERT_TRUE(rect.fTop < rect.fBottom);
170 ASSERT_FALSE(rect.isEmpty());
171 ASSERT_TRUE(invertedLR.fLeft > invertedLR.fRight);
172 ASSERT_TRUE(invertedLR.isEmpty());
173 ASSERT_TRUE(invertedTB.fTop > invertedTB.fBottom);
174 ASSERT_TRUE(invertedTB.isEmpty());
175 ASSERT_TRUE(invertedLTRB.fLeft > invertedLTRB.fRight);
176 ASSERT_TRUE(invertedLTRB.fTop > invertedLTRB.fBottom);
177 ASSERT_TRUE(invertedLTRB.isEmpty());
178
179 DlPaint ref_paint = DlPaint();
180 SkRect ref_bounds = rect;
181 verify_inverted_bounds(empty_setup, renderer, ref_paint, invertedLR,
182 ref_bounds, desc + " LR swapped");
183 verify_inverted_bounds(empty_setup, renderer, ref_paint, invertedTB,
184 ref_bounds, desc + " TB swapped");
185 verify_inverted_bounds(empty_setup, renderer, ref_paint, invertedLTRB,
186 ref_bounds, desc + " LR&TB swapped");
187
188 // Round joins are used because miter joins greatly pad the bounds,
189 // but only on paths. So we use round joins for consistency there.
190 // We aren't fully testing all stroke-related bounds computations here,
191 // those are more fully tested in the render tests. We are simply
192 // checking that they are applied to the ordered bounds.
193 DlPaint stroke_paint = DlPaint() //
196 .setStrokeWidth(2.0f);
197 SkRect stroke_bounds = rect.makeOutset(1.0f, 1.0f);
198 verify_inverted_bounds(empty_setup, renderer, stroke_paint, invertedLR,
199 stroke_bounds, desc + " LR swapped, sw 2");
200 verify_inverted_bounds(empty_setup, renderer, stroke_paint, invertedTB,
201 stroke_bounds, desc + " TB swapped, sw 2");
202 verify_inverted_bounds(empty_setup, renderer, stroke_paint, invertedLTRB,
203 stroke_bounds, desc + " LR&TB swapped, sw 2");
204
206 DlPaint maskblur_paint = DlPaint() //
208 SkRect maskblur_bounds = rect.makeOutset(6.0f, 6.0f);
209 verify_inverted_bounds(empty_setup, renderer, maskblur_paint, invertedLR,
210 maskblur_bounds, desc + " LR swapped, mask 2");
211 verify_inverted_bounds(empty_setup, renderer, maskblur_paint, invertedTB,
212 maskblur_bounds, desc + " TB swapped, mask 2");
213 verify_inverted_bounds(empty_setup, renderer, maskblur_paint, invertedLTRB,
214 maskblur_bounds, desc + " LR&TB swapped, mask 2");
215
216 DlErodeImageFilter erode_filter(2.0f, 2.0f);
217 DlPaint erode_paint = DlPaint() //
218 .setImageFilter(&erode_filter);
219 SkRect erode_bounds = rect.makeInset(2.0f, 2.0f);
220 verify_inverted_bounds(empty_setup, renderer, erode_paint, invertedLR,
221 erode_bounds, desc + " LR swapped, erode 2");
222 verify_inverted_bounds(empty_setup, renderer, erode_paint, invertedTB,
223 erode_bounds, desc + " TB swapped, erode 2");
224 verify_inverted_bounds(empty_setup, renderer, erode_paint, invertedLTRB,
225 erode_bounds, desc + " LR&TB swapped, erode 2");
226 }
227
228 private:
229 FML_DISALLOW_COPY_AND_ASSIGN(DisplayListTestBase);
230};
232
235 check_defaults(builder);
236}
237
240 auto dl = builder.Build();
241 EXPECT_EQ(dl->op_count(), 0u);
242 EXPECT_EQ(dl->bytes(), sizeof(DisplayList));
243 EXPECT_EQ(dl->total_depth(), 0u);
244}
245
246TEST_F(DisplayListTest, EmptyRebuild) {
248 auto dl1 = builder.Build();
249 auto dl2 = builder.Build();
250 auto dl3 = builder.Build();
251 ASSERT_TRUE(dl1->Equals(dl2));
252 ASSERT_TRUE(dl2->Equals(dl3));
253}
254
255TEST_F(DisplayListTest, BuilderCanBeReused) {
257 builder.DrawRect(kTestBounds, DlPaint());
258 auto dl = builder.Build();
259 builder.DrawRect(kTestBounds, DlPaint());
260 auto dl2 = builder.Build();
261 ASSERT_TRUE(dl->Equals(dl2));
262}
263
264TEST_F(DisplayListTest, SaveRestoreRestoresTransform) {
265 SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f);
266 DisplayListBuilder builder(cull_rect);
267
268 builder.Save();
269 builder.Translate(10.0f, 10.0f);
270 builder.Restore();
271 check_defaults(builder, cull_rect);
272
273 builder.Save();
274 builder.Scale(10.0f, 10.0f);
275 builder.Restore();
276 check_defaults(builder, cull_rect);
277
278 builder.Save();
279 builder.Skew(0.1f, 0.1f);
280 builder.Restore();
281 check_defaults(builder, cull_rect);
282
283 builder.Save();
284 builder.Rotate(45.0f);
285 builder.Restore();
286 check_defaults(builder, cull_rect);
287
288 builder.Save();
289 builder.Transform(SkMatrix::Scale(10.0f, 10.0f));
290 builder.Restore();
291 check_defaults(builder, cull_rect);
292
293 builder.Save();
294 builder.Transform2DAffine(1.0f, 0.0f, 12.0f, //
295 0.0f, 1.0f, 35.0f);
296 builder.Restore();
297 check_defaults(builder, cull_rect);
298
299 builder.Save();
300 builder.Transform(SkM44(SkMatrix::Scale(10.0f, 10.0f)));
301 builder.Restore();
302 check_defaults(builder, cull_rect);
303
304 builder.Save();
305 builder.TransformFullPerspective(1.0f, 0.0f, 0.0f, 12.0f, //
306 0.0f, 1.0f, 0.0f, 35.0f, //
307 0.0f, 0.0f, 1.0f, 5.0f, //
308 0.0f, 0.0f, 0.0f, 1.0f);
309 builder.Restore();
310 check_defaults(builder, cull_rect);
311}
312
313TEST_F(DisplayListTest, BuildRestoresTransform) {
314 SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f);
315 DisplayListBuilder builder(cull_rect);
316
317 builder.Translate(10.0f, 10.0f);
318 builder.Build();
319 check_defaults(builder, cull_rect);
320
321 builder.Scale(10.0f, 10.0f);
322 builder.Build();
323 check_defaults(builder, cull_rect);
324
325 builder.Skew(0.1f, 0.1f);
326 builder.Build();
327 check_defaults(builder, cull_rect);
328
329 builder.Rotate(45.0f);
330 builder.Build();
331 check_defaults(builder, cull_rect);
332
333 builder.Transform(SkMatrix::Scale(10.0f, 10.0f));
334 builder.Build();
335 check_defaults(builder, cull_rect);
336
337 builder.Transform2DAffine(1.0f, 0.0f, 12.0f, //
338 0.0f, 1.0f, 35.0f);
339 builder.Build();
340 check_defaults(builder, cull_rect);
341
342 builder.Transform(SkM44(SkMatrix::Scale(10.0f, 10.0f)));
343 builder.Build();
344 check_defaults(builder, cull_rect);
345
346 builder.TransformFullPerspective(1.0f, 0.0f, 0.0f, 12.0f, //
347 0.0f, 1.0f, 0.0f, 35.0f, //
348 0.0f, 0.0f, 1.0f, 5.0f, //
349 0.0f, 0.0f, 0.0f, 1.0f);
350 builder.Build();
351 check_defaults(builder, cull_rect);
352}
353
354TEST_F(DisplayListTest, SaveRestoreRestoresClip) {
355 SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f);
356 DisplayListBuilder builder(cull_rect);
357
358 builder.Save();
359 builder.ClipRect({0.0f, 0.0f, 10.0f, 10.0f});
360 builder.Restore();
361 check_defaults(builder, cull_rect);
362
363 builder.Save();
364 builder.ClipRRect(SkRRect::MakeRectXY({0.0f, 0.0f, 5.0f, 5.0f}, 2.0f, 2.0f));
365 builder.Restore();
366 check_defaults(builder, cull_rect);
367
368 builder.Save();
369 builder.ClipPath(SkPath().addOval({0.0f, 0.0f, 10.0f, 10.0f}));
370 builder.Restore();
371 check_defaults(builder, cull_rect);
372}
373
374TEST_F(DisplayListTest, BuildRestoresClip) {
375 SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f);
376 DisplayListBuilder builder(cull_rect);
377
378 builder.ClipRect({0.0f, 0.0f, 10.0f, 10.0f});
379 builder.Build();
380 check_defaults(builder, cull_rect);
381
382 builder.ClipRRect(SkRRect::MakeRectXY({0.0f, 0.0f, 5.0f, 5.0f}, 2.0f, 2.0f));
383 builder.Build();
384 check_defaults(builder, cull_rect);
385
386 builder.ClipPath(SkPath().addOval({0.0f, 0.0f, 10.0f, 10.0f}));
387 builder.Build();
388 check_defaults(builder, cull_rect);
389}
390
391TEST_F(DisplayListTest, BuildRestoresAttributes) {
392 SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f);
393 DisplayListBuilder builder(cull_rect);
394 DlOpReceiver& receiver = ToReceiver(builder);
395
396 receiver.setAntiAlias(true);
397 builder.Build();
398 check_defaults(builder, cull_rect);
399
400 receiver.setInvertColors(true);
401 builder.Build();
402 check_defaults(builder, cull_rect);
403
404 receiver.setColor(DlColor::kRed());
405 builder.Build();
406 check_defaults(builder, cull_rect);
407
409 builder.Build();
410 check_defaults(builder, cull_rect);
411
413 builder.Build();
414 check_defaults(builder, cull_rect);
415
416 receiver.setStrokeWidth(300.0f);
417 builder.Build();
418 check_defaults(builder, cull_rect);
419
420 receiver.setStrokeMiter(300.0f);
421 builder.Build();
422 check_defaults(builder, cull_rect);
423
425 builder.Build();
426 check_defaults(builder, cull_rect);
427
429 builder.Build();
430 check_defaults(builder, cull_rect);
431
432 receiver.setColorSource(&kTestSource1);
433 builder.Build();
434 check_defaults(builder, cull_rect);
435
437 builder.Build();
438 check_defaults(builder, cull_rect);
439
441 builder.Build();
442 check_defaults(builder, cull_rect);
443
445 builder.Build();
446 check_defaults(builder, cull_rect);
447}
448
449TEST_F(DisplayListTest, BuilderBoundsTransformComparedToSkia) {
450 const SkRect frame_rect = SkRect::MakeLTRB(10, 10, 100, 100);
451 DisplayListBuilder builder(frame_rect);
452 SkPictureRecorder recorder;
453 SkCanvas* canvas = recorder.beginRecording(frame_rect);
454 ASSERT_EQ(builder.GetDestinationClipBounds(),
456 ASSERT_EQ(builder.GetLocalClipBounds().makeOutset(1, 1),
457 canvas->getLocalClipBounds());
458 ASSERT_EQ(builder.GetTransform(), canvas->getTotalMatrix());
459}
460
461TEST_F(DisplayListTest, BuilderInitialClipBounds) {
462 SkRect cull_rect = SkRect::MakeWH(100, 100);
463 SkRect clip_bounds = SkRect::MakeWH(100, 100);
464 DisplayListBuilder builder(cull_rect);
465 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
466}
467
468TEST_F(DisplayListTest, BuilderInitialClipBoundsNaN) {
470 SkRect clip_bounds = SkRect::MakeEmpty();
471 DisplayListBuilder builder(cull_rect);
472 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
473}
474
475TEST_F(DisplayListTest, BuilderClipBoundsAfterClipRect) {
476 SkRect cull_rect = SkRect::MakeWH(100, 100);
477 SkRect clip_rect = SkRect::MakeLTRB(10, 10, 20, 20);
478 SkRect clip_bounds = SkRect::MakeLTRB(10, 10, 20, 20);
479 DisplayListBuilder builder(cull_rect);
480 builder.ClipRect(clip_rect, ClipOp::kIntersect, false);
481 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
482}
483
484TEST_F(DisplayListTest, BuilderClipBoundsAfterClipRRect) {
485 SkRect cull_rect = SkRect::MakeWH(100, 100);
486 SkRect clip_rect = SkRect::MakeLTRB(10, 10, 20, 20);
487 SkRRect clip_rrect = SkRRect::MakeRectXY(clip_rect, 2, 2);
488 SkRect clip_bounds = SkRect::MakeLTRB(10, 10, 20, 20);
489 DisplayListBuilder builder(cull_rect);
490 builder.ClipRRect(clip_rrect, ClipOp::kIntersect, false);
491 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
492}
493
494TEST_F(DisplayListTest, BuilderClipBoundsAfterClipPath) {
495 SkRect cull_rect = SkRect::MakeWH(100, 100);
496 SkPath clip_path = SkPath().addRect(10, 10, 15, 15).addRect(15, 15, 20, 20);
497 SkRect clip_bounds = SkRect::MakeLTRB(10, 10, 20, 20);
498 DisplayListBuilder builder(cull_rect);
499 builder.ClipPath(clip_path, ClipOp::kIntersect, false);
500 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
501}
502
503TEST_F(DisplayListTest, BuilderInitialClipBoundsNonZero) {
504 SkRect cull_rect = SkRect::MakeLTRB(10, 10, 100, 100);
505 SkRect clip_bounds = SkRect::MakeLTRB(10, 10, 100, 100);
506 DisplayListBuilder builder(cull_rect);
507 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
508}
509
510TEST_F(DisplayListTest, UnclippedSaveLayerContentAccountsForFilter) {
511 SkRect cull_rect = SkRect::MakeLTRB(0.0f, 0.0f, 300.0f, 300.0f);
512 SkRect clip_rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
513 SkRect draw_rect = SkRect::MakeLTRB(50.0f, 140.0f, 101.0f, 160.0f);
514 auto filter = DlBlurImageFilter::Make(10.0f, 10.0f, DlTileMode::kDecal);
515 DlPaint layer_paint = DlPaint().setImageFilter(filter);
516
517 ASSERT_TRUE(clip_rect.intersects(draw_rect));
518 ASSERT_TRUE(cull_rect.contains(clip_rect));
519 ASSERT_TRUE(cull_rect.contains(draw_rect));
520
522 builder.Save();
523 {
524 builder.ClipRect(clip_rect, ClipOp::kIntersect, false);
525 builder.SaveLayer(&cull_rect, &layer_paint);
526 { //
527 builder.DrawRect(draw_rect, DlPaint());
528 }
529 builder.Restore();
530 }
531 builder.Restore();
532 auto display_list = builder.Build();
533
534 EXPECT_EQ(display_list->op_count(), 6u);
535 EXPECT_EQ(display_list->total_depth(), 2u);
536
537 SkRect result_rect = draw_rect.makeOutset(30.0f, 30.0f);
538 ASSERT_TRUE(result_rect.intersect(clip_rect));
539 ASSERT_EQ(result_rect, SkRect::MakeLTRB(100.0f, 110.0f, 131.0f, 190.0f));
540 EXPECT_EQ(display_list->bounds(), result_rect);
541}
542
543TEST_F(DisplayListTest, ClippedSaveLayerContentAccountsForFilter) {
544 SkRect cull_rect = SkRect::MakeLTRB(0.0f, 0.0f, 300.0f, 300.0f);
545 SkRect clip_rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
546 SkRect draw_rect = SkRect::MakeLTRB(50.0f, 140.0f, 99.0f, 160.0f);
547 auto filter = DlBlurImageFilter::Make(10.0f, 10.0f, DlTileMode::kDecal);
548 DlPaint layer_paint = DlPaint().setImageFilter(filter);
549
550 ASSERT_FALSE(clip_rect.intersects(draw_rect));
551 ASSERT_TRUE(cull_rect.contains(clip_rect));
552 ASSERT_TRUE(cull_rect.contains(draw_rect));
553
555 builder.Save();
556 {
557 builder.ClipRect(clip_rect, ClipOp::kIntersect, false);
558 builder.SaveLayer(&cull_rect, &layer_paint);
559 { //
560 builder.DrawRect(draw_rect, DlPaint());
561 }
562 builder.Restore();
563 }
564 builder.Restore();
565 auto display_list = builder.Build();
566
567 EXPECT_EQ(display_list->op_count(), 6u);
568 EXPECT_EQ(display_list->total_depth(), 2u);
569
570 SkRect result_rect = draw_rect.makeOutset(30.0f, 30.0f);
571 ASSERT_TRUE(result_rect.intersect(clip_rect));
572 ASSERT_EQ(result_rect, SkRect::MakeLTRB(100.0f, 110.0f, 129.0f, 190.0f));
573 EXPECT_EQ(display_list->bounds(), result_rect);
574}
575
576TEST_F(DisplayListTest, OOBSaveLayerContentCulledWithBlurFilter) {
577 SkRect cull_rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
578 SkRect draw_rect = SkRect::MakeLTRB(25.0f, 25.0f, 99.0f, 75.0f);
579 auto filter = DlBlurImageFilter::Make(10.0f, 10.0f, DlTileMode::kDecal);
580 DlPaint layer_paint = DlPaint().setImageFilter(filter);
581
582 // We want a draw rect that is outside the layer bounds even though its
583 // filtered output might be inside. The drawn rect should be culled by
584 // the expectations of the layer bounds even though it is close enough
585 // to be visible due to filtering.
586 ASSERT_FALSE(cull_rect.intersects(draw_rect));
587 SkRect mapped_rect;
588 ASSERT_TRUE(filter->map_local_bounds(draw_rect, mapped_rect));
589 ASSERT_TRUE(mapped_rect.intersects(cull_rect));
590
592 builder.SaveLayer(&cull_rect, &layer_paint);
593 { //
594 builder.DrawRect(draw_rect, DlPaint());
595 }
596 builder.Restore();
597 auto display_list = builder.Build();
598
599 EXPECT_EQ(display_list->op_count(), 2u);
600 EXPECT_EQ(display_list->total_depth(), 1u);
601
602 EXPECT_TRUE(display_list->bounds().isEmpty()) << display_list->bounds();
603}
604
605TEST_F(DisplayListTest, OOBSaveLayerContentCulledWithMatrixFilter) {
606 SkRect cull_rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
607 SkRect draw_rect = SkRect::MakeLTRB(25.0f, 125.0f, 75.0f, 175.0f);
608 auto filter = DlMatrixImageFilter::Make(SkMatrix::Translate(100.0f, 0.0f),
610 DlPaint layer_paint = DlPaint().setImageFilter(filter);
611
612 // We want a draw rect that is outside the layer bounds even though its
613 // filtered output might be inside. The drawn rect should be culled by
614 // the expectations of the layer bounds even though it is close enough
615 // to be visible due to filtering.
616 ASSERT_FALSE(cull_rect.intersects(draw_rect));
617 SkRect mapped_rect;
618 ASSERT_TRUE(filter->map_local_bounds(draw_rect, mapped_rect));
619 ASSERT_TRUE(mapped_rect.intersects(cull_rect));
620
622 builder.SaveLayer(&cull_rect, &layer_paint);
623 { //
624 builder.DrawRect(draw_rect, DlPaint());
625 }
626 builder.Restore();
627 auto display_list = builder.Build();
628
629 EXPECT_EQ(display_list->op_count(), 2u);
630 EXPECT_EQ(display_list->total_depth(), 1u);
631
632 EXPECT_TRUE(display_list->bounds().isEmpty()) << display_list->bounds();
633}
634
635TEST_F(DisplayListTest, SingleOpSizes) {
636 for (auto& group : allGroups) {
637 for (size_t i = 0; i < group.variants.size(); i++) {
638 auto& invocation = group.variants[i];
639 sk_sp<DisplayList> dl = Build(invocation);
640 auto desc = group.op_name + "(variant " + std::to_string(i + 1) + ")";
641 EXPECT_EQ(dl->op_count(false), invocation.op_count()) << desc;
642 EXPECT_EQ(dl->bytes(false), invocation.byte_count()) << desc;
643 EXPECT_EQ(dl->total_depth(), invocation.depth_accumulated()) << desc;
644 }
645 }
646}
647
648TEST_F(DisplayListTest, SingleOpDisplayListsNotEqualEmpty) {
650 for (auto& group : allGroups) {
651 for (size_t i = 0; i < group.variants.size(); i++) {
652 sk_sp<DisplayList> dl = Build(group.variants[i]);
653 auto desc =
654 group.op_name + "(variant " + std::to_string(i + 1) + " != empty)";
655 if (group.variants[i].is_empty()) {
656 ASSERT_TRUE(DisplayListsEQ_Verbose(dl, empty));
657 ASSERT_TRUE(empty->Equals(*dl)) << desc;
658 } else {
659 ASSERT_TRUE(DisplayListsNE_Verbose(dl, empty));
660 ASSERT_FALSE(empty->Equals(*dl)) << desc;
661 }
662 }
663 }
664}
665
666TEST_F(DisplayListTest, SingleOpDisplayListsRecapturedAreEqual) {
667 for (auto& group : allGroups) {
668 for (size_t i = 0; i < group.variants.size(); i++) {
669 sk_sp<DisplayList> dl = Build(group.variants[i]);
670 // Verify recapturing the replay of the display list is Equals()
671 // when dispatching directly from the DL to another builder
672 DisplayListBuilder copy_builder;
673 DlOpReceiver& r = ToReceiver(copy_builder);
674 dl->Dispatch(r);
675 sk_sp<DisplayList> copy = copy_builder.Build();
676 auto desc =
677 group.op_name + "(variant " + std::to_string(i + 1) + " == copy)";
679 ASSERT_EQ(copy->op_count(false), dl->op_count(false)) << desc;
680 ASSERT_EQ(copy->bytes(false), dl->bytes(false)) << desc;
681 ASSERT_EQ(copy->op_count(true), dl->op_count(true)) << desc;
682 ASSERT_EQ(copy->bytes(true), dl->bytes(true)) << desc;
683 EXPECT_EQ(copy->total_depth(), dl->total_depth()) << desc;
684 ASSERT_EQ(copy->bounds(), dl->bounds()) << desc;
685 ASSERT_TRUE(copy->Equals(*dl)) << desc;
686 ASSERT_TRUE(dl->Equals(*copy)) << desc;
687 }
688 }
689}
690
691TEST_F(DisplayListTest, SingleOpDisplayListsCompareToEachOther) {
692 for (auto& group : allGroups) {
693 std::vector<sk_sp<DisplayList>> lists_a;
694 std::vector<sk_sp<DisplayList>> lists_b;
695 for (size_t i = 0; i < group.variants.size(); i++) {
696 lists_a.push_back(Build(group.variants[i]));
697 lists_b.push_back(Build(group.variants[i]));
698 }
699
700 for (size_t i = 0; i < lists_a.size(); i++) {
701 sk_sp<DisplayList> listA = lists_a[i];
702 for (size_t j = 0; j < lists_b.size(); j++) {
703 sk_sp<DisplayList> listB = lists_b[j];
704 auto desc = group.op_name + "(variant " + std::to_string(i + 1) +
705 " ==? variant " + std::to_string(j + 1) + ")";
706 if (i == j ||
707 (group.variants[i].is_empty() && group.variants[j].is_empty())) {
708 // They are the same variant, or both variants are NOPs
709 ASSERT_EQ(listA->op_count(false), listB->op_count(false)) << desc;
710 ASSERT_EQ(listA->bytes(false), listB->bytes(false)) << desc;
711 ASSERT_EQ(listA->op_count(true), listB->op_count(true)) << desc;
712 ASSERT_EQ(listA->bytes(true), listB->bytes(true)) << desc;
713 EXPECT_EQ(listA->total_depth(), listB->total_depth()) << desc;
714 ASSERT_EQ(listA->bounds(), listB->bounds()) << desc;
715 ASSERT_TRUE(listA->Equals(*listB)) << desc;
716 ASSERT_TRUE(listB->Equals(*listA)) << desc;
717 } else {
718 // No assertion on op/byte counts or bounds
719 // they may or may not be equal between variants
720 ASSERT_FALSE(listA->Equals(*listB)) << desc;
721 ASSERT_FALSE(listB->Equals(*listA)) << desc;
722 }
723 }
724 }
725 }
726}
727
728TEST_F(DisplayListTest, SingleOpDisplayListsAreEqualWithOrWithoutRtree) {
729 for (auto& group : allGroups) {
730 for (size_t i = 0; i < group.variants.size(); i++) {
731 DisplayListBuilder builder1(/*prepare_rtree=*/false);
732 DisplayListBuilder builder2(/*prepare_rtree=*/true);
733 group.variants[i].Invoke(ToReceiver(builder1));
734 group.variants[i].Invoke(ToReceiver(builder2));
735 sk_sp<DisplayList> dl1 = builder1.Build();
736 sk_sp<DisplayList> dl2 = builder2.Build();
737
738 auto desc = group.op_name + "(variant " + std::to_string(i + 1) + " )";
739 ASSERT_EQ(dl1->op_count(false), dl2->op_count(false)) << desc;
740 ASSERT_EQ(dl1->bytes(false), dl2->bytes(false)) << desc;
741 ASSERT_EQ(dl1->op_count(true), dl2->op_count(true)) << desc;
742 ASSERT_EQ(dl1->bytes(true), dl2->bytes(true)) << desc;
743 EXPECT_EQ(dl1->total_depth(), dl2->total_depth()) << desc;
744 ASSERT_EQ(dl1->bounds(), dl2->bounds()) << desc;
745 ASSERT_EQ(dl1->total_depth(), dl2->total_depth()) << desc;
746 ASSERT_TRUE(DisplayListsEQ_Verbose(dl1, dl2)) << desc;
747 ASSERT_TRUE(DisplayListsEQ_Verbose(dl2, dl2)) << desc;
748 ASSERT_EQ(dl1->rtree().get(), nullptr) << desc;
749 ASSERT_NE(dl2->rtree().get(), nullptr) << desc;
750 }
751 }
752}
753
754TEST_F(DisplayListTest, FullRotationsAreNop) {
756 DlOpReceiver& receiver = ToReceiver(builder);
757 receiver.rotate(0);
758 receiver.rotate(360);
759 receiver.rotate(720);
760 receiver.rotate(1080);
761 receiver.rotate(1440);
762 sk_sp<DisplayList> dl = builder.Build();
763 ASSERT_EQ(dl->bytes(false), sizeof(DisplayList));
764 ASSERT_EQ(dl->bytes(true), sizeof(DisplayList));
765 ASSERT_EQ(dl->op_count(false), 0u);
766 ASSERT_EQ(dl->op_count(true), 0u);
767 EXPECT_EQ(dl->total_depth(), 0u);
768}
769
770TEST_F(DisplayListTest, AllBlendModeNops) {
772 DlOpReceiver& receiver = ToReceiver(builder);
774 sk_sp<DisplayList> dl = builder.Build();
775 ASSERT_EQ(dl->bytes(false), sizeof(DisplayList));
776 ASSERT_EQ(dl->bytes(true), sizeof(DisplayList));
777 ASSERT_EQ(dl->op_count(false), 0u);
778 ASSERT_EQ(dl->op_count(true), 0u);
779 EXPECT_EQ(dl->total_depth(), 0u);
780}
781
782TEST_F(DisplayListTest, DisplayListsWithVaryingOpComparisons) {
783 sk_sp<DisplayList> default_dl = Build(allGroups.size(), 0);
784 ASSERT_TRUE(default_dl->Equals(*default_dl)) << "Default == itself";
785 for (size_t gi = 0; gi < allGroups.size(); gi++) {
787 sk_sp<DisplayList> missing_dl = Build(gi, group.variants.size());
788 auto desc = "[Group " + group.op_name + " omitted]";
789 ASSERT_TRUE(missing_dl->Equals(*missing_dl)) << desc << " == itself";
790 ASSERT_FALSE(missing_dl->Equals(*default_dl)) << desc << " != Default";
791 ASSERT_FALSE(default_dl->Equals(*missing_dl)) << "Default != " << desc;
792 for (size_t vi = 0; vi < group.variants.size(); vi++) {
793 auto desc = "[Group " + group.op_name + " variant " +
794 std::to_string(vi + 1) + "]";
795 sk_sp<DisplayList> variant_dl = Build(gi, vi);
796 ASSERT_TRUE(variant_dl->Equals(*variant_dl)) << desc << " == itself";
797 if (vi == 0) {
798 ASSERT_TRUE(variant_dl->Equals(*default_dl)) << desc << " == Default";
799 ASSERT_TRUE(default_dl->Equals(*variant_dl)) << "Default == " << desc;
800 } else {
801 ASSERT_FALSE(variant_dl->Equals(*default_dl)) << desc << " != Default";
802 ASSERT_FALSE(default_dl->Equals(*variant_dl)) << "Default != " << desc;
803 }
804 if (group.variants[vi].is_empty()) {
805 ASSERT_TRUE(variant_dl->Equals(*missing_dl)) << desc << " != omitted";
806 ASSERT_TRUE(missing_dl->Equals(*variant_dl)) << "omitted != " << desc;
807 } else {
808 ASSERT_FALSE(variant_dl->Equals(*missing_dl)) << desc << " != omitted";
809 ASSERT_FALSE(missing_dl->Equals(*variant_dl)) << "omitted != " << desc;
810 }
811 }
812 }
813}
814
815TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) {
816 SkRect build_bounds = SkRect::MakeLTRB(-100, -100, 200, 200);
817 SkRect save_bounds = SkRect::MakeWH(100, 100);
818 SkRect rect = SkRect::MakeLTRB(30, 30, 70, 70);
819 // clang-format off
820 const float color_matrix[] = {
821 0, 0, 0, 0, 0,
822 0, 1, 0, 0, 0,
823 0, 0, 1, 0, 0,
824 0, 0, 0, 1, 0,
825 };
826 // clang-format on
827 DlMatrixColorFilter base_color_filter(color_matrix);
828 // clang-format off
829 const float alpha_matrix[] = {
830 0, 0, 0, 0, 0,
831 0, 1, 0, 0, 0,
832 0, 0, 1, 0, 0,
833 0, 0, 0, 0, 1,
834 };
835 // clang-format on
836 DlMatrixColorFilter alpha_color_filter(alpha_matrix);
837 sk_sp<SkColorFilter> sk_alpha_color_filter =
838 SkColorFilters::Matrix(alpha_matrix);
839
840 {
841 // No tricky stuff, just verifying drawing a rect produces rect bounds
842 DisplayListBuilder builder(build_bounds);
843 DlOpReceiver& receiver = ToReceiver(builder);
844 receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
845 receiver.drawRect(rect);
846 receiver.restore();
847 sk_sp<DisplayList> display_list = builder.Build();
848 ASSERT_EQ(display_list->bounds(), rect);
849 }
850
851 {
852 // Now checking that a normal color filter still produces rect bounds
853 DisplayListBuilder builder(build_bounds);
854 DlOpReceiver& receiver = ToReceiver(builder);
855 receiver.setColorFilter(&base_color_filter);
856 receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
857 receiver.setColorFilter(nullptr);
858 receiver.drawRect(rect);
859 receiver.restore();
860 sk_sp<DisplayList> display_list = builder.Build();
861 ASSERT_EQ(display_list->bounds(), rect);
862 }
863
864 {
865 // Now checking how SkPictureRecorder deals with a color filter
866 // that modifies alpha channels (save layer bounds are meaningless
867 // under those circumstances)
868 SkPictureRecorder recorder;
869 SkRTreeFactory rtree_factory;
870 SkCanvas* canvas = recorder.beginRecording(build_bounds, &rtree_factory);
871 SkPaint p1;
872 p1.setColorFilter(sk_alpha_color_filter);
873 canvas->saveLayer(save_bounds, &p1);
874 SkPaint p2;
875 canvas->drawRect(rect, p2);
876 canvas->restore();
878 ASSERT_EQ(picture->cullRect(), build_bounds);
879 }
880
881 {
882 // Now checking that DisplayList has the same behavior that we
883 // saw in the SkPictureRecorder example above - returning the
884 // cull rect of the DisplayListBuilder when it encounters a
885 // save layer that modifies an unbounded region
886 DisplayListBuilder builder(build_bounds);
887 DlOpReceiver& receiver = ToReceiver(builder);
888 receiver.setColorFilter(&alpha_color_filter);
889 receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
890 receiver.setColorFilter(nullptr);
891 receiver.drawRect(rect);
892 receiver.restore();
893 sk_sp<DisplayList> display_list = builder.Build();
894 ASSERT_EQ(display_list->bounds(), build_bounds);
895 }
896
897 {
898 // Verifying that the save layer bounds are not relevant
899 // to the behavior in the previous example
900 DisplayListBuilder builder(build_bounds);
901 DlOpReceiver& receiver = ToReceiver(builder);
902 receiver.setColorFilter(&alpha_color_filter);
904 receiver.setColorFilter(nullptr);
905 receiver.drawRect(rect);
906 receiver.restore();
907 sk_sp<DisplayList> display_list = builder.Build();
908 ASSERT_EQ(display_list->bounds(), build_bounds);
909 }
910
911 {
912 // Making sure hiding a ColorFilter as an ImageFilter will
913 // generate the same behavior as setting it as a ColorFilter
914 DisplayListBuilder builder(build_bounds);
915 DlOpReceiver& receiver = ToReceiver(builder);
916 DlColorFilterImageFilter color_filter_image_filter(base_color_filter);
917 receiver.setImageFilter(&color_filter_image_filter);
918 receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
919 receiver.setImageFilter(nullptr);
920 receiver.drawRect(rect);
921 receiver.restore();
922 sk_sp<DisplayList> display_list = builder.Build();
923 ASSERT_EQ(display_list->bounds(), rect);
924 }
925
926 {
927 // Making sure hiding a problematic ColorFilter as an ImageFilter
928 // will generate the same behavior as setting it as a ColorFilter
929 DisplayListBuilder builder(build_bounds);
930 DlOpReceiver& receiver = ToReceiver(builder);
931 DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter);
932 receiver.setImageFilter(&color_filter_image_filter);
933 receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
934 receiver.setImageFilter(nullptr);
935 receiver.drawRect(rect);
936 receiver.restore();
937 sk_sp<DisplayList> display_list = builder.Build();
938 ASSERT_EQ(display_list->bounds(), build_bounds);
939 }
940
941 {
942 // Same as above (ImageFilter hiding ColorFilter) with no save bounds
943 DisplayListBuilder builder(build_bounds);
944 DlOpReceiver& receiver = ToReceiver(builder);
945 DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter);
946 receiver.setImageFilter(&color_filter_image_filter);
948 receiver.setImageFilter(nullptr);
949 receiver.drawRect(rect);
950 receiver.restore();
951 sk_sp<DisplayList> display_list = builder.Build();
952 ASSERT_EQ(display_list->bounds(), build_bounds);
953 }
954
955 {
956 // Testing behavior with an unboundable blend mode
957 DisplayListBuilder builder(build_bounds);
958 DlOpReceiver& receiver = ToReceiver(builder);
960 receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
962 receiver.drawRect(rect);
963 receiver.restore();
964 sk_sp<DisplayList> display_list = builder.Build();
965 ASSERT_EQ(display_list->bounds(), build_bounds);
966 }
967
968 {
969 // Same as previous with no save bounds
970 DisplayListBuilder builder(build_bounds);
971 DlOpReceiver& receiver = ToReceiver(builder);
975 receiver.drawRect(rect);
976 receiver.restore();
977 sk_sp<DisplayList> display_list = builder.Build();
978 ASSERT_EQ(display_list->bounds(), build_bounds);
979 }
980}
981
982TEST_F(DisplayListTest, NestedOpCountMetricsSameAsSkPicture) {
983 SkPictureRecorder recorder;
984 recorder.beginRecording(SkRect::MakeWH(150, 100));
985 SkCanvas* canvas = recorder.getRecordingCanvas();
987 for (int y = 10; y <= 60; y += 10) {
988 for (int x = 10; x <= 60; x += 10) {
989 paint.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE);
990 canvas->drawRect(SkRect::MakeXYWH(x, y, 80, 80), paint);
991 }
992 }
993 SkPictureRecorder outer_recorder;
994 outer_recorder.beginRecording(SkRect::MakeWH(150, 100));
995 canvas = outer_recorder.getRecordingCanvas();
996 canvas->drawPicture(recorder.finishRecordingAsPicture());
997
998 auto picture = outer_recorder.finishRecordingAsPicture();
999 ASSERT_EQ(picture->approximateOpCount(), 1);
1000 ASSERT_EQ(picture->approximateOpCount(true), 36);
1001
1003 DlOpReceiver& receiver = ToReceiver(builder);
1004 for (int y = 10; y <= 60; y += 10) {
1005 for (int x = 10; x <= 60; x += 10) {
1006 receiver.setColor(((x + y) % 20) == 10 ? DlColor(SK_ColorRED)
1008 receiver.drawRect(SkRect::MakeXYWH(x, y, 80, 80));
1009 }
1010 }
1011
1012 DisplayListBuilder outer_builder(SkRect::MakeWH(150, 100));
1013 DlOpReceiver& outer_receiver = ToReceiver(outer_builder);
1014 outer_receiver.drawDisplayList(builder.Build());
1015 auto display_list = outer_builder.Build();
1016
1017 ASSERT_EQ(display_list->op_count(), 1u);
1018 ASSERT_EQ(display_list->op_count(true), 36u);
1019 EXPECT_EQ(display_list->total_depth(), 37u);
1020
1021 ASSERT_EQ(picture->approximateOpCount(),
1022 static_cast<int>(display_list->op_count()));
1023 ASSERT_EQ(picture->approximateOpCount(true),
1024 static_cast<int>(display_list->op_count(true)));
1025}
1026
1027TEST_F(DisplayListTest, DisplayListFullPerspectiveTransformHandling) {
1028 // SkM44 constructor takes row-major order
1030 // clang-format off
1031 1, 2, 3, 4,
1032 5, 6, 7, 8,
1033 9, 10, 11, 12,
1034 13, 14, 15, 16
1035 // clang-format on
1036 );
1037
1038 { // First test ==
1040 DlOpReceiver& receiver = ToReceiver(builder);
1041 // receiver.transformFullPerspective takes row-major order
1042 receiver.transformFullPerspective(
1043 // clang-format off
1044 1, 2, 3, 4,
1045 5, 6, 7, 8,
1046 9, 10, 11, 12,
1047 13, 14, 15, 16
1048 // clang-format on
1049 );
1050 sk_sp<DisplayList> display_list = builder.Build();
1053 SkCanvas* canvas = surface->getCanvas();
1054 // We can't use DlSkCanvas.DrawDisplayList as that method protects
1055 // the canvas against mutations from the display list being drawn.
1056 auto dispatcher = DlSkCanvasDispatcher(surface->getCanvas());
1057 display_list->Dispatch(dispatcher);
1058 SkM44 dl_matrix = canvas->getLocalToDevice();
1059 ASSERT_EQ(sk_matrix, dl_matrix);
1060 }
1061 { // Next test !=
1063 DlOpReceiver& receiver = ToReceiver(builder);
1064 // receiver.transformFullPerspective takes row-major order
1065 receiver.transformFullPerspective(
1066 // clang-format off
1067 1, 5, 9, 13,
1068 2, 6, 7, 11,
1069 3, 7, 11, 15,
1070 4, 8, 12, 16
1071 // clang-format on
1072 );
1073 sk_sp<DisplayList> display_list = builder.Build();
1076 SkCanvas* canvas = surface->getCanvas();
1077 // We can't use DlSkCanvas.DrawDisplayList as that method protects
1078 // the canvas against mutations from the display list being drawn.
1079 auto dispatcher = DlSkCanvasDispatcher(surface->getCanvas());
1080 display_list->Dispatch(dispatcher);
1081 SkM44 dl_matrix = canvas->getLocalToDevice();
1082 ASSERT_NE(sk_matrix, dl_matrix);
1083 }
1084}
1085
1086TEST_F(DisplayListTest, DisplayListTransformResetHandling) {
1088 DlOpReceiver& receiver = ToReceiver(builder);
1089 receiver.scale(20.0, 20.0);
1090 receiver.transformReset();
1091 auto display_list = builder.Build();
1092 ASSERT_NE(display_list, nullptr);
1095 SkCanvas* canvas = surface->getCanvas();
1096 // We can't use DlSkCanvas.DrawDisplayList as that method protects
1097 // the canvas against mutations from the display list being drawn.
1098 auto dispatcher = DlSkCanvasDispatcher(surface->getCanvas());
1099 display_list->Dispatch(dispatcher);
1100 ASSERT_TRUE(canvas->getTotalMatrix().isIdentity());
1101}
1102
1103TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) {
1104 auto run_tests = [](const std::string& name,
1105 void build(DlOpReceiver & receiver), bool expect_for_op,
1106 bool expect_with_kSrc) {
1107 {
1108 // First test is the draw op, by itself
1109 // (usually supports group opacity)
1111 DlOpReceiver& receiver = ToReceiver(builder);
1112 build(receiver);
1113 auto display_list = builder.Build();
1114 EXPECT_EQ(display_list->can_apply_group_opacity(), expect_for_op)
1115 << "{" << std::endl
1116 << " " << name << std::endl
1117 << "}";
1118 }
1119 {
1120 // Second test i the draw op with kSrc,
1121 // (usually fails group opacity)
1123 DlOpReceiver& receiver = ToReceiver(builder);
1125 build(receiver);
1126 auto display_list = builder.Build();
1127 EXPECT_EQ(display_list->can_apply_group_opacity(), expect_with_kSrc)
1128 << "{" << std::endl
1129 << " receiver.setBlendMode(kSrc);" << std::endl
1130 << " " << name << std::endl
1131 << "}";
1132 }
1133 };
1134
1135#define RUN_TESTS(body) \
1136 run_tests(#body, [](DlOpReceiver& receiver) { body }, true, false)
1137#define RUN_TESTS2(body, expect) \
1138 run_tests(#body, [](DlOpReceiver& receiver) { body }, expect, expect)
1139
1140 RUN_TESTS(receiver.drawPaint(););
1142 , true);
1144 , false);
1145 RUN_TESTS(receiver.drawLine({0, 0}, {10, 10}););
1146 RUN_TESTS(receiver.drawRect({0, 0, 10, 10}););
1147 RUN_TESTS(receiver.drawOval({0, 0, 10, 10}););
1148 RUN_TESTS(receiver.drawCircle({10, 10}, 5););
1149 RUN_TESTS(receiver.drawRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2)););
1150 RUN_TESTS(receiver.drawDRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2),
1151 SkRRect::MakeRectXY({2, 2, 8, 8}, 2, 2)););
1152 RUN_TESTS(receiver.drawPath(
1153 SkPath().addOval({0, 0, 10, 10}).addOval({5, 5, 15, 15})););
1154 RUN_TESTS(receiver.drawArc({0, 0, 10, 10}, 0, math::kPi, true););
1155 RUN_TESTS2(
1157 , false);
1159 , false);
1160 RUN_TESTS(receiver.drawImage(TestImage1, {0, 0}, kLinearSampling, true););
1161 RUN_TESTS2(receiver.drawImage(TestImage1, {0, 0}, kLinearSampling, false);
1162 , true);
1163 RUN_TESTS(receiver.drawImageRect(TestImage1, {10, 10, 20, 20}, {0, 0, 10, 10},
1164 kNearestSampling, true,
1166 RUN_TESTS2(receiver.drawImageRect(TestImage1, {10, 10, 20, 20},
1167 {0, 0, 10, 10}, kNearestSampling, false,
1169 , true);
1170 RUN_TESTS(receiver.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20},
1171 DlFilterMode::kLinear, true););
1172 RUN_TESTS2(
1173 receiver.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20},
1174 DlFilterMode::kLinear, false);
1175 , true);
1176 static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
1177 static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
1178 RUN_TESTS2(
1179 receiver.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
1180 DlBlendMode::kSrcIn, kNearestSampling, nullptr, true);
1181 , false);
1182 RUN_TESTS2(
1183 receiver.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
1184 DlBlendMode::kSrcIn, kNearestSampling, nullptr, false);
1185 , false);
1186 EXPECT_TRUE(TestDisplayList1->can_apply_group_opacity());
1188 {
1190 builder.DrawRect({0, 0, 10, 10}, DlPaint());
1191 builder.DrawRect({5, 5, 15, 15}, DlPaint());
1192 static auto display_list = builder.Build();
1193 RUN_TESTS2(receiver.drawDisplayList(display_list);, false);
1194 }
1195 RUN_TESTS2(receiver.drawTextBlob(GetTestTextBlob(1), 0, 0);, false);
1196 RUN_TESTS2(
1197 receiver.drawShadow(kTestPath1, DlColor(SK_ColorBLACK), 1.0, false, 1.0);
1198 , false);
1199
1200#undef RUN_TESTS2
1201#undef RUN_TESTS
1202}
1203
1204TEST_F(DisplayListTest, OverlappingOpsDoNotSupportGroupOpacity) {
1206 for (int i = 0; i < 10; i++) {
1207 builder.DrawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint());
1208 }
1209 auto display_list = builder.Build();
1210 EXPECT_FALSE(display_list->can_apply_group_opacity());
1211}
1212
1213TEST_F(DisplayListTest, LineOfNonOverlappingOpsSupportGroupOpacity) {
1215 for (int i = 0; i < 10; i++) {
1216 builder.DrawRect(SkRect::MakeXYWH(i * 30, 0, 30, 30), DlPaint());
1217 }
1218 auto display_list = builder.Build();
1219 EXPECT_TRUE(display_list->can_apply_group_opacity());
1220}
1221
1222TEST_F(DisplayListTest, CrossOfNonOverlappingOpsSupportGroupOpacity) {
1224 builder.DrawRect(SkRect::MakeLTRB(200, 200, 300, 300), DlPaint()); // center
1225 builder.DrawRect(SkRect::MakeLTRB(100, 200, 200, 300), DlPaint()); // left
1226 builder.DrawRect(SkRect::MakeLTRB(200, 100, 300, 200), DlPaint()); // above
1227 builder.DrawRect(SkRect::MakeLTRB(300, 200, 400, 300), DlPaint()); // right
1228 builder.DrawRect(SkRect::MakeLTRB(200, 300, 300, 400), DlPaint()); // below
1229 auto display_list = builder.Build();
1230 EXPECT_TRUE(display_list->can_apply_group_opacity());
1231}
1232
1233TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityOverlappingChidren) {
1235 DlOpReceiver& receiver = ToReceiver(builder);
1236 receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
1237 for (int i = 0; i < 10; i++) {
1238 receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30));
1239 }
1240 receiver.restore();
1241 auto display_list = builder.Build();
1242 EXPECT_TRUE(display_list->can_apply_group_opacity());
1243}
1244
1245TEST_F(DisplayListTest, SaveLayerTrueSupportsGroupOpacityOverlappingChidren) {
1247 DlOpReceiver& receiver = ToReceiver(builder);
1249 for (int i = 0; i < 10; i++) {
1250 receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30));
1251 }
1252 receiver.restore();
1253 auto display_list = builder.Build();
1254 EXPECT_TRUE(display_list->can_apply_group_opacity());
1255}
1256
1257TEST_F(DisplayListTest, SaveLayerFalseWithSrcBlendSupportsGroupOpacity) {
1259 DlOpReceiver& receiver = ToReceiver(builder);
1261 receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
1262 receiver.drawRect({0, 0, 10, 10});
1263 receiver.restore();
1264 auto display_list = builder.Build();
1265 EXPECT_TRUE(display_list->can_apply_group_opacity());
1266}
1267
1268TEST_F(DisplayListTest, SaveLayerTrueWithSrcBlendDoesNotSupportGroupOpacity) {
1270 DlOpReceiver& receiver = ToReceiver(builder);
1273 receiver.drawRect({0, 0, 10, 10});
1274 receiver.restore();
1275 auto display_list = builder.Build();
1276 EXPECT_FALSE(display_list->can_apply_group_opacity());
1277}
1278
1279TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityWithChildSrcBlend) {
1281 DlOpReceiver& receiver = ToReceiver(builder);
1282 receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
1284 receiver.drawRect({0, 0, 10, 10});
1285 receiver.restore();
1286 auto display_list = builder.Build();
1287 EXPECT_TRUE(display_list->can_apply_group_opacity());
1288}
1289
1290TEST_F(DisplayListTest, SaveLayerTrueSupportsGroupOpacityWithChildSrcBlend) {
1292 DlOpReceiver& receiver = ToReceiver(builder);
1295 receiver.drawRect({0, 0, 10, 10});
1296 receiver.restore();
1297 auto display_list = builder.Build();
1298 EXPECT_TRUE(display_list->can_apply_group_opacity());
1299}
1300
1301TEST_F(DisplayListTest, SaveLayerBoundsSnapshotsImageFilter) {
1303 DlOpReceiver& receiver = ToReceiver(builder);
1305 receiver.drawRect({50, 50, 100, 100});
1306 // This image filter should be ignored since it was not set before saveLayer
1308 receiver.restore();
1309 SkRect bounds = builder.Build()->bounds();
1310 EXPECT_EQ(bounds, SkRect::MakeLTRB(50, 50, 100, 100));
1311}
1312
1313class SaveLayerExpector : public virtual DlOpReceiver,
1318 public:
1320 // NOLINTNEXTLINE(google-explicit-constructor)
1322 // NOLINTNEXTLINE(google-explicit-constructor)
1324
1325 std::optional<SaveLayerOptions> options;
1326 std::optional<DlBlendMode> max_blend_mode;
1327 };
1328
1329 explicit SaveLayerExpector(const Expectations& expected) {
1330 expected_.push_back(expected);
1331 }
1332
1333 explicit SaveLayerExpector(std::vector<Expectations> expected)
1334 : expected_(std::move(expected)) {}
1335
1338 const DlImageFilter* backdrop) override {
1340 }
1341
1342 virtual void saveLayer(const SkRect& bounds,
1344 uint32_t total_content_depth,
1345 DlBlendMode max_content_blend_mode,
1346 const DlImageFilter* backdrop = nullptr) {
1347 auto label = "index " + std::to_string(save_layer_count_);
1348 ASSERT_LT(save_layer_count_, expected_.size());
1349 auto expect = expected_[save_layer_count_++];
1350 if (expect.options.has_value()) {
1351 EXPECT_EQ(options, expect.options.value()) << label;
1352 }
1353 if (expect.max_blend_mode.has_value()) {
1354 EXPECT_EQ(max_content_blend_mode, expect.max_blend_mode.value()) << label;
1355 }
1356 }
1357
1359 return save_layer_count_ == expected_.size();
1360 }
1361
1362 private:
1363 std::vector<Expectations> expected_;
1364 size_t save_layer_count_ = 0;
1365};
1366
1367TEST_F(DisplayListTest, SaveLayerOneSimpleOpInheritsOpacity) {
1368 SaveLayerOptions expected =
1370 SaveLayerExpector expector(expected);
1371
1373 DlOpReceiver& receiver = ToReceiver(builder);
1374 receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255)));
1376 receiver.drawRect({10, 10, 20, 20});
1377 receiver.restore();
1378
1379 builder.Build()->Dispatch(expector);
1381}
1382
1383TEST_F(DisplayListTest, SaveLayerNoAttributesInheritsOpacity) {
1384 SaveLayerOptions expected =
1386 SaveLayerExpector expector(expected);
1387
1389 DlOpReceiver& receiver = ToReceiver(builder);
1390 receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
1391 receiver.drawRect({10, 10, 20, 20});
1392 receiver.restore();
1393
1394 builder.Build()->Dispatch(expector);
1396}
1397
1398TEST_F(DisplayListTest, SaveLayerTwoOverlappingOpsDoesNotInheritOpacity) {
1400 SaveLayerExpector expector(expected);
1401
1403 DlOpReceiver& receiver = ToReceiver(builder);
1404 receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255)));
1406 receiver.drawRect({10, 10, 20, 20});
1407 receiver.drawRect({15, 15, 25, 25});
1408 receiver.restore();
1409
1410 builder.Build()->Dispatch(expector);
1412}
1413
1414TEST_F(DisplayListTest, NestedSaveLayersMightInheritOpacity) {
1415 SaveLayerOptions expected1 =
1418 SaveLayerOptions expected3 =
1420 SaveLayerExpector expector({expected1, expected2, expected3});
1421
1423 DlOpReceiver& receiver = ToReceiver(builder);
1424 receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255)));
1427 receiver.drawRect({10, 10, 20, 20});
1429 receiver.drawRect({15, 15, 25, 25});
1430 receiver.restore();
1431 receiver.restore();
1432 receiver.restore();
1433
1434 builder.Build()->Dispatch(expector);
1435 EXPECT_TRUE(expector.all_expectations_checked());
1436}
1437
1438TEST_F(DisplayListTest, NestedSaveLayersCanBothSupportOpacityOptimization) {
1439 SaveLayerOptions expected1 =
1441 SaveLayerOptions expected2 =
1443 SaveLayerExpector expector({expected1, expected2});
1444
1446 DlOpReceiver& receiver = ToReceiver(builder);
1447 receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255)));
1449 receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
1450 receiver.drawRect({10, 10, 20, 20});
1451 receiver.restore();
1452 receiver.restore();
1453
1454 builder.Build()->Dispatch(expector);
1455 EXPECT_TRUE(expector.all_expectations_checked());
1456}
1457
1458TEST_F(DisplayListTest, SaveLayerImageFilterDoesNotInheritOpacity) {
1460 SaveLayerExpector expector(expected);
1461
1463 DlOpReceiver& receiver = ToReceiver(builder);
1464 receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255)));
1467 receiver.setImageFilter(nullptr);
1468 receiver.drawRect({10, 10, 20, 20});
1469 receiver.restore();
1470
1471 builder.Build()->Dispatch(expector);
1473}
1474
1475TEST_F(DisplayListTest, SaveLayerColorFilterDoesNotInheritOpacity) {
1477 SaveLayerExpector expector(expected);
1478
1480 DlOpReceiver& receiver = ToReceiver(builder);
1481 receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255)));
1484 receiver.setColorFilter(nullptr);
1485 receiver.drawRect({10, 10, 20, 20});
1486 receiver.restore();
1487
1488 builder.Build()->Dispatch(expector);
1490}
1491
1492TEST_F(DisplayListTest, SaveLayerSrcBlendDoesNotInheritOpacity) {
1494 SaveLayerExpector expector(expected);
1495
1497 DlOpReceiver& receiver = ToReceiver(builder);
1498 receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255)));
1502 receiver.drawRect({10, 10, 20, 20});
1503 receiver.restore();
1504
1505 builder.Build()->Dispatch(expector);
1507}
1508
1509TEST_F(DisplayListTest, SaveLayerImageFilterOnChildInheritsOpacity) {
1510 SaveLayerOptions expected =
1512 SaveLayerExpector expector(expected);
1513
1515 DlOpReceiver& receiver = ToReceiver(builder);
1516 receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255)));
1519 receiver.drawRect({10, 10, 20, 20});
1520 receiver.restore();
1521
1522 builder.Build()->Dispatch(expector);
1524}
1525
1526TEST_F(DisplayListTest, SaveLayerColorFilterOnChildDoesNotInheritOpacity) {
1528 SaveLayerExpector expector(expected);
1529
1531 DlOpReceiver& receiver = ToReceiver(builder);
1532 receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255)));
1535 receiver.drawRect({10, 10, 20, 20});
1536 receiver.restore();
1537
1538 builder.Build()->Dispatch(expector);
1540}
1541
1542TEST_F(DisplayListTest, SaveLayerSrcBlendOnChildDoesNotInheritOpacity) {
1544 SaveLayerExpector expector(expected);
1545
1547 DlOpReceiver& receiver = ToReceiver(builder);
1548 receiver.setColor(DlColor(SkColorSetARGB(127, 255, 255, 255)));
1551 receiver.drawRect({10, 10, 20, 20});
1552 receiver.restore();
1553
1554 builder.Build()->Dispatch(expector);
1556}
1557
1558TEST_F(DisplayListTest, FlutterSvgIssue661BoundsWereEmpty) {
1559 // See https://github.com/dnfield/flutter_svg/issues/661
1560
1561 SkPath path1;
1563 path1.moveTo(25.54f, 37.52f);
1564 path1.cubicTo(20.91f, 37.52f, 16.54f, 33.39f, 13.62f, 30.58f);
1565 path1.lineTo(13, 30);
1566 path1.lineTo(12.45f, 29.42f);
1567 path1.cubicTo(8.39f, 25.15f, 1.61f, 18, 8.37f, 11.27f);
1568 path1.cubicTo(10.18f, 9.46f, 12.37f, 9.58f, 14.49f, 11.58f);
1569 path1.cubicTo(15.67f, 12.71f, 17.05f, 14.69f, 17.07f, 16.58f);
1570 path1.cubicTo(17.0968f, 17.458f, 16.7603f, 18.3081f, 16.14f, 18.93f);
1571 path1.cubicTo(15.8168f, 19.239f, 15.4653f, 19.5169f, 15.09f, 19.76f);
1572 path1.cubicTo(14.27f, 20.33f, 14.21f, 20.44f, 14.27f, 20.62f);
1573 path1.cubicTo(15.1672f, 22.3493f, 16.3239f, 23.9309f, 17.7f, 25.31f);
1574 path1.cubicTo(19.0791f, 26.6861f, 20.6607f, 27.8428f, 22.39f, 28.74f);
1575 path1.cubicTo(22.57f, 28.8f, 22.69f, 28.74f, 23.25f, 27.92f);
1576 path1.cubicTo(23.5f, 27.566f, 23.778f, 27.231f, 24.08f, 26.92f);
1577 path1.cubicTo(24.7045f, 26.3048f, 25.5538f, 25.9723f, 26.43f, 26);
1578 path1.cubicTo(28.29f, 26, 30.27f, 27.4f, 31.43f, 28.58f);
1579 path1.cubicTo(33.43f, 30.67f, 33.55f, 32.9f, 31.74f, 34.7f);
1580 path1.cubicTo(30.1477f, 36.4508f, 27.906f, 37.4704f, 25.54f, 37.52f);
1581 path1.close();
1582 path1.moveTo(11.17f, 12.23f);
1583 path1.cubicTo(10.6946f, 12.2571f, 10.2522f, 12.4819f, 9.95f, 12.85f);
1584 path1.cubicTo(5.12f, 17.67f, 8.95f, 22.5f, 14.05f, 27.85f);
1585 path1.lineTo(14.62f, 28.45f);
1586 path1.lineTo(15.16f, 28.96f);
1587 path1.cubicTo(20.52f, 34.06f, 25.35f, 37.89f, 30.16f, 33.06f);
1588 path1.cubicTo(30.83f, 32.39f, 31.25f, 31.56f, 29.81f, 30.06f);
1589 path1.cubicTo(28.9247f, 29.07f, 27.7359f, 28.4018f, 26.43f, 28.16f);
1590 path1.cubicTo(26.1476f, 28.1284f, 25.8676f, 28.2367f, 25.68f, 28.45f);
1591 path1.cubicTo(25.4633f, 28.6774f, 25.269f, 28.9252f, 25.1f, 29.19f);
1592 path1.cubicTo(24.53f, 30.01f, 23.47f, 31.54f, 21.54f, 30.79f);
1593 path1.lineTo(21.41f, 30.72f);
1594 path1.cubicTo(19.4601f, 29.7156f, 17.6787f, 28.4133f, 16.13f, 26.86f);
1595 path1.cubicTo(14.5748f, 25.3106f, 13.2693f, 23.5295f, 12.26f, 21.58f);
1596 path1.lineTo(12.2f, 21.44f);
1597 path1.cubicTo(11.45f, 19.51f, 12.97f, 18.44f, 13.8f, 17.88f);
1598 path1.cubicTo(14.061f, 17.706f, 14.308f, 17.512f, 14.54f, 17.3f);
1599 path1.cubicTo(14.7379f, 17.1067f, 14.8404f, 16.8359f, 14.82f, 16.56f);
1600 path1.cubicTo(14.5978f, 15.268f, 13.9585f, 14.0843f, 13, 13.19f);
1601 path1.cubicTo(12.5398f, 12.642f, 11.8824f, 12.2971f, 11.17f, 12.23f);
1602 path1.lineTo(11.17f, 12.23f);
1603 path1.close();
1604 path1.moveTo(27, 19.34f);
1605 path1.lineTo(24.74f, 19.34f);
1606 path1.cubicTo(24.7319f, 18.758f, 24.262f, 18.2881f, 23.68f, 18.28f);
1607 path1.lineTo(23.68f, 16.05f);
1608 path1.lineTo(23.7f, 16.05f);
1609 path1.cubicTo(25.5153f, 16.0582f, 26.9863f, 17.5248f, 27, 19.34f);
1610 path1.lineTo(27, 19.34f);
1611 path1.close();
1612 path1.moveTo(32.3f, 19.34f);
1613 path1.lineTo(30.07f, 19.34f);
1614 path1.cubicTo(30.037f, 15.859f, 27.171f, 13.011f, 23.69f, 13);
1615 path1.lineTo(23.69f, 10.72f);
1616 path1.cubicTo(28.415f, 10.725f, 32.3f, 14.615f, 32.3f, 19.34f);
1617 path1.close();
1618
1619 SkPath path2;
1621 path2.moveTo(37.5f, 19.33f);
1622 path2.lineTo(35.27f, 19.33f);
1623 path2.cubicTo(35.265f, 12.979f, 30.041f, 7.755f, 23.69f, 7.75f);
1624 path2.lineTo(23.69f, 5.52f);
1625 path2.cubicTo(31.264f, 5.525f, 37.495f, 11.756f, 37.5f, 19.33f);
1626 path2.close();
1627
1630 {
1631 builder.Save();
1632 builder.ClipRect({0, 0, 100, 100}, ClipOp::kIntersect, true);
1633 {
1634 builder.Save();
1635 builder.Transform2DAffine(2.17391, 0, -2547.83, //
1636 0, 2.04082, -500);
1637 {
1638 builder.Save();
1639 builder.ClipRect({1172, 245, 1218, 294}, ClipOp::kIntersect, true);
1640 {
1641 builder.SaveLayer(nullptr, nullptr, nullptr);
1642 {
1643 builder.Save();
1644 builder.Transform2DAffine(1.4375, 0, 1164.09, //
1645 0, 1.53125, 236.548);
1646 builder.DrawPath(path1, paint);
1647 builder.Restore();
1648 }
1649 {
1650 builder.Save();
1651 builder.Transform2DAffine(1.4375, 0, 1164.09, //
1652 0, 1.53125, 236.548);
1653 builder.DrawPath(path2, paint);
1654 builder.Restore();
1655 }
1656 builder.Restore();
1657 }
1658 builder.Restore();
1659 }
1660 builder.Restore();
1661 }
1662 builder.Restore();
1663 }
1664 sk_sp<DisplayList> display_list = builder.Build();
1665 // Prior to the fix, the bounds were empty.
1666 EXPECT_FALSE(display_list->bounds().isEmpty());
1667 // These are just inside and outside of the expected bounds, but
1668 // testing float values can be flaky wrt minor changes in the bounds
1669 // calculations. If these lines have to be revised too often as the DL
1670 // implementation is improved and maintained, then we can eliminate
1671 // this test and just rely on the "rounded out" bounds test that follows.
1672 SkRect min_bounds = SkRect::MakeLTRB(0, 0.00191, 99.983, 100);
1673 SkRect max_bounds = SkRect::MakeLTRB(0, 0.00189, 99.985, 100);
1674 ASSERT_TRUE(max_bounds.contains(min_bounds));
1675 EXPECT_TRUE(max_bounds.contains(display_list->bounds()));
1676 EXPECT_TRUE(display_list->bounds().contains(min_bounds));
1677
1678 // This is the more practical result. The bounds are "almost" 0,0,100x100
1679 EXPECT_EQ(display_list->bounds().roundOut(), SkIRect::MakeWH(100, 100));
1680 EXPECT_EQ(display_list->op_count(), 19u);
1681 EXPECT_EQ(display_list->bytes(), sizeof(DisplayList) + 408u);
1682 EXPECT_EQ(display_list->total_depth(), 3u);
1683}
1684
1685TEST_F(DisplayListTest, TranslateAffectsCurrentTransform) {
1687 DlOpReceiver& receiver = ToReceiver(builder);
1688 receiver.translate(12.3, 14.5);
1689 SkMatrix matrix = SkMatrix::Translate(12.3, 14.5);
1690 SkM44 m44 = SkM44(matrix);
1691 SkM44 cur_m44 = builder.GetTransformFullPerspective();
1692 SkMatrix cur_matrix = builder.GetTransform();
1693 ASSERT_EQ(cur_m44, m44);
1694 ASSERT_EQ(cur_matrix, matrix);
1695 receiver.translate(10, 10);
1696 // CurrentTransform has changed
1697 ASSERT_NE(builder.GetTransformFullPerspective(), m44);
1698 ASSERT_NE(builder.GetTransform(), cur_matrix);
1699 // Previous return values have not
1700 ASSERT_EQ(cur_m44, m44);
1701 ASSERT_EQ(cur_matrix, matrix);
1702}
1703
1704TEST_F(DisplayListTest, ScaleAffectsCurrentTransform) {
1706 DlOpReceiver& receiver = ToReceiver(builder);
1707 receiver.scale(12.3, 14.5);
1708 SkMatrix matrix = SkMatrix::Scale(12.3, 14.5);
1709 SkM44 m44 = SkM44(matrix);
1710 SkM44 cur_m44 = builder.GetTransformFullPerspective();
1711 SkMatrix cur_matrix = builder.GetTransform();
1712 ASSERT_EQ(cur_m44, m44);
1713 ASSERT_EQ(cur_matrix, matrix);
1714 receiver.translate(10, 10);
1715 // CurrentTransform has changed
1716 ASSERT_NE(builder.GetTransformFullPerspective(), m44);
1717 ASSERT_NE(builder.GetTransform(), cur_matrix);
1718 // Previous return values have not
1719 ASSERT_EQ(cur_m44, m44);
1720 ASSERT_EQ(cur_matrix, matrix);
1721}
1722
1723TEST_F(DisplayListTest, RotateAffectsCurrentTransform) {
1725 DlOpReceiver& receiver = ToReceiver(builder);
1726 receiver.rotate(12.3);
1728 SkM44 m44 = SkM44(matrix);
1729 SkM44 cur_m44 = builder.GetTransformFullPerspective();
1730 SkMatrix cur_matrix = builder.GetTransform();
1731 ASSERT_EQ(cur_m44, m44);
1732 ASSERT_EQ(cur_matrix, matrix);
1733 receiver.translate(10, 10);
1734 // CurrentTransform has changed
1735 ASSERT_NE(builder.GetTransformFullPerspective(), m44);
1736 ASSERT_NE(builder.GetTransform(), cur_matrix);
1737 // Previous return values have not
1738 ASSERT_EQ(cur_m44, m44);
1739 ASSERT_EQ(cur_matrix, matrix);
1740}
1741
1742TEST_F(DisplayListTest, SkewAffectsCurrentTransform) {
1744 DlOpReceiver& receiver = ToReceiver(builder);
1745 receiver.skew(12.3, 14.5);
1746 SkMatrix matrix = SkMatrix::Skew(12.3, 14.5);
1747 SkM44 m44 = SkM44(matrix);
1748 SkM44 cur_m44 = builder.GetTransformFullPerspective();
1749 SkMatrix cur_matrix = builder.GetTransform();
1750 ASSERT_EQ(cur_m44, m44);
1751 ASSERT_EQ(cur_matrix, matrix);
1752 receiver.translate(10, 10);
1753 // CurrentTransform has changed
1754 ASSERT_NE(builder.GetTransformFullPerspective(), m44);
1755 ASSERT_NE(builder.GetTransform(), cur_matrix);
1756 // Previous return values have not
1757 ASSERT_EQ(cur_m44, m44);
1758 ASSERT_EQ(cur_matrix, matrix);
1759}
1760
1761TEST_F(DisplayListTest, TransformAffectsCurrentTransform) {
1763 DlOpReceiver& receiver = ToReceiver(builder);
1764 receiver.transform2DAffine(3, 0, 12.3, //
1765 1, 5, 14.5);
1766 SkMatrix matrix = SkMatrix::MakeAll(3, 0, 12.3, //
1767 1, 5, 14.5, //
1768 0, 0, 1);
1769 SkM44 m44 = SkM44(matrix);
1770 SkM44 cur_m44 = builder.GetTransformFullPerspective();
1771 SkMatrix cur_matrix = builder.GetTransform();
1772 ASSERT_EQ(cur_m44, m44);
1773 ASSERT_EQ(cur_matrix, matrix);
1774 receiver.translate(10, 10);
1775 // CurrentTransform has changed
1776 ASSERT_NE(builder.GetTransformFullPerspective(), m44);
1777 ASSERT_NE(builder.GetTransform(), cur_matrix);
1778 // Previous return values have not
1779 ASSERT_EQ(cur_m44, m44);
1780 ASSERT_EQ(cur_matrix, matrix);
1781}
1782
1783TEST_F(DisplayListTest, FullTransformAffectsCurrentTransform) {
1785 DlOpReceiver& receiver = ToReceiver(builder);
1786 receiver.transformFullPerspective(3, 0, 4, 12.3, //
1787 1, 5, 3, 14.5, //
1788 0, 0, 7, 16.2, //
1789 0, 0, 0, 1);
1790 SkMatrix matrix = SkMatrix::MakeAll(3, 0, 12.3, //
1791 1, 5, 14.5, //
1792 0, 0, 1);
1793 SkM44 m44 = SkM44(3, 0, 4, 12.3, //
1794 1, 5, 3, 14.5, //
1795 0, 0, 7, 16.2, //
1796 0, 0, 0, 1);
1797 SkM44 cur_m44 = builder.GetTransformFullPerspective();
1798 SkMatrix cur_matrix = builder.GetTransform();
1799 ASSERT_EQ(cur_m44, m44);
1800 ASSERT_EQ(cur_matrix, matrix);
1801 receiver.translate(10, 10);
1802 // CurrentTransform has changed
1803 ASSERT_NE(builder.GetTransformFullPerspective(), m44);
1804 ASSERT_NE(builder.GetTransform(), cur_matrix);
1805 // Previous return values have not
1806 ASSERT_EQ(cur_m44, m44);
1807 ASSERT_EQ(cur_matrix, matrix);
1808}
1809
1810TEST_F(DisplayListTest, ClipRectAffectsClipBounds) {
1812 DlOpReceiver& receiver = ToReceiver(builder);
1813 SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
1814 receiver.clipRect(clip_bounds, ClipOp::kIntersect, false);
1815
1816 // Save initial return values for testing restored values
1817 SkRect initial_local_bounds = builder.GetLocalClipBounds();
1818 SkRect initial_destination_bounds = builder.GetDestinationClipBounds();
1819 ASSERT_EQ(initial_local_bounds, clip_bounds);
1820 ASSERT_EQ(initial_destination_bounds, clip_bounds);
1821
1822 receiver.save();
1823 receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false);
1824 // Both clip bounds have changed
1825 ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds);
1826 ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds);
1827 // Previous return values have not changed
1828 ASSERT_EQ(initial_local_bounds, clip_bounds);
1829 ASSERT_EQ(initial_destination_bounds, clip_bounds);
1830 receiver.restore();
1831
1832 // save/restore returned the values to their original values
1833 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
1834 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
1835
1836 receiver.save();
1837 receiver.scale(2, 2);
1838 SkRect scaled_clip_bounds = SkRect::MakeLTRB(5.1, 5.65, 10.2, 12.85);
1839 ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds);
1840 // Destination bounds are unaffected by transform
1841 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
1842 receiver.restore();
1843
1844 // save/restore returned the values to their original values
1845 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
1846 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
1847}
1848
1849TEST_F(DisplayListTest, ClipRectDoAAAffectsClipBounds) {
1851 DlOpReceiver& receiver = ToReceiver(builder);
1852 SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
1853 SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26);
1854 receiver.clipRect(clip_bounds, ClipOp::kIntersect, true);
1855
1856 // Save initial return values for testing restored values
1857 SkRect initial_local_bounds = builder.GetLocalClipBounds();
1858 SkRect initial_destination_bounds = builder.GetDestinationClipBounds();
1859 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
1860 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
1861
1862 receiver.save();
1863 receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true);
1864 // Both clip bounds have changed
1865 ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds);
1866 ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds);
1867 // Previous return values have not changed
1868 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
1869 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
1870 receiver.restore();
1871
1872 // save/restore returned the values to their original values
1873 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
1874 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
1875
1876 receiver.save();
1877 receiver.scale(2, 2);
1878 SkRect scaled_expanded_bounds = SkRect::MakeLTRB(5, 5.5, 10.5, 13);
1879 ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds);
1880 // Destination bounds are unaffected by transform
1881 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds);
1882 receiver.restore();
1883
1884 // save/restore returned the values to their original values
1885 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
1886 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
1887}
1888
1889TEST_F(DisplayListTest, ClipRectAffectsClipBoundsWithMatrix) {
1891 DlOpReceiver& receiver = ToReceiver(builder);
1892 SkRect clip_bounds_1 = SkRect::MakeLTRB(0, 0, 10, 10);
1893 SkRect clip_bounds_2 = SkRect::MakeLTRB(10, 10, 20, 20);
1894 receiver.save();
1895 receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false);
1896 receiver.translate(10, 0);
1897 receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false);
1898 ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty());
1899 receiver.restore();
1900
1901 receiver.save();
1902 receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false);
1903 receiver.translate(-10, -10);
1904 receiver.clipRect(clip_bounds_2, ClipOp::kIntersect, false);
1905 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds_1);
1906 receiver.restore();
1907}
1908
1909TEST_F(DisplayListTest, ClipRRectAffectsClipBounds) {
1911 DlOpReceiver& receiver = ToReceiver(builder);
1912 SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
1913 SkRRect clip = SkRRect::MakeRectXY(clip_bounds, 3, 2);
1914 receiver.clipRRect(clip, ClipOp::kIntersect, false);
1915
1916 // Save initial return values for testing restored values
1917 SkRect initial_local_bounds = builder.GetLocalClipBounds();
1918 SkRect initial_destination_bounds = builder.GetDestinationClipBounds();
1919 ASSERT_EQ(initial_local_bounds, clip_bounds);
1920 ASSERT_EQ(initial_destination_bounds, clip_bounds);
1921
1922 receiver.save();
1923 receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false);
1924 // Both clip bounds have changed
1925 ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds);
1926 ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds);
1927 // Previous return values have not changed
1928 ASSERT_EQ(initial_local_bounds, clip_bounds);
1929 ASSERT_EQ(initial_destination_bounds, clip_bounds);
1930 receiver.restore();
1931
1932 // save/restore returned the values to their original values
1933 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
1934 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
1935
1936 receiver.save();
1937 receiver.scale(2, 2);
1938 SkRect scaled_clip_bounds = SkRect::MakeLTRB(5.1, 5.65, 10.2, 12.85);
1939 ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds);
1940 // Destination bounds are unaffected by transform
1941 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
1942 receiver.restore();
1943
1944 // save/restore returned the values to their original values
1945 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
1946 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
1947}
1948
1949TEST_F(DisplayListTest, ClipRRectDoAAAffectsClipBounds) {
1951 DlOpReceiver& receiver = ToReceiver(builder);
1952 SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
1953 SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26);
1954 SkRRect clip = SkRRect::MakeRectXY(clip_bounds, 3, 2);
1955 receiver.clipRRect(clip, ClipOp::kIntersect, true);
1956
1957 // Save initial return values for testing restored values
1958 SkRect initial_local_bounds = builder.GetLocalClipBounds();
1959 SkRect initial_destination_bounds = builder.GetDestinationClipBounds();
1960 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
1961 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
1962
1963 receiver.save();
1964 receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true);
1965 // Both clip bounds have changed
1966 ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds);
1967 ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds);
1968 // Previous return values have not changed
1969 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
1970 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
1971 receiver.restore();
1972
1973 // save/restore returned the values to their original values
1974 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
1975 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
1976
1977 receiver.save();
1978 receiver.scale(2, 2);
1979 SkRect scaled_expanded_bounds = SkRect::MakeLTRB(5, 5.5, 10.5, 13);
1980 ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds);
1981 // Destination bounds are unaffected by transform
1982 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds);
1983 receiver.restore();
1984
1985 // save/restore returned the values to their original values
1986 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
1987 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
1988}
1989
1990TEST_F(DisplayListTest, ClipRRectAffectsClipBoundsWithMatrix) {
1992 DlOpReceiver& receiver = ToReceiver(builder);
1993 SkRect clip_bounds_1 = SkRect::MakeLTRB(0, 0, 10, 10);
1994 SkRect clip_bounds_2 = SkRect::MakeLTRB(10, 10, 20, 20);
1995 SkRRect clip1 = SkRRect::MakeRectXY(clip_bounds_1, 3, 2);
1996 SkRRect clip2 = SkRRect::MakeRectXY(clip_bounds_2, 3, 2);
1997
1998 receiver.save();
1999 receiver.clipRRect(clip1, ClipOp::kIntersect, false);
2000 receiver.translate(10, 0);
2001 receiver.clipRRect(clip1, ClipOp::kIntersect, false);
2002 ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty());
2003 receiver.restore();
2004
2005 receiver.save();
2006 receiver.clipRRect(clip1, ClipOp::kIntersect, false);
2007 receiver.translate(-10, -10);
2008 receiver.clipRRect(clip2, ClipOp::kIntersect, false);
2009 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds_1);
2010 receiver.restore();
2011}
2012
2013TEST_F(DisplayListTest, ClipPathAffectsClipBounds) {
2015 DlOpReceiver& receiver = ToReceiver(builder);
2016 SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
2017 SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
2018 receiver.clipPath(clip, ClipOp::kIntersect, false);
2019
2020 // Save initial return values for testing restored values
2021 SkRect initial_local_bounds = builder.GetLocalClipBounds();
2022 SkRect initial_destination_bounds = builder.GetDestinationClipBounds();
2023 ASSERT_EQ(initial_local_bounds, clip_bounds);
2024 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2025
2026 receiver.save();
2027 receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false);
2028 // Both clip bounds have changed
2029 ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds);
2030 ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds);
2031 // Previous return values have not changed
2032 ASSERT_EQ(initial_local_bounds, clip_bounds);
2033 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2034 receiver.restore();
2035
2036 // save/restore returned the values to their original values
2037 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
2038 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
2039
2040 receiver.save();
2041 receiver.scale(2, 2);
2042 SkRect scaled_clip_bounds = SkRect::MakeLTRB(4.1, 4.65, 11.2, 13.85);
2043 ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds);
2044 // Destination bounds are unaffected by transform
2045 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
2046 receiver.restore();
2047
2048 // save/restore returned the values to their original values
2049 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
2050 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
2051}
2052
2053TEST_F(DisplayListTest, ClipPathDoAAAffectsClipBounds) {
2055 DlOpReceiver& receiver = ToReceiver(builder);
2056 SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
2057 SkRect clip_expanded_bounds = SkRect::MakeLTRB(8, 9, 23, 28);
2058 receiver.clipPath(clip, ClipOp::kIntersect, true);
2059
2060 // Save initial return values for testing restored values
2061 SkRect initial_local_bounds = builder.GetLocalClipBounds();
2062 SkRect initial_destination_bounds = builder.GetDestinationClipBounds();
2063 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
2064 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
2065
2066 receiver.save();
2067 receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true);
2068 // Both clip bounds have changed
2069 ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds);
2070 ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds);
2071 // Previous return values have not changed
2072 ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
2073 ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
2074 receiver.restore();
2075
2076 // save/restore returned the values to their original values
2077 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
2078 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
2079
2080 receiver.save();
2081 receiver.scale(2, 2);
2082 SkRect scaled_expanded_bounds = SkRect::MakeLTRB(4, 4.5, 11.5, 14);
2083 ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds);
2084 // Destination bounds are unaffected by transform
2085 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds);
2086 receiver.restore();
2087
2088 // save/restore returned the values to their original values
2089 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
2090 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
2091}
2092
2093TEST_F(DisplayListTest, ClipPathAffectsClipBoundsWithMatrix) {
2095 DlOpReceiver& receiver = ToReceiver(builder);
2096 SkRect clip_bounds = SkRect::MakeLTRB(0, 0, 10, 10);
2097 SkPath clip1 = SkPath().addCircle(2.5, 2.5, 2.5).addCircle(7.5, 7.5, 2.5);
2098 SkPath clip2 = SkPath().addCircle(12.5, 12.5, 2.5).addCircle(17.5, 17.5, 2.5);
2099
2100 receiver.save();
2101 receiver.clipPath(clip1, ClipOp::kIntersect, false);
2102 receiver.translate(10, 0);
2103 receiver.clipPath(clip1, ClipOp::kIntersect, false);
2104 ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty());
2105 receiver.restore();
2106
2107 receiver.save();
2108 receiver.clipPath(clip1, ClipOp::kIntersect, false);
2109 receiver.translate(-10, -10);
2110 receiver.clipPath(clip2, ClipOp::kIntersect, false);
2111 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
2112 receiver.restore();
2113}
2114
2115TEST_F(DisplayListTest, DiffClipRectDoesNotAffectClipBounds) {
2117 DlOpReceiver& receiver = ToReceiver(builder);
2118 SkRect diff_clip = SkRect::MakeLTRB(0, 0, 15, 15);
2119 SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
2120 receiver.clipRect(clip_bounds, ClipOp::kIntersect, false);
2121
2122 // Save initial return values for testing after kDifference clip
2123 SkRect initial_local_bounds = builder.GetLocalClipBounds();
2124 SkRect initial_destination_bounds = builder.GetDestinationClipBounds();
2125 ASSERT_EQ(initial_local_bounds, clip_bounds);
2126 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2127
2128 receiver.clipRect(diff_clip, ClipOp::kDifference, false);
2129 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
2130 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
2131}
2132
2133TEST_F(DisplayListTest, DiffClipRRectDoesNotAffectClipBounds) {
2135 DlOpReceiver& receiver = ToReceiver(builder);
2136 SkRRect diff_clip = SkRRect::MakeRectXY({0, 0, 15, 15}, 1, 1);
2137 SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
2138 SkRRect clip = SkRRect::MakeRectXY({10.2, 11.3, 20.4, 25.7}, 3, 2);
2139 receiver.clipRRect(clip, ClipOp::kIntersect, false);
2140
2141 // Save initial return values for testing after kDifference clip
2142 SkRect initial_local_bounds = builder.GetLocalClipBounds();
2143 SkRect initial_destination_bounds = builder.GetDestinationClipBounds();
2144 ASSERT_EQ(initial_local_bounds, clip_bounds);
2145 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2146
2147 receiver.clipRRect(diff_clip, ClipOp::kDifference, false);
2148 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
2149 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
2150}
2151
2152TEST_F(DisplayListTest, DiffClipPathDoesNotAffectClipBounds) {
2154 DlOpReceiver& receiver = ToReceiver(builder);
2155 SkPath diff_clip = SkPath().addRect({0, 0, 15, 15});
2156 SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
2157 SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
2158 receiver.clipPath(clip, ClipOp::kIntersect, false);
2159
2160 // Save initial return values for testing after kDifference clip
2161 SkRect initial_local_bounds = builder.GetLocalClipBounds();
2162 SkRect initial_destination_bounds = builder.GetDestinationClipBounds();
2163 ASSERT_EQ(initial_local_bounds, clip_bounds);
2164 ASSERT_EQ(initial_destination_bounds, clip_bounds);
2165
2166 receiver.clipPath(diff_clip, ClipOp::kDifference, false);
2167 ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
2168 ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
2169}
2170
2171TEST_F(DisplayListTest, ClipPathWithInvertFillTypeDoesNotAffectClipBounds) {
2172 SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0);
2173 DisplayListBuilder builder(cull_rect);
2174 DlOpReceiver& receiver = ToReceiver(builder);
2175 SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
2177 receiver.clipPath(clip, ClipOp::kIntersect, false);
2178
2179 ASSERT_EQ(builder.GetLocalClipBounds(), cull_rect);
2180 ASSERT_EQ(builder.GetDestinationClipBounds(), cull_rect);
2181}
2182
2183TEST_F(DisplayListTest, DiffClipPathWithInvertFillTypeAffectsClipBounds) {
2184 SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0);
2185 DisplayListBuilder builder(cull_rect);
2186 DlOpReceiver& receiver = ToReceiver(builder);
2187 SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
2189 SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
2190 receiver.clipPath(clip, ClipOp::kDifference, false);
2191
2192 ASSERT_EQ(builder.GetLocalClipBounds(), clip_bounds);
2193 ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
2194}
2195
2196TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) {
2197 SkPoint horizontal_points[2] = {{10, 10}, {20, 10}};
2198 SkPoint vertical_points[2] = {{10, 10}, {10, 20}};
2199 {
2201 DlOpReceiver& receiver = ToReceiver(builder);
2202 receiver.drawPoints(PointMode::kPolygon, 2, horizontal_points);
2203 SkRect bounds = builder.Build()->bounds();
2204 EXPECT_TRUE(bounds.contains(10, 10));
2205 EXPECT_TRUE(bounds.contains(20, 10));
2206 EXPECT_GE(bounds.width(), 10);
2207 }
2208 {
2210 DlOpReceiver& receiver = ToReceiver(builder);
2211 receiver.drawPoints(PointMode::kPolygon, 2, vertical_points);
2212 SkRect bounds = builder.Build()->bounds();
2213 EXPECT_TRUE(bounds.contains(10, 10));
2214 EXPECT_TRUE(bounds.contains(10, 20));
2215 EXPECT_GE(bounds.height(), 10);
2216 }
2217 {
2219 DlOpReceiver& receiver = ToReceiver(builder);
2220 receiver.drawPoints(PointMode::kPoints, 1, horizontal_points);
2221 SkRect bounds = builder.Build()->bounds();
2222 EXPECT_TRUE(bounds.contains(10, 10));
2223 }
2224 {
2226 DlOpReceiver& receiver = ToReceiver(builder);
2227 receiver.setStrokeWidth(2);
2228 receiver.drawPoints(PointMode::kPolygon, 2, horizontal_points);
2229 SkRect bounds = builder.Build()->bounds();
2230 EXPECT_TRUE(bounds.contains(10, 10));
2231 EXPECT_TRUE(bounds.contains(20, 10));
2232 EXPECT_EQ(bounds, SkRect::MakeLTRB(9, 9, 21, 11));
2233 }
2234 {
2236 DlOpReceiver& receiver = ToReceiver(builder);
2237 receiver.setStrokeWidth(2);
2238 receiver.drawPoints(PointMode::kPolygon, 2, vertical_points);
2239 SkRect bounds = builder.Build()->bounds();
2240 EXPECT_TRUE(bounds.contains(10, 10));
2241 EXPECT_TRUE(bounds.contains(10, 20));
2242 EXPECT_EQ(bounds, SkRect::MakeLTRB(9, 9, 11, 21));
2243 }
2244 {
2246 DlOpReceiver& receiver = ToReceiver(builder);
2247 receiver.setStrokeWidth(2);
2248 receiver.drawPoints(PointMode::kPoints, 1, horizontal_points);
2249 SkRect bounds = builder.Build()->bounds();
2250 EXPECT_TRUE(bounds.contains(10, 10));
2251 EXPECT_EQ(bounds, SkRect::MakeLTRB(9, 9, 11, 11));
2252 }
2253}
2254
2255#define TEST_RTREE(rtree, query, expected_rects, expected_indices) \
2256 test_rtree(rtree, query, expected_rects, expected_indices, __FILE__, __LINE__)
2257
2258static void test_rtree(const sk_sp<const DlRTree>& rtree,
2259 const SkRect& query,
2260 std::vector<SkRect> expected_rects,
2261 const std::vector<int>& expected_indices,
2262 const std::string& file,
2263 int line) {
2264 std::vector<int> indices;
2265 auto label = "from " + file + ":" + std::to_string(line);
2266 rtree->search(query, &indices);
2267 EXPECT_EQ(indices, expected_indices) << label;
2268 EXPECT_EQ(indices.size(), expected_indices.size()) << label;
2269 std::list<SkRect> rects = rtree->searchAndConsolidateRects(query, false);
2270 // ASSERT_EQ(rects.size(), expected_indices.size());
2271 auto iterator = rects.cbegin();
2272 for (int i : expected_indices) {
2273 ASSERT_TRUE(iterator != rects.cend()) << label;
2274 EXPECT_EQ(*iterator++, expected_rects[i]) << label;
2275 }
2276}
2277
2278TEST_F(DisplayListTest, RTreeOfSimpleScene) {
2279 DisplayListBuilder builder(/*prepare_rtree=*/true);
2280 DlOpReceiver& receiver = ToReceiver(builder);
2281 std::vector<SkRect> rects = {
2282 {10, 10, 20, 20},
2283 {50, 50, 60, 60},
2284 };
2285 receiver.drawRect(rects[0]);
2286 receiver.drawRect(rects[1]);
2287 auto display_list = builder.Build();
2288 auto rtree = display_list->rtree();
2289
2290 // Missing all drawRect calls
2291 TEST_RTREE(rtree, SkRect::MakeLTRB(5, 5, 10, 10), rects, {});
2292 TEST_RTREE(rtree, SkRect::MakeLTRB(20, 20, 25, 25), rects, {});
2293 TEST_RTREE(rtree, SkRect::MakeLTRB(45, 45, 50, 50), rects, {});
2294 TEST_RTREE(rtree, SkRect::MakeLTRB(60, 60, 65, 65), rects, {});
2295
2296 // Hitting just 1 of the drawRects
2297 TEST_RTREE(rtree, SkRect::MakeLTRB(5, 5, 11, 11), rects, {0});
2298 TEST_RTREE(rtree, SkRect::MakeLTRB(19, 19, 25, 25), rects, {0});
2299 TEST_RTREE(rtree, SkRect::MakeLTRB(45, 45, 51, 51), rects, {1});
2300 TEST_RTREE(rtree, SkRect::MakeLTRB(59, 59, 65, 65), rects, {1});
2301
2302 // Hitting both drawRect calls
2303 TEST_RTREE(rtree, SkRect::MakeLTRB(19, 19, 51, 51), rects,
2304 std::vector<int>({0, 1}));
2305}
2306
2307TEST_F(DisplayListTest, RTreeOfSaveRestoreScene) {
2308 DisplayListBuilder builder(/*prepare_rtree=*/true);
2309 DlOpReceiver& receiver = ToReceiver(builder);
2310 receiver.drawRect({10, 10, 20, 20});
2311 receiver.save();
2312 receiver.drawRect({50, 50, 60, 60});
2313 receiver.restore();
2314 auto display_list = builder.Build();
2315 auto rtree = display_list->rtree();
2316 std::vector<SkRect> rects = {
2317 {10, 10, 20, 20},
2318 {50, 50, 60, 60},
2319 };
2320
2321 // Missing all drawRect calls
2322 TEST_RTREE(rtree, SkRect::MakeLTRB(5, 5, 10, 10), rects, {});
2323 TEST_RTREE(rtree, SkRect::MakeLTRB(20, 20, 25, 25), rects, {});
2324 TEST_RTREE(rtree, SkRect::MakeLTRB(45, 45, 50, 50), rects, {});
2325 TEST_RTREE(rtree, SkRect::MakeLTRB(60, 60, 65, 65), rects, {});
2326
2327 // Hitting just 1 of the drawRects
2328 TEST_RTREE(rtree, SkRect::MakeLTRB(5, 5, 11, 11), rects, {0});
2329 TEST_RTREE(rtree, SkRect::MakeLTRB(19, 19, 25, 25), rects, {0});
2330 TEST_RTREE(rtree, SkRect::MakeLTRB(45, 45, 51, 51), rects, {1});
2331 TEST_RTREE(rtree, SkRect::MakeLTRB(59, 59, 65, 65), rects, {1});
2332
2333 // Hitting both drawRect calls
2334 TEST_RTREE(rtree, SkRect::MakeLTRB(19, 19, 51, 51), rects,
2335 std::vector<int>({0, 1}));
2336}
2337
2338TEST_F(DisplayListTest, RTreeOfSaveLayerFilterScene) {
2339 DisplayListBuilder builder(/*prepare_rtree=*/true);
2340 // blur filter with sigma=1 expands by 3 on all sides
2341 auto filter = DlBlurImageFilter(1.0, 1.0, DlTileMode::kClamp);
2342 DlPaint default_paint = DlPaint();
2343 DlPaint filter_paint = DlPaint().setImageFilter(&filter);
2344 builder.DrawRect({10, 10, 20, 20}, default_paint);
2345 builder.SaveLayer(nullptr, &filter_paint);
2346 // the following rectangle will be expanded to 50,50,60,60
2347 // by the saveLayer filter during the restore operation
2348 builder.DrawRect({53, 53, 57, 57}, default_paint);
2349 builder.Restore();
2350 auto display_list = builder.Build();
2351 auto rtree = display_list->rtree();
2352 std::vector<SkRect> rects = {
2353 {10, 10, 20, 20},
2354 {50, 50, 60, 60},
2355 };
2356
2357 // Missing all drawRect calls
2358 TEST_RTREE(rtree, SkRect::MakeLTRB(5, 5, 10, 10), rects, {});
2359 TEST_RTREE(rtree, SkRect::MakeLTRB(20, 20, 25, 25), rects, {});
2360 TEST_RTREE(rtree, SkRect::MakeLTRB(45, 45, 50, 50), rects, {});
2361 TEST_RTREE(rtree, SkRect::MakeLTRB(60, 60, 65, 65), rects, {});
2362
2363 // Hitting just 1 of the drawRects
2364 TEST_RTREE(rtree, SkRect::MakeLTRB(5, 5, 11, 11), rects, {0});
2365 TEST_RTREE(rtree, SkRect::MakeLTRB(19, 19, 25, 25), rects, {0});
2366 TEST_RTREE(rtree, SkRect::MakeLTRB(45, 45, 51, 51), rects, {1});
2367 TEST_RTREE(rtree, SkRect::MakeLTRB(59, 59, 65, 65), rects, {1});
2368
2369 // Hitting both drawRect calls
2370 auto expected_indices = std::vector<int>{0, 1};
2371 TEST_RTREE(rtree, SkRect::MakeLTRB(19, 19, 51, 51), rects, expected_indices);
2372}
2373
2374TEST_F(DisplayListTest, NestedDisplayListRTreesAreSparse) {
2375 DisplayListBuilder nested_dl_builder(/**prepare_rtree=*/true);
2376 DlOpReceiver& nested_dl_receiver = ToReceiver(nested_dl_builder);
2377 nested_dl_receiver.drawRect({10, 10, 20, 20});
2378 nested_dl_receiver.drawRect({50, 50, 60, 60});
2379 auto nested_display_list = nested_dl_builder.Build();
2380
2381 DisplayListBuilder builder(/**prepare_rtree=*/true);
2382 DlOpReceiver& receiver = ToReceiver(builder);
2383 receiver.drawDisplayList(nested_display_list);
2384 auto display_list = builder.Build();
2385
2386 auto rtree = display_list->rtree();
2387 std::vector<SkRect> rects = {
2388 {10, 10, 20, 20},
2389 {50, 50, 60, 60},
2390 };
2391
2392 // Hitting both sub-dl drawRect calls
2393 TEST_RTREE(rtree, SkRect::MakeLTRB(19, 19, 51, 51), rects,
2394 std::vector<int>({0, 1}));
2395}
2396
2397TEST_F(DisplayListTest, RemoveUnnecessarySaveRestorePairs) {
2398 {
2400 DlOpReceiver& receiver = ToReceiver(builder);
2401 receiver.drawRect({10, 10, 20, 20});
2402 receiver.save(); // This save op is unnecessary
2403 receiver.drawRect({50, 50, 60, 60});
2404 receiver.restore();
2405
2406 DisplayListBuilder builder2;
2407 DlOpReceiver& receiver2 = ToReceiver(builder2);
2408 receiver2.drawRect({10, 10, 20, 20});
2409 receiver2.drawRect({50, 50, 60, 60});
2410 ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build()));
2411 }
2412
2413 {
2415 DlOpReceiver& receiver = ToReceiver(builder);
2416 receiver.drawRect({10, 10, 20, 20});
2417 receiver.save();
2418 receiver.translate(1.0, 1.0);
2419 {
2420 receiver.save(); // unnecessary
2421 receiver.drawRect({50, 50, 60, 60});
2422 receiver.restore();
2423 }
2424
2425 receiver.restore();
2426
2427 DisplayListBuilder builder2;
2428 DlOpReceiver& receiver2 = ToReceiver(builder2);
2429 receiver2.drawRect({10, 10, 20, 20});
2430 receiver2.save();
2431 receiver2.translate(1.0, 1.0);
2432 { receiver2.drawRect({50, 50, 60, 60}); }
2433 receiver2.restore();
2434 ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build()));
2435 }
2436}
2437
2438TEST_F(DisplayListTest, CollapseMultipleNestedSaveRestore) {
2439 DisplayListBuilder builder1;
2440 DlOpReceiver& receiver1 = ToReceiver(builder1);
2441 receiver1.save();
2442 receiver1.save();
2443 receiver1.save();
2444 receiver1.translate(10, 10);
2445 receiver1.scale(2, 2);
2446 receiver1.clipRect({10, 10, 20, 20}, ClipOp::kIntersect, false);
2447 receiver1.drawRect({0, 0, 100, 100});
2448 receiver1.restore();
2449 receiver1.restore();
2450 receiver1.restore();
2451 auto display_list1 = builder1.Build();
2452
2453 DisplayListBuilder builder2;
2454 DlOpReceiver& receiver2 = ToReceiver(builder2);
2455 receiver2.save();
2456 receiver2.translate(10, 10);
2457 receiver2.scale(2, 2);
2458 receiver2.clipRect({10, 10, 20, 20}, ClipOp::kIntersect, false);
2459 receiver2.drawRect({0, 0, 100, 100});
2460 receiver2.restore();
2461 auto display_list2 = builder2.Build();
2462
2463 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2464}
2465
2466TEST_F(DisplayListTest, CollapseNestedSaveAndSaveLayerRestore) {
2467 DisplayListBuilder builder1;
2468 DlOpReceiver& receiver1 = ToReceiver(builder1);
2469 receiver1.save();
2470 receiver1.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
2471 receiver1.drawRect({0, 0, 100, 100});
2472 receiver1.scale(2, 2);
2473 receiver1.restore();
2474 receiver1.restore();
2475 auto display_list1 = builder1.Build();
2476
2477 DisplayListBuilder builder2;
2478 DlOpReceiver& receiver2 = ToReceiver(builder2);
2479 receiver2.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
2480 receiver2.drawRect({0, 0, 100, 100});
2481 receiver2.scale(2, 2);
2482 receiver2.restore();
2483 auto display_list2 = builder2.Build();
2484
2485 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2486}
2487
2488TEST_F(DisplayListTest, RemoveUnnecessarySaveRestorePairsInSetPaint) {
2489 SkRect build_bounds = SkRect::MakeLTRB(-100, -100, 200, 200);
2490 SkRect rect = SkRect::MakeLTRB(30, 30, 70, 70);
2491 // clang-format off
2492 const float alpha_matrix[] = {
2493 0, 0, 0, 0, 0,
2494 0, 1, 0, 0, 0,
2495 0, 0, 1, 0, 0,
2496 0, 0, 0, 0, 1,
2497 };
2498 // clang-format on
2499 DlMatrixColorFilter alpha_color_filter(alpha_matrix);
2500 // Making sure hiding a problematic ColorFilter as an ImageFilter
2501 // will generate the same behavior as setting it as a ColorFilter
2502
2503 DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter);
2504 {
2505 DisplayListBuilder builder(build_bounds);
2506 builder.Save();
2507 DlPaint paint;
2508 paint.setImageFilter(&color_filter_image_filter);
2509 builder.DrawRect(rect, paint);
2510 builder.Restore();
2511 sk_sp<DisplayList> display_list1 = builder.Build();
2512
2513 DisplayListBuilder builder2(build_bounds);
2514 DlPaint paint2;
2515 paint2.setImageFilter(&color_filter_image_filter);
2516 builder2.DrawRect(rect, paint2);
2517 sk_sp<DisplayList> display_list2 = builder2.Build();
2518 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2519 }
2520
2521 {
2522 DisplayListBuilder builder(build_bounds);
2523 builder.Save();
2524 builder.SaveLayer(&build_bounds);
2525 DlPaint paint;
2526 paint.setImageFilter(&color_filter_image_filter);
2527 builder.DrawRect(rect, paint);
2528 builder.Restore();
2529 builder.Restore();
2530 sk_sp<DisplayList> display_list1 = builder.Build();
2531
2532 DisplayListBuilder builder2(build_bounds);
2533 builder2.SaveLayer(&build_bounds);
2534 DlPaint paint2;
2535 paint2.setImageFilter(&color_filter_image_filter);
2536 builder2.DrawRect(rect, paint2);
2537 builder2.Restore();
2538 sk_sp<DisplayList> display_list2 = builder2.Build();
2539 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2540 }
2541}
2542
2543TEST_F(DisplayListTest, TransformTriggersDeferredSave) {
2544 DisplayListBuilder builder1;
2545 DlOpReceiver& receiver1 = ToReceiver(builder1);
2546 receiver1.save();
2547 receiver1.save();
2548 receiver1.transformFullPerspective(1, 0, 0, 10, //
2549 0, 1, 0, 100, //
2550 0, 0, 1, 0, //
2551 0, 0, 0, 1);
2552 receiver1.drawRect({0, 0, 100, 100});
2553 receiver1.restore();
2554 receiver1.transformFullPerspective(1, 0, 0, 10, //
2555 0, 1, 0, 100, //
2556 0, 0, 1, 0, //
2557 0, 0, 0, 1);
2558 receiver1.drawRect({0, 0, 100, 100});
2559 receiver1.restore();
2560 auto display_list1 = builder1.Build();
2561
2562 DisplayListBuilder builder2;
2563 DlOpReceiver& receiver2 = ToReceiver(builder2);
2564 receiver2.save();
2565 receiver2.transformFullPerspective(1, 0, 0, 10, //
2566 0, 1, 0, 100, //
2567 0, 0, 1, 0, //
2568 0, 0, 0, 1);
2569 receiver2.drawRect({0, 0, 100, 100});
2570 receiver2.restore();
2571 receiver2.save();
2572 receiver2.transformFullPerspective(1, 0, 0, 10, //
2573 0, 1, 0, 100, //
2574 0, 0, 1, 0, //
2575 0, 0, 0, 1);
2576 receiver2.drawRect({0, 0, 100, 100});
2577 receiver2.restore();
2578 auto display_list2 = builder2.Build();
2579
2580 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2581}
2582
2583TEST_F(DisplayListTest, Transform2DTriggersDeferredSave) {
2584 DisplayListBuilder builder1;
2585 DlOpReceiver& receiver1 = ToReceiver(builder1);
2586 receiver1.save();
2587 receiver1.save();
2588 receiver1.transform2DAffine(0, 1, 12, 1, 0, 33);
2589 receiver1.drawRect({0, 0, 100, 100});
2590 receiver1.restore();
2591 receiver1.restore();
2592 auto display_list1 = builder1.Build();
2593
2594 DisplayListBuilder builder2;
2595 DlOpReceiver& receiver2 = ToReceiver(builder2);
2596 receiver2.save();
2597 receiver2.transform2DAffine(0, 1, 12, 1, 0, 33);
2598 receiver2.drawRect({0, 0, 100, 100});
2599 receiver2.restore();
2600 auto display_list2 = builder2.Build();
2601
2602 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2603}
2604
2605TEST_F(DisplayListTest, TransformPerspectiveTriggersDeferredSave) {
2606 DisplayListBuilder builder1;
2607 DlOpReceiver& receiver1 = ToReceiver(builder1);
2608 receiver1.save();
2609 receiver1.save();
2610 receiver1.transformFullPerspective(0, 1, 0, 12, //
2611 1, 0, 0, 33, //
2612 3, 2, 5, 29, //
2613 0, 0, 0, 12);
2614 receiver1.drawRect({0, 0, 100, 100});
2615 receiver1.restore();
2616 receiver1.restore();
2617 auto display_list1 = builder1.Build();
2618
2619 DisplayListBuilder builder2;
2620 DlOpReceiver& receiver2 = ToReceiver(builder2);
2621 receiver2.save();
2622 receiver2.transformFullPerspective(0, 1, 0, 12, //
2623 1, 0, 0, 33, //
2624 3, 2, 5, 29, //
2625 0, 0, 0, 12);
2626 receiver2.drawRect({0, 0, 100, 100});
2627 receiver2.restore();
2628 auto display_list2 = builder2.Build();
2629
2630 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2631}
2632
2633TEST_F(DisplayListTest, ResetTransformTriggersDeferredSave) {
2634 DisplayListBuilder builder1;
2635 DlOpReceiver& receiver1 = ToReceiver(builder1);
2636 receiver1.save();
2637 receiver1.save();
2638 receiver1.transformReset();
2639 receiver1.drawRect({0, 0, 100, 100});
2640 receiver1.restore();
2641 receiver1.restore();
2642 auto display_list1 = builder1.Build();
2643
2644 DisplayListBuilder builder2;
2645 DlOpReceiver& receiver2 = ToReceiver(builder2);
2646 receiver2.save();
2647 receiver2.transformReset();
2648 receiver2.drawRect({0, 0, 100, 100});
2649 receiver2.restore();
2650 auto display_list2 = builder2.Build();
2651
2652 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2653}
2654
2655TEST_F(DisplayListTest, SkewTriggersDeferredSave) {
2656 DisplayListBuilder builder1;
2657 DlOpReceiver& receiver1 = ToReceiver(builder1);
2658 receiver1.save();
2659 receiver1.save();
2660 receiver1.skew(10, 10);
2661 receiver1.drawRect({0, 0, 100, 100});
2662 receiver1.restore();
2663 receiver1.restore();
2664 auto display_list1 = builder1.Build();
2665
2666 DisplayListBuilder builder2;
2667 DlOpReceiver& receiver2 = ToReceiver(builder2);
2668 receiver2.save();
2669 receiver2.skew(10, 10);
2670 receiver2.drawRect({0, 0, 100, 100});
2671 receiver2.restore();
2672 auto display_list2 = builder2.Build();
2673
2674 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2675}
2676
2677TEST_F(DisplayListTest, TranslateTriggersDeferredSave) {
2678 DisplayListBuilder builder1;
2679 DlOpReceiver& receiver1 = ToReceiver(builder1);
2680 receiver1.save();
2681 receiver1.save();
2682 receiver1.translate(10, 10);
2683 receiver1.drawRect({0, 0, 100, 100});
2684 receiver1.restore();
2685 receiver1.restore();
2686 auto display_list1 = builder1.Build();
2687
2688 DisplayListBuilder builder2;
2689 DlOpReceiver& receiver2 = ToReceiver(builder2);
2690 receiver2.save();
2691 receiver2.translate(10, 10);
2692 receiver2.drawRect({0, 0, 100, 100});
2693 receiver2.restore();
2694 auto display_list2 = builder2.Build();
2695
2696 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2697}
2698
2699TEST_F(DisplayListTest, ScaleTriggersDeferredSave) {
2700 DisplayListBuilder builder1;
2701 DlOpReceiver& receiver1 = ToReceiver(builder1);
2702 receiver1.save();
2703 receiver1.save();
2704 receiver1.scale(0.5, 0.5);
2705 receiver1.drawRect({0, 0, 100, 100});
2706 receiver1.restore();
2707 receiver1.restore();
2708 auto display_list1 = builder1.Build();
2709
2710 DisplayListBuilder builder2;
2711 DlOpReceiver& receiver2 = ToReceiver(builder2);
2712 receiver2.save();
2713 receiver2.scale(0.5, 0.5);
2714 receiver2.drawRect({0, 0, 100, 100});
2715 receiver2.restore();
2716 auto display_list2 = builder2.Build();
2717
2718 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2719}
2720
2721TEST_F(DisplayListTest, ClipRectTriggersDeferredSave) {
2722 DisplayListBuilder builder1;
2723 DlOpReceiver& receiver1 = ToReceiver(builder1);
2724 receiver1.save();
2725 receiver1.save();
2726 receiver1.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect,
2727 true);
2728 receiver1.drawRect({0, 0, 100, 100});
2729 receiver1.restore();
2730 receiver1.transformFullPerspective(1, 0, 0, 0, //
2731 0, 1, 0, 0, //
2732 0, 0, 1, 0, //
2733 0, 0, 0, 1);
2734 receiver1.drawRect({0, 0, 100, 100});
2735 receiver1.restore();
2736 auto display_list1 = builder1.Build();
2737
2738 DisplayListBuilder builder2;
2739 DlOpReceiver& receiver2 = ToReceiver(builder2);
2740 receiver2.save();
2741 receiver2.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect,
2742 true);
2743 receiver2.drawRect({0, 0, 100, 100});
2744 receiver2.restore();
2745 receiver2.transformFullPerspective(1, 0, 0, 0, //
2746 0, 1, 0, 0, //
2747 0, 0, 1, 0, //
2748 0, 0, 0, 1);
2749 receiver2.drawRect({0, 0, 100, 100});
2750 auto display_list2 = builder2.Build();
2751
2752 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2753}
2754
2755TEST_F(DisplayListTest, ClipRRectTriggersDeferredSave) {
2756 DisplayListBuilder builder1;
2757 DlOpReceiver& receiver1 = ToReceiver(builder1);
2758 receiver1.save();
2759 receiver1.save();
2760 receiver1.clipRRect(kTestRRect, ClipOp::kIntersect, true);
2761
2762 receiver1.drawRect({0, 0, 100, 100});
2763 receiver1.restore();
2764 receiver1.transformFullPerspective(1, 0, 0, 0, //
2765 0, 1, 0, 0, //
2766 0, 0, 1, 0, //
2767 0, 0, 0, 1);
2768 receiver1.drawRect({0, 0, 100, 100});
2769 receiver1.restore();
2770 auto display_list1 = builder1.Build();
2771
2772 DisplayListBuilder builder2;
2773 DlOpReceiver& receiver2 = ToReceiver(builder2);
2774 receiver2.save();
2775 receiver2.clipRRect(kTestRRect, ClipOp::kIntersect, true);
2776
2777 receiver2.drawRect({0, 0, 100, 100});
2778 receiver2.restore();
2779 receiver2.transformFullPerspective(1, 0, 0, 0, //
2780 0, 1, 0, 0, //
2781 0, 0, 1, 0, //
2782 0, 0, 0, 1);
2783 receiver2.drawRect({0, 0, 100, 100});
2784 auto display_list2 = builder2.Build();
2785
2786 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2787}
2788
2789TEST_F(DisplayListTest, ClipPathTriggersDeferredSave) {
2790 DisplayListBuilder builder1;
2791 DlOpReceiver& receiver1 = ToReceiver(builder1);
2792 receiver1.save();
2793 receiver1.save();
2794 receiver1.clipPath(kTestPath1, ClipOp::kIntersect, true);
2795 receiver1.drawRect({0, 0, 100, 100});
2796 receiver1.restore();
2797 receiver1.transformFullPerspective(1, 0, 0, 0, //
2798 0, 1, 0, 0, //
2799 0, 0, 1, 0, //
2800 0, 0, 0, 1);
2801 receiver1.drawRect({0, 0, 100, 100});
2802 receiver1.restore();
2803 auto display_list1 = builder1.Build();
2804
2805 DisplayListBuilder builder2;
2806 DlOpReceiver& receiver2 = ToReceiver(builder2);
2807 receiver2.save();
2808 receiver2.clipPath(kTestPath1, ClipOp::kIntersect, true);
2809 receiver2.drawRect({0, 0, 100, 100});
2810 receiver2.restore();
2811 receiver2.transformFullPerspective(1, 0, 0, 0, //
2812 0, 1, 0, 0, //
2813 0, 0, 1, 0, //
2814 0, 0, 0, 1);
2815 receiver2.drawRect({0, 0, 100, 100});
2816 auto display_list2 = builder2.Build();
2817
2818 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2819}
2820
2821TEST_F(DisplayListTest, NOPTranslateDoesNotTriggerDeferredSave) {
2822 DisplayListBuilder builder1;
2823 DlOpReceiver& receiver1 = ToReceiver(builder1);
2824 receiver1.save();
2825 receiver1.save();
2826 receiver1.translate(0, 0);
2827 receiver1.drawRect({0, 0, 100, 100});
2828 receiver1.restore();
2829 receiver1.drawRect({0, 0, 100, 100});
2830 receiver1.restore();
2831 auto display_list1 = builder1.Build();
2832
2833 DisplayListBuilder builder2;
2834 DlOpReceiver& receiver2 = ToReceiver(builder2);
2835 receiver2.drawRect({0, 0, 100, 100});
2836 receiver2.drawRect({0, 0, 100, 100});
2837 auto display_list2 = builder2.Build();
2838
2839 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2840}
2841
2842TEST_F(DisplayListTest, NOPScaleDoesNotTriggerDeferredSave) {
2843 DisplayListBuilder builder1;
2844 DlOpReceiver& receiver1 = ToReceiver(builder1);
2845 receiver1.save();
2846 receiver1.save();
2847 receiver1.scale(1.0, 1.0);
2848 receiver1.drawRect({0, 0, 100, 100});
2849 receiver1.restore();
2850 receiver1.drawRect({0, 0, 100, 100});
2851 receiver1.restore();
2852 auto display_list1 = builder1.Build();
2853
2854 DisplayListBuilder builder2;
2855 DlOpReceiver& receiver2 = ToReceiver(builder2);
2856 receiver2.drawRect({0, 0, 100, 100});
2857 receiver2.drawRect({0, 0, 100, 100});
2858 auto display_list2 = builder2.Build();
2859
2860 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2861}
2862
2863TEST_F(DisplayListTest, NOPRotationDoesNotTriggerDeferredSave) {
2864 DisplayListBuilder builder1;
2865 DlOpReceiver& receiver1 = ToReceiver(builder1);
2866 receiver1.save();
2867 receiver1.save();
2868 receiver1.rotate(360);
2869 receiver1.drawRect({0, 0, 100, 100});
2870 receiver1.restore();
2871 receiver1.drawRect({0, 0, 100, 100});
2872 receiver1.restore();
2873 auto display_list1 = builder1.Build();
2874
2875 DisplayListBuilder builder2;
2876 DlOpReceiver& receiver2 = ToReceiver(builder2);
2877 receiver2.drawRect({0, 0, 100, 100});
2878 receiver2.drawRect({0, 0, 100, 100});
2879 auto display_list2 = builder2.Build();
2880
2881 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2882}
2883
2884TEST_F(DisplayListTest, NOPSkewDoesNotTriggerDeferredSave) {
2885 DisplayListBuilder builder1;
2886 DlOpReceiver& receiver1 = ToReceiver(builder1);
2887 receiver1.save();
2888 receiver1.save();
2889 receiver1.skew(0, 0);
2890 receiver1.drawRect({0, 0, 100, 100});
2891 receiver1.restore();
2892 receiver1.drawRect({0, 0, 100, 100});
2893 receiver1.restore();
2894 auto display_list1 = builder1.Build();
2895
2896 DisplayListBuilder builder2;
2897 DlOpReceiver& receiver2 = ToReceiver(builder2);
2898 receiver2.drawRect({0, 0, 100, 100});
2899 receiver2.drawRect({0, 0, 100, 100});
2900 auto display_list2 = builder2.Build();
2901
2902 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2903}
2904
2905TEST_F(DisplayListTest, NOPTransformDoesNotTriggerDeferredSave) {
2906 DisplayListBuilder builder1;
2907 DlOpReceiver& receiver1 = ToReceiver(builder1);
2908 receiver1.save();
2909 receiver1.save();
2910 receiver1.transformFullPerspective(1, 0, 0, 0, //
2911 0, 1, 0, 0, //
2912 0, 0, 1, 0, //
2913 0, 0, 0, 1);
2914 receiver1.drawRect({0, 0, 100, 100});
2915 receiver1.restore();
2916 receiver1.transformFullPerspective(1, 0, 0, 0, //
2917 0, 1, 0, 0, //
2918 0, 0, 1, 0, //
2919 0, 0, 0, 1);
2920 receiver1.drawRect({0, 0, 100, 100});
2921 receiver1.restore();
2922 auto display_list1 = builder1.Build();
2923
2924 DisplayListBuilder builder2;
2925 DlOpReceiver& receiver2 = ToReceiver(builder2);
2926 receiver2.drawRect({0, 0, 100, 100});
2927 receiver2.drawRect({0, 0, 100, 100});
2928 auto display_list2 = builder2.Build();
2929
2930 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2931}
2932
2933TEST_F(DisplayListTest, NOPTransform2DDoesNotTriggerDeferredSave) {
2934 DisplayListBuilder builder1;
2935 DlOpReceiver& receiver1 = ToReceiver(builder1);
2936 receiver1.save();
2937 receiver1.save();
2938 receiver1.transform2DAffine(1, 0, 0, 0, 1, 0);
2939 receiver1.drawRect({0, 0, 100, 100});
2940 receiver1.restore();
2941 receiver1.drawRect({0, 0, 100, 100});
2942 receiver1.restore();
2943 auto display_list1 = builder1.Build();
2944
2945 DisplayListBuilder builder2;
2946 DlOpReceiver& receiver2 = ToReceiver(builder2);
2947 receiver2.drawRect({0, 0, 100, 100});
2948 receiver2.drawRect({0, 0, 100, 100});
2949 auto display_list2 = builder2.Build();
2950
2951 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2952}
2953
2954TEST_F(DisplayListTest, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) {
2955 {
2956 DisplayListBuilder builder1;
2957 DlOpReceiver& receiver1 = ToReceiver(builder1);
2958 receiver1.save();
2959 receiver1.save();
2960 receiver1.transformFullPerspective(1, 0, 0, 0, //
2961 0, 1, 0, 0, //
2962 0, 0, 1, 0, //
2963 0, 0, 0, 1);
2964 receiver1.drawRect({0, 0, 100, 100});
2965 receiver1.restore();
2966 receiver1.drawRect({0, 0, 100, 100});
2967 receiver1.restore();
2968 auto display_list1 = builder1.Build();
2969
2970 DisplayListBuilder builder2;
2971 DlOpReceiver& receiver2 = ToReceiver(builder2);
2972 receiver2.drawRect({0, 0, 100, 100});
2973 receiver2.drawRect({0, 0, 100, 100});
2974 auto display_list2 = builder2.Build();
2975
2976 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
2977 }
2978
2979 {
2980 DisplayListBuilder builder1;
2981 DlOpReceiver& receiver1 = ToReceiver(builder1);
2982 receiver1.save();
2983 receiver1.save();
2984 receiver1.transformFullPerspective(1, 0, 0, 0, //
2985 0, 1, 0, 0, //
2986 0, 0, 1, 0, //
2987 0, 0, 0, 1);
2988 receiver1.transformReset();
2989 receiver1.drawRect({0, 0, 100, 100});
2990 receiver1.restore();
2991 receiver1.drawRect({0, 0, 100, 100});
2992 receiver1.restore();
2993 auto display_list1 = builder1.Build();
2994
2995 DisplayListBuilder builder2;
2996 DlOpReceiver& receiver2 = ToReceiver(builder2);
2997 receiver2.save();
2998 receiver2.transformReset();
2999 receiver2.drawRect({0, 0, 100, 100});
3000 receiver2.restore();
3001 receiver2.drawRect({0, 0, 100, 100});
3002
3003 auto display_list2 = builder2.Build();
3004
3005 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3006 }
3007}
3008
3009TEST_F(DisplayListTest, NOPClipDoesNotTriggerDeferredSave) {
3010 DisplayListBuilder builder1;
3011 DlOpReceiver& receiver1 = ToReceiver(builder1);
3012 receiver1.save();
3013 receiver1.save();
3015 ClipOp::kIntersect, true);
3016 receiver1.drawRect({0, 0, 100, 100});
3017 receiver1.restore();
3018 receiver1.drawRect({0, 0, 100, 100});
3019 receiver1.restore();
3020 auto display_list1 = builder1.Build();
3021
3022 DisplayListBuilder builder2;
3023 DlOpReceiver& receiver2 = ToReceiver(builder2);
3024 receiver2.drawRect({0, 0, 100, 100});
3025 receiver2.drawRect({0, 0, 100, 100});
3026 auto display_list2 = builder2.Build();
3027
3028 ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
3029}
3030
3031TEST_F(DisplayListTest, RTreeOfClippedSaveLayerFilterScene) {
3032 DisplayListBuilder builder(/*prepare_rtree=*/true);
3033 // blur filter with sigma=1 expands by 30 on all sides
3034 auto filter = DlBlurImageFilter(10.0, 10.0, DlTileMode::kClamp);
3035 DlPaint default_paint = DlPaint();
3036 DlPaint filter_paint = DlPaint().setImageFilter(&filter);
3037 builder.DrawRect({10, 10, 20, 20}, default_paint);
3038 builder.ClipRect({50, 50, 60, 60}, ClipOp::kIntersect, false);
3039 builder.SaveLayer(nullptr, &filter_paint);
3040 // the following rectangle will be expanded to 23,23,87,87
3041 // by the saveLayer filter during the restore operation
3042 // but it will then be clipped to 50,50,60,60
3043 builder.DrawRect({53, 53, 57, 57}, default_paint);
3044 builder.Restore();
3045 auto display_list = builder.Build();
3046 auto rtree = display_list->rtree();
3047 std::vector<SkRect> rects = {
3048 {10, 10, 20, 20},
3049 {50, 50, 60, 60},
3050 };
3051
3052 // Missing all drawRect calls
3053 TEST_RTREE(rtree, SkRect::MakeLTRB(5, 5, 10, 10), rects, {});
3054 TEST_RTREE(rtree, SkRect::MakeLTRB(20, 20, 25, 25), rects, {});
3055 TEST_RTREE(rtree, SkRect::MakeLTRB(45, 45, 50, 50), rects, {});
3056 TEST_RTREE(rtree, SkRect::MakeLTRB(60, 60, 65, 65), rects, {});
3057
3058 // Hitting just 1 of the drawRects
3059 TEST_RTREE(rtree, SkRect::MakeLTRB(5, 5, 11, 11), rects, {0});
3060 TEST_RTREE(rtree, SkRect::MakeLTRB(19, 19, 25, 25), rects, {0});
3061 TEST_RTREE(rtree, SkRect::MakeLTRB(45, 45, 51, 51), rects, {1});
3062 TEST_RTREE(rtree, SkRect::MakeLTRB(59, 59, 65, 65), rects, {1});
3063
3064 // Hitting both drawRect calls
3065 TEST_RTREE(rtree, SkRect::MakeLTRB(19, 19, 51, 51), rects,
3066 std::vector<int>({0, 1}));
3067}
3068
3069TEST_F(DisplayListTest, RTreeRenderCulling) {
3070 DisplayListBuilder main_builder(true);
3071 DlOpReceiver& main_receiver = ToReceiver(main_builder);
3072 main_receiver.drawRect({0, 0, 10, 10});
3073 main_receiver.drawRect({20, 0, 30, 10});
3074 main_receiver.drawRect({0, 20, 10, 30});
3075 main_receiver.drawRect({20, 20, 30, 30});
3076 auto main = main_builder.Build();
3077
3078 auto test = [main](SkIRect cull_rect, const sk_sp<DisplayList>& expected) {
3079 { // Test SkIRect culling
3080 DisplayListBuilder culling_builder;
3081 main->Dispatch(ToReceiver(culling_builder), cull_rect);
3082
3083 EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
3084 }
3085
3086 { // Test SkRect culling
3087 DisplayListBuilder culling_builder;
3088 main->Dispatch(ToReceiver(culling_builder), SkRect::Make(cull_rect));
3089
3090 EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
3091 }
3092 };
3093
3094 { // No rects
3095 SkIRect cull_rect = {11, 11, 19, 19};
3096
3097 DisplayListBuilder expected_builder;
3098 auto expected = expected_builder.Build();
3099
3100 test(cull_rect, expected);
3101 }
3102
3103 { // Rect 1
3104 SkIRect cull_rect = {9, 9, 19, 19};
3105
3106 DisplayListBuilder expected_builder;
3107 DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
3108 expected_receiver.drawRect({0, 0, 10, 10});
3109 auto expected = expected_builder.Build();
3110
3111 test(cull_rect, expected);
3112 }
3113
3114 { // Rect 2
3115 SkIRect cull_rect = {11, 9, 21, 19};
3116
3117 DisplayListBuilder expected_builder;
3118 DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
3119 expected_receiver.drawRect({20, 0, 30, 10});
3120 auto expected = expected_builder.Build();
3121
3122 test(cull_rect, expected);
3123 }
3124
3125 { // Rect 3
3126 SkIRect cull_rect = {9, 11, 19, 21};
3127
3128 DisplayListBuilder expected_builder;
3129 DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
3130 expected_receiver.drawRect({0, 20, 10, 30});
3131 auto expected = expected_builder.Build();
3132
3133 test(cull_rect, expected);
3134 }
3135
3136 { // Rect 4
3137 SkIRect cull_rect = {11, 11, 21, 21};
3138
3139 DisplayListBuilder expected_builder;
3140 DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
3141 expected_receiver.drawRect({20, 20, 30, 30});
3142 auto expected = expected_builder.Build();
3143
3144 test(cull_rect, expected);
3145 }
3146
3147 { // All 4 rects
3148 SkIRect cull_rect = {9, 9, 21, 21};
3149
3150 test(cull_rect, main);
3151 }
3152}
3153
3154TEST_F(DisplayListTest, DrawSaveDrawCannotInheritOpacity) {
3156 builder.DrawCircle({10, 10}, 5, DlPaint());
3157 builder.Save();
3158 builder.ClipRect({0, 0, 20, 20}, DlCanvas::ClipOp::kIntersect, false);
3159 builder.DrawRect({5, 5, 15, 15}, DlPaint());
3160 builder.Restore();
3161 auto display_list = builder.Build();
3162
3163 ASSERT_FALSE(display_list->can_apply_group_opacity());
3164}
3165
3166TEST_F(DisplayListTest, DrawUnorderedRect) {
3167 auto renderer = [](DlCanvas& canvas, DlPaint& paint, SkRect& rect) {
3168 canvas.DrawRect(rect, paint);
3169 };
3170 check_inverted_bounds(renderer, "DrawRect");
3171}
3172
3173TEST_F(DisplayListTest, DrawUnorderedRoundRect) {
3174 auto renderer = [](DlCanvas& canvas, DlPaint& paint, SkRect& rect) {
3175 canvas.DrawRRect(SkRRect::MakeRectXY(rect, 2.0f, 2.0f), paint);
3176 };
3177 check_inverted_bounds(renderer, "DrawRoundRect");
3178}
3179
3180TEST_F(DisplayListTest, DrawUnorderedOval) {
3181 auto renderer = [](DlCanvas& canvas, DlPaint& paint, SkRect& rect) {
3182 canvas.DrawOval(rect, paint);
3183 };
3184 check_inverted_bounds(renderer, "DrawOval");
3185}
3186
3187TEST_F(DisplayListTest, DrawUnorderedRectangularPath) {
3188 auto renderer = [](DlCanvas& canvas, DlPaint& paint, SkRect& rect) {
3189 canvas.DrawPath(SkPath().addRect(rect), paint);
3190 };
3191 check_inverted_bounds(renderer, "DrawRectangularPath");
3192}
3193
3194TEST_F(DisplayListTest, DrawUnorderedOvalPath) {
3195 auto renderer = [](DlCanvas& canvas, DlPaint& paint, SkRect& rect) {
3196 canvas.DrawPath(SkPath().addOval(rect), paint);
3197 };
3198 check_inverted_bounds(renderer, "DrawOvalPath");
3199}
3200
3201TEST_F(DisplayListTest, DrawUnorderedRoundRectPathCW) {
3202 auto renderer = [](DlCanvas& canvas, DlPaint& paint, SkRect& rect) {
3203 SkPath path = SkPath() //
3204 .addRoundRect(rect, 2.0f, 2.0f, SkPathDirection::kCW);
3205 canvas.DrawPath(path, paint);
3206 };
3207 check_inverted_bounds(renderer, "DrawRoundRectPath Clockwise");
3208}
3209
3210TEST_F(DisplayListTest, DrawUnorderedRoundRectPathCCW) {
3211 auto renderer = [](DlCanvas& canvas, DlPaint& paint, SkRect& rect) {
3212 SkPath path = SkPath() //
3214 canvas.DrawPath(path, paint);
3215 };
3216 check_inverted_bounds(renderer, "DrawRoundRectPath Counter-Clockwise");
3217}
3218
3219TEST_F(DisplayListTest, NopOperationsOmittedFromRecords) {
3220 auto run_tests = [](const std::string& name,
3222 uint32_t expected_op_count = 0u,
3223 uint32_t expected_total_depth = 0u) {
3224 auto run_one_test =
3225 [init](const std::string& name,
3227 uint32_t expected_op_count = 0u,
3228 uint32_t expected_total_depth = 0u) {
3230 DlPaint paint;
3231 init(builder, paint);
3233 auto list = builder.Build();
3234 if (list->op_count() != expected_op_count) {
3235 FML_LOG(ERROR) << *list;
3236 }
3237 ASSERT_EQ(list->op_count(), expected_op_count) << name;
3238 EXPECT_EQ(list->total_depth(), expected_total_depth) << name;
3239 ASSERT_TRUE(list->bounds().isEmpty()) << name;
3240 };
3241 run_one_test(
3242 name + " DrawColor",
3244 builder.DrawColor(paint.getColor(), paint.getBlendMode());
3245 },
3246 expected_op_count, expected_total_depth);
3247 run_one_test(
3248 name + " DrawPaint",
3250 builder.DrawPaint(paint);
3251 },
3252 expected_op_count, expected_total_depth);
3253 run_one_test(
3254 name + " DrawRect",
3256 builder.DrawRect({10, 10, 20, 20}, paint);
3257 },
3258 expected_op_count, expected_total_depth);
3259 run_one_test(
3260 name + " Other Draw Ops",
3262 builder.DrawLine({10, 10}, {20, 20}, paint);
3263 builder.DrawOval({10, 10, 20, 20}, paint);
3264 builder.DrawCircle({50, 50}, 20, paint);
3265 builder.DrawRRect(SkRRect::MakeRectXY({10, 10, 20, 20}, 5, 5), paint);
3266 builder.DrawDRRect(SkRRect::MakeRectXY({5, 5, 100, 100}, 5, 5),
3267 SkRRect::MakeRectXY({10, 10, 20, 20}, 5, 5),
3268 paint);
3269 builder.DrawPath(kTestPath1, paint);
3270 builder.DrawArc({10, 10, 20, 20}, 45, 90, true, paint);
3271 SkPoint pts[] = {{10, 10}, {20, 20}};
3272 builder.DrawPoints(PointMode::kLines, 2, pts, paint);
3274 builder.DrawImage(TestImage1, {10, 10}, DlImageSampling::kLinear,
3275 &paint);
3276 builder.DrawImageRect(TestImage1, SkRect{0.0f, 0.0f, 10.0f, 10.0f},
3277 SkRect{10.0f, 10.0f, 25.0f, 25.0f},
3279 builder.DrawImageNine(TestImage1, {10, 10, 20, 20},
3280 {10, 10, 100, 100}, DlFilterMode::kLinear,
3281 &paint);
3282 SkRSXform xforms[] = {{1, 0, 10, 10}, {0, 1, 10, 10}};
3283 SkRect rects[] = {{10, 10, 20, 20}, {10, 20, 30, 20}};
3284 builder.DrawAtlas(TestImage1, xforms, rects, nullptr, 2,
3286 nullptr, &paint);
3287 builder.DrawTextBlob(GetTestTextBlob(1), 10, 10, paint);
3288
3289 // Dst mode eliminates most rendering ops except for
3290 // the following two, so we'll prune those manually...
3291 if (paint.getBlendMode() != DlBlendMode::kDst) {
3292 builder.DrawDisplayList(TestDisplayList1, paint.getOpacity());
3293 builder.DrawShadow(kTestPath1, paint.getColor(), 1, true, 1);
3294 }
3295 },
3296 expected_op_count, expected_total_depth);
3297 run_one_test(
3298 name + " SaveLayer",
3300 builder.SaveLayer(nullptr, &paint, nullptr);
3301 builder.DrawRect({10, 10, 20, 20}, DlPaint());
3302 builder.Restore();
3303 },
3304 expected_op_count, expected_total_depth);
3305 run_one_test(
3306 name + " inside Save",
3308 builder.Save();
3309 builder.DrawRect({10, 10, 20, 20}, paint);
3310 builder.Restore();
3311 },
3312 expected_op_count, expected_total_depth);
3313 };
3314 run_tests("transparent color", //
3316 paint.setColor(DlColor::kTransparent());
3317 });
3318 run_tests("0 alpha", //
3320 // The transparent test above already tested transparent
3321 // black (all 0s), we set White color here so we can test
3322 // the case of all 1s with a 0 alpha
3323 paint.setColor(DlColor::kWhite());
3324 paint.setAlpha(0);
3325 });
3326 run_tests("BlendMode::kDst", //
3328 paint.setBlendMode(DlBlendMode::kDst);
3329 });
3330 run_tests("Empty rect clip", //
3332 builder.ClipRect(SkRect::MakeEmpty(), ClipOp::kIntersect, false);
3333 });
3334 run_tests("Empty rrect clip", //
3337 false);
3338 });
3339 run_tests("Empty path clip", //
3341 builder.ClipPath(SkPath(), ClipOp::kIntersect, false);
3342 });
3343 run_tests("Transparent SaveLayer", //
3345 DlPaint save_paint;
3346 save_paint.setColor(DlColor::kTransparent());
3347 builder.SaveLayer(nullptr, &save_paint);
3348 });
3349 run_tests("0 alpha SaveLayer", //
3351 DlPaint save_paint;
3352 // The transparent test above already tested transparent
3353 // black (all 0s), we set White color here so we can test
3354 // the case of all 1s with a 0 alpha
3355 save_paint.setColor(DlColor::kWhite());
3356 save_paint.setAlpha(0);
3357 builder.SaveLayer(nullptr, &save_paint);
3358 });
3359 run_tests("Dst blended SaveLayer", //
3361 DlPaint save_paint;
3362 save_paint.setBlendMode(DlBlendMode::kDst);
3363 builder.SaveLayer(nullptr, &save_paint);
3364 });
3365 run_tests(
3366 "Nop inside SaveLayer",
3368 builder.SaveLayer(nullptr, nullptr);
3369 paint.setBlendMode(DlBlendMode::kDst);
3370 },
3371 2u, 1u);
3372 run_tests("DrawImage inside Culled SaveLayer", //
3374 DlPaint save_paint;
3375 save_paint.setColor(DlColor::kTransparent());
3376 builder.SaveLayer(nullptr, &save_paint);
3377 builder.DrawImage(TestImage1, {10, 10}, DlImageSampling::kLinear);
3378 });
3379}
3380
3381TEST_F(DisplayListTest, ImpellerPathPreferenceIsHonored) {
3382 class Tester : public virtual DlOpReceiver,
3387 public:
3388 explicit Tester(bool prefer_impeller_paths)
3389 : prefer_impeller_paths_(prefer_impeller_paths) {}
3390
3391 bool PrefersImpellerPaths() const override {
3392 return prefer_impeller_paths_;
3393 }
3394
3395 void drawPath(const SkPath& path) override { skia_draw_path_calls_++; }
3396
3397 void drawPath(const CacheablePath& cache) override {
3398 impeller_draw_path_calls_++;
3399 }
3400
3401 void clipPath(const SkPath& path, ClipOp op, bool is_aa) override {
3402 skia_clip_path_calls_++;
3403 }
3404
3405 void clipPath(const CacheablePath& cache, ClipOp op, bool is_aa) override {
3406 impeller_clip_path_calls_++;
3407 }
3408
3409 virtual void drawShadow(const SkPath& sk_path,
3410 const DlColor color,
3411 const SkScalar elevation,
3412 bool transparent_occluder,
3413 SkScalar dpr) override {
3414 skia_draw_shadow_calls_++;
3415 }
3416
3417 virtual void drawShadow(const CacheablePath& cache,
3418 const DlColor color,
3419 const SkScalar elevation,
3420 bool transparent_occluder,
3421 SkScalar dpr) override {
3422 impeller_draw_shadow_calls_++;
3423 }
3424
3425 int skia_draw_path_calls() const { return skia_draw_path_calls_; }
3426 int skia_clip_path_calls() const { return skia_draw_path_calls_; }
3427 int skia_draw_shadow_calls() const { return skia_draw_path_calls_; }
3428 int impeller_draw_path_calls() const { return impeller_draw_path_calls_; }
3429 int impeller_clip_path_calls() const { return impeller_draw_path_calls_; }
3430 int impeller_draw_shadow_calls() const { return impeller_draw_path_calls_; }
3431
3432 private:
3433 const bool prefer_impeller_paths_;
3434 int skia_draw_path_calls_ = 0;
3435 int skia_clip_path_calls_ = 0;
3436 int skia_draw_shadow_calls_ = 0;
3437 int impeller_draw_path_calls_ = 0;
3438 int impeller_clip_path_calls_ = 0;
3439 int impeller_draw_shadow_calls_ = 0;
3440 };
3441
3443 builder.DrawPath(SkPath::Rect(SkRect::MakeLTRB(0, 0, 100, 100)), DlPaint());
3444 builder.ClipPath(SkPath::Rect(SkRect::MakeLTRB(0, 0, 100, 100)),
3445 ClipOp::kIntersect, true);
3446 builder.DrawShadow(SkPath::Rect(SkRect::MakeLTRB(20, 20, 80, 80)),
3447 DlColor::kBlue(), 1.0f, true, 1.0f);
3448 auto display_list = builder.Build();
3449
3450 {
3451 Tester skia_tester(false);
3452 display_list->Dispatch(skia_tester);
3453 EXPECT_EQ(skia_tester.skia_draw_path_calls(), 1);
3454 EXPECT_EQ(skia_tester.skia_clip_path_calls(), 1);
3455 EXPECT_EQ(skia_tester.skia_draw_shadow_calls(), 1);
3456 EXPECT_EQ(skia_tester.impeller_draw_path_calls(), 0);
3457 EXPECT_EQ(skia_tester.impeller_clip_path_calls(), 0);
3458 EXPECT_EQ(skia_tester.impeller_draw_shadow_calls(), 0);
3459 }
3460
3461 {
3462 Tester impeller_tester(true);
3463 display_list->Dispatch(impeller_tester);
3464 EXPECT_EQ(impeller_tester.skia_draw_path_calls(), 0);
3465 EXPECT_EQ(impeller_tester.skia_clip_path_calls(), 0);
3466 EXPECT_EQ(impeller_tester.skia_draw_shadow_calls(), 0);
3467 EXPECT_EQ(impeller_tester.impeller_draw_path_calls(), 1);
3468 EXPECT_EQ(impeller_tester.impeller_clip_path_calls(), 1);
3469 EXPECT_EQ(impeller_tester.impeller_draw_shadow_calls(), 1);
3470 }
3471}
3472
3478 public:
3480
3482 expected_.emplace_back(BoundsExpectation{
3483 .bounds = bounds,
3484 .options = SaveLayerOptions(),
3485 });
3486 return *this;
3487 }
3488
3490 bool clipped = false) {
3492 options = options.with_bounds_from_caller();
3493 if (clipped) {
3494 options = options.with_content_is_clipped();
3495 }
3496 expected_.emplace_back(BoundsExpectation{
3497 .bounds = bounds,
3498 .options = options,
3499 });
3500 return *this;
3501 }
3502
3505 const DlImageFilter* backdrop) override {
3506 ASSERT_LT(save_layer_count_, expected_.size());
3507 auto expected = expected_[save_layer_count_];
3508 EXPECT_EQ(options.bounds_from_caller(),
3509 expected.options.bounds_from_caller())
3510 << "expected bounds index " << save_layer_count_;
3511 EXPECT_EQ(options.content_is_clipped(),
3512 expected.options.content_is_clipped())
3513 << "expected bounds index " << save_layer_count_;
3514 if (!SkScalarNearlyEqual(bounds.fLeft, expected.bounds.fLeft) ||
3515 !SkScalarNearlyEqual(bounds.fTop, expected.bounds.fTop) ||
3516 !SkScalarNearlyEqual(bounds.fRight, expected.bounds.fRight) ||
3517 !SkScalarNearlyEqual(bounds.fBottom, expected.bounds.fBottom)) {
3518 EXPECT_EQ(bounds, expected.bounds)
3519 << "expected bounds index " << save_layer_count_;
3520 }
3521 save_layer_count_++;
3522 }
3523
3524 bool all_bounds_checked() const {
3525 return save_layer_count_ == expected_.size();
3526 }
3527
3528 private:
3529 struct BoundsExpectation {
3530 const SkRect bounds;
3532 };
3533
3534 std::vector<BoundsExpectation> expected_;
3535 size_t save_layer_count_ = 0;
3536};
3537
3538TEST_F(DisplayListTest, SaveLayerBoundsComputationOfSimpleRect) {
3539 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3540
3542 builder.SaveLayer(nullptr, nullptr);
3543 { //
3544 builder.DrawRect(rect, DlPaint());
3545 }
3546 builder.Restore();
3547 auto display_list = builder.Build();
3548
3549 SaveLayerBoundsExpector expector;
3550 expector.addComputedExpectation(rect);
3551 display_list->Dispatch(expector);
3552 EXPECT_TRUE(expector.all_bounds_checked());
3553}
3554
3555TEST_F(DisplayListTest, SaveLayerBoundsComputationOfMaskBlurredRect) {
3556 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3559 draw_paint.setMaskFilter(mask_filter);
3560
3562 builder.SaveLayer(nullptr, nullptr);
3563 { //
3564 builder.DrawRect(rect, draw_paint);
3565 }
3566 builder.Restore();
3567 auto display_list = builder.Build();
3568
3569 SaveLayerBoundsExpector expector;
3570 expector.addComputedExpectation(rect.makeOutset(6.0f, 6.0f));
3571 display_list->Dispatch(expector);
3572 EXPECT_TRUE(expector.all_bounds_checked());
3573}
3574
3575TEST_F(DisplayListTest, SaveLayerBoundsComputationOfImageBlurredRect) {
3576 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3578 auto image_filter = DlBlurImageFilter::Make(2.0f, 3.0f, DlTileMode::kDecal);
3579 draw_paint.setImageFilter(image_filter);
3580
3582 builder.SaveLayer(nullptr, nullptr);
3583 { //
3584 builder.DrawRect(rect, draw_paint);
3585 }
3586 builder.Restore();
3587 auto display_list = builder.Build();
3588
3589 SaveLayerBoundsExpector expector;
3590 expector.addComputedExpectation(rect.makeOutset(6.0f, 9.0f));
3591 display_list->Dispatch(expector);
3592 EXPECT_TRUE(expector.all_bounds_checked());
3593}
3594
3595TEST_F(DisplayListTest, SaveLayerBoundsComputationOfStrokedRect) {
3596 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3598 draw_paint.setStrokeWidth(5.0f);
3599 draw_paint.setDrawStyle(DlDrawStyle::kStroke);
3600
3602 builder.SaveLayer(nullptr, nullptr);
3603 { //
3604 builder.DrawRect(rect, draw_paint);
3605 }
3606 builder.Restore();
3607 auto display_list = builder.Build();
3608
3609 SaveLayerBoundsExpector expector;
3610 expector.addComputedExpectation(rect.makeOutset(2.5f, 2.5f));
3611 display_list->Dispatch(expector);
3612 EXPECT_TRUE(expector.all_bounds_checked());
3613}
3614
3615TEST_F(DisplayListTest, TranslatedSaveLayerBoundsComputationOfSimpleRect) {
3616 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3617
3619 builder.Translate(10.0f, 10.0f);
3620 builder.SaveLayer(nullptr, nullptr);
3621 { //
3622 builder.DrawRect(rect, DlPaint());
3623 }
3624 builder.Restore();
3625 auto display_list = builder.Build();
3626
3627 SaveLayerBoundsExpector expector;
3628 expector.addComputedExpectation(rect);
3629 display_list->Dispatch(expector);
3630 EXPECT_TRUE(expector.all_bounds_checked());
3631}
3632
3633TEST_F(DisplayListTest, ScaledSaveLayerBoundsComputationOfSimpleRect) {
3634 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3635
3637 builder.Scale(10.0f, 10.0f);
3638 builder.SaveLayer(nullptr, nullptr);
3639 { //
3640 builder.DrawRect(rect, DlPaint());
3641 }
3642 builder.Restore();
3643 auto display_list = builder.Build();
3644
3645 SaveLayerBoundsExpector expector;
3646 expector.addComputedExpectation(rect);
3647 display_list->Dispatch(expector);
3648 EXPECT_TRUE(expector.all_bounds_checked());
3649}
3650
3651TEST_F(DisplayListTest, RotatedSaveLayerBoundsComputationOfSimpleRect) {
3652 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3653
3655 builder.Rotate(45.0f);
3656 builder.SaveLayer(nullptr, nullptr);
3657 { //
3658 builder.DrawRect(rect, DlPaint());
3659 }
3660 builder.Restore();
3661 auto display_list = builder.Build();
3662
3663 SaveLayerBoundsExpector expector;
3664 expector.addComputedExpectation(rect);
3665 display_list->Dispatch(expector);
3666 EXPECT_TRUE(expector.all_bounds_checked());
3667}
3668
3669TEST_F(DisplayListTest, TransformResetSaveLayerBoundsComputationOfSimpleRect) {
3670 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3671 SkRect rect_doubled = SkMatrix::Scale(2.0f, 2.0f).mapRect(rect);
3672
3674 builder.Scale(10.0f, 10.0f);
3675 builder.SaveLayer(nullptr, nullptr);
3676 builder.TransformReset();
3677 builder.Scale(20.0f, 20.0f);
3678 // Net local transform for saveLayer is Scale(2, 2)
3679 { //
3680 builder.DrawRect(rect, DlPaint());
3681 }
3682 builder.Restore();
3683 auto display_list = builder.Build();
3684
3685 SaveLayerBoundsExpector expector;
3686 expector.addComputedExpectation(rect_doubled);
3687 display_list->Dispatch(expector);
3688 EXPECT_TRUE(expector.all_bounds_checked());
3689}
3690
3691TEST_F(DisplayListTest, SaveLayerBoundsComputationOfTranslatedSimpleRect) {
3692 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3693
3695 builder.SaveLayer(nullptr, nullptr);
3696 { //
3697 builder.Translate(10.0f, 10.0f);
3698 builder.DrawRect(rect, DlPaint());
3699 }
3700 builder.Restore();
3701 auto display_list = builder.Build();
3702
3703 SaveLayerBoundsExpector expector;
3704 expector.addComputedExpectation(rect.makeOffset(10.0f, 10.0f));
3705 display_list->Dispatch(expector);
3706 EXPECT_TRUE(expector.all_bounds_checked());
3707}
3708
3709TEST_F(DisplayListTest, SaveLayerBoundsComputationOfScaledSimpleRect) {
3710 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3711
3713 builder.SaveLayer(nullptr, nullptr);
3714 { //
3715 builder.Scale(10.0f, 10.0f);
3716 builder.DrawRect(rect, DlPaint());
3717 }
3718 builder.Restore();
3719 auto display_list = builder.Build();
3720
3721 SaveLayerBoundsExpector expector;
3722 expector.addComputedExpectation(
3723 SkRect::MakeLTRB(1000.0f, 1000.0f, 2000.0f, 2000.0f));
3724 display_list->Dispatch(expector);
3725 EXPECT_TRUE(expector.all_bounds_checked());
3726}
3727
3728TEST_F(DisplayListTest, SaveLayerBoundsComputationOfRotatedSimpleRect) {
3729 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3730
3732 builder.SaveLayer(nullptr, nullptr);
3733 { //
3734 builder.Rotate(45.0f);
3735 builder.DrawRect(rect, DlPaint());
3736 }
3737 builder.Restore();
3738 auto display_list = builder.Build();
3739
3741 SaveLayerBoundsExpector expector;
3742 expector.addComputedExpectation(matrix.mapRect(rect));
3743 display_list->Dispatch(expector);
3744 EXPECT_TRUE(expector.all_bounds_checked());
3745}
3746
3747TEST_F(DisplayListTest, SaveLayerBoundsComputationOfNestedSimpleRect) {
3748 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3749
3751 builder.SaveLayer(nullptr, nullptr);
3752 { //
3753 builder.SaveLayer(nullptr, nullptr);
3754 { //
3755 builder.DrawRect(rect, DlPaint());
3756 }
3757 builder.Restore();
3758 }
3759 builder.Restore();
3760 auto display_list = builder.Build();
3761
3762 SaveLayerBoundsExpector expector;
3763 expector.addComputedExpectation(rect);
3764 expector.addComputedExpectation(rect);
3765 display_list->Dispatch(expector);
3766 EXPECT_TRUE(expector.all_bounds_checked());
3767}
3768
3769TEST_F(DisplayListTest, FloodingSaveLayerBoundsComputationOfSimpleRect) {
3770 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3771 DlPaint save_paint;
3772 auto color_filter =
3774 ASSERT_TRUE(color_filter->modifies_transparent_black());
3775 save_paint.setColorFilter(color_filter);
3776 SkRect clip_rect = rect.makeOutset(100.0f, 100.0f);
3777 ASSERT_NE(clip_rect, rect);
3778 ASSERT_TRUE(clip_rect.contains(rect));
3779
3781 builder.ClipRect(clip_rect);
3782 builder.SaveLayer(nullptr, &save_paint);
3783 { //
3784 builder.DrawRect(rect, DlPaint());
3785 }
3786 builder.Restore();
3787 auto display_list = builder.Build();
3788
3789 SaveLayerBoundsExpector expector;
3790 expector.addComputedExpectation(rect);
3791 display_list->Dispatch(expector);
3792 EXPECT_TRUE(expector.all_bounds_checked());
3793}
3794
3795TEST_F(DisplayListTest, NestedFloodingSaveLayerBoundsComputationOfSimpleRect) {
3796 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3797 DlPaint save_paint;
3798 auto color_filter =
3800 ASSERT_TRUE(color_filter->modifies_transparent_black());
3801 save_paint.setColorFilter(color_filter);
3802 SkRect clip_rect = rect.makeOutset(100.0f, 100.0f);
3803 ASSERT_NE(clip_rect, rect);
3804 ASSERT_TRUE(clip_rect.contains(rect));
3805
3807 builder.ClipRect(clip_rect);
3808 builder.SaveLayer(nullptr, nullptr);
3809 {
3810 builder.SaveLayer(nullptr, &save_paint);
3811 { //
3812 builder.DrawRect(rect, DlPaint());
3813 }
3814 builder.Restore();
3815 }
3816 builder.Restore();
3817 auto display_list = builder.Build();
3818
3819 EXPECT_EQ(display_list->bounds(), clip_rect);
3820
3821 SaveLayerBoundsExpector expector;
3822 expector.addComputedExpectation(clip_rect);
3823 expector.addComputedExpectation(rect);
3824 display_list->Dispatch(expector);
3825 EXPECT_TRUE(expector.all_bounds_checked());
3826}
3827
3828TEST_F(DisplayListTest, SaveLayerBoundsComputationOfFloodingImageFilter) {
3829 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3831 auto color_filter =
3833 ASSERT_TRUE(color_filter->modifies_transparent_black());
3834 auto image_filter = DlColorFilterImageFilter::Make(color_filter);
3835 draw_paint.setImageFilter(image_filter);
3836 SkRect clip_rect = rect.makeOutset(100.0f, 100.0f);
3837 ASSERT_NE(clip_rect, rect);
3838 ASSERT_TRUE(clip_rect.contains(rect));
3839
3841 builder.ClipRect(clip_rect);
3842 builder.SaveLayer(nullptr, nullptr);
3843 { //
3844 builder.DrawRect(rect, draw_paint);
3845 }
3846 builder.Restore();
3847 auto display_list = builder.Build();
3848
3849 SaveLayerBoundsExpector expector;
3850 expector.addComputedExpectation(clip_rect);
3851 display_list->Dispatch(expector);
3852 EXPECT_TRUE(expector.all_bounds_checked());
3853}
3854
3855TEST_F(DisplayListTest, SaveLayerBoundsComputationOfFloodingColorFilter) {
3856 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3858 auto color_filter =
3860 ASSERT_TRUE(color_filter->modifies_transparent_black());
3861 draw_paint.setColorFilter(color_filter);
3862 SkRect clip_rect = rect.makeOutset(100.0f, 100.0f);
3863 ASSERT_NE(clip_rect, rect);
3864 ASSERT_TRUE(clip_rect.contains(rect));
3865
3867 builder.ClipRect(clip_rect);
3868 builder.SaveLayer(nullptr, nullptr);
3869 { //
3870 builder.DrawRect(rect, draw_paint);
3871 }
3872 builder.Restore();
3873 auto display_list = builder.Build();
3874
3875 // A color filter is implicitly clipped to the draw bounds so the layer
3876 // bounds will be the same as the draw bounds.
3877 SaveLayerBoundsExpector expector;
3878 expector.addComputedExpectation(rect);
3879 display_list->Dispatch(expector);
3880 EXPECT_TRUE(expector.all_bounds_checked());
3881}
3882
3883TEST_F(DisplayListTest, SaveLayerBoundsClipDetectionSimpleUnclippedRect) {
3884 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3885 SkRect save_rect = SkRect::MakeLTRB(50.0f, 50.0f, 250.0f, 250.0f);
3886
3888 builder.SaveLayer(&save_rect, nullptr);
3889 { //
3890 builder.DrawRect(rect, DlPaint());
3891 }
3892 builder.Restore();
3893 auto display_list = builder.Build();
3894
3895 SaveLayerBoundsExpector expector;
3896 expector.addSuppliedExpectation(rect);
3897 display_list->Dispatch(expector);
3898 EXPECT_TRUE(expector.all_bounds_checked());
3899}
3900
3901TEST_F(DisplayListTest, SaveLayerBoundsClipDetectionSimpleClippedRect) {
3902 SkRect rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3903 SkRect save_rect = SkRect::MakeLTRB(50.0f, 50.0f, 110.0f, 110.0f);
3904 SkRect content_rect = SkRect::MakeLTRB(100.0f, 100.0f, 110.0f, 110.0f);
3905
3907 builder.SaveLayer(&save_rect, nullptr);
3908 { //
3909 builder.DrawRect(rect, DlPaint());
3910 }
3911 builder.Restore();
3912 auto display_list = builder.Build();
3913
3914 SaveLayerBoundsExpector expector;
3915 expector.addSuppliedExpectation(content_rect, true);
3916 display_list->Dispatch(expector);
3917 EXPECT_TRUE(expector.all_bounds_checked());
3918}
3919
3920class DepthExpector : public virtual DlOpReceiver,
3924 virtual IgnoreDrawDispatchHelper {
3925 public:
3926 explicit DepthExpector(std::vector<uint32_t> expectations)
3927 : depth_expectations_(std::move(expectations)) {}
3928
3929 void save() override {
3930 // This method should not be called since we override the variant with
3931 // the total_content_depth parameter.
3932 FAIL() << "save(no depth parameter) method should not be called";
3933 }
3934
3935 void save(uint32_t total_content_depth) override {
3936 ASSERT_LT(index_, depth_expectations_.size());
3937 EXPECT_EQ(depth_expectations_[index_], total_content_depth)
3938 << "at index " << index_;
3939 index_++;
3940 }
3941
3944 const DlImageFilter* backdrop) override {
3945 // This method should not be called since we override the variant with
3946 // the total_content_depth parameter.
3947 FAIL() << "saveLayer(no depth parameter) method should not be called";
3948 }
3949
3952 uint32_t total_content_depth,
3953 DlBlendMode max_content_mode,
3954 const DlImageFilter* backdrop) override {
3955 ASSERT_LT(index_, depth_expectations_.size());
3956 EXPECT_EQ(depth_expectations_[index_], total_content_depth)
3957 << "at index " << index_;
3958 index_++;
3959 }
3960
3961 bool all_depths_checked() const {
3962 return index_ == depth_expectations_.size();
3963 }
3964
3965 private:
3966 size_t index_ = 0;
3967 std::vector<uint32_t> depth_expectations_;
3968};
3969
3970TEST_F(DisplayListTest, SaveContentDepthTest) {
3971 DisplayListBuilder child_builder;
3972 child_builder.DrawRect({10, 10, 20, 20}, DlPaint()); // depth 1
3973 auto child = child_builder.Build();
3974
3976 builder.DrawRect({10, 10, 20, 20}, DlPaint()); // depth 1
3977
3978 builder.Save(); // covers depth 1->9
3979 {
3980 builder.Translate(5, 5); // triggers deferred save at depth 1
3981 builder.DrawRect({10, 10, 20, 20}, DlPaint()); // depth 2
3982
3983 builder.DrawDisplayList(child, 1.0f); // depth 3 (content) + 4 (self)
3984
3985 builder.SaveLayer(nullptr, nullptr); // covers depth 5->6
3986 {
3987 builder.DrawRect({12, 12, 22, 22}, DlPaint()); // depth 5
3988 builder.DrawRect({14, 14, 24, 24}, DlPaint()); // depth 6
3989 }
3990 builder.Restore(); // layer is restored with depth 6
3991
3992 builder.DrawRect({16, 16, 26, 26}, DlPaint()); // depth 8
3993 builder.DrawRect({18, 18, 28, 28}, DlPaint()); // depth 9
3994 }
3995 builder.Restore(); // save is restored with depth 9
3996
3997 builder.DrawRect({16, 16, 26, 26}, DlPaint()); // depth 10
3998 builder.DrawRect({18, 18, 28, 28}, DlPaint()); // depth 11
3999 auto display_list = builder.Build();
4000
4001 EXPECT_EQ(display_list->total_depth(), 11u);
4002
4003 DepthExpector expector({8, 2});
4004 display_list->Dispatch(expector);
4005}
4006
4007TEST_F(DisplayListTest, FloodingFilteredLayerPushesRestoreOpIndex) {
4009 builder.ClipRect(SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f));
4010 // ClipRect does not contribute to rtree rects, no id needed
4011
4012 DlPaint save_paint;
4013 // clang-format off
4014 const float matrix[] = {
4015 0.5f, 0.0f, 0.0f, 0.0f, 0.5f,
4016 0.5f, 0.0f, 0.0f, 0.0f, 0.5f,
4017 0.5f, 0.0f, 0.0f, 0.0f, 0.5f,
4018 0.5f, 0.0f, 0.0f, 0.0f, 0.5f
4019 };
4020 // clang-format on
4023 builder.SaveLayer(nullptr, &save_paint);
4024 int save_layer_id = DisplayListBuilderTestingLastOpIndex(builder);
4025
4026 builder.DrawRect(SkRect::MakeLTRB(120.0f, 120.0f, 125.0f, 125.0f), DlPaint());
4028
4029 builder.Restore();
4031
4032 auto dl = builder.Build();
4033 std::vector<int> indices;
4034 dl->rtree()->search(SkRect::MakeLTRB(0.0f, 0.0f, 500.0f, 500.0f), &indices);
4035 ASSERT_EQ(indices.size(), 3u);
4036 EXPECT_EQ(dl->rtree()->id(indices[0]), save_layer_id);
4037 EXPECT_EQ(dl->rtree()->id(indices[1]), draw_rect_id);
4038 EXPECT_EQ(dl->rtree()->id(indices[2]), restore_id);
4039}
4040
4041TEST_F(DisplayListTest, TransformingFilterSaveLayerSimpleContentBounds) {
4043 builder.ClipRect(SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f));
4044
4045 DlPaint save_paint;
4046 auto image_filter = DlMatrixImageFilter::Make(
4048 save_paint.setImageFilter(image_filter);
4049 builder.SaveLayer(nullptr, &save_paint);
4050
4051 builder.DrawRect(SkRect::MakeLTRB(20.0f, 20.0f, 25.0f, 25.0f), DlPaint());
4052
4053 builder.Restore();
4054
4055 auto dl = builder.Build();
4056 EXPECT_EQ(dl->bounds(), SkRect::MakeLTRB(120.0f, 120.0f, 125.0f, 125.0f));
4057}
4058
4059TEST_F(DisplayListTest, TransformingFilterSaveLayerFloodedContentBounds) {
4061 builder.ClipRect(SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f));
4062
4063 DlPaint save_paint;
4064 auto image_filter = DlMatrixImageFilter::Make(
4066 save_paint.setImageFilter(image_filter);
4067 builder.SaveLayer(nullptr, &save_paint);
4068
4070
4071 builder.Restore();
4072
4073 auto dl = builder.Build();
4074 EXPECT_EQ(dl->bounds(), SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f));
4075}
4076
4077TEST_F(DisplayListTest, OpacityIncompatibleRenderOpInsideDeferredSave) {
4078 {
4079 // Without deferred save
4081 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4082 DlPaint().setBlendMode(DlBlendMode::kClear));
4083 EXPECT_FALSE(builder.Build()->can_apply_group_opacity());
4084 }
4085
4086 {
4087 // With deferred save
4089 builder.Save();
4090 {
4091 // Nothing to trigger the deferred save...
4092 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4093 DlPaint().setBlendMode(DlBlendMode::kClear));
4094 }
4095 // Deferred save was not triggered, did it forward the incompatibility
4096 // flags?
4097 builder.Restore();
4098 EXPECT_FALSE(builder.Build()->can_apply_group_opacity());
4099 }
4100}
4101
4102TEST_F(DisplayListTest, MaxBlendModeEmptyDisplayList) {
4104 EXPECT_EQ(builder.Build()->max_root_blend_mode(), DlBlendMode::kClear);
4105}
4106
4107TEST_F(DisplayListTest, MaxBlendModeSimpleRect) {
4108 auto test = [](DlBlendMode mode) {
4110 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4112 DlBlendMode expect =
4114 EXPECT_EQ(builder.Build()->max_root_blend_mode(), expect) //
4115 << "testing " << mode;
4116 };
4117
4118 for (int i = 0; i < static_cast<int>(DlBlendMode::kLastMode); i++) {
4119 test(static_cast<DlBlendMode>(i));
4120 }
4121}
4122
4123TEST_F(DisplayListTest, MaxBlendModeInsideNonDeferredSave) {
4125 builder.Save();
4126 {
4127 // Trigger the deferred save
4128 builder.Scale(2.0f, 2.0f);
4129 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4130 DlPaint().setBlendMode(DlBlendMode::kModulate));
4131 }
4132 // Save was triggered, did it forward the max blend mode?
4133 builder.Restore();
4134 EXPECT_EQ(builder.Build()->max_root_blend_mode(), DlBlendMode::kModulate);
4135}
4136
4137TEST_F(DisplayListTest, MaxBlendModeInsideDeferredSave) {
4139 builder.Save();
4140 {
4141 // Nothing to trigger the deferred save...
4142 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4143 DlPaint().setBlendMode(DlBlendMode::kModulate));
4144 }
4145 // Deferred save was not triggered, did it forward the max blend mode?
4146 builder.Restore();
4147 EXPECT_EQ(builder.Build()->max_root_blend_mode(), DlBlendMode::kModulate);
4148}
4149
4150TEST_F(DisplayListTest, MaxBlendModeInsideSaveLayer) {
4152 builder.SaveLayer(nullptr, nullptr);
4153 {
4154 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4155 DlPaint().setBlendMode(DlBlendMode::kModulate));
4156 }
4157 builder.Restore();
4158 auto dl = builder.Build();
4159 EXPECT_EQ(dl->max_root_blend_mode(), DlBlendMode::kSrcOver);
4161 dl->Dispatch(expector);
4163}
4164
4165TEST_F(DisplayListTest, MaxBlendModeInsideNonDefaultBlendedSaveLayer) {
4167 DlPaint save_paint;
4169 builder.SaveLayer(nullptr, &save_paint);
4170 {
4171 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4172 DlPaint().setBlendMode(DlBlendMode::kModulate));
4173 }
4174 builder.Restore();
4175 auto dl = builder.Build();
4176 EXPECT_EQ(dl->max_root_blend_mode(), DlBlendMode::kScreen);
4178 dl->Dispatch(expector);
4180}
4181
4182TEST_F(DisplayListTest, MaxBlendModeInsideComplexDeferredSaves) {
4184 builder.Save();
4185 {
4186 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4187 DlPaint().setBlendMode(DlBlendMode::kModulate));
4188 builder.Save();
4189 {
4190 // We want to use a blend mode that is greater than modulate here
4192 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4193 DlPaint().setBlendMode(DlBlendMode::kScreen));
4194 }
4195 builder.Restore();
4196
4197 // We want to use a blend mode that is smaller than modulate here
4199 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4200 DlPaint().setBlendMode(DlBlendMode::kSrc));
4201 }
4202 builder.Restore();
4203
4204 // Double check that kScreen is the max blend mode
4206 expect = std::max(expect, DlBlendMode::kSrc);
4207 ASSERT_EQ(expect, DlBlendMode::kScreen);
4208
4209 EXPECT_EQ(builder.Build()->max_root_blend_mode(), DlBlendMode::kScreen);
4210}
4211
4212TEST_F(DisplayListTest, MaxBlendModeInsideComplexSaveLayers) {
4214 builder.SaveLayer(nullptr, nullptr);
4215 {
4216 // outer save layer has Modulate now and Src later - Modulate is larger
4217 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4218 DlPaint().setBlendMode(DlBlendMode::kModulate));
4219 builder.SaveLayer(nullptr, nullptr);
4220 {
4221 // inner save layer only has a Screen blend
4222 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4223 DlPaint().setBlendMode(DlBlendMode::kScreen));
4224 }
4225 builder.Restore();
4226
4227 // We want to use a blend mode that is smaller than modulate here
4229 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4230 DlPaint().setBlendMode(DlBlendMode::kSrc));
4231 }
4232 builder.Restore();
4233
4234 // Double check that kModulate is the max blend mode for the first
4235 // saveLayer operations
4237 ASSERT_EQ(expect, DlBlendMode::kModulate);
4238
4239 auto dl = builder.Build();
4240 EXPECT_EQ(dl->max_root_blend_mode(), DlBlendMode::kSrcOver);
4242 dl->Dispatch(expector);
4243 EXPECT_TRUE(expector.all_expectations_checked());
4244}
4245
4246TEST_F(DisplayListTest, BackdropDetectionEmptyDisplayList) {
4248 EXPECT_FALSE(builder.Build()->root_has_backdrop_filter());
4249}
4250
4251TEST_F(DisplayListTest, BackdropDetectionSimpleRect) {
4253 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10), DlPaint());
4254 EXPECT_FALSE(builder.Build()->root_has_backdrop_filter());
4255}
4256
4257TEST_F(DisplayListTest, BackdropDetectionSimpleSaveLayer) {
4259 builder.SaveLayer(nullptr, nullptr, &kTestBlurImageFilter1);
4260 {
4261 // inner content has no backdrop filter
4262 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10), DlPaint());
4263 }
4264 builder.Restore();
4265 auto dl = builder.Build();
4266
4267 EXPECT_TRUE(dl->root_has_backdrop_filter());
4268 // The saveLayer itself, though, does not have the contains backdrop
4269 // flag set because its content does not contain a saveLayer with backdrop
4270 SaveLayerExpector expector(
4271 SaveLayerOptions::kNoAttributes.with_can_distribute_opacity());
4272 dl->Dispatch(expector);
4274}
4275
4276TEST_F(DisplayListTest, BackdropDetectionNestedSaveLayer) {
4278 builder.SaveLayer(nullptr, nullptr);
4279 {
4280 // first inner content does have backdrop filter
4281 builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10), DlPaint());
4282 builder.SaveLayer(nullptr, nullptr, &kTestBlurImageFilter1);
4283 {
4284 // second inner content has no backdrop filter
4285 builder.DrawRect(SkRect::MakeLTRB(10, 10, 20, 20), DlPaint());
4286 }
4287 builder.Restore();
4288 }
4289 builder.Restore();
4290 auto dl = builder.Build();
4291
4292 EXPECT_FALSE(dl->root_has_backdrop_filter());
4293 SaveLayerExpector expector({
4295 SaveLayerOptions::kNoAttributes.with_can_distribute_opacity(),
4296 });
4297 dl->Dispatch(expector);
4298 EXPECT_TRUE(expector.all_expectations_checked());
4299}
4300
4301TEST_F(DisplayListTest, DrawDisplayListForwardsMaxBlend) {
4302 DisplayListBuilder child_builder;
4303 child_builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4304 DlPaint().setBlendMode(DlBlendMode::kMultiply));
4305 auto child_dl = child_builder.Build();
4306 EXPECT_EQ(child_dl->max_root_blend_mode(), DlBlendMode::kMultiply);
4307 EXPECT_FALSE(child_dl->root_has_backdrop_filter());
4308
4309 DisplayListBuilder parent_builder;
4310 parent_builder.DrawDisplayList(child_dl);
4311 auto parent_dl = parent_builder.Build();
4312 EXPECT_EQ(parent_dl->max_root_blend_mode(), DlBlendMode::kMultiply);
4313 EXPECT_FALSE(parent_dl->root_has_backdrop_filter());
4314}
4315
4316TEST_F(DisplayListTest, DrawDisplayListForwardsBackdropFlag) {
4317 DisplayListBuilder child_builder;
4319 child_builder.SaveLayer(nullptr, nullptr, &backdrop);
4320 child_builder.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10),
4321 DlPaint().setBlendMode(DlBlendMode::kMultiply));
4322 child_builder.Restore();
4323 auto child_dl = child_builder.Build();
4324 EXPECT_EQ(child_dl->max_root_blend_mode(), DlBlendMode::kSrcOver);
4325 EXPECT_TRUE(child_dl->root_has_backdrop_filter());
4326
4327 DisplayListBuilder parent_builder;
4328 parent_builder.DrawDisplayList(child_dl);
4329 auto parent_dl = parent_builder.Build();
4330 EXPECT_EQ(parent_dl->max_root_blend_mode(), DlBlendMode::kSrcOver);
4331 EXPECT_TRUE(parent_dl->root_has_backdrop_filter());
4332}
4333
4334#define CLIP_EXPECTOR(name) ClipExpector name(__FILE__, __LINE__)
4335
4336class ClipExpector : public virtual DlOpReceiver,
4339 virtual IgnoreDrawDispatchHelper {
4340 public:
4342 std::variant<SkRect, SkRRect, SkPath> shape;
4344 bool is_aa;
4345
4346 std::string shape_name() {
4347 switch (shape.index()) {
4348 case 0:
4349 return "SkRect";
4350 case 1:
4351 return "SkRRect";
4352 case 2:
4353 return "SkPath";
4354 default:
4355 return "Unknown";
4356 }
4357 }
4358 };
4359
4360 // file and line supplied automatically from CLIP_EXPECTOR macro
4361 explicit ClipExpector(const std::string& file, int line)
4362 : file_(file), line_(line) {}
4363
4365 EXPECT_EQ(index_, clip_expectations_.size()) << label();
4366 }
4367
4369 ClipOp clip_op = ClipOp::kIntersect,
4370 bool is_aa = false) {
4371 clip_expectations_.push_back({
4372 .shape = rect,
4373 .clip_op = clip_op,
4374 .is_aa = is_aa,
4375 });
4376 return *this;
4377 }
4378
4380 ClipOp clip_op = ClipOp::kIntersect,
4381 bool is_aa = false) {
4382 clip_expectations_.push_back({
4383 .shape = rrect,
4384 .clip_op = clip_op,
4385 .is_aa = is_aa,
4386 });
4387 return *this;
4388 }
4389
4391 ClipOp clip_op = ClipOp::kIntersect,
4392 bool is_aa = false) {
4393 clip_expectations_.push_back({
4394 .shape = path,
4395 .clip_op = clip_op,
4396 .is_aa = is_aa,
4397 });
4398 return *this;
4399 }
4400
4401 void clipRect(const SkRect& rect,
4402 DlCanvas::ClipOp clip_op,
4403 bool is_aa) override {
4404 check(rect, clip_op, is_aa);
4405 }
4407 DlCanvas::ClipOp clip_op,
4408 bool is_aa) override {
4409 check(rrect, clip_op, is_aa);
4410 }
4411 void clipPath(const SkPath& path,
4412 DlCanvas::ClipOp clip_op,
4413 bool is_aa) override {
4414 check(path, clip_op, is_aa);
4415 }
4416
4417 private:
4418 size_t index_ = 0;
4419 std::vector<Expectation> clip_expectations_;
4420
4421 template <typename T>
4422 void check(T shape, ClipOp clip_op, bool is_aa) {
4423 ASSERT_LT(index_, clip_expectations_.size())
4424 << label() << std::endl
4425 << "extra clip shape = " << shape;
4426 auto expected = clip_expectations_[index_];
4427 EXPECT_EQ(expected.clip_op, clip_op) << label();
4428 EXPECT_EQ(expected.is_aa, is_aa) << label();
4429 if (!std::holds_alternative<T>(expected.shape)) {
4430 EXPECT_TRUE(std::holds_alternative<T>(expected.shape))
4431 << label() << ", expected type: " << expected.shape_name();
4432 } else {
4433 EXPECT_EQ(std::get<T>(expected.shape), shape) << label();
4434 }
4435 index_++;
4436 }
4437
4438 const std::string file_;
4439 const int line_;
4440
4441 std::string label() {
4442 return "at index " + std::to_string(index_) + //
4443 ", from " + file_ + //
4444 ":" + std::to_string(line_);
4445 }
4446};
4447
4448TEST_F(DisplayListTest, ClipRectCullingPixel6a) {
4449 // These particular values create bit errors if we use the path that
4450 // tests for inclusion in local space, but work OK if we use a forward
4451 // path that tests for inclusion in device space, due to the fact that
4452 // the extra matrix inversion is just enough math to cause the transform
4453 // to place the local space cull corners just outside the original rect.
4454 // The test in device space only works under a simple scale, such as we
4455 // use for DPR adjustments (and which are not always inversion friendly).
4456
4457 auto frame = SkRect::MakeLTRB(0.0f, 0.0f, 1080.0f, 2400.0f);
4458 DlScalar DPR = 2.625f;
4459 auto clip = SkRect::MakeLTRB(0.0f, 0.0f, 1080.0f / DPR, 2400.0f / DPR);
4460
4461 DisplayListBuilder cull_builder;
4462 cull_builder.ClipRect(frame, ClipOp::kIntersect, false);
4463 cull_builder.Scale(DPR, DPR);
4464 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4465 auto cull_dl = cull_builder.Build();
4466
4467 CLIP_EXPECTOR(expector);
4468 expector.addExpectation(frame, ClipOp::kIntersect, false);
4469 cull_dl->Dispatch(expector);
4470}
4471
4472TEST_F(DisplayListTest, ClipRectCulling) {
4473 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4474
4475 DisplayListBuilder cull_builder;
4476 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4477 cull_builder.ClipRect(clip.makeOutset(1.0f, 1.0f), ClipOp::kIntersect, false);
4478 auto cull_dl = cull_builder.Build();
4479
4480 CLIP_EXPECTOR(expector);
4481 expector.addExpectation(clip, ClipOp::kIntersect, false);
4482 cull_dl->Dispatch(expector);
4483}
4484
4485TEST_F(DisplayListTest, ClipRectNonCulling) {
4486 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4487 auto smaller_clip = clip.makeInset(1.0f, 1.0f);
4488
4489 DisplayListBuilder cull_builder;
4490 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4491 cull_builder.ClipRect(smaller_clip, ClipOp::kIntersect, false);
4492 auto cull_dl = cull_builder.Build();
4493
4494 CLIP_EXPECTOR(expector);
4495 expector.addExpectation(clip, ClipOp::kIntersect, false);
4496 expector.addExpectation(smaller_clip, ClipOp::kIntersect, false);
4497 cull_dl->Dispatch(expector);
4498}
4499
4500TEST_F(DisplayListTest, ClipRectNestedCulling) {
4501 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4502 auto larger_clip = clip.makeOutset(1.0f, 1.0f);
4503
4504 DisplayListBuilder cull_builder;
4505 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4506 cull_builder.Save();
4507 cull_builder.ClipRect(larger_clip, ClipOp::kIntersect, false);
4508 cull_builder.Restore();
4509 auto cull_dl = cull_builder.Build();
4510
4511 CLIP_EXPECTOR(expector);
4512 expector.addExpectation(clip, ClipOp::kIntersect, false);
4513 cull_dl->Dispatch(expector);
4514}
4515
4516TEST_F(DisplayListTest, ClipRectNestedNonCulling) {
4517 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4518 auto larger_clip = clip.makeOutset(1.0f, 1.0f);
4519
4520 DisplayListBuilder cull_builder;
4521 cull_builder.Save();
4522 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4523 cull_builder.Restore();
4524 // Should not be culled because we have restored the prior clip
4525 cull_builder.ClipRect(larger_clip, ClipOp::kIntersect, false);
4526 auto cull_dl = cull_builder.Build();
4527
4528 CLIP_EXPECTOR(expector);
4529 expector.addExpectation(clip, ClipOp::kIntersect, false);
4530 expector.addExpectation(larger_clip, ClipOp::kIntersect, false);
4531 cull_dl->Dispatch(expector);
4532}
4533
4534TEST_F(DisplayListTest, ClipRectNestedCullingComplex) {
4535 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4536 auto smaller_clip = clip.makeInset(1.0f, 1.0f);
4537 auto smallest_clip = clip.makeInset(2.0f, 2.0f);
4538
4539 DisplayListBuilder cull_builder;
4540 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4541 cull_builder.Save();
4542 cull_builder.ClipRect(smallest_clip, ClipOp::kIntersect, false);
4543 cull_builder.ClipRect(smaller_clip, ClipOp::kIntersect, false);
4544 cull_builder.Restore();
4545 auto cull_dl = cull_builder.Build();
4546
4547 CLIP_EXPECTOR(expector);
4548 expector.addExpectation(clip, ClipOp::kIntersect, false);
4549 expector.addExpectation(smallest_clip, ClipOp::kIntersect, false);
4550 cull_dl->Dispatch(expector);
4551}
4552
4553TEST_F(DisplayListTest, ClipRectNestedNonCullingComplex) {
4554 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4555 auto smaller_clip = clip.makeInset(1.0f, 1.0f);
4556 auto smallest_clip = clip.makeInset(2.0f, 2.0f);
4557
4558 DisplayListBuilder cull_builder;
4559 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4560 cull_builder.Save();
4561 cull_builder.ClipRect(smallest_clip, ClipOp::kIntersect, false);
4562 cull_builder.Restore();
4563 // Would not be culled if it was inside the clip
4564 cull_builder.ClipRect(smaller_clip, ClipOp::kIntersect, false);
4565 auto cull_dl = cull_builder.Build();
4566
4567 CLIP_EXPECTOR(expector);
4568 expector.addExpectation(clip, ClipOp::kIntersect, false);
4569 expector.addExpectation(smallest_clip, ClipOp::kIntersect, false);
4570 expector.addExpectation(smaller_clip, ClipOp::kIntersect, false);
4571 cull_dl->Dispatch(expector);
4572}
4573
4574TEST_F(DisplayListTest, ClipRRectCulling) {
4575 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4576 auto rrect = SkRRect::MakeRectXY(clip.makeOutset(2.0f, 2.0f), 2.0f, 2.0f);
4577
4578 DisplayListBuilder cull_builder;
4579 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4580 cull_builder.ClipRRect(rrect, ClipOp::kIntersect, false);
4581 auto cull_dl = cull_builder.Build();
4582
4583 CLIP_EXPECTOR(expector);
4584 expector.addExpectation(clip, ClipOp::kIntersect, false);
4585 cull_dl->Dispatch(expector);
4586}
4587
4588TEST_F(DisplayListTest, ClipRRectNonCulling) {
4589 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4590 auto rrect = SkRRect::MakeRectXY(clip.makeOutset(2.0f, 2.0f), 12.0f, 12.0f);
4591
4592 DisplayListBuilder cull_builder;
4593 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4594 cull_builder.ClipRRect(rrect, ClipOp::kIntersect, false);
4595 auto cull_dl = cull_builder.Build();
4596
4597 CLIP_EXPECTOR(expector);
4598 expector.addExpectation(clip, ClipOp::kIntersect, false);
4599 expector.addExpectation(rrect, ClipOp::kIntersect, false);
4600 cull_dl->Dispatch(expector);
4601}
4602
4603TEST_F(DisplayListTest, ClipPathNonCulling) {
4604 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4605 SkPath path;
4606 path.moveTo(0.0f, 0.0f);
4607 path.lineTo(1000.0f, 0.0f);
4608 path.lineTo(0.0f, 1000.0f);
4609 path.close();
4610
4611 // Double checking that the path does indeed contain the clip. But,
4612 // sadly, the Builder will not check paths for coverage to this level
4613 // of detail. (In particular, path containment of the corners is not
4614 // authoritative of true containment, but we know in this case that
4615 // a triangle contains a rect if it contains all 4 corners...)
4616 ASSERT_TRUE(path.contains(clip.fLeft, clip.fTop));
4617 ASSERT_TRUE(path.contains(clip.fRight, clip.fTop));
4618 ASSERT_TRUE(path.contains(clip.fRight, clip.fBottom));
4619 ASSERT_TRUE(path.contains(clip.fLeft, clip.fBottom));
4620
4621 DisplayListBuilder cull_builder;
4622 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4623 cull_builder.ClipPath(path, ClipOp::kIntersect, false);
4624 auto cull_dl = cull_builder.Build();
4625
4626 CLIP_EXPECTOR(expector);
4627 expector.addExpectation(clip, ClipOp::kIntersect, false);
4628 expector.addExpectation(path, ClipOp::kIntersect, false);
4629 cull_dl->Dispatch(expector);
4630}
4631
4632TEST_F(DisplayListTest, ClipPathRectCulling) {
4633 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4634 SkPath path;
4635 path.addRect(clip.makeOutset(1.0f, 1.0f));
4636
4637 DisplayListBuilder cull_builder;
4638 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4639 cull_builder.ClipPath(path, ClipOp::kIntersect, false);
4640 auto cull_dl = cull_builder.Build();
4641
4642 CLIP_EXPECTOR(expector);
4643 expector.addExpectation(clip, ClipOp::kIntersect, false);
4644 cull_dl->Dispatch(expector);
4645}
4646
4647TEST_F(DisplayListTest, ClipPathRectNonCulling) {
4648 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4649 auto smaller_clip = clip.makeInset(1.0f, 1.0f);
4650 SkPath path;
4651 path.addRect(smaller_clip);
4652
4653 DisplayListBuilder cull_builder;
4654 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4655 cull_builder.ClipPath(path, ClipOp::kIntersect, false);
4656 auto cull_dl = cull_builder.Build();
4657
4658 CLIP_EXPECTOR(expector);
4659 expector.addExpectation(clip, ClipOp::kIntersect, false);
4660 // Builder will not cull this clip, but it will turn it into a ClipRect
4661 expector.addExpectation(smaller_clip, ClipOp::kIntersect, false);
4662 cull_dl->Dispatch(expector);
4663}
4664
4665TEST_F(DisplayListTest, ClipPathRRectCulling) {
4666 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4667 auto rrect = SkRRect::MakeRectXY(clip.makeOutset(2.0f, 2.0f), 2.0f, 2.0f);
4668 SkPath path;
4669 path.addRRect(rrect);
4670
4671 DisplayListBuilder cull_builder;
4672 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4673 cull_builder.ClipPath(path, ClipOp::kIntersect, false);
4674 auto cull_dl = cull_builder.Build();
4675
4676 CLIP_EXPECTOR(expector);
4677 expector.addExpectation(clip, ClipOp::kIntersect, false);
4678 cull_dl->Dispatch(expector);
4679}
4680
4681TEST_F(DisplayListTest, ClipPathRRectNonCulling) {
4682 auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
4683 auto rrect = SkRRect::MakeRectXY(clip.makeOutset(2.0f, 2.0f), 12.0f, 12.0f);
4684 SkPath path;
4685 path.addRRect(rrect);
4686
4687 DisplayListBuilder cull_builder;
4688 cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
4689 cull_builder.ClipPath(path, ClipOp::kIntersect, false);
4690 auto cull_dl = cull_builder.Build();
4691
4692 CLIP_EXPECTOR(expector);
4693 expector.addExpectation(clip, ClipOp::kIntersect, false);
4694 // Builder will not cull this clip, but it will turn it into a ClipRRect
4695 expector.addExpectation(rrect, ClipOp::kIntersect, false);
4696 cull_dl->Dispatch(expector);
4697}
4698
4699} // namespace testing
4700} // namespace flutter
const char * options
#define test(name)
static SkPath path1()
static SkPath path2()
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
static constexpr SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
Definition: SkColor.h:49
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
static sk_sp< SkImage > color_filter(const SkImage *image, SkColorFilter *colorFilter)
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:107
#define SK_ScalarNaN
Definition: SkScalar.h:28
int main(int argc, char **argv)
Definition: benchmarking.cc:29
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void restore()
Definition: SkCanvas.cpp:461
SkRect getLocalClipBounds() const
Definition: SkCanvas.cpp:1586
SkM44 getLocalToDevice() const
Definition: SkCanvas.cpp:1633
SkMatrix getTotalMatrix() const
Definition: SkCanvas.cpp:1629
SkIRect getDeviceClipBounds() const
Definition: SkCanvas.cpp:1607
void drawPicture(const SkPicture *picture)
Definition: SkCanvas.h:1961
static sk_sp< SkColorFilter > Matrix(const SkColorMatrix &)
Definition: SkM44.h:150
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition: SkMatrix.h:75
static SkMatrix RotateDeg(SkScalar deg)
Definition: SkMatrix.h:104
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition: SkMatrix.h:179
static SkMatrix Skew(SkScalar kx, SkScalar ky)
Definition: SkMatrix.h:124
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
bool isIdentity() const
Definition: SkMatrix.h:223
void setColorFilter(sk_sp< SkColorFilter > colorFilter)
Definition: SkPath.h:59
static SkPath Rect(const SkRect &, SkPathDirection=SkPathDirection::kCW, unsigned startIndex=0)
Definition: SkPath.cpp:3586
SkPath & addCircle(SkScalar x, SkScalar y, SkScalar radius, SkPathDirection dir=SkPathDirection::kCW)
Definition: SkPath.cpp:1213
SkPath & moveTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:688
void setFillType(SkPathFillType ft)
Definition: SkPath.h:235
SkPath & lineTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:728
SkPath & addRoundRect(const SkRect &rect, SkScalar rx, SkScalar ry, SkPathDirection dir=SkPathDirection::kCW)
Definition: SkPath.cpp:1093
SkPath & addOval(const SkRect &oval, SkPathDirection dir=SkPathDirection::kCW)
Definition: SkPath.cpp:1106
SkPath & cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3)
Definition: SkPath.cpp:799
SkPath & close()
Definition: SkPath.cpp:823
SkPath & addRect(const SkRect &rect, SkPathDirection dir, unsigned start)
Definition: SkPath.cpp:864
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
SkCanvas * getRecordingCanvas()
sk_sp< SkPicture > finishRecordingAsPicture()
virtual SkRect cullRect() const =0
virtual int approximateOpCount(bool nested=false) const =0
static SkRRect MakeRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition: SkRRect.h:180
static SkRRect MakeEmpty()
Definition: SkRRect.h:142
static constexpr SkRect kMaxCullRect
Definition: dl_builder.h:32
void DrawRect(const SkRect &rect, const DlPaint &paint) override
Definition: dl_builder.cc:1116
void ClipRRect(const SkRRect &rrect, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false) override
Definition: dl_builder.cc:966
void Scale(SkScalar sx, SkScalar sy) override
Definition: dl_builder.cc:807
void SaveLayer(const SkRect *bounds, const DlPaint *paint=nullptr, const DlImageFilter *backdrop=nullptr) override
Definition: dl_builder.cc:549
sk_sp< DisplayList > Build()
Definition: dl_builder.cc:67
void ClipRect(const SkRect &rect, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false) override
Definition: dl_builder.cc:934
void DrawDisplayList(const sk_sp< DisplayList > display_list, SkScalar opacity=SK_Scalar1) override
Definition: dl_builder.cc:1535
void ClipPath(const SkPath &path, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false) override
Definition: dl_builder.cc:999
static std::shared_ptr< DlColorFilter > Make(DlColor color, DlBlendMode mode)
static std::shared_ptr< DlImageFilter > Make(SkScalar sigma_x, SkScalar sigma_y, DlTileMode tile_mode)
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:38
virtual void DrawRect(const SkRect &rect, const DlPaint &paint)=0
@ 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
virtual void DrawRRect(const SkRRect &rrect, const DlPaint &paint)=0
virtual void DrawOval(const SkRect &bounds, const DlPaint &paint)=0
virtual void DrawPath(const SkPath &path, const DlPaint &paint)=0
static std::shared_ptr< DlImageFilter > Make(const std::shared_ptr< const DlColorFilter > &filter)
static std::shared_ptr< DlColorFilter > Make(const float matrix[20])
static std::shared_ptr< DlImageFilter > Make(const SkMatrix &matrix, DlImageSampling sampling)
Internal API for rendering recorded display lists to backends.
virtual void drawImageRect(const sk_sp< DlImage > image, const SkRect &src, const SkRect &dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint=SrcRectConstraint::kFast)=0
virtual void save()=0
virtual void drawArc(const SkRect &oval_bounds, SkScalar start_degrees, SkScalar sweep_degrees, bool use_center)=0
virtual void saveLayer(const SkRect &bounds, const SaveLayerOptions options, const DlImageFilter *backdrop=nullptr)=0
virtual void drawRect(const SkRect &rect)=0
virtual void setStrokeMiter(float limit)=0
virtual void clipRect(const SkRect &rect, ClipOp clip_op, bool is_aa)=0
virtual void transformReset()=0
virtual void drawImage(const sk_sp< DlImage > image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes)=0
virtual void drawCircle(const SkPoint &center, SkScalar radius)=0
virtual void drawVertices(const DlVertices *vertices, DlBlendMode mode)=0
virtual void restore()=0
virtual void drawPath(const CacheablePath &cache)
virtual void drawPoints(PointMode mode, uint32_t count, const SkPoint points[])=0
virtual void skew(SkScalar sx, SkScalar sy)=0
virtual void drawOval(const SkRect &bounds)=0
virtual void drawRRect(const SkRRect &rrect)=0
virtual void drawDisplayList(const sk_sp< DisplayList > display_list, SkScalar opacity=SK_Scalar1)=0
virtual void setStrokeWidth(float width)=0
virtual void setMaskFilter(const DlMaskFilter *filter)=0
virtual void drawDRRect(const SkRRect &outer, const SkRRect &inner)=0
virtual void setColorFilter(const DlColorFilter *filter)=0
virtual void drawImageNine(const sk_sp< DlImage > image, const SkIRect &center, const SkRect &dst, DlFilterMode filter, bool render_with_attributes)=0
virtual void setAntiAlias(bool aa)=0
virtual void clipRRect(const SkRRect &rrect, ClipOp clip_op, bool is_aa)=0
virtual void drawColor(DlColor color, DlBlendMode mode)=0
virtual void drawAtlas(const sk_sp< DlImage > atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], int count, DlBlendMode mode, DlImageSampling sampling, const SkRect *cull_rect, bool render_with_attributes)=0
virtual void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myt)=0
virtual void translate(SkScalar tx, SkScalar ty)=0
virtual void clipPath(const CacheablePath &cache, ClipOp clip_op, bool is_aa)
virtual void scale(SkScalar sx, SkScalar sy)=0
virtual void setStrokeJoin(DlStrokeJoin join)=0
virtual void drawShadow(const CacheablePath &cache, const DlColor color, const SkScalar elevation, bool transparent_occluder, SkScalar dpr)
virtual void drawLine(const SkPoint &p0, const SkPoint &p1)=0
virtual void setImageFilter(const DlImageFilter *filter)=0
virtual void setColorSource(const DlColorSource *source)=0
virtual void rotate(SkScalar degrees)=0
virtual void drawPaint()=0
virtual void setDrawStyle(DlDrawStyle style)=0
virtual void drawTextBlob(const sk_sp< SkTextBlob > blob, SkScalar x, SkScalar y)=0
virtual void setBlendMode(DlBlendMode mode)=0
virtual void transformFullPerspective(SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt)=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:99
bool isDefault() const
Definition: dl_paint.h:179
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:83
DlPaint & setColorFilter(const std::shared_ptr< const DlColorFilter > &filter)
Definition: dl_paint.h:144
DlPaint & setMaskFilter(const std::shared_ptr< DlMaskFilter > &filter)
Definition: dl_paint.h:170
float getStrokeMiter() const
Definition: dl_paint.h:121
std::shared_ptr< const DlColorSource > getColorSource() const
Definition: dl_paint.h:127
DlStrokeJoin getStrokeJoin() const
Definition: dl_paint.h:107
DlPaint & setStrokeWidth(float width)
Definition: dl_paint.h:116
DlPaint & setAlpha(uint8_t alpha)
Definition: dl_paint.h:76
DlPaint & setBlendMode(DlBlendMode mode)
Definition: dl_paint.h:86
DlDrawStyle getDrawStyle() const
Definition: dl_paint.h:91
std::shared_ptr< const DlMaskFilter > getMaskFilter() const
Definition: dl_paint.h:166
std::shared_ptr< const DlColorFilter > getColorFilter() const
Definition: dl_paint.h:140
DlPaint & setImageFilter(const std::shared_ptr< const DlImageFilter > &filter)
Definition: dl_paint.h:157
float getStrokeWidth() const
Definition: dl_paint.h:115
std::shared_ptr< const DlImageFilter > getImageFilter() const
Definition: dl_paint.h:153
DlPaint & setDrawStyle(DlDrawStyle style)
Definition: dl_paint.h:94
DlPaint & setStrokeJoin(DlStrokeJoin join)
Definition: dl_paint.h:110
bool isInvertColors() const
Definition: dl_paint.h:63
Backend implementation of |DlOpReceiver| for |SkCanvas|.
static const SaveLayerOptions kNoAttributes
Definition: display_list.h:155
SaveLayerOptions with_can_distribute_opacity() const
Definition: display_list.h:177
SaveLayerOptions with_contains_backdrop_filter() const
Definition: display_list.h:211
static const SaveLayerOptions kWithAttributes
Definition: display_list.h:154
ClipExpector & addExpectation(const SkPath &path, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false)
void clipRect(const SkRect &rect, DlCanvas::ClipOp clip_op, bool is_aa) override
void clipPath(const SkPath &path, DlCanvas::ClipOp clip_op, bool is_aa) override
ClipExpector(const std::string &file, int line)
void clipRRect(const SkRRect &rrect, DlCanvas::ClipOp clip_op, bool is_aa) override
ClipExpector & addExpectation(const SkRRect &rrect, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false)
ClipExpector & addExpectation(const SkRect &rect, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false)
void saveLayer(const SkRect &bounds, SaveLayerOptions options, const DlImageFilter *backdrop) override
void saveLayer(const SkRect &bounds, const SaveLayerOptions &options, uint32_t total_content_depth, DlBlendMode max_content_mode, const DlImageFilter *backdrop) 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 &, DlPaint &, SkRect &rect)> DlRenderer
const std::function< void(DlCanvas &)> DlSetup
static void check_defaults(DisplayListBuilder &builder, const SkRect &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, SkRect render_rect, SkRect expected_bounds, const std::string &desc)
static sk_sp< DisplayList > Build(DisplayListInvocation &invocation)
SaveLayerBoundsExpector & addSuppliedExpectation(const SkRect &bounds, bool clipped=false)
SaveLayerBoundsExpector & addComputedExpectation(const SkRect &bounds)
void saveLayer(const SkRect &bounds, const SaveLayerOptions options, const DlImageFilter *backdrop) override
void saveLayer(const SkRect &bounds, const SaveLayerOptions options, const DlImageFilter *backdrop) override
virtual void saveLayer(const SkRect &bounds, const SaveLayerOptions &options, uint32_t total_content_depth, DlBlendMode max_content_blend_mode, const DlImageFilter *backdrop=nullptr)
SaveLayerExpector(std::vector< Expectations > expected)
SaveLayerExpector(const Expectations &expected)
T * get() const
Definition: SkRefCnt.h:303
const Paint & paint
Definition: color_source.cc:38
#define CLIP_EXPECTOR(name)
#define RUN_TESTS2(body, expect)
#define RUN_TESTS(body)
#define TEST_RTREE(rtree, query, expected_rects, expected_indices)
DlColor color
std::shared_ptr< DlMaskFilter > mask_filter
#define TestPointCount
SkMatrix sk_matrix
static void draw_paint(SkCanvas *canvas, const SkRect &r, sk_sp< SkImageFilter > imf)
#define FAIL(name, result)
VkSurfaceKHR surface
Definition: main.cc:49
double frame
Definition: examples.cpp:31
float SkScalar
Definition: extension.cpp:12
EMSCRIPTEN_KEEPALIVE void empty()
#define FML_LOG(severity)
Definition: logging.h:82
#define FML_UNREACHABLE()
Definition: logging.h:109
Dart_NativeFunction function
Definition: fuchsia.cc:51
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static void drawPath(SkPath &path, SkCanvas *canvas, SkColor color, const SkRect &clip, SkPaint::Cap cap, SkPaint::Join join, SkPaint::Style style, SkPathFillType fill, SkScalar strokeWidth)
Definition: linepaths.cpp:22
double y
double x
static bool init()
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
Optional< SkRect > bounds
Definition: SkRecords.h:189
PODArray< SkRect > texs
Definition: SkRecords.h:333
sk_sp< const SkPicture > picture
Definition: SkRecords.h:299
clipPath(r.path, r.opAA.op(), r.opAA.aa())) DRAW(ClipRRect
sk_sp< const SkImageFilter > backdrop
Definition: SkRecords.h:191
SkRRect rrect
Definition: SkRecords.h:232
PODArray< SkRSXform > xforms
Definition: SkRecords.h:332
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
def Build(configs, env, options)
Definition: build.py:232
Definition: copy.py:1
def build()
Definition: dom.py:52
constexpr float kPi
Definition: math.h:27
TEST_F(DisplayListTest, Defaults)
constexpr SkRect kTestBounds
static const DlBlurMaskFilter kTestMaskFilter1(DlBlurStyle::kNormal, 3.0)
bool DisplayListsNE_Verbose(const DisplayList *a, const DisplayList *b)
static const SkPath kTestPath1
static const DlImageColorSource kTestSource1(TestImage1, DlTileMode::kClamp, DlTileMode::kMirror, kLinearSampling)
static void test_rtree(const sk_sp< const DlRTree > &rtree, const SkRect &query, std::vector< SkRect > expected_rects, const std::vector< int > &expected_indices, const std::string &file, int line)
static std::vector< testing::DisplayListInvocationGroup > allGroups
static constexpr SkPoint kTestPoints[2]
static const SkRRect kTestRRect
static auto TestImage1
static const DlMatrixColorFilter kTestMatrixColorFilter1(kRotateColorMatrix)
static DlImageSampling kLinearSampling
static std::shared_ptr< const DlVertices > TestVertices1
bool DisplayListsEQ_Verbose(const DisplayList *a, const DisplayList *b)
static DlImageSampling kNearestSampling
sk_sp< SkTextBlob > GetTestTextBlob(int index)
static const DlBlurImageFilter kTestBlurImageFilter1(5.0, 5.0, DlTileMode::kClamp)
static auto TestImage2
std::vector< DisplayListInvocationGroup > CreateAllGroups()
static sk_sp< DisplayList > TestDisplayList1
impeller::Scalar DlScalar
DlPaint DisplayListBuilderTestingAttributes(DisplayListBuilder &builder)
@ kRound
adds circle
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 to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
DlOpReceiver & DisplayListBuilderTestingAccessor(DisplayListBuilder &builder)
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: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
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 to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent Remove all existing persistent cache This is mainly for debugging purposes such as reproducing the shader compilation jank trace to file
Definition: switches.h:201
@ kStrokeAndFill
both strokes and fills shapes
@ kStroke
strokes boundary of shapes
@ kNormal
fuzzy inside and outside
it will be possible to load the file into Perfetto s trace viewer 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
Definition: switches.h:228
int DisplayListBuilderTestingLastOpIndex(DisplayListBuilder &builder)
@ kColorBurn
darken destination to reflect source
@ kMultiply
r = s*(1-da) + d*(1-sa) + s*d
@ kScreen
r = s + d - s*d
@ kSrcOver
r = s + (1-sa)*d
@ kLastMode
last valid value
Definition: main.py:1
Definition: setup.py:1
static void draw_rect(SkCanvas *canvas, const SkRect &r, const SkPaint &p)
Definition: ref_ptr.h:256
flutter::DlColor DlColor
flutter::DlPaint DlPaint
flutter::SaveLayerOptions SaveLayerOptions
static SkString to_string(int n)
Definition: nanobench.cpp:119
#define T
Definition: precompiler.cc:65
Definition: SkRect.h:32
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
static SkImageInfo MakeN32Premul(int width, int height)
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
bool intersects(const SkRect &r) const
Definition: SkRect.h:1121
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
bool contains(SkScalar x, SkScalar y) const
Definition: extension.cpp:19
bool isEmpty() const
Definition: SkRect.h:693
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15
static constexpr DlColor kWhite()
Definition: dl_color.h:23
static constexpr DlColor kBlue()
Definition: dl_color.h:26
static constexpr DlColor kTransparent()
Definition: dl_color.h:21
static constexpr DlColor kRed()
Definition: dl_color.h:24
std::variant< SkRect, SkRRect, SkPath > shape
uint32_t adjust_render_op_depth_cost(uint32_t previous_cost)
uint32_t depth_accumulated(uint32_t depth_scale=1u)
void Invoke(DlOpReceiver &builder)
#define ERROR(message)
Definition: elf_loader.cc:260
static void setup(SkCanvas *canvas, SkPaint *paint, const SkBitmap &bm, SkFilterMode fm, SkTileMode tmx, SkTileMode tmy)
Definition: tilemodes.cpp:52
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678