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