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