| Flutter Impeller |

dl_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 <array>
6 #include <cmath>
7 #include <memory>
8 #include <vector>
9
10 #include "flutter/display_list/dl_blend_mode.h"
11 #include "flutter/display_list/dl_builder.h"
12 #include "flutter/display_list/dl_color.h"
13 #include "flutter/display_list/dl_paint.h"
14 #include "flutter/display_list/dl_tile_mode.h"
15 #include "flutter/display_list/effects/dl_color_filter.h"
16 #include "flutter/display_list/effects/dl_color_source.h"
17 #include "flutter/display_list/effects/dl_image_filter.h"
18 #include "flutter/display_list/effects/dl_mask_filter.h"
19 #include "flutter/testing/testing.h"
20 #include "gtest/gtest.h"
21 #include "impeller/display_list/dl_dispatcher.h"
22 #include "impeller/display_list/dl_image_impeller.h"
23 #include "impeller/display_list/dl_playground.h"
24 #include "impeller/entity/contents/clip_contents.h"
25 #include "impeller/entity/contents/solid_color_contents.h"
26 #include "impeller/entity/contents/solid_rrect_blur_contents.h"
27 #include "impeller/geometry/constants.h"
28 #include "impeller/geometry/point.h"
29 #include "impeller/geometry/scalar.h"
30 #include "impeller/playground/widgets.h"
31 #include "impeller/scene/node.h"
32 #include "third_party/imgui/imgui.h"
33 #include "third_party/skia/include/core/SkBlurTypes.h"
34 #include "third_party/skia/include/core/SkClipOp.h"
35 #include "third_party/skia/include/core/SkPathBuilder.h"
36 #include "third_party/skia/include/core/SkRRect.h"
37
38 namespace impeller {
39 namespace testing {
40
41 flutter::DlColor toColor(const float* components) {
42 return flutter::DlColor(Color::ToIColor(
43 Color(components[0], components[1], components[2], components[3])));
44 }
45
46 using DisplayListTest = DlPlayground;
47 INSTANTIATE_PLAYGROUND_SUITE(DisplayListTest);
48
49 TEST_P(DisplayListTest, DrawPictureWithAClip) {
50 flutter::DisplayListBuilder sub_builder;
51 sub_builder.ClipRect(SkRect::MakeXYWH(0, 0, 24, 24));
52 sub_builder.DrawPaint(flutter::DlPaint(flutter::DlColor::kBlue()));
53
54 auto display_list = sub_builder.Build();
55 flutter::DisplayListBuilder builder;
56 builder.DrawDisplayList(display_list);
57 builder.DrawRect(SkRect::MakeXYWH(30, 30, 24, 24),
58 flutter::DlPaint(flutter::DlColor::kRed()));
59 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
60 }
61
62 TEST_P(DisplayListTest, CanDrawRect) {
63 flutter::DisplayListBuilder builder;
64 builder.DrawRect(SkRect::MakeXYWH(10, 10, 100, 100),
65 flutter::DlPaint(flutter::DlColor::kBlue()));
66 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
67 }
68
69 TEST_P(DisplayListTest, CanDrawTextBlob) {
70 flutter::DisplayListBuilder builder;
71 builder.DrawTextBlob(SkTextBlob::MakeFromString("Hello", CreateTestFont()),
72 100, 100, flutter::DlPaint(flutter::DlColor::kBlue()));
73 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
74 }
75
76 TEST_P(DisplayListTest, CanDrawTextBlobWithGradient) {
77 flutter::DisplayListBuilder builder;
78
79 std::vector<flutter::DlColor> colors = {flutter::DlColor::kBlue(),
80 flutter::DlColor::kRed()};
81 const float stops[2] = {0.0, 1.0};
82
83 auto linear = flutter::DlColorSource::MakeLinear({0.0, 0.0}, {300.0, 300.0},
84 2, colors.data(), stops,
85 flutter::DlTileMode::kClamp);
86 flutter::DlPaint paint;
87 paint.setColorSource(linear);
88
89 builder.DrawTextBlob(
90 SkTextBlob::MakeFromString("Hello World", CreateTestFont()), 100, 100,
91 paint);
92 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
93 }
94
95 TEST_P(DisplayListTest, CanDrawTextWithSaveLayer) {
96 flutter::DisplayListBuilder builder;
97 builder.DrawTextBlob(SkTextBlob::MakeFromString("Hello", CreateTestFont()),
98 100, 100, flutter::DlPaint(flutter::DlColor::kRed()));
99
100 flutter::DlPaint save_paint;
101 float alpha = 0.5;
102 save_paint.setAlpha(static_cast<uint8_t>(255 * alpha));
103 builder.SaveLayer(nullptr, &save_paint);
104 builder.DrawTextBlob(SkTextBlob::MakeFromString("Hello with half alpha",
105 CreateTestFontOfSize(100)),
106 100, 300, flutter::DlPaint(flutter::DlColor::kRed()));
107 builder.Restore();
108 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
109 }
110
111 TEST_P(DisplayListTest, CanDrawImage) {
112 auto texture = CreateTextureForFixture("embarcadero.jpg");
113 flutter::DisplayListBuilder builder;
114 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
115 flutter::DlImageSampling::kNearestNeighbor, nullptr);
116 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
117 }
118
119 TEST_P(DisplayListTest, CanDrawCapsAndJoins) {
120 flutter::DisplayListBuilder builder;
121 flutter::DlPaint paint;
122
123 paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
124 paint.setStrokeWidth(30);
125 paint.setColor(flutter::DlColor::kRed());
126
127 auto path =
128 SkPathBuilder{}.moveTo(-50, 0).lineTo(0, -50).lineTo(50, 0).snapshot();
129
130 builder.Translate(100, 100);
131 {
132 paint.setStrokeCap(flutter::DlStrokeCap::kButt);
133 paint.setStrokeJoin(flutter::DlStrokeJoin::kMiter);
134 paint.setStrokeMiter(4);
135 builder.DrawPath(path, paint);
136 }
137
138 {
139 builder.Save();
140 builder.Translate(0, 100);
141 // The joint in the path is 45 degrees. A miter length of 1 convert to a
142 // bevel in this case.
143 paint.setStrokeMiter(1);
144 builder.DrawPath(path, paint);
145 builder.Restore();
146 }
147
148 builder.Translate(150, 0);
149 {
150 paint.setStrokeCap(flutter::DlStrokeCap::kSquare);
151 paint.setStrokeJoin(flutter::DlStrokeJoin::kBevel);
152 builder.DrawPath(path, paint);
153 }
154
155 builder.Translate(150, 0);
156 {
157 paint.setStrokeCap(flutter::DlStrokeCap::kRound);
158 paint.setStrokeJoin(flutter::DlStrokeJoin::kRound);
159 builder.DrawPath(path, paint);
160 }
161
162 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
163 }
164
165 TEST_P(DisplayListTest, CanDrawArc) {
166 auto callback = [&]() {
167 static float start_angle = 45;
168 static float sweep_angle = 270;
169 static float stroke_width = 10;
170 static bool use_center = true;
171
172 static int selected_cap = 0;
173 const char* cap_names[] = {"Butt", "Round", "Square"};
174 flutter::DlStrokeCap cap;
175
176 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
177 ImGui::SliderFloat("Start angle", &start_angle, -360, 360);
178 ImGui::SliderFloat("Sweep angle", &sweep_angle, -360, 360);
179 ImGui::SliderFloat("Stroke width", &stroke_width, 0, 300);
180 ImGui::Combo("Cap", &selected_cap, cap_names,
181 sizeof(cap_names) / sizeof(char*));
182 ImGui::Checkbox("Use center", &use_center);
183 ImGui::End();
184
185 switch (selected_cap) {
186 case 0:
187 cap = flutter::DlStrokeCap::kButt;
188 break;
189 case 1:
190 cap = flutter::DlStrokeCap::kRound;
191 break;
192 case 2:
193 cap = flutter::DlStrokeCap::kSquare;
194 break;
195 default:
196 cap = flutter::DlStrokeCap::kButt;
197 break;
198 }
199
200 auto [p1, p2] = IMPELLER_PLAYGROUND_LINE(
201 Point(200, 200), Point(400, 400), 20, Color::White(), Color::White());
202
203 flutter::DisplayListBuilder builder;
204 flutter::DlPaint paint;
205
206 Vector2 scale = GetContentScale();
207 builder.Scale(scale.x, scale.y);
208 paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
209 paint.setStrokeCap(cap);
210 paint.setStrokeJoin(flutter::DlStrokeJoin::kMiter);
211 paint.setStrokeMiter(10);
212 auto rect = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
213 paint.setColor(flutter::DlColor::kGreen());
214 paint.setStrokeWidth(2);
215 builder.DrawRect(rect, paint);
216 paint.setColor(flutter::DlColor::kRed());
217 paint.setStrokeWidth(stroke_width);
218 builder.DrawArc(rect, start_angle, sweep_angle, use_center, paint);
219
220 return builder.Build();
221 };
222 ASSERT_TRUE(OpenPlaygroundHere(callback));
223 }
224
225 TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) {
226 auto callback = [&]() {
227 flutter::DisplayListBuilder builder;
228 flutter::DlPaint paint;
229
230 paint.setColor(flutter::DlColor::kRed());
231 paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
232
233 static float stroke_width = 10.0f;
234 static int selected_stroke_type = 0;
235 static int selected_join_type = 0;
236 const char* stroke_types[] = {"Butte", "Round", "Square"};
237 const char* join_type[] = {"kMiter", "Round", "kBevel"};
238
239 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
240 ImGui::Combo("Cap", &selected_stroke_type, stroke_types,
241 sizeof(stroke_types) / sizeof(char*));
242 ImGui::Combo("Join", &selected_join_type, join_type,
243 sizeof(join_type) / sizeof(char*));
244 ImGui::SliderFloat("Stroke Width", &stroke_width, 10.0f, 50.0f);
245 ImGui::End();
246
247 flutter::DlStrokeCap cap;
248 flutter::DlStrokeJoin join;
249 switch (selected_stroke_type) {
250 case 0:
251 cap = flutter::DlStrokeCap::kButt;
252 break;
253 case 1:
254 cap = flutter::DlStrokeCap::kRound;
255 break;
256 case 2:
257 cap = flutter::DlStrokeCap::kSquare;
258 break;
259 default:
260 cap = flutter::DlStrokeCap::kButt;
261 break;
262 }
263 switch (selected_join_type) {
264 case 0:
265 join = flutter::DlStrokeJoin::kMiter;
266 break;
267 case 1:
268 join = flutter::DlStrokeJoin::kRound;
269 break;
270 case 2:
271 join = flutter::DlStrokeJoin::kBevel;
272 break;
273 default:
274 join = flutter::DlStrokeJoin::kMiter;
275 break;
276 }
277 paint.setStrokeCap(cap);
278 paint.setStrokeJoin(join);
279 paint.setStrokeWidth(stroke_width);
280
281 // Make rendering better to watch.
282 builder.Scale(1.5f, 1.5f);
283
284 // Rectangle
285 builder.Translate(100, 100);
286 builder.DrawRect(SkRect::MakeSize({100, 100}), paint);
287
288 // Rounded rectangle
289 builder.Translate(150, 0);
290 builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10),
291 paint);
292
293 // Double rounded rectangle
294 builder.Translate(150, 0);
295 builder.DrawDRRect(
296 SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10),
297 SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 30), 10, 10), paint);
298
299 // Contour with duplicate join points
300 {
301 builder.Translate(150, 0);
302 SkPath path;
303 path.moveTo(0, 0);
304 path.lineTo(0, 0);
305 path.lineTo({100, 0});
306 path.lineTo({100, 0});
307 path.lineTo({100, 100});
308 builder.DrawPath(path, paint);
309 }
310
311 // Contour with duplicate start and end points
312
313 // Line.
314 builder.Translate(200, 0);
315 {
316 builder.Save();
317
318 SkPath line_path;
319 line_path.moveTo(0, 0);
320 line_path.moveTo(0, 0);
321 line_path.lineTo({0, 0});
322 line_path.lineTo({0, 0});
323 line_path.lineTo({50, 50});
324 line_path.lineTo({50, 50});
325 line_path.lineTo({100, 0});
326 line_path.lineTo({100, 0});
327 builder.DrawPath(line_path, paint);
328
329 builder.Translate(0, 100);
330 builder.DrawPath(line_path, paint);
331
332 builder.Translate(0, 100);
333 SkPath line_path2;
334 line_path2.moveTo(0, 0);
335 line_path2.lineTo(0, 0);
336 line_path2.lineTo(0, 0);
337 builder.DrawPath(line_path2, paint);
338
339 builder.Restore();
340 }
341
342 // Cubic.
343 builder.Translate(150, 0);
344 {
345 builder.Save();
346
347 SkPath cubic_path;
348 cubic_path.moveTo({0, 0});
349 cubic_path.cubicTo(0, 0, 140.0, 100.0, 140, 20);
350 builder.DrawPath(cubic_path, paint);
351
352 builder.Translate(0, 100);
353 SkPath cubic_path2;
354 cubic_path2.moveTo({0, 0});
355 cubic_path2.cubicTo(0, 0, 0, 0, 150, 150);
356 builder.DrawPath(cubic_path2, paint);
357
358 builder.Translate(0, 100);
359 SkPath cubic_path3;
360 cubic_path3.moveTo({0, 0});
361 cubic_path3.cubicTo(0, 0, 0, 0, 0, 0);
362 builder.DrawPath(cubic_path3, paint);
363
364 builder.Restore();
365 }
366
367 // Quad.
368 builder.Translate(200, 0);
369 {
370 builder.Save();
371
372 SkPath quad_path;
373 quad_path.moveTo(0, 0);
374 quad_path.moveTo(0, 0);
375 quad_path.quadTo({100, 40}, {50, 80});
376 builder.DrawPath(quad_path, paint);
377
378 builder.Translate(0, 150);
379 SkPath quad_path2;
380 quad_path2.moveTo(0, 0);
381 quad_path2.moveTo(0, 0);
382 quad_path2.quadTo({0, 0}, {100, 100});
383 builder.DrawPath(quad_path2, paint);
384
385 builder.Translate(0, 100);
386 SkPath quad_path3;
387 quad_path3.moveTo(0, 0);
388 quad_path3.quadTo({0, 0}, {0, 0});
389 builder.DrawPath(quad_path3, paint);
390
391 builder.Restore();
392 }
393 return builder.Build();
394 };
395 ASSERT_TRUE(OpenPlaygroundHere(callback));
396 }
397
398 TEST_P(DisplayListTest, CanDrawWithOddPathWinding) {
399 flutter::DisplayListBuilder builder;
400 flutter::DlPaint paint;
401
402 paint.setColor(flutter::DlColor::kRed());
403 paint.setDrawStyle(flutter::DlDrawStyle::kFill);
404
405 builder.Translate(300, 300);
406 SkPath path;
407 path.setFillType(SkPathFillType::kEvenOdd);
408 path.addCircle(0, 0, 100);
409 path.addCircle(0, 0, 50);
410 builder.DrawPath(path, paint);
411
412 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
413 }
414
415 // Regression test for https://github.com/flutter/flutter/issues/134816.
416 //
417 // It should be possible to draw 3 lines, and not have an implicit close path.
418 TEST_P(DisplayListTest, CanDrawAnOpenPath) {
419 flutter::DisplayListBuilder builder;
420 flutter::DlPaint paint;
421
422 paint.setColor(flutter::DlColor::kRed());
423 paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
424 paint.setStrokeWidth(10);
425
426 builder.Translate(300, 300);
427
428 // Move to (50, 50) and draw lines from:
429 // 1. (50, height)
430 // 2. (width, height)
431 // 3. (width, 50)
432 SkPath path;
433 path.moveTo(50, 50);
434 path.lineTo(50, 100);
435 path.lineTo(100, 100);
436 path.lineTo(100, 50);
437 builder.DrawPath(path, paint);
438
439 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
440 }
441
442 TEST_P(DisplayListTest, CanDrawWithMaskBlur) {
443 auto texture = CreateTextureForFixture("embarcadero.jpg");
444 flutter::DisplayListBuilder builder;
445 flutter::DlPaint paint;
446
447 // Mask blurred image.
448 {
449 auto filter =
450 flutter::DlBlurMaskFilter(flutter::DlBlurStyle::kNormal, 10.0f);
451 paint.setMaskFilter(&filter);
452 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
453 flutter::DlImageSampling::kNearestNeighbor, &paint);
454 }
455
456 // Mask blurred filled path.
457 {
458 paint.setColor(flutter::DlColor::kYellow());
459 auto filter =
460 flutter::DlBlurMaskFilter(flutter::DlBlurStyle::kOuter, 10.0f);
461 paint.setMaskFilter(&filter);
462 builder.DrawArc(SkRect::MakeXYWH(410, 110, 100, 100), 45, 270, true, paint);
463 }
464
465 // Mask blurred text.
466 {
467 auto filter =
468 flutter::DlBlurMaskFilter(flutter::DlBlurStyle::kSolid, 10.0f);
469 paint.setMaskFilter(&filter);
470 builder.DrawTextBlob(
471 SkTextBlob::MakeFromString("Testing", CreateTestFont()), 220, 170,
472 paint);
473 }
474
475 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
476 }
477
478 TEST_P(DisplayListTest, CanDrawStrokedText) {
479 flutter::DisplayListBuilder builder;
480 flutter::DlPaint paint;
481
482 paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
483 paint.setColor(flutter::DlColor::kRed());
484 builder.DrawTextBlob(
485 SkTextBlob::MakeFromString("stoked about stroked text", CreateTestFont()),
486 250, 250, paint);
487
488 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
489 }
490
491 // Regression test for https://github.com/flutter/flutter/issues/133157.
492 TEST_P(DisplayListTest, StrokedTextNotOffsetFromNormalText) {
493 flutter::DisplayListBuilder builder;
494 flutter::DlPaint paint;
495 auto const& text_blob = SkTextBlob::MakeFromString("00000", CreateTestFont());
496
497 // https://api.flutter.dev/flutter/material/Colors/blue-constant.html.
498 auto const& mat_blue = flutter::DlColor(0xFF2196f3);
499
500 // Draw a blue filled rectangle so the text is easier to see.
501 paint.setDrawStyle(flutter::DlDrawStyle::kFill);
502 paint.setColor(mat_blue);
503 builder.DrawRect(SkRect::MakeXYWH(0, 0, 500, 500), paint);
504
505 // Draw stacked text, with stroked text on top.
506 paint.setDrawStyle(flutter::DlDrawStyle::kFill);
507 paint.setColor(flutter::DlColor::kWhite());
508 builder.DrawTextBlob(text_blob, 250, 250, paint);
509
510 paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
511 paint.setColor(flutter::DlColor::kBlack());
512 builder.DrawTextBlob(text_blob, 250, 250, paint);
513
514 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
515 }
516
517 TEST_P(DisplayListTest, IgnoreMaskFilterWhenSavingLayer) {
518 auto texture = CreateTextureForFixture("embarcadero.jpg");
519 flutter::DisplayListBuilder builder;
520 auto filter = flutter::DlBlurMaskFilter(flutter::DlBlurStyle::kNormal, 10.0f);
521 flutter::DlPaint paint;
522 paint.setMaskFilter(&filter);
523 builder.SaveLayer(nullptr, &paint);
524 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
525 flutter::DlImageSampling::kNearestNeighbor);
526 builder.Restore();
527 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
528 }
529
530 TEST_P(DisplayListTest, CanDrawWithBlendColorFilter) {
531 auto texture = CreateTextureForFixture("embarcadero.jpg");
532 flutter::DisplayListBuilder builder;
533 flutter::DlPaint paint;
534
535 // Pipeline blended image.
536 {
537 auto filter = flutter::DlBlendColorFilter(flutter::DlColor::kYellow(),
538 flutter::DlBlendMode::kModulate);
539 paint.setColorFilter(&filter);
540 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
541 flutter::DlImageSampling::kNearestNeighbor, &paint);
542 }
543
544 // Advanced blended image.
545 {
546 auto filter = flutter::DlBlendColorFilter(flutter::DlColor::kRed(),
547 flutter::DlBlendMode::kScreen);
548 paint.setColorFilter(&filter);
549 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(250, 250),
550 flutter::DlImageSampling::kNearestNeighbor, &paint);
551 }
552
553 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
554 }
555
556 TEST_P(DisplayListTest, CanDrawWithColorFilterImageFilter) {
557 const float invert_color_matrix[20] = {
558 -1, 0, 0, 0, 1, //
559 0, -1, 0, 0, 1, //
560 0, 0, -1, 0, 1, //
561 0, 0, 0, 1, 0, //
562 };
563 auto texture = CreateTextureForFixture("boston.jpg");
564 flutter::DisplayListBuilder builder;
565 flutter::DlPaint paint;
566
567 auto color_filter =
568 std::make_shared<flutter::DlMatrixColorFilter>(invert_color_matrix);
569 auto image_filter =
570 std::make_shared<flutter::DlColorFilterImageFilter>(color_filter);
571
572 paint.setImageFilter(image_filter.get());
573 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
574 flutter::DlImageSampling::kNearestNeighbor, &paint);
575
576 builder.Translate(0, 700);
577 paint.setColorFilter(color_filter.get());
578 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
579 flutter::DlImageSampling::kNearestNeighbor, &paint);
580 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
581 }
582
583 TEST_P(DisplayListTest, CanDrawWithImageBlurFilter) {
584 auto texture = CreateTextureForFixture("embarcadero.jpg");
585
586 auto callback = [&]() {
587 static float sigma[] = {10, 10};
588
589 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
590 ImGui::SliderFloat2("Sigma", sigma, 0, 100);
591 ImGui::End();
592
593 flutter::DisplayListBuilder builder;
594 flutter::DlPaint paint;
595
596 auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
597 flutter::DlTileMode::kClamp);
598 paint.setImageFilter(&filter);
599 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200),
600 flutter::DlImageSampling::kNearestNeighbor, &paint);
601
602 return builder.Build();
603 };
604
605 ASSERT_TRUE(OpenPlaygroundHere(callback));
606 }
607
608 TEST_P(DisplayListTest, CanDrawWithComposeImageFilter) {
609 auto texture = CreateTextureForFixture("boston.jpg");
610 flutter::DisplayListBuilder builder;
611 flutter::DlPaint paint;
612
613 auto dilate = std::make_shared<flutter::DlDilateImageFilter>(10.0, 10.0);
614 auto erode = std::make_shared<flutter::DlErodeImageFilter>(10.0, 10.0);
615 auto open = std::make_shared<flutter::DlComposeImageFilter>(dilate, erode);
616 auto close = std::make_shared<flutter::DlComposeImageFilter>(erode, dilate);
617
618 paint.setImageFilter(open.get());
619 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
620 flutter::DlImageSampling::kNearestNeighbor, &paint);
621 builder.Translate(0, 700);
622 paint.setImageFilter(close.get());
623 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
624 flutter::DlImageSampling::kNearestNeighbor, &paint);
625 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
626 }
627
628 TEST_P(DisplayListTest, CanClampTheResultingColorOfColorMatrixFilter) {
629 auto texture = CreateTextureForFixture("boston.jpg");
630 const float inner_color_matrix[20] = {
631 1, 0, 0, 0, 0, //
632 0, 1, 0, 0, 0, //
633 0, 0, 1, 0, 0, //
634 0, 0, 0, 2, 0, //
635 };
636 const float outer_color_matrix[20] = {
637 1, 0, 0, 0, 0, //
638 0, 1, 0, 0, 0, //
639 0, 0, 1, 0, 0, //
640 0, 0, 0, 0.5, 0, //
641 };
642 auto inner_color_filter =
643 std::make_shared<flutter::DlMatrixColorFilter>(inner_color_matrix);
644 auto outer_color_filter =
645 std::make_shared<flutter::DlMatrixColorFilter>(outer_color_matrix);
646 auto inner =
647 std::make_shared<flutter::DlColorFilterImageFilter>(inner_color_filter);
648 auto outer =
649 std::make_shared<flutter::DlColorFilterImageFilter>(outer_color_filter);
650 auto compose = std::make_shared<flutter::DlComposeImageFilter>(outer, inner);
651
652 flutter::DisplayListBuilder builder;
653 flutter::DlPaint paint;
654 paint.setImageFilter(compose.get());
655 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
656 flutter::DlImageSampling::kNearestNeighbor, &paint);
657 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
658 }
659
660 TEST_P(DisplayListTest, CanDrawBackdropFilter) {
661 auto texture = CreateTextureForFixture("embarcadero.jpg");
662
663 auto callback = [&]() {
664 static float sigma[] = {10, 10};
665 static float ctm_scale = 1;
666 static bool use_bounds = true;
667 static bool draw_circle = true;
668 static bool add_clip = true;
669
670 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
671 ImGui::SliderFloat2("Sigma", sigma, 0, 100);
672 ImGui::SliderFloat("Scale", &ctm_scale, 0, 10);
673 ImGui::NewLine();
674 ImGui::TextWrapped(
675 "If everything is working correctly, none of the options below should "
676 "impact the filter's appearance.");
677 ImGui::Checkbox("Use SaveLayer bounds", &use_bounds);
678 ImGui::Checkbox("Draw child element", &draw_circle);
679 ImGui::Checkbox("Add pre-clip", &add_clip);
680 ImGui::End();
681
682 flutter::DisplayListBuilder builder;
683
684 Vector2 scale = ctm_scale * GetContentScale();
685 builder.Scale(scale.x, scale.y);
686
687 auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
688 flutter::DlTileMode::kClamp);
689
690 std::optional<SkRect> bounds;
691 if (use_bounds) {
692 auto [p1, p2] = IMPELLER_PLAYGROUND_LINE(
693 Point(350, 150), Point(800, 600), 20, Color::White(), Color::White());
694 bounds = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
695 }
696
697 // Insert a clip to test that the backdrop filter handles stencil depths > 0
698 // correctly.
699 if (add_clip) {
700 builder.ClipRect(SkRect::MakeLTRB(0, 0, 99999, 99999),
701 flutter::DlCanvas::ClipOp::kIntersect, true);
702 }
703
704 builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200),
705 flutter::DlImageSampling::kNearestNeighbor, nullptr);
706 builder.SaveLayer(bounds.has_value() ? &bounds.value() : nullptr, nullptr,
707 &filter);
708
709 if (draw_circle) {
710 auto circle_center =
711 IMPELLER_PLAYGROUND_POINT(Point(500, 400), 20, Color::Red());
712
713 flutter::DlPaint paint;
714 paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
715 paint.setStrokeCap(flutter::DlStrokeCap::kButt);
716 paint.setStrokeJoin(flutter::DlStrokeJoin::kBevel);
717 paint.setStrokeWidth(10);
718 paint.setColor(flutter::DlColor::kRed().withAlpha(100));
719 builder.DrawCircle({circle_center.x, circle_center.y}, 100, paint);
720 }
721
722 return builder.Build();
723 };
724
725 ASSERT_TRUE(OpenPlaygroundHere(callback));
726 }
727
728 TEST_P(DisplayListTest, CanDrawNinePatchImage) {
729 // Image is drawn with corners to scale and center pieces stretched to fit.
730 auto texture = CreateTextureForFixture("embarcadero.jpg");
731 flutter::DisplayListBuilder builder;
732 auto size = texture->GetSize();
733 builder.DrawImageNine(
734 DlImageImpeller::Make(texture),
735 SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
736 size.height * 3 / 4),
737 SkRect::MakeLTRB(0, 0, size.width * 2, size.height * 2),
738 flutter::DlFilterMode::kNearest, nullptr);
739 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
740 }
741
742 TEST_P(DisplayListTest, CanDrawNinePatchImageCenterWidthBiggerThanDest) {
743 // Edge case, the width of the corners does not leave any room for the
744 // center slice. The center (across the vertical axis) is folded out of the
745 // resulting image.
746 auto texture = CreateTextureForFixture("embarcadero.jpg");
747 flutter::DisplayListBuilder builder;
748 auto size = texture->GetSize();
749 builder.DrawImageNine(
750 DlImageImpeller::Make(texture),
751 SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
752 size.height * 3 / 4),
753 SkRect::MakeLTRB(0, 0, size.width / 2, size.height),
754 flutter::DlFilterMode::kNearest, nullptr);
755 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
756 }
757
758 TEST_P(DisplayListTest, CanDrawNinePatchImageCenterHeightBiggerThanDest) {
759 // Edge case, the height of the corners does not leave any room for the
760 // center slice. The center (across the horizontal axis) is folded out of the
761 // resulting image.
762 auto texture = CreateTextureForFixture("embarcadero.jpg");
763 flutter::DisplayListBuilder builder;
764 auto size = texture->GetSize();
765 builder.DrawImageNine(
766 DlImageImpeller::Make(texture),
767 SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
768 size.height * 3 / 4),
769 SkRect::MakeLTRB(0, 0, size.width, size.height / 2),
770 flutter::DlFilterMode::kNearest, nullptr);
771 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
772 }
773
774 TEST_P(DisplayListTest, CanDrawNinePatchImageCenterBiggerThanDest) {
775 // Edge case, the width and height of the corners does not leave any
776 // room for the center slices. Only the corners are displayed.
777 auto texture = CreateTextureForFixture("embarcadero.jpg");
778 flutter::DisplayListBuilder builder;
779 auto size = texture->GetSize();
780 builder.DrawImageNine(
781 DlImageImpeller::Make(texture),
782 SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
783 size.height * 3 / 4),
784 SkRect::MakeLTRB(0, 0, size.width / 2, size.height / 2),
785 flutter::DlFilterMode::kNearest, nullptr);
786 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
787 }
788
789 TEST_P(DisplayListTest, CanDrawNinePatchImageCornersScaledDown) {
790 // Edge case, there is not enough room for the corners to be drawn
791 // without scaling them down.
792 auto texture = CreateTextureForFixture("embarcadero.jpg");
793 flutter::DisplayListBuilder builder;
794 auto size = texture->GetSize();
795 builder.DrawImageNine(
796 DlImageImpeller::Make(texture),
797 SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
798 size.height * 3 / 4),
799 SkRect::MakeLTRB(0, 0, size.width / 4, size.height / 4),
800 flutter::DlFilterMode::kNearest, nullptr);
801 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
802 }
803
804 TEST_P(DisplayListTest, CanDrawPoints) {
805 flutter::DisplayListBuilder builder;
806 SkPoint points[7] = {
807 {0, 0}, //
808 {100, 100}, //
809 {100, 0}, //
810 {0, 100}, //
811 {0, 0}, //
812 {48, 48}, //
813 {52, 52}, //
814 };
815 std::vector<flutter::DlStrokeCap> caps = {
816 flutter::DlStrokeCap::kButt,
817 flutter::DlStrokeCap::kRound,
818 flutter::DlStrokeCap::kSquare,
819 };
820 flutter::DlPaint paint =
821 flutter::DlPaint() //
822 .setColor(flutter::DlColor::kYellow().withAlpha(127)) //
823 .setStrokeWidth(20);
824 builder.Translate(50, 50);
825 for (auto cap : caps) {
826 paint.setStrokeCap(cap);
827 builder.Save();
828 builder.DrawPoints(flutter::DlCanvas::PointMode::kPoints, 7, points, paint);
829 builder.Translate(150, 0);
830 builder.DrawPoints(flutter::DlCanvas::PointMode::kLines, 5, points, paint);
831 builder.Translate(150, 0);
832 builder.DrawPoints(flutter::DlCanvas::PointMode::kPolygon, 5, points,
833 paint);
834 builder.Restore();
835 builder.Translate(0, 150);
836 }
837 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
838 }
839
840 TEST_P(DisplayListTest, CanDrawZeroLengthLine) {
841 flutter::DisplayListBuilder builder;
842 std::vector<flutter::DlStrokeCap> caps = {
843 flutter::DlStrokeCap::kButt,
844 flutter::DlStrokeCap::kRound,
845 flutter::DlStrokeCap::kSquare,
846 };
847 flutter::DlPaint paint =
848 flutter::DlPaint() //
849 .setColor(flutter::DlColor::kYellow().withAlpha(127)) //
850 .setDrawStyle(flutter::DlDrawStyle::kStroke) //
851 .setStrokeCap(flutter::DlStrokeCap::kButt) //
852 .setStrokeWidth(20);
853 SkPath path = SkPath().addPoly({{150, 50}, {150, 50}}, false);
854 for (auto cap : caps) {
855 paint.setStrokeCap(cap);
856 builder.DrawLine({50, 50}, {50, 50}, paint);
857 builder.DrawPath(path, paint);
858 builder.Translate(0, 150);
859 }
860 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
861 }
862
863 TEST_P(DisplayListTest, CanDrawShadow) {
864 flutter::DisplayListBuilder builder;
865 flutter::DlPaint paint;
866
867 auto content_scale = GetContentScale() * 0.8;
868 builder.Scale(content_scale.x, content_scale.y);
869
870 constexpr size_t star_spikes = 5;
871 constexpr SkScalar half_spike_rotation = kPi / star_spikes;
872 constexpr SkScalar radius = 40;
873 constexpr SkScalar spike_size = 10;
874 constexpr SkScalar outer_radius = radius + spike_size;
875 constexpr SkScalar inner_radius = radius - spike_size;
876 std::array<SkPoint, star_spikes * 2> star;
877 for (size_t i = 0; i < star_spikes; i++) {
878 const SkScalar rotation = half_spike_rotation * i * 2;
879 star[i * 2] = SkPoint::Make(50 + std::sin(rotation) * outer_radius,
880 50 - std::cos(rotation) * outer_radius);
881 star[i * 2 + 1] = SkPoint::Make(
882 50 + std::sin(rotation + half_spike_rotation) * inner_radius,
883 50 - std::cos(rotation + half_spike_rotation) * inner_radius);
884 }
885
886 std::array<SkPath, 4> paths = {
887 SkPath{}.addRect(SkRect::MakeXYWH(0, 0, 200, 100)),
888 SkPath{}.addRRect(
889 SkRRect::MakeRectXY(SkRect::MakeXYWH(20, 0, 200, 100), 30, 30)),
890 SkPath{}.addCircle(100, 50, 50),
891 SkPath{}.addPoly(star.data(), star.size(), true),
892 };
893 paint.setColor(flutter::DlColor::kWhite());
894 builder.DrawPaint(paint);
895 paint.setColor(flutter::DlColor::kCyan());
896 builder.Translate(100, 50);
897 for (size_t x = 0; x < paths.size(); x++) {
898 builder.Save();
899 for (size_t y = 0; y < 6; y++) {
900 builder.DrawShadow(paths[x], flutter::DlColor::kBlack(), 3 + y * 8, false,
901 1);
902 builder.DrawPath(paths[x], paint);
903 builder.Translate(0, 150);
904 }
905 builder.Restore();
906 builder.Translate(250, 0);
907 }
908
909 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
910 }
911
912 TEST_P(DisplayListTest,
913 DispatcherDoesNotCullPerspectiveTransformedChildDisplayLists) {
914 // Regression test for https://github.com/flutter/flutter/issues/130613
915 flutter::DisplayListBuilder sub_builder(true);
916 sub_builder.DrawRect(SkRect::MakeXYWH(0, 0, 50, 50),
917 flutter::DlPaint(flutter::DlColor::kRed()));
918 auto display_list = sub_builder.Build();
919
920 DlDispatcher dispatcher(Rect::MakeLTRB(0, 0, 2400, 1800));
921 dispatcher.scale(2.0, 2.0);
922 dispatcher.translate(-93.0, 0.0);
923 // clang-format off
924 dispatcher.transformFullPerspective(
925 0.8, -0.2, -0.1, -0.0,
926 0.0, 1.0, 0.0, 0.0,
927 1.4, 1.3, 1.0, 0.0,
928 63.2, 65.3, 48.6, 1.1
929 );
930 // clang-format on
931 dispatcher.translate(35.0, 75.0);
932 dispatcher.drawDisplayList(display_list, 1.0f);
933 auto picture = dispatcher.EndRecordingAsPicture();
934
935 bool found = false;
936 picture.pass->IterateAllEntities([&found](Entity& entity) {
937 if (std::static_pointer_cast<SolidColorContents>(entity.GetContents())
938 ->GetColor() == Color::Red()) {
939 found = true;
940 return false;
941 }
942
943 return true;
944 });
945 EXPECT_TRUE(found);
946 }
947
948 TEST_P(DisplayListTest, TransparentShadowProducesCorrectColor) {
949 DlDispatcher dispatcher;
950 dispatcher.save();
951 dispatcher.scale(1.618, 1.618);
952 dispatcher.drawShadow(SkPath{}.addRect(SkRect::MakeXYWH(0, 0, 200, 100)),
953 flutter::DlColor::kTransparent(), 15, false, 1);
954 dispatcher.restore();
955 auto picture = dispatcher.EndRecordingAsPicture();
956
957 std::shared_ptr<SolidRRectBlurContents> rrect_blur;
958 picture.pass->IterateAllEntities([&rrect_blur](Entity& entity) {
959 if (ScalarNearlyEqual(entity.GetTransform().GetScale().x, 1.618f)) {
960 rrect_blur = std::static_pointer_cast<SolidRRectBlurContents>(
961 entity.GetContents());
962 return false;
963 }
964 return true;
965 });
966
967 ASSERT_NE(rrect_blur, nullptr);
968 ASSERT_EQ(rrect_blur->GetColor().red, 0);
969 ASSERT_EQ(rrect_blur->GetColor().green, 0);
970 ASSERT_EQ(rrect_blur->GetColor().blue, 0);
971 ASSERT_EQ(rrect_blur->GetColor().alpha, 0);
972 }
973
974 // Draw a hexagon using triangle fan
975 TEST_P(DisplayListTest, CanConvertTriangleFanToTriangles) {
976 constexpr Scalar hexagon_radius = 125;
977 auto hex_start = Point(200.0, -hexagon_radius + 200.0);
978 auto center_to_flat = 1.73 / 2 * hexagon_radius;
979
980 // clang-format off
981 std::vector<SkPoint> vertices = {
982 SkPoint::Make(hex_start.x, hex_start.y),
983 SkPoint::Make(hex_start.x + center_to_flat, hex_start.y + 0.5 * hexagon_radius),
984 SkPoint::Make(hex_start.x + center_to_flat, hex_start.y + 1.5 * hexagon_radius),
985 SkPoint::Make(hex_start.x + center_to_flat, hex_start.y + 1.5 * hexagon_radius),
986 SkPoint::Make(hex_start.x, hex_start.y + 2 * hexagon_radius),
987 SkPoint::Make(hex_start.x, hex_start.y + 2 * hexagon_radius),
988 SkPoint::Make(hex_start.x - center_to_flat, hex_start.y + 1.5 * hexagon_radius),
989 SkPoint::Make(hex_start.x - center_to_flat, hex_start.y + 1.5 * hexagon_radius),
990 SkPoint::Make(hex_start.x - center_to_flat, hex_start.y + 0.5 * hexagon_radius)
991 };
992 // clang-format on
993 auto paint = flutter::DlPaint(flutter::DlColor::kDarkGrey());
994 auto dl_vertices = flutter::DlVertices::Make(
995 flutter::DlVertexMode::kTriangleFan, vertices.size(), vertices.data(),
996 nullptr, nullptr);
997 flutter::DisplayListBuilder builder;
998 builder.DrawVertices(dl_vertices, flutter::DlBlendMode::kSrcOver, paint);
999 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1000 }
1001
1002 TEST_P(DisplayListTest, CanDrawZeroWidthLine) {
1003 flutter::DisplayListBuilder builder;
1004 std::vector<flutter::DlStrokeCap> caps = {
1005 flutter::DlStrokeCap::kButt,
1006 flutter::DlStrokeCap::kRound,
1007 flutter::DlStrokeCap::kSquare,
1008 };
1009 flutter::DlPaint paint = //
1010 flutter::DlPaint() //
1011 .setColor(flutter::DlColor::kWhite()) //
1012 .setDrawStyle(flutter::DlDrawStyle::kStroke) //
1013 .setStrokeWidth(0);
1014 flutter::DlPaint outline_paint = //
1015 flutter::DlPaint() //
1016 .setColor(flutter::DlColor::kYellow()) //
1017 .setDrawStyle(flutter::DlDrawStyle::kStroke) //
1018 .setStrokeCap(flutter::DlStrokeCap::kSquare) //
1019 .setStrokeWidth(1);
1020 SkPath path = SkPath().addPoly({{150, 50}, {160, 50}}, false);
1021 for (auto cap : caps) {
1022 paint.setStrokeCap(cap);
1023 builder.DrawLine({50, 50}, {60, 50}, paint);
1024 builder.DrawRect({45, 45, 65, 55}, outline_paint);
1025 builder.DrawLine({100, 50}, {100, 50}, paint);
1026 if (cap != flutter::DlStrokeCap::kButt) {
1027 builder.DrawRect({95, 45, 105, 55}, outline_paint);
1028 }
1029 builder.DrawPath(path, paint);
1030 builder.DrawRect(path.getBounds().makeOutset(5, 5), outline_paint);
1031 builder.Translate(0, 150);
1032 }
1033 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1034 }
1035
1036 TEST_P(DisplayListTest, CanDrawWithMatrixFilter) {
1037 auto boston = CreateTextureForFixture("boston.jpg");
1038
1039 auto callback = [&]() {
1040 static int selected_matrix_type = 0;
1041 const char* matrix_type_names[] = {"Matrix", "Local Matrix"};
1042
1043 static float ctm_translation[2] = {200, 200};
1044 static float ctm_scale[2] = {0.65, 0.65};
1045 static float ctm_skew[2] = {0, 0};
1046
1047 static bool enable = true;
1048 static float translation[2] = {100, 100};
1049 static float scale[2] = {0.8, 0.8};
1050 static float skew[2] = {0.2, 0.2};
1051
1052 static bool enable_savelayer = true;
1053
1054 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1055 {
1056 ImGui::Combo("Filter type", &selected_matrix_type, matrix_type_names,
1057 sizeof(matrix_type_names) / sizeof(char*));
1058
1059 ImGui::TextWrapped("Current Transform");
1060 ImGui::SliderFloat2("CTM Translation", ctm_translation, 0, 1000);
1061 ImGui::SliderFloat2("CTM Scale", ctm_scale, 0, 3);
1062 ImGui::SliderFloat2("CTM Skew", ctm_skew, -3, 3);
1063
1064 ImGui::TextWrapped(
1065 "MatrixFilter and LocalMatrixFilter modify the CTM in the same way. "
1066 "The only difference is that MatrixFilter doesn't affect the effect "
1067 "transform, whereas LocalMatrixFilter does.");
1068 // Note: See this behavior in:
1069 // https://fiddle.skia.org/c/6cbb551ab36d06f163db8693972be954
1070 ImGui::Checkbox("Enable", &enable);
1071 ImGui::SliderFloat2("Filter Translation", translation, 0, 1000);
1072 ImGui::SliderFloat2("Filter Scale", scale, 0, 3);
1073 ImGui::SliderFloat2("Filter Skew", skew, -3, 3);
1074
1075 ImGui::TextWrapped(
1076 "Rendering the filtered image within a layer can expose bounds "
1077 "issues. If the rendered image gets cut off when this setting is "
1078 "enabled, there's a coverage bug in the filter.");
1079 ImGui::Checkbox("Render in layer", &enable_savelayer);
1080 }
1081 ImGui::End();
1082
1083 flutter::DisplayListBuilder builder;
1084 flutter::DlPaint paint;
1085
1086 if (enable_savelayer) {
1087 builder.SaveLayer(nullptr, nullptr);
1088 }
1089 {
1090 auto content_scale = GetContentScale();
1091 builder.Scale(content_scale.x, content_scale.y);
1092
1093 // Set the current transform
1094 auto ctm_matrix =
1095 SkMatrix::MakeAll(ctm_scale[0], ctm_skew[0], ctm_translation[0], //
1096 ctm_skew[1], ctm_scale[1], ctm_translation[1], //
1097 0, 0, 1);
1098 builder.Transform(ctm_matrix);
1099
1100 // Set the matrix filter
1101 auto filter_matrix =
1102 SkMatrix::MakeAll(scale[0], skew[0], translation[0], //
1103 skew[1], scale[1], translation[1], //
1104 0, 0, 1);
1105
1106 if (enable) {
1107 switch (selected_matrix_type) {
1108 case 0: {
1109 auto filter = flutter::DlMatrixImageFilter(
1110 filter_matrix, flutter::DlImageSampling::kLinear);
1111 paint.setImageFilter(&filter);
1112 break;
1113 }
1114 case 1: {
1115 auto internal_filter =
1116 flutter::DlBlurImageFilter(10, 10, flutter::DlTileMode::kDecal)
1117 .shared();
1118 auto filter = flutter::DlLocalMatrixImageFilter(filter_matrix,
1119 internal_filter);
1120 paint.setImageFilter(&filter);
1121 break;
1122 }
1123 }
1124 }
1125
1126 builder.DrawImage(DlImageImpeller::Make(boston), {},
1127 flutter::DlImageSampling::kLinear, &paint);
1128 }
1129 if (enable_savelayer) {
1130 builder.Restore();
1131 }
1132
1133 return builder.Build();
1134 };
1135
1136 ASSERT_TRUE(OpenPlaygroundHere(callback));
1137 }
1138
1139 TEST_P(DisplayListTest, CanDrawWithMatrixFilterWhenSavingLayer) {
1140 auto callback = [&]() {
1141 static float translation[2] = {0, 0};
1142 static bool enable_save_layer = true;
1143
1144 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1145 ImGui::SliderFloat2("Translation", translation, -130, 130);
1146 ImGui::Checkbox("Enable save layer", &enable_save_layer);
1147 ImGui::End();
1148
1149 flutter::DisplayListBuilder builder;
1150 builder.Save();
1151 builder.Scale(2.0, 2.0);
1152 flutter::DlPaint paint;
1153 paint.setColor(flutter::DlColor::kYellow());
1154 builder.DrawRect(SkRect::MakeWH(300, 300), paint);
1155 paint.setStrokeWidth(1.0);
1156 paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
1157 paint.setColor(flutter::DlColor::kBlack().withAlpha(0x80));
1158 builder.DrawLine(SkPoint::Make(150, 0), SkPoint::Make(150, 300), paint);
1159 builder.DrawLine(SkPoint::Make(0, 150), SkPoint::Make(300, 150), paint);
1160
1161 flutter::DlPaint save_paint;
1162 SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
1163 SkMatrix translate_matrix =
1164 SkMatrix::Translate(translation[0], translation[1]);
1165 if (enable_save_layer) {
1166 auto filter = flutter::DlMatrixImageFilter(
1167 translate_matrix, flutter::DlImageSampling::kNearestNeighbor);
1168 save_paint.setImageFilter(filter.shared());
1169 builder.SaveLayer(&bounds, &save_paint);
1170 } else {
1171 builder.Save();
1172 builder.Transform(translate_matrix);
1173 }
1174
1175 SkMatrix filter_matrix = SkMatrix::I();
1176 filter_matrix.postTranslate(-150, -150);
1177 filter_matrix.postScale(0.2f, 0.2f);
1178 filter_matrix.postTranslate(150, 150);
1179 auto filter = flutter::DlMatrixImageFilter(
1180 filter_matrix, flutter::DlImageSampling::kNearestNeighbor);
1181
1182 save_paint.setImageFilter(filter.shared());
1183
1184 builder.SaveLayer(&bounds, &save_paint);
1185 flutter::DlPaint paint2;
1186 paint2.setColor(flutter::DlColor::kBlue());
1187 builder.DrawRect(bounds, paint2);
1188 builder.Restore();
1189 builder.Restore();
1190 return builder.Build();
1191 };
1192
1193 ASSERT_TRUE(OpenPlaygroundHere(callback));
1194 }
1195
1196 TEST_P(DisplayListTest, CanDrawRectWithLinearToSrgbColorFilter) {
1197 flutter::DlPaint paint;
1198 paint.setColor(flutter::DlColor(0xFF2196F3).withAlpha(128));
1199 flutter::DisplayListBuilder builder;
1200 paint.setColorFilter(
1201 flutter::DlLinearToSrgbGammaColorFilter::kInstance.get());
1202 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), paint);
1203 builder.Translate(0, 200);
1204
1205 paint.setColorFilter(
1206 flutter::DlSrgbToLinearGammaColorFilter::kInstance.get());
1207 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), paint);
1208
1209 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1210 }
1211
1212 TEST_P(DisplayListTest, CanDrawPaintWithColorSource) {
1213 const flutter::DlColor colors[2] = {
1214 flutter::DlColor(0xFFF44336),
1215 flutter::DlColor(0xFF2196F3),
1216 };
1217 const float stops[2] = {0.0, 1.0};
1218 flutter::DlPaint paint;
1219 flutter::DisplayListBuilder builder;
1220 auto clip_bounds = SkRect::MakeWH(300.0, 300.0);
1221 builder.Save();
1222 builder.Translate(100, 100);
1223 builder.ClipRect(clip_bounds, flutter::DlCanvas::ClipOp::kIntersect, false);
1224 auto linear =
1225 flutter::DlColorSource::MakeLinear({0.0, 0.0}, {100.0, 100.0}, 2, colors,
1226 stops, flutter::DlTileMode::kRepeat);
1227 paint.setColorSource(linear);
1228 builder.DrawPaint(paint);
1229 builder.Restore();
1230
1231 builder.Save();
1232 builder.Translate(500, 100);
1233 builder.ClipRect(clip_bounds, flutter::DlCanvas::ClipOp::kIntersect, false);
1234 auto radial = flutter::DlColorSource::MakeRadial(
1235 {100.0, 100.0}, 100.0, 2, colors, stops, flutter::DlTileMode::kRepeat);
1236 paint.setColorSource(radial);
1237 builder.DrawPaint(paint);
1238 builder.Restore();
1239
1240 builder.Save();
1241 builder.Translate(100, 500);
1242 builder.ClipRect(clip_bounds, flutter::DlCanvas::ClipOp::kIntersect, false);
1243 auto sweep =
1244 flutter::DlColorSource::MakeSweep({100.0, 100.0}, 180.0, 270.0, 2, colors,
1245 stops, flutter::DlTileMode::kRepeat);
1246 paint.setColorSource(sweep);
1247 builder.DrawPaint(paint);
1248 builder.Restore();
1249
1250 builder.Save();
1251 builder.Translate(500, 500);
1252 builder.ClipRect(clip_bounds, flutter::DlCanvas::ClipOp::kIntersect, false);
1253 auto texture = CreateTextureForFixture("table_mountain_nx.png");
1254 auto image = std::make_shared<flutter::DlImageColorSource>(
1255 DlImageImpeller::Make(texture), flutter::DlTileMode::kRepeat,
1256 flutter::DlTileMode::kRepeat);
1257 paint.setColorSource(image);
1258 builder.DrawPaint(paint);
1259 builder.Restore();
1260
1261 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1262 }
1263
1264 TEST_P(DisplayListTest, CanBlendDstOverAndDstCorrectly) {
1265 flutter::DisplayListBuilder builder;
1266
1267 {
1268 builder.SaveLayer(nullptr, nullptr);
1269 builder.Translate(100, 100);
1270 flutter::DlPaint paint;
1271 paint.setColor(flutter::DlColor::kRed());
1272 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1273 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1274 paint.setBlendMode(flutter::DlBlendMode::kSrcOver);
1275 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1276 builder.Restore();
1277 }
1278 {
1279 builder.SaveLayer(nullptr, nullptr);
1280 builder.Translate(300, 100);
1281 flutter::DlPaint paint;
1282 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1283 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1284 paint.setColor(flutter::DlColor::kRed());
1285 paint.setBlendMode(flutter::DlBlendMode::kDstOver);
1286 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1287 builder.Restore();
1288 }
1289 {
1290 builder.SaveLayer(nullptr, nullptr);
1291 builder.Translate(100, 300);
1292 flutter::DlPaint paint;
1293 paint.setColor(flutter::DlColor::kRed());
1294 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1295 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1296 paint.setBlendMode(flutter::DlBlendMode::kSrc);
1297 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1298 builder.Restore();
1299 }
1300 {
1301 builder.SaveLayer(nullptr, nullptr);
1302 builder.Translate(300, 300);
1303 flutter::DlPaint paint;
1304 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1305 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1306 paint.setColor(flutter::DlColor::kRed());
1307 paint.setBlendMode(flutter::DlBlendMode::kDst);
1308 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1309 builder.Restore();
1310 }
1311
1312 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1313 }
1314
1315 TEST_P(DisplayListTest, CanDrawCorrectlyWithColorFilterAndImageFilter) {
1316 flutter::DisplayListBuilder builder;
1317 const float green_color_matrix[20] = {
1318 0, 0, 0, 0, 0, //
1319 0, 0, 0, 0, 1, //
1320 0, 0, 0, 0, 0, //
1321 0, 0, 0, 1, 0, //
1322 };
1323 const float blue_color_matrix[20] = {
1324 0, 0, 0, 0, 0, //
1325 0, 0, 0, 0, 0, //
1326 0, 0, 0, 0, 1, //
1327 0, 0, 0, 1, 0, //
1328 };
1329 auto green_color_filter =
1330 std::make_shared<flutter::DlMatrixColorFilter>(green_color_matrix);
1331 auto blue_color_filter =
1332 std::make_shared<flutter::DlMatrixColorFilter>(blue_color_matrix);
1333 auto blue_image_filter =
1334 std::make_shared<flutter::DlColorFilterImageFilter>(blue_color_filter);
1335
1336 flutter::DlPaint paint;
1337 paint.setColor(flutter::DlColor::kRed());
1338 paint.setColorFilter(green_color_filter);
1339 paint.setImageFilter(blue_image_filter);
1340 builder.DrawRect(SkRect::MakeLTRB(100, 100, 500, 500), paint);
1341 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1342 }
1343
1344 TEST_P(DisplayListTest, MaskBlursApplyCorrectlyToColorSources) {
1345 auto blur_filter = std::make_shared<flutter::DlBlurMaskFilter>(
1346 flutter::DlBlurStyle::kNormal, 10);
1347
1348 flutter::DisplayListBuilder builder;
1349
1350 std::array<flutter::DlColor, 2> colors = {flutter::DlColor::kBlue(),
1351 flutter::DlColor::kGreen()};
1352 std::array<float, 2> stops = {0, 1};
1353 std::array<std::shared_ptr<flutter::DlColorSource>, 2> color_sources = {
1354 std::make_shared<flutter::DlColorColorSource>(flutter::DlColor::kWhite()),
1355 flutter::DlColorSource::MakeLinear(
1356 SkPoint::Make(0, 0), SkPoint::Make(100, 50), 2, colors.data(),
1357 stops.data(), flutter::DlTileMode::kClamp)};
1358
1359 int offset = 100;
1360 for (const auto& color_source : color_sources) {
1361 flutter::DlPaint paint;
1362 paint.setColorSource(color_source);
1363 paint.setMaskFilter(blur_filter);
1364
1365 paint.setDrawStyle(flutter::DlDrawStyle::kFill);
1366 builder.DrawRRect(
1367 SkRRect::MakeRectXY(SkRect::MakeXYWH(100, offset, 100, 50), 30, 30),
1368 paint);
1369 paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
1370 paint.setStrokeWidth(10);
1371 builder.DrawRRect(
1372 SkRRect::MakeRectXY(SkRect::MakeXYWH(300, offset, 100, 50), 30, 30),
1373 paint);
1374
1375 offset += 100;
1376 }
1377
1378 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1379 }
1380
1381 TEST_P(DisplayListTest, DrawVerticesSolidColorTrianglesWithoutIndices) {
1382 // Use negative coordinates and then scale the transform by -1, -1 to make
1383 // sure coverage is taking the transform into account.
1384 std::vector<SkPoint> positions = {SkPoint::Make(-100, -300),
1385 SkPoint::Make(-200, -100),
1386 SkPoint::Make(-300, -300)};
1387 std::vector<flutter::DlColor> colors = {flutter::DlColor::kWhite(),
1388 flutter::DlColor::kGreen(),
1389 flutter::DlColor::kWhite()};
1390
1391 auto vertices = flutter::DlVertices::Make(
1392 flutter::DlVertexMode::kTriangles, 3, positions.data(),
1393 /*texture_coordinates=*/nullptr, colors.data());
1394
1395 flutter::DisplayListBuilder builder;
1396 flutter::DlPaint paint;
1397
1398 paint.setColor(flutter::DlColor::kRed().modulateOpacity(0.5));
1399 builder.Scale(-1, -1);
1400 builder.DrawVertices(vertices, flutter::DlBlendMode::kSrcOver, paint);
1401
1402 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1403 }
1404
1405 TEST_P(DisplayListTest, DrawVerticesLinearGradientWithoutIndices) {
1406 std::vector<SkPoint> positions = {SkPoint::Make(100, 300),
1407 SkPoint::Make(200, 100),
1408 SkPoint::Make(300, 300)};
1409
1410 auto vertices = flutter::DlVertices::Make(
1411 flutter::DlVertexMode::kTriangles, 3, positions.data(),
1412 /*texture_coordinates=*/nullptr, /*colors=*/nullptr);
1413
1414 std::vector<flutter::DlColor> colors = {flutter::DlColor::kBlue(),
1415 flutter::DlColor::kRed()};
1416 const float stops[2] = {0.0, 1.0};
1417
1418 auto linear = flutter::DlColorSource::MakeLinear(
1419 {100.0, 100.0}, {300.0, 300.0}, 2, colors.data(), stops,
1420 flutter::DlTileMode::kRepeat);
1421
1422 flutter::DisplayListBuilder builder;
1423 flutter::DlPaint paint;
1424
1425 paint.setColorSource(linear);
1426 builder.DrawVertices(vertices, flutter::DlBlendMode::kSrcOver, paint);
1427
1428 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1429 }
1430
1431 TEST_P(DisplayListTest, DrawVerticesLinearGradientWithTextureCoordinates) {
1432 std::vector<SkPoint> positions = {SkPoint::Make(100, 300),
1433 SkPoint::Make(200, 100),
1434 SkPoint::Make(300, 300)};
1435 std::vector<SkPoint> texture_coordinates = {SkPoint::Make(300, 100),
1436 SkPoint::Make(100, 200),
1437 SkPoint::Make(300, 300)};
1438
1439 auto vertices = flutter::DlVertices::Make(
1440 flutter::DlVertexMode::kTriangles, 3, positions.data(),
1441 texture_coordinates.data(), /*colors=*/nullptr);
1442
1443 std::vector<flutter::DlColor> colors = {flutter::DlColor::kBlue(),
1444 flutter::DlColor::kRed()};
1445 const float stops[2] = {0.0, 1.0};
1446
1447 auto linear = flutter::DlColorSource::MakeLinear(
1448 {100.0, 100.0}, {300.0, 300.0}, 2, colors.data(), stops,
1449 flutter::DlTileMode::kRepeat);
1450
1451 flutter::DisplayListBuilder builder;
1452 flutter::DlPaint paint;
1453
1454 paint.setColorSource(linear);
1455 builder.DrawVertices(vertices, flutter::DlBlendMode::kSrcOver, paint);
1456
1457 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1458 }
1459
1460 TEST_P(DisplayListTest, DrawVerticesImageSourceWithTextureCoordinates) {
1461 auto texture = CreateTextureForFixture("embarcadero.jpg");
1462 auto dl_image = DlImageImpeller::Make(texture);
1463 std::vector<SkPoint> positions = {SkPoint::Make(100, 300),
1464 SkPoint::Make(200, 100),
1465 SkPoint::Make(300, 300)};
1466 std::vector<SkPoint> texture_coordinates = {
1467 SkPoint::Make(0, 0), SkPoint::Make(100, 200), SkPoint::Make(200, 100)};
1468
1469 auto vertices = flutter::DlVertices::Make(
1470 flutter::DlVertexMode::kTriangles, 3, positions.data(),
1471 texture_coordinates.data(), /*colors=*/nullptr);
1472
1473 flutter::DisplayListBuilder builder;
1474 flutter::DlPaint paint;
1475
1476 auto image_source = flutter::DlImageColorSource(
1477 dl_image, flutter::DlTileMode::kRepeat, flutter::DlTileMode::kRepeat);
1478
1479 paint.setColorSource(&image_source);
1480 builder.DrawVertices(vertices, flutter::DlBlendMode::kSrcOver, paint);
1481
1482 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1483 }
1484
1485 TEST_P(DisplayListTest,
1486 DrawVerticesImageSourceWithTextureCoordinatesAndColorBlending) {
1487 auto texture = CreateTextureForFixture("embarcadero.jpg");
1488 auto dl_image = DlImageImpeller::Make(texture);
1489 std::vector<SkPoint> positions = {SkPoint::Make(100, 300),
1490 SkPoint::Make(200, 100),
1491 SkPoint::Make(300, 300)};
1492 std::vector<flutter::DlColor> colors = {flutter::DlColor::kWhite(),
1493 flutter::DlColor::kGreen(),
1494 flutter::DlColor::kWhite()};
1495 std::vector<SkPoint> texture_coordinates = {
1496 SkPoint::Make(0, 0), SkPoint::Make(100, 200), SkPoint::Make(200, 100)};
1497
1498 auto vertices = flutter::DlVertices::Make(
1499 flutter::DlVertexMode::kTriangles, 3, positions.data(),
1500 texture_coordinates.data(), colors.data());
1501
1502 flutter::DisplayListBuilder builder;
1503 flutter::DlPaint paint;
1504
1505 auto image_source = flutter::DlImageColorSource(
1506 dl_image, flutter::DlTileMode::kRepeat, flutter::DlTileMode::kRepeat);
1507
1508 paint.setColorSource(&image_source);
1509 builder.DrawVertices(vertices, flutter::DlBlendMode::kModulate, paint);
1510
1511 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1512 }
1513
1514 TEST_P(DisplayListTest, DrawVerticesSolidColorTrianglesWithIndices) {
1515 std::vector<SkPoint> positions = {
1516 SkPoint::Make(100, 300), SkPoint::Make(200, 100), SkPoint::Make(300, 300),
1517 SkPoint::Make(200, 500)};
1518 std::vector<uint16_t> indices = {0, 1, 2, 0, 2, 3};
1519
1520 auto vertices = flutter::DlVertices::Make(
1521 flutter::DlVertexMode::kTriangles, 6, positions.data(),
1522 /*texture_coordinates=*/nullptr, /*colors=*/nullptr, 6, indices.data());
1523
1524 flutter::DisplayListBuilder builder;
1525 flutter::DlPaint paint;
1526
1527 paint.setColor(flutter::DlColor::kWhite());
1528 builder.DrawVertices(vertices, flutter::DlBlendMode::kSrcOver, paint);
1529
1530 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1531 }
1532
1533 TEST_P(DisplayListTest, DrawVerticesPremultipliesColors) {
1534 std::vector<SkPoint> positions = {
1535 SkPoint::Make(100, 300), SkPoint::Make(200, 100), SkPoint::Make(300, 300),
1536 SkPoint::Make(200, 500)};
1537 auto color = flutter::DlColor::kBlue().withAlpha(0x99);
1538 std::vector<uint16_t> indices = {0, 1, 2, 0, 2, 3};
1539 std::vector<flutter::DlColor> colors = {color, color, color, color};
1540
1541 auto vertices = flutter::DlVertices::Make(
1542 flutter::DlVertexMode::kTriangles, 6, positions.data(),
1543 /*texture_coordinates=*/nullptr, colors.data(), 6, indices.data());
1544
1545 flutter::DisplayListBuilder builder;
1546 flutter::DlPaint paint;
1547 paint.setBlendMode(flutter::DlBlendMode::kSrcOver);
1548 paint.setColor(flutter::DlColor::kRed());
1549
1550 builder.DrawRect(SkRect::MakeLTRB(0, 0, 400, 400), paint);
1551 builder.DrawVertices(vertices, flutter::DlBlendMode::kDst, paint);
1552
1553 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1554 }
1555
1556 TEST_P(DisplayListTest, DrawShapes) {
1557 flutter::DisplayListBuilder builder;
1558 std::vector<flutter::DlStrokeJoin> joins = {
1559 flutter::DlStrokeJoin::kBevel,
1560 flutter::DlStrokeJoin::kRound,
1561 flutter::DlStrokeJoin::kMiter,
1562 };
1563 flutter::DlPaint paint = //
1564 flutter::DlPaint() //
1565 .setColor(flutter::DlColor::kWhite()) //
1566 .setDrawStyle(flutter::DlDrawStyle::kFill) //
1567 .setStrokeWidth(10);
1568 flutter::DlPaint stroke_paint = //
1569 flutter::DlPaint() //
1570 .setColor(flutter::DlColor::kWhite()) //
1571 .setDrawStyle(flutter::DlDrawStyle::kStroke) //
1572 .setStrokeWidth(10);
1573 SkPath path = SkPath().addPoly({{150, 50}, {160, 50}}, false);
1574
1575 builder.Translate(300, 50);
1576 builder.Scale(0.8, 0.8);
1577 for (auto join : joins) {
1578 paint.setStrokeJoin(join);
1579 stroke_paint.setStrokeJoin(join);
1580 builder.DrawRect(SkRect::MakeXYWH(0, 0, 100, 100), paint);
1581 builder.DrawRect(SkRect::MakeXYWH(0, 150, 100, 100), stroke_paint);
1582 builder.DrawRRect(
1583 SkRRect::MakeRectXY(SkRect::MakeXYWH(150, 0, 100, 100), 30, 30), paint);
1584 builder.DrawRRect(
1585 SkRRect::MakeRectXY(SkRect::MakeXYWH(150, 150, 100, 100), 30, 30),
1586 stroke_paint);
1587 builder.DrawCircle({350, 50}, 50, paint);
1588 builder.DrawCircle({350, 200}, 50, stroke_paint);
1589 builder.Translate(0, 300);
1590 }
1591 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1592 }
1593
1594 TEST_P(DisplayListTest, ClipDrawRRectWithNonCircularRadii) {
1595 flutter::DisplayListBuilder builder;
1596
1597 flutter::DlPaint fill_paint = //
1598 flutter::DlPaint() //
1599 .setColor(flutter::DlColor::kBlue()) //
1600 .setDrawStyle(flutter::DlDrawStyle::kFill) //
1601 .setStrokeWidth(10);
1602 flutter::DlPaint stroke_paint = //
1603 flutter::DlPaint() //
1604 .setColor(flutter::DlColor::kGreen()) //
1605 .setDrawStyle(flutter::DlDrawStyle::kStroke) //
1606 .setStrokeWidth(10);
1607
1608 builder.DrawRRect(
1609 SkRRect::MakeRectXY(SkRect::MakeXYWH(500, 100, 300, 300), 120, 40),
1610 fill_paint);
1611 builder.DrawRRect(
1612 SkRRect::MakeRectXY(SkRect::MakeXYWH(500, 100, 300, 300), 120, 40),
1613 stroke_paint);
1614
1615 builder.DrawRRect(
1616 SkRRect::MakeRectXY(SkRect::MakeXYWH(100, 500, 300, 300), 40, 120),
1617 fill_paint);
1618 builder.DrawRRect(
1619 SkRRect::MakeRectXY(SkRect::MakeXYWH(100, 500, 300, 300), 40, 120),
1620 stroke_paint);
1621
1622 flutter::DlPaint reference_paint = //
1623 flutter::DlPaint() //
1624 .setColor(flutter::DlColor::kMidGrey()) //
1625 .setDrawStyle(flutter::DlDrawStyle::kFill) //
1626 .setStrokeWidth(10);
1627
1628 builder.DrawRRect(
1629 SkRRect::MakeRectXY(SkRect::MakeXYWH(500, 500, 300, 300), 40, 40),
1630 reference_paint);
1631 builder.DrawRRect(
1632 SkRRect::MakeRectXY(SkRect::MakeXYWH(100, 100, 300, 300), 120, 120),
1633 reference_paint);
1634
1635 flutter::DlPaint clip_fill_paint = //
1636 flutter::DlPaint() //
1637 .setColor(flutter::DlColor::kCyan()) //
1638 .setDrawStyle(flutter::DlDrawStyle::kFill) //
1639 .setStrokeWidth(10);
1640
1641 builder.Save();
1642 builder.ClipRRect(
1643 SkRRect::MakeRectXY(SkRect::MakeXYWH(900, 100, 300, 300), 120, 40));
1644 builder.DrawPaint(clip_fill_paint);
1645 builder.Restore();
1646
1647 builder.Save();
1648 builder.ClipRRect(
1649 SkRRect::MakeRectXY(SkRect::MakeXYWH(100, 900, 300, 300), 40, 120));
1650 builder.DrawPaint(clip_fill_paint);
1651 builder.Restore();
1652
1653 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1654 }
1655
1656 TEST_P(DisplayListTest, DrawVerticesBlendModes) {
1657 std::vector<const char*> blend_mode_names;
1658 std::vector<flutter::DlBlendMode> blend_mode_values;
1659 {
1660 const std::vector<std::tuple<const char*, flutter::DlBlendMode>> blends = {
1661 // Pipeline blends (Porter-Duff alpha compositing)
1662 {"Clear", flutter::DlBlendMode::kClear},
1663 {"Source", flutter::DlBlendMode::kSrc},
1664 {"Destination", flutter::DlBlendMode::kDst},
1665 {"SourceOver", flutter::DlBlendMode::kSrcOver},
1666 {"DestinationOver", flutter::DlBlendMode::kDstOver},
1667 {"SourceIn", flutter::DlBlendMode::kSrcIn},
1668 {"DestinationIn", flutter::DlBlendMode::kDstIn},
1669 {"SourceOut", flutter::DlBlendMode::kSrcOut},
1670 {"DestinationOut", flutter::DlBlendMode::kDstOut},
1671 {"SourceATop", flutter::DlBlendMode::kSrcATop},
1672 {"DestinationATop", flutter::DlBlendMode::kDstATop},
1673 {"Xor", flutter::DlBlendMode::kXor},
1674 {"Plus", flutter::DlBlendMode::kPlus},
1675 {"Modulate", flutter::DlBlendMode::kModulate},
1676 // Advanced blends (color component blends)
1677 {"Screen", flutter::DlBlendMode::kScreen},
1678 {"Overlay", flutter::DlBlendMode::kOverlay},
1679 {"Darken", flutter::DlBlendMode::kDarken},
1680 {"Lighten", flutter::DlBlendMode::kLighten},
1681 {"ColorDodge", flutter::DlBlendMode::kColorDodge},
1682 {"ColorBurn", flutter::DlBlendMode::kColorBurn},
1683 {"HardLight", flutter::DlBlendMode::kHardLight},
1684 {"SoftLight", flutter::DlBlendMode::kSoftLight},
1685 {"Difference", flutter::DlBlendMode::kDifference},
1686 {"Exclusion", flutter::DlBlendMode::kExclusion},
1687 {"Multiply", flutter::DlBlendMode::kMultiply},
1688 {"Hue", flutter::DlBlendMode::kHue},
1689 {"Saturation", flutter::DlBlendMode::kSaturation},
1690 {"Color", flutter::DlBlendMode::kColor},
1691 {"Luminosity", flutter::DlBlendMode::kLuminosity},
1692 };
1693 assert(blends.size() ==
1694 static_cast<size_t>(flutter::DlBlendMode::kLastMode) + 1);
1695 for (const auto& [name, mode] : blends) {
1696 blend_mode_names.push_back(name);
1697 blend_mode_values.push_back(mode);
1698 }
1699 }
1700
1701 auto callback = [&]() {
1702 static int current_blend_index = 3;
1703 static float dst_alpha = 1;
1704 static float src_alpha = 1;
1705 static float color0[4] = {1.0f, 0.0f, 0.0f, 1.0f};
1706 static float color1[4] = {0.0f, 1.0f, 0.0f, 1.0f};
1707 static float color2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
1708 static float src_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
1709
1710 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1711 {
1712 ImGui::ListBox("Blending mode", &current_blend_index,
1713 blend_mode_names.data(), blend_mode_names.size());
1714 ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
1715 ImGui::ColorEdit4("Color A", color0);
1716 ImGui::ColorEdit4("Color B", color1);
1717 ImGui::ColorEdit4("Color C", color2);
1718 ImGui::ColorEdit4("Source Color", src_color);
1719 ImGui::SliderFloat("Destination alpha", &dst_alpha, 0, 1);
1720 }
1721 ImGui::End();
1722
1723 std::vector<SkPoint> positions = {SkPoint::Make(100, 300),
1724 SkPoint::Make(200, 100),
1725 SkPoint::Make(300, 300)};
1726 std::vector<flutter::DlColor> colors = {
1727 toColor(color0).modulateOpacity(dst_alpha),
1728 toColor(color1).modulateOpacity(dst_alpha),
1729 toColor(color2).modulateOpacity(dst_alpha)};
1730
1731 auto vertices = flutter::DlVertices::Make(
1732 flutter::DlVertexMode::kTriangles, 3, positions.data(),
1733 /*texture_coordinates=*/nullptr, colors.data());
1734
1735 flutter::DisplayListBuilder builder;
1736 flutter::DlPaint paint;
1737
1738 paint.setColor(toColor(src_color).modulateOpacity(src_alpha));
1739 builder.DrawVertices(vertices, blend_mode_values[current_blend_index],
1740 paint);
1741 return builder.Build();
1742 };
1743
1744 ASSERT_TRUE(OpenPlaygroundHere(callback));
1745 }
1746
1747 template <typename Contents>
1748 static std::optional<Rect> GetCoverageOfFirstEntity(const Picture& picture) {
1749 std::optional<Rect> coverage;
1750 picture.pass->IterateAllEntities([&coverage](Entity& entity) {
1751 if (std::static_pointer_cast<Contents>(entity.GetContents())) {
1752 auto contents = std::static_pointer_cast<Contents>(entity.GetContents());
1753 Entity entity;
1754 coverage = contents->GetCoverage(entity);
1755 return false;
1756 }
1757 return true;
1758 });
1759 return coverage;
1760 }
1761
1762 TEST(DisplayListTest, RRectBoundsComputation) {
1763 SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeLTRB(0, 0, 100, 100), 4, 4);
1764 SkPath path = SkPath().addRRect(rrect);
1765
1766 flutter::DlPaint paint;
1767 flutter::DisplayListBuilder builder;
1768
1769 builder.DrawPath(path, paint);
1770 auto display_list = builder.Build();
1771
1772 DlDispatcher dispatcher;
1773 display_list->Dispatch(dispatcher);
1774 auto picture = dispatcher.EndRecordingAsPicture();
1775
1776 std::optional<Rect> coverage =
1777 GetCoverageOfFirstEntity<SolidColorContents>(picture);
1778
1779 // Validate that the RRect coverage is _exactly_ the same as the input rect.
1780 ASSERT_TRUE(coverage.has_value());
1781 ASSERT_EQ(coverage.value_or(Rect::MakeMaximum()),
1782 Rect::MakeLTRB(0, 0, 100, 100));
1783 }
1784
1785 TEST(DisplayListTest, CircleBoundsComputation) {
1786 SkPath path = SkPath().addCircle(0, 0, 5);
1787
1788 flutter::DlPaint paint;
1789 flutter::DisplayListBuilder builder;
1790
1791 builder.DrawPath(path, paint);
1792 auto display_list = builder.Build();
1793
1794 DlDispatcher dispatcher;
1795 display_list->Dispatch(dispatcher);
1796 auto picture = dispatcher.EndRecordingAsPicture();
1797
1798 std::optional<Rect> coverage =
1799 GetCoverageOfFirstEntity<SolidColorContents>(picture);
1800
1801 ASSERT_TRUE(coverage.has_value());
1802 ASSERT_EQ(coverage.value_or(Rect::MakeMaximum()),
1803 Rect::MakeLTRB(-5, -5, 5, 5));
1804 }
1805
1806 #ifdef IMPELLER_ENABLE_3D
1807 TEST_P(DisplayListTest, SceneColorSource) {
1808 // Load up the scene.
1809 auto mapping =
1810 flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb.ipscene");
1811 ASSERT_NE(mapping, nullptr);
1812
1813 std::shared_ptr<scene::Node> gltf_scene =
1814 impeller::scene::Node::MakeFromFlatbuffer(
1815 *mapping, *GetContext()->GetResourceAllocator());
1816 ASSERT_NE(gltf_scene, nullptr);
1817
1818 flutter::DisplayListBuilder builder;
1819
1820 auto color_source = std::make_shared<flutter::DlSceneColorSource>(
1821 gltf_scene,
1822 Matrix::MakePerspective(Degrees(45), GetWindowSize(), 0.1, 1000) *
1823 Matrix::MakeLookAt({3, 2, -5}, {0, 0, 0}, {0, 1, 0}));
1824
1825 flutter::DlPaint paint = flutter::DlPaint().setColorSource(color_source);
1826
1827 builder.DrawPaint(paint);
1828
1829 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1830 }
1831 #endif
1832
1833 } // namespace testing
1834 } // namespace impeller
impeller::Picture::pass
std::unique_ptr< EntityPass > pass
Definition: picture.h:21
impeller::DlDispatcher::save
void save() override
Definition: dl_dispatcher.cc:621
point.h
impeller::DlDispatcher::scale
void scale(SkScalar sx, SkScalar sy) override
Definition: dl_dispatcher.cc:645
impeller::TPoint::y
Type y
Definition: point.h:26
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::DlDispatcher
Definition: dl_dispatcher.h:14
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:264
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:49
solid_color_contents.h
impeller::Color
Definition: color.h:124
impeller::DlDispatcher::EndRecordingAsPicture
Picture EndRecordingAsPicture()
Definition: dl_dispatcher.cc:1118
dl_dispatcher.h
impeller::testing::DisplayListTest
DlPlayground DisplayListTest
Definition: dl_unittests.cc:46
impeller::DlDispatcher::transformFullPerspective
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) override
Definition: dl_dispatcher.cc:677
impeller::kPi
constexpr float kPi
Definition: constants.h:26
IMPELLER_PLAYGROUND_POINT
#define IMPELLER_PLAYGROUND_POINT(default_position, radius, color)
Definition: widgets.h:15
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::DlDispatcher::restore
void restore() override
Definition: dl_dispatcher.cc:635
impeller::DlImageImpeller::Make
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Definition: dl_image_impeller.cc:12
impeller::DlDispatcher::drawDisplayList
void drawDisplayList(const sk_sp< flutter::DisplayList > display_list, SkScalar opacity) override
Definition: dl_dispatcher.cc:983
impeller::Matrix::MakeLookAt
static constexpr Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:494
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:468
dl_playground.h
impeller::testing::INSTANTIATE_PLAYGROUND_SUITE
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
impeller::kColor
@ kColor
Definition: geometry.h:36
impeller::Entity
Definition: entity.h:21
impeller::testing::toColor
flutter::DlColor toColor(const float *components)
Definition: dl_unittests.cc:41
impeller::Picture
Definition: picture.h:20
impeller::Point
TPoint< Scalar > Point
Definition: point.h:308
node.h
impeller::Color::ToIColor
static constexpr uint32_t ToIColor(Color color)
Convert this color to a 32-bit representation.
Definition: color.h:161
impeller::Matrix::GetScale
constexpr Vector3 GetScale() const
Definition: matrix.h:303
widgets.h
impeller::testing::TEST
TEST(CanvasRecorder, Save)
Definition: canvas_recorder_unittests.cc:61
impeller::Entity::GetContents
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:85
impeller::Color::White
static constexpr Color White()
Definition: color.h:256
clip_contents.h
impeller::DlDispatcher::drawShadow
void drawShadow(const SkPath &path, const flutter::DlColor color, const SkScalar elevation, bool transparent_occluder, SkScalar dpr) override
Definition: dl_dispatcher.cc:1060
impeller::TPoint::x
Type x
Definition: point.h:25
scalar.h
constants.h
impeller::TPoint< Scalar >
impeller::TRect< Scalar >::MakeMaximum
constexpr static TRect MakeMaximum()
Definition: rect.h:72
impeller::ScalarNearlyEqual
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:30
impeller::Degrees
Definition: scalar.h:46
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:27
impeller::testing::TEST_P
TEST_P(AiksTest, CanRenderLinearGradientClamp)
Definition: aiks_gradient_unittests.cc:48
impeller::scene::Node::MakeFromFlatbuffer
static std::shared_ptr< Node > MakeFromFlatbuffer(const fml::Mapping &ipscene_mapping, Allocator &allocator)
Definition: node.cc:48
IMPELLER_PLAYGROUND_LINE
#define IMPELLER_PLAYGROUND_LINE(default_position_a, default_position_b, radius, color_a, color_b)
Definition: widgets.h:56
impeller
Definition: aiks_context.cc:10
impeller::testing::GetCoverageOfFirstEntity
static std::optional< Rect > GetCoverageOfFirstEntity(const Picture &picture)
Definition: dl_unittests.cc:1748
solid_rrect_blur_contents.h
impeller::DlDispatcher::translate
void translate(SkScalar tx, SkScalar ty) override
Definition: dl_dispatcher.cc:640
dl_image_impeller.h
impeller::DlPlayground
Definition: dl_playground.h:15