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"
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 
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 
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 
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 =
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