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