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, CanDrawNinePatchImage) {
725  // Image is drawn with corners to scale and center pieces stretched to fit.
726  auto texture = CreateTextureForFixture("embarcadero.jpg");
727  flutter::DisplayListBuilder builder;
728  auto size = texture->GetSize();
729  builder.DrawImageNine(
730  DlImageImpeller::Make(texture),
731  DlIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
732  size.height * 3 / 4),
733  DlRect::MakeLTRB(0, 0, size.width * 2, size.height * 2),
734  flutter::DlFilterMode::kNearest, nullptr);
735  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
736 }
737 
738 TEST_P(DisplayListTest, CanDrawNinePatchImageCenterWidthBiggerThanDest) {
739  // Edge case, the width of the corners does not leave any room for the
740  // center slice. The center (across the vertical axis) is folded out of the
741  // resulting image.
742  auto texture = CreateTextureForFixture("embarcadero.jpg");
743  flutter::DisplayListBuilder builder;
744  auto size = texture->GetSize();
745  builder.DrawImageNine(
746  DlImageImpeller::Make(texture),
747  DlIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
748  size.height * 3 / 4),
749  DlRect::MakeLTRB(0, 0, size.width / 2, size.height),
750  flutter::DlFilterMode::kNearest, nullptr);
751  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
752 }
753 
754 TEST_P(DisplayListTest, CanDrawNinePatchImageCenterHeightBiggerThanDest) {
755  // Edge case, the height of the corners does not leave any room for the
756  // center slice. The center (across the horizontal axis) is folded out of the
757  // resulting image.
758  auto texture = CreateTextureForFixture("embarcadero.jpg");
759  flutter::DisplayListBuilder builder;
760  auto size = texture->GetSize();
761  builder.DrawImageNine(
762  DlImageImpeller::Make(texture),
763  DlIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
764  size.height * 3 / 4),
765  DlRect::MakeLTRB(0, 0, size.width, size.height / 2),
766  flutter::DlFilterMode::kNearest, nullptr);
767  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
768 }
769 
770 TEST_P(DisplayListTest, CanDrawNinePatchImageCenterBiggerThanDest) {
771  // Edge case, the width and height of the corners does not leave any
772  // room for the center slices. Only the corners are displayed.
773  auto texture = CreateTextureForFixture("embarcadero.jpg");
774  flutter::DisplayListBuilder builder;
775  auto size = texture->GetSize();
776  builder.DrawImageNine(
777  DlImageImpeller::Make(texture),
778  DlIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
779  size.height * 3 / 4),
780  DlRect::MakeLTRB(0, 0, size.width / 2, size.height / 2),
781  flutter::DlFilterMode::kNearest, nullptr);
782  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
783 }
784 
785 TEST_P(DisplayListTest, CanDrawNinePatchImageCornersScaledDown) {
786  // Edge case, there is not enough room for the corners to be drawn
787  // without scaling them down.
788  auto texture = CreateTextureForFixture("embarcadero.jpg");
789  flutter::DisplayListBuilder builder;
790  auto size = texture->GetSize();
791  builder.DrawImageNine(
792  DlImageImpeller::Make(texture),
793  DlIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
794  size.height * 3 / 4),
795  DlRect::MakeLTRB(0, 0, size.width / 4, size.height / 4),
796  flutter::DlFilterMode::kNearest, nullptr);
797  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
798 }
799 
800 TEST_P(DisplayListTest, NinePatchImagePrecision) {
801  // Draw a nine patch image with colored corners and verify that the corner
802  // color does not leak outside the intended region.
803  auto texture = CreateTextureForFixture("nine_patch_corners.png");
804  flutter::DisplayListBuilder builder;
805  builder.DrawImageNine(DlImageImpeller::Make(texture),
806  DlIRect::MakeXYWH(10, 10, 1, 1),
807  DlRect::MakeXYWH(0, 0, 200, 100),
808  flutter::DlFilterMode::kNearest, nullptr);
809  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
810 }
811 
812 TEST_P(DisplayListTest, NinePatchImageColorFilter) {
813  auto texture = CreateTextureForFixture("nine_patch2.png");
814 
815  auto filter = flutter::DlColorFilter::MakeBlend(flutter::DlColor::kGreen(),
816  flutter::DlBlendMode::kSrcIn);
817  flutter::DlPaint paint;
818  paint.setColorFilter(filter);
819 
820  flutter::DisplayListBuilder builder;
821  builder.DrawImageNine(DlImageImpeller::Make(texture),
822  DlIRect::MakeXYWH(10, 10, 1, 1),
823  DlRect::MakeXYWH(0, 0, 200, 100),
824  flutter::DlFilterMode::kNearest, &paint);
825  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
826 }
827 
828 TEST_P(DisplayListTest, CanDrawPoints) {
829  flutter::DisplayListBuilder builder;
830  DlPoint points[7] = {
831  {0, 0}, //
832  {100, 100}, //
833  {100, 0}, //
834  {0, 100}, //
835  {0, 0}, //
836  {48, 48}, //
837  {52, 52}, //
838  };
839  std::vector<flutter::DlStrokeCap> caps = {
840  flutter::DlStrokeCap::kButt,
841  flutter::DlStrokeCap::kRound,
842  flutter::DlStrokeCap::kSquare,
843  };
844  flutter::DlPaint paint =
845  flutter::DlPaint() //
846  .setColor(flutter::DlColor::kYellow().withAlpha(127)) //
847  .setStrokeWidth(20);
848  builder.Translate(50, 50);
849  for (auto cap : caps) {
850  paint.setStrokeCap(cap);
851  builder.Save();
852  builder.DrawPoints(flutter::DlPointMode::kPoints, 7, points, paint);
853  builder.Translate(150, 0);
854  builder.DrawPoints(flutter::DlPointMode::kLines, 5, points, paint);
855  builder.Translate(150, 0);
856  builder.DrawPoints(flutter::DlPointMode::kPolygon, 5, points, paint);
857  builder.Restore();
858  builder.Translate(0, 150);
859  }
860  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
861 }
862 
863 TEST_P(DisplayListTest, CanDrawZeroLengthLine) {
864  flutter::DisplayListBuilder builder;
865  std::vector<flutter::DlStrokeCap> caps = {
866  flutter::DlStrokeCap::kButt,
867  flutter::DlStrokeCap::kRound,
868  flutter::DlStrokeCap::kSquare,
869  };
870  flutter::DlPaint paint =
871  flutter::DlPaint() //
872  .setColor(flutter::DlColor::kYellow().withAlpha(127)) //
873  .setDrawStyle(flutter::DlDrawStyle::kStroke) //
874  .setStrokeCap(flutter::DlStrokeCap::kButt) //
875  .setStrokeWidth(20);
876  DlPath path = DlPath::MakeLine({150, 50}, {150, 50});
877  for (auto cap : caps) {
878  paint.setStrokeCap(cap);
879  builder.DrawLine(DlPoint(50, 50), DlPoint(50, 50), paint);
880  builder.DrawPath(path, paint);
881  builder.Translate(0, 150);
882  }
883  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
884 }
885 
886 TEST_P(DisplayListTest, CanDrawShadow) {
887  flutter::DisplayListBuilder builder;
888  flutter::DlPaint paint;
889 
890  auto content_scale = GetContentScale() * 0.8;
891  builder.Scale(content_scale.x, content_scale.y);
892 
893  constexpr size_t star_spikes = 5;
894  constexpr DlScalar half_spike_rotation = kPi / star_spikes;
895  constexpr DlScalar radius = 40;
896  constexpr DlScalar spike_size = 10;
897  constexpr DlScalar outer_radius = radius + spike_size;
898  constexpr DlScalar inner_radius = radius - spike_size;
899  std::array<DlPoint, star_spikes * 2> star;
900  for (size_t i = 0; i < star_spikes; i++) {
901  const DlScalar rotation = half_spike_rotation * i * 2;
902  star[i * 2] = DlPoint(50 + std::sin(rotation) * outer_radius,
903  50 - std::cos(rotation) * outer_radius);
904  star[i * 2 + 1] =
905  DlPoint(50 + std::sin(rotation + half_spike_rotation) * inner_radius,
906  50 - std::cos(rotation + half_spike_rotation) * inner_radius);
907  }
908 
909  std::array<DlPath, 4> paths = {
910  DlPath::MakeRect(DlRect::MakeXYWH(0, 0, 200, 100)),
911  DlPath::MakeRoundRectXY(DlRect::MakeXYWH(20, 0, 200, 100), 30, 30),
912  DlPath::MakeCircle(DlPoint(100, 50), 50),
913  DlPath::MakePoly(star.data(), star.size(), true),
914  };
915  paint.setColor(flutter::DlColor::kWhite());
916  builder.DrawPaint(paint);
917  paint.setColor(flutter::DlColor::kCyan());
918  builder.Translate(100, 50);
919  for (size_t x = 0; x < paths.size(); x++) {
920  builder.Save();
921  for (size_t y = 0; y < 6; y++) {
922  builder.DrawShadow(paths[x], flutter::DlColor::kBlack(), 3 + y * 8, false,
923  1);
924  builder.DrawPath(paths[x], paint);
925  builder.Translate(0, 150);
926  }
927  builder.Restore();
928  builder.Translate(250, 0);
929  }
930 
931  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
932 }
933 
934 TEST_P(DisplayListTest, CanDrawZeroWidthLine) {
935  flutter::DisplayListBuilder builder;
936  std::vector<flutter::DlStrokeCap> caps = {
937  flutter::DlStrokeCap::kButt,
938  flutter::DlStrokeCap::kRound,
939  flutter::DlStrokeCap::kSquare,
940  };
941  flutter::DlPaint paint = //
942  flutter::DlPaint() //
943  .setColor(flutter::DlColor::kWhite()) //
944  .setDrawStyle(flutter::DlDrawStyle::kStroke) //
945  .setStrokeWidth(0);
946  flutter::DlPaint outline_paint = //
947  flutter::DlPaint() //
948  .setColor(flutter::DlColor::kYellow()) //
949  .setDrawStyle(flutter::DlDrawStyle::kStroke) //
950  .setStrokeCap(flutter::DlStrokeCap::kSquare) //
951  .setStrokeWidth(1);
952  DlPath path = DlPath::MakeLine({150, 50}, {160, 50});
953  for (auto cap : caps) {
954  paint.setStrokeCap(cap);
955  builder.DrawLine(DlPoint(50, 50), DlPoint(60, 50), paint);
956  builder.DrawRect(DlRect::MakeLTRB(45, 45, 65, 55), outline_paint);
957  builder.DrawLine(DlPoint{100, 50}, DlPoint{100, 50}, paint);
958  if (cap != flutter::DlStrokeCap::kButt) {
959  builder.DrawRect(DlRect::MakeLTRB(95, 45, 105, 55), outline_paint);
960  }
961  builder.DrawPath(path, paint);
962  builder.DrawRect(path.GetBounds().Expand(5, 5), outline_paint);
963  builder.Translate(0, 150);
964  }
965  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
966 }
967 
968 TEST_P(DisplayListTest, CanDrawWithMatrixFilter) {
969  auto boston = CreateTextureForFixture("boston.jpg");
970 
971  auto callback = [&]() {
972  static int selected_matrix_type = 0;
973  const char* matrix_type_names[] = {"Matrix", "Local Matrix"};
974 
975  static float ctm_translation[2] = {200, 200};
976  static float ctm_scale[2] = {0.65, 0.65};
977  static float ctm_skew[2] = {0, 0};
978 
979  static bool enable = true;
980  static float translation[2] = {100, 100};
981  static float scale[2] = {0.8, 0.8};
982  static float skew[2] = {0.2, 0.2};
983 
984  static bool enable_savelayer = true;
985 
986  ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
987  {
988  ImGui::Combo("Filter type", &selected_matrix_type, matrix_type_names,
989  sizeof(matrix_type_names) / sizeof(char*));
990 
991  ImGui::TextWrapped("Current Transform");
992  ImGui::SliderFloat2("CTM Translation", ctm_translation, 0, 1000);
993  ImGui::SliderFloat2("CTM Scale", ctm_scale, 0, 3);
994  ImGui::SliderFloat2("CTM Skew", ctm_skew, -3, 3);
995 
996  ImGui::TextWrapped(
997  "MatrixFilter and LocalMatrixFilter modify the CTM in the same way. "
998  "The only difference is that MatrixFilter doesn't affect the effect "
999  "transform, whereas LocalMatrixFilter does.");
1000  // Note: See this behavior in:
1001  // https://fiddle.skia.org/c/6cbb551ab36d06f163db8693972be954
1002  ImGui::Checkbox("Enable", &enable);
1003  ImGui::SliderFloat2("Filter Translation", translation, 0, 1000);
1004  ImGui::SliderFloat2("Filter Scale", scale, 0, 3);
1005  ImGui::SliderFloat2("Filter Skew", skew, -3, 3);
1006 
1007  ImGui::TextWrapped(
1008  "Rendering the filtered image within a layer can expose bounds "
1009  "issues. If the rendered image gets cut off when this setting is "
1010  "enabled, there's a coverage bug in the filter.");
1011  ImGui::Checkbox("Render in layer", &enable_savelayer);
1012  }
1013  ImGui::End();
1014 
1015  flutter::DisplayListBuilder builder;
1016  flutter::DlPaint paint;
1017 
1018  if (enable_savelayer) {
1019  builder.SaveLayer(std::nullopt, nullptr);
1020  }
1021  {
1022  auto content_scale = GetContentScale();
1023  builder.Scale(content_scale.x, content_scale.y);
1024 
1025  // Set the current transform
1026  auto ctm_matrix = Matrix::MakeRow(
1027  ctm_scale[0], ctm_skew[0], 0.0f, ctm_translation[0], //
1028  ctm_skew[1], ctm_scale[1], 0.0f, ctm_translation[1], //
1029  0, 0, 1, 0, //
1030  0, 0, 0, 1);
1031  builder.Transform(ctm_matrix);
1032 
1033  // Set the matrix filter
1034  auto filter_matrix =
1035  Matrix::MakeRow(scale[0], skew[0], 0.0f, translation[0], //
1036  skew[1], scale[1], 0.0f, translation[1], //
1037  0.0f, 0.0f, 1.0f, 0.0f, //
1038  0.0f, 0.0f, 0.0f, 1.0f);
1039 
1040  if (enable) {
1041  switch (selected_matrix_type) {
1042  case 0: {
1043  auto filter = flutter::DlMatrixImageFilter(
1044  filter_matrix, flutter::DlImageSampling::kLinear);
1045  paint.setImageFilter(&filter);
1046  break;
1047  }
1048  case 1: {
1049  auto internal_filter =
1050  flutter::DlBlurImageFilter(10, 10, flutter::DlTileMode::kDecal)
1051  .shared();
1052  auto filter = flutter::DlLocalMatrixImageFilter(filter_matrix,
1053  internal_filter);
1054  paint.setImageFilter(&filter);
1055  break;
1056  }
1057  }
1058  }
1059 
1060  builder.DrawImage(DlImageImpeller::Make(boston), DlPoint(),
1061  flutter::DlImageSampling::kLinear, &paint);
1062  }
1063  if (enable_savelayer) {
1064  builder.Restore();
1065  }
1066 
1067  return builder.Build();
1068  };
1069 
1070  ASSERT_TRUE(OpenPlaygroundHere(callback));
1071 }
1072 
1073 TEST_P(DisplayListTest, CanDrawWithMatrixFilterWhenSavingLayer) {
1074  auto callback = [&]() {
1075  static float translation[2] = {0, 0};
1076  static bool enable_save_layer = true;
1077 
1078  ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1079  ImGui::SliderFloat2("Translation", translation, -130, 130);
1080  ImGui::Checkbox("Enable save layer", &enable_save_layer);
1081  ImGui::End();
1082 
1083  flutter::DisplayListBuilder builder;
1084  builder.Save();
1085  builder.Scale(2.0, 2.0);
1086  flutter::DlPaint paint;
1087  paint.setColor(flutter::DlColor::kYellow());
1088  builder.DrawRect(DlRect::MakeWH(300, 300), paint);
1089  paint.setStrokeWidth(1.0);
1090  paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
1091  paint.setColor(flutter::DlColor::kBlack().withAlpha(0x80));
1092  builder.DrawLine(DlPoint(150, 0), DlPoint(150, 300), paint);
1093  builder.DrawLine(DlPoint(0, 150), DlPoint(300, 150), paint);
1094 
1095  flutter::DlPaint save_paint;
1096  DlRect bounds = DlRect::MakeXYWH(100, 100, 100, 100);
1097  Matrix translate_matrix =
1098  Matrix::MakeTranslation({translation[0], translation[1]});
1099  if (enable_save_layer) {
1100  auto filter = flutter::DlMatrixImageFilter(
1101  translate_matrix, flutter::DlImageSampling::kNearestNeighbor);
1102  save_paint.setImageFilter(filter.shared());
1103  builder.SaveLayer(bounds, &save_paint);
1104  } else {
1105  builder.Save();
1106  builder.Transform(translate_matrix);
1107  }
1108 
1109  Matrix filter_matrix;
1110  filter_matrix.Translate({150, 150});
1111  filter_matrix.Scale({0.2f, 0.2f});
1112  filter_matrix.Translate({-150, -150});
1113  auto filter = flutter::DlMatrixImageFilter(
1114  filter_matrix, flutter::DlImageSampling::kNearestNeighbor);
1115 
1116  save_paint.setImageFilter(filter.shared());
1117 
1118  builder.SaveLayer(bounds, &save_paint);
1119  flutter::DlPaint paint2;
1120  paint2.setColor(flutter::DlColor::kBlue());
1121  builder.DrawRect(bounds, paint2);
1122  builder.Restore();
1123  builder.Restore();
1124  return builder.Build();
1125  };
1126 
1127  ASSERT_TRUE(OpenPlaygroundHere(callback));
1128 }
1129 
1130 TEST_P(DisplayListTest, CanDrawRectWithLinearToSrgbColorFilter) {
1131  flutter::DlPaint paint;
1132  paint.setColor(flutter::DlColor(0xFF2196F3).withAlpha(128));
1133  flutter::DisplayListBuilder builder;
1134  paint.setColorFilter(flutter::DlColorFilter::MakeLinearToSrgbGamma());
1135  builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), paint);
1136  builder.Translate(0, 200);
1137 
1138  paint.setColorFilter(flutter::DlColorFilter::MakeSrgbToLinearGamma());
1139  builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), paint);
1140 
1141  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1142 }
1143 
1144 TEST_P(DisplayListTest, CanDrawPaintWithColorSource) {
1145  const flutter::DlColor colors[2] = {
1146  flutter::DlColor(0xFFF44336),
1147  flutter::DlColor(0xFF2196F3),
1148  };
1149  const float stops[2] = {0.0, 1.0};
1150  flutter::DlPaint paint;
1151  flutter::DisplayListBuilder builder;
1152  auto clip_bounds = DlRect::MakeWH(300.0, 300.0);
1153  builder.Save();
1154  builder.Translate(100, 100);
1155  builder.ClipRect(clip_bounds, flutter::DlClipOp::kIntersect, false);
1156  auto linear =
1157  flutter::DlColorSource::MakeLinear({0.0, 0.0}, {100.0, 100.0}, 2, colors,
1158  stops, flutter::DlTileMode::kRepeat);
1159  paint.setColorSource(linear);
1160  builder.DrawPaint(paint);
1161  builder.Restore();
1162 
1163  builder.Save();
1164  builder.Translate(500, 100);
1165  builder.ClipRect(clip_bounds, flutter::DlClipOp::kIntersect, false);
1166  auto radial = flutter::DlColorSource::MakeRadial(
1167  {100.0, 100.0}, 100.0, 2, colors, stops, flutter::DlTileMode::kRepeat);
1168  paint.setColorSource(radial);
1169  builder.DrawPaint(paint);
1170  builder.Restore();
1171 
1172  builder.Save();
1173  builder.Translate(100, 500);
1174  builder.ClipRect(clip_bounds, flutter::DlClipOp::kIntersect, false);
1175  auto sweep =
1176  flutter::DlColorSource::MakeSweep({100.0, 100.0}, 180.0, 270.0, 2, colors,
1177  stops, flutter::DlTileMode::kRepeat);
1178  paint.setColorSource(sweep);
1179  builder.DrawPaint(paint);
1180  builder.Restore();
1181 
1182  builder.Save();
1183  builder.Translate(500, 500);
1184  builder.ClipRect(clip_bounds, flutter::DlClipOp::kIntersect, false);
1185  auto texture = CreateTextureForFixture("table_mountain_nx.png");
1186  auto image = flutter::DlColorSource::MakeImage(DlImageImpeller::Make(texture),
1187  flutter::DlTileMode::kRepeat,
1188  flutter::DlTileMode::kRepeat);
1189  paint.setColorSource(image);
1190  builder.DrawPaint(paint);
1191  builder.Restore();
1192 
1193  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1194 }
1195 
1196 TEST_P(DisplayListTest, CanBlendDstOverAndDstCorrectly) {
1197  flutter::DisplayListBuilder builder;
1198 
1199  {
1200  builder.SaveLayer(std::nullopt, nullptr);
1201  builder.Translate(100, 100);
1202  flutter::DlPaint paint;
1203  paint.setColor(flutter::DlColor::kRed());
1204  builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1205  paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1206  paint.setBlendMode(flutter::DlBlendMode::kSrcOver);
1207  builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1208  builder.Restore();
1209  }
1210  {
1211  builder.SaveLayer(std::nullopt, nullptr);
1212  builder.Translate(300, 100);
1213  flutter::DlPaint paint;
1214  paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1215  builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1216  paint.setColor(flutter::DlColor::kRed());
1217  paint.setBlendMode(flutter::DlBlendMode::kDstOver);
1218  builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1219  builder.Restore();
1220  }
1221  {
1222  builder.SaveLayer(std::nullopt, nullptr);
1223  builder.Translate(100, 300);
1224  flutter::DlPaint paint;
1225  paint.setColor(flutter::DlColor::kRed());
1226  builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1227  paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1228  paint.setBlendMode(flutter::DlBlendMode::kSrc);
1229  builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1230  builder.Restore();
1231  }
1232  {
1233  builder.SaveLayer(std::nullopt, nullptr);
1234  builder.Translate(300, 300);
1235  flutter::DlPaint paint;
1236  paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1237  builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1238  paint.setColor(flutter::DlColor::kRed());
1239  paint.setBlendMode(flutter::DlBlendMode::kDst);
1240  builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1241  builder.Restore();
1242  }
1243 
1244  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1245 }
1246 
1247 TEST_P(DisplayListTest, CanDrawCorrectlyWithColorFilterAndImageFilter) {
1248  flutter::DisplayListBuilder builder;
1249  const float green_color_matrix[20] = {
1250  0, 0, 0, 0, 0, //
1251  0, 0, 0, 0, 1, //
1252  0, 0, 0, 0, 0, //
1253  0, 0, 0, 1, 0, //
1254  };
1255  const float blue_color_matrix[20] = {
1256  0, 0, 0, 0, 0, //
1257  0, 0, 0, 0, 0, //
1258  0, 0, 0, 0, 1, //
1259  0, 0, 0, 1, 0, //
1260  };
1261  auto green_color_filter =
1262  flutter::DlColorFilter::MakeMatrix(green_color_matrix);
1263  auto blue_color_filter =
1264  flutter::DlColorFilter::MakeMatrix(blue_color_matrix);
1265  auto blue_image_filter =
1266  flutter::DlImageFilter::MakeColorFilter(blue_color_filter);
1267 
1268  flutter::DlPaint paint;
1269  paint.setColor(flutter::DlColor::kRed());
1270  paint.setColorFilter(green_color_filter);
1271  paint.setImageFilter(blue_image_filter);
1272  builder.DrawRect(DlRect::MakeLTRB(100, 100, 500, 500), paint);
1273  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1274 }
1275 
1276 TEST_P(DisplayListTest, MaskBlursApplyCorrectlyToColorSources) {
1277  auto blur_filter = std::make_shared<flutter::DlBlurMaskFilter>(
1278  flutter::DlBlurStyle::kNormal, 10);
1279 
1280  flutter::DisplayListBuilder builder;
1281 
1282  std::array<flutter::DlColor, 2> colors = {flutter::DlColor::kBlue(),
1283  flutter::DlColor::kGreen()};
1284  std::array<float, 2> stops = {0, 1};
1285  auto texture = CreateTextureForFixture("airplane.jpg");
1286  auto matrix = flutter::DlMatrix::MakeTranslation({-300, -110});
1287  std::array<std::shared_ptr<flutter::DlColorSource>, 2> color_sources = {
1288  flutter::DlColorSource::MakeImage(
1289  DlImageImpeller::Make(texture), flutter::DlTileMode::kRepeat,
1290  flutter::DlTileMode::kRepeat, flutter::DlImageSampling::kLinear,
1291  &matrix),
1292  flutter::DlColorSource::MakeLinear(
1293  flutter::DlPoint(0, 0), flutter::DlPoint(100, 50), 2, colors.data(),
1294  stops.data(), flutter::DlTileMode::kClamp),
1295  };
1296 
1297  builder.Save();
1298  builder.Translate(0, 100);
1299  for (const auto& color_source : color_sources) {
1300  flutter::DlPaint paint;
1301  paint.setColorSource(color_source);
1302  paint.setMaskFilter(blur_filter);
1303 
1304  builder.Save();
1305  builder.Translate(100, 0);
1306  paint.setDrawStyle(flutter::DlDrawStyle::kFill);
1307  builder.DrawRoundRect(
1308  DlRoundRect::MakeRectXY(DlRect::MakeWH(100, 50), 30, 30), paint);
1309 
1310  paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
1311  paint.setStrokeWidth(10);
1312  builder.Translate(200, 0);
1313  builder.DrawRoundRect(
1314  DlRoundRect::MakeRectXY(DlRect::MakeWH(100, 50), 30, 30), paint);
1315 
1316  builder.Restore();
1317  builder.Translate(0, 100);
1318  }
1319  builder.Restore();
1320 
1321  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1322 }
1323 
1324 TEST_P(DisplayListTest, DrawShapes) {
1325  flutter::DisplayListBuilder builder;
1326  std::vector<flutter::DlStrokeJoin> joins = {
1327  flutter::DlStrokeJoin::kBevel,
1328  flutter::DlStrokeJoin::kRound,
1329  flutter::DlStrokeJoin::kMiter,
1330  };
1331  flutter::DlPaint paint = //
1332  flutter::DlPaint() //
1333  .setColor(flutter::DlColor::kWhite()) //
1334  .setDrawStyle(flutter::DlDrawStyle::kFill) //
1335  .setStrokeWidth(10);
1336  flutter::DlPaint stroke_paint = //
1337  flutter::DlPaint() //
1338  .setColor(flutter::DlColor::kWhite()) //
1339  .setDrawStyle(flutter::DlDrawStyle::kStroke) //
1340  .setStrokeWidth(10);
1341  DlPath path = DlPath::MakeLine({150, 50}, {160, 50});
1342 
1343  builder.Translate(300, 50);
1344  builder.Scale(0.8, 0.8);
1345  for (auto join : joins) {
1346  paint.setStrokeJoin(join);
1347  stroke_paint.setStrokeJoin(join);
1348  builder.DrawRect(DlRect::MakeXYWH(0, 0, 100, 100), paint);
1349  builder.DrawRect(DlRect::MakeXYWH(0, 150, 100, 100), stroke_paint);
1350  builder.DrawRoundRect(
1351  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(150, 0, 100, 100), 30, 30),
1352  paint);
1353  builder.DrawRoundRect(
1354  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(150, 150, 100, 100), 30, 30),
1355  stroke_paint);
1356  builder.DrawCircle(DlPoint(350, 50), 50, paint);
1357  builder.DrawCircle(DlPoint(350, 200), 50, stroke_paint);
1358  builder.Translate(0, 300);
1359  }
1360  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1361 }
1362 
1363 TEST_P(DisplayListTest, DrawCirclesWithTransformations) {
1364  auto callback = [&]() {
1365  static float filled_radius = 100.0;
1366  static float filled_alpha = 255.0;
1367  static float filled_scale[2] = {1.0, 1.0};
1368  static float stroked_radius = 20.0;
1369  static float stroke_width = 10.0;
1370  static float stroked_alpha = 255.0;
1371  static float stroked_scale[2] = {1.0, 1.0};
1372 
1373  ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1374  {
1375  ImGui::SliderFloat("Filled Radius", &filled_radius, 0, 500);
1376  ImGui::SliderFloat("Filled Alpha", &filled_alpha, 0, 255);
1377  ImGui::SliderFloat2("Filled Scale", filled_scale, 0, 10.0);
1378  ImGui::SliderFloat("Stroked Radius", &stroked_radius, 0, 10.0);
1379  ImGui::SliderFloat("Stroked Width", &stroke_width, 0, 500);
1380  ImGui::SliderFloat("Stroked Alpha", &stroked_alpha, 0, 10.0);
1381  ImGui::SliderFloat2("Stroked Scale", stroked_scale, 0, 10.0);
1382  }
1383  ImGui::End();
1384 
1385  flutter::DisplayListBuilder builder;
1386  flutter::DlPaint paint;
1387 
1388  paint.setColor(flutter::DlColor::kBlue().withAlpha(filled_alpha));
1389  paint.setDrawStyle(flutter::DlDrawStyle::kFill);
1390  builder.Save();
1391  builder.Scale(filled_scale[0], filled_scale[1]);
1392  builder.DrawCircle(DlPoint(500, 750), filled_radius, paint);
1393  builder.Restore();
1394 
1395  paint.setColor(flutter::DlColor::kRed().withAlpha(stroked_alpha));
1396  paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
1397  paint.setStrokeWidth(stroke_width);
1398  builder.Save();
1399  builder.Scale(stroked_scale[0], stroked_scale[1]);
1400  builder.DrawCircle(DlPoint(1250, 750), stroked_radius, paint);
1401  builder.Restore();
1402  return builder.Build();
1403  };
1404 
1405  ASSERT_TRUE(OpenPlaygroundHere(callback));
1406 }
1407 
1408 TEST_P(DisplayListTest, ClipDrawRRectWithNonCircularRadii) {
1409  flutter::DisplayListBuilder builder;
1410 
1411  flutter::DlPaint fill_paint = //
1412  flutter::DlPaint() //
1413  .setColor(flutter::DlColor::kBlue()) //
1414  .setDrawStyle(flutter::DlDrawStyle::kFill) //
1415  .setStrokeWidth(10);
1416  flutter::DlPaint stroke_paint = //
1417  flutter::DlPaint() //
1418  .setColor(flutter::DlColor::kGreen()) //
1419  .setDrawStyle(flutter::DlDrawStyle::kStroke) //
1420  .setStrokeWidth(10);
1421 
1422  builder.DrawRoundRect(
1423  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(500, 100, 300, 300), 120, 40),
1424  fill_paint);
1425  builder.DrawRoundRect(
1426  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(500, 100, 300, 300), 120, 40),
1427  stroke_paint);
1428 
1429  builder.DrawRoundRect(
1430  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(100, 500, 300, 300), 40, 120),
1431  fill_paint);
1432  builder.DrawRoundRect(
1433  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(100, 500, 300, 300), 40, 120),
1434  stroke_paint);
1435 
1436  flutter::DlPaint reference_paint = //
1437  flutter::DlPaint() //
1438  .setColor(flutter::DlColor::kMidGrey()) //
1439  .setDrawStyle(flutter::DlDrawStyle::kFill) //
1440  .setStrokeWidth(10);
1441 
1442  builder.DrawRoundRect(
1443  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(500, 500, 300, 300), 40, 40),
1444  reference_paint);
1445  builder.DrawRoundRect(
1446  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(100, 100, 300, 300), 120, 120),
1447  reference_paint);
1448 
1449  flutter::DlPaint clip_fill_paint = //
1450  flutter::DlPaint() //
1451  .setColor(flutter::DlColor::kCyan()) //
1452  .setDrawStyle(flutter::DlDrawStyle::kFill) //
1453  .setStrokeWidth(10);
1454 
1455  builder.Save();
1456  builder.ClipRoundRect(
1457  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(900, 100, 300, 300), 120, 40));
1458  builder.DrawPaint(clip_fill_paint);
1459  builder.Restore();
1460 
1461  builder.Save();
1462  builder.ClipRoundRect(
1463  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(100, 900, 300, 300), 40, 120));
1464  builder.DrawPaint(clip_fill_paint);
1465  builder.Restore();
1466 
1467  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1468 }
1469 
1470 TEST_P(DisplayListTest, DrawVerticesBlendModes) {
1471  std::vector<const char*> blend_mode_names;
1472  std::vector<flutter::DlBlendMode> blend_mode_values;
1473  {
1474  const std::vector<std::tuple<const char*, flutter::DlBlendMode>> blends = {
1475  // Pipeline blends (Porter-Duff alpha compositing)
1476  {"Clear", flutter::DlBlendMode::kClear},
1477  {"Source", flutter::DlBlendMode::kSrc},
1478  {"Destination", flutter::DlBlendMode::kDst},
1479  {"SourceOver", flutter::DlBlendMode::kSrcOver},
1480  {"DestinationOver", flutter::DlBlendMode::kDstOver},
1481  {"SourceIn", flutter::DlBlendMode::kSrcIn},
1482  {"DestinationIn", flutter::DlBlendMode::kDstIn},
1483  {"SourceOut", flutter::DlBlendMode::kSrcOut},
1484  {"DestinationOut", flutter::DlBlendMode::kDstOut},
1485  {"SourceATop", flutter::DlBlendMode::kSrcATop},
1486  {"DestinationATop", flutter::DlBlendMode::kDstATop},
1487  {"Xor", flutter::DlBlendMode::kXor},
1488  {"Plus", flutter::DlBlendMode::kPlus},
1489  {"Modulate", flutter::DlBlendMode::kModulate},
1490  // Advanced blends (color component blends)
1491  {"Screen", flutter::DlBlendMode::kScreen},
1492  {"Overlay", flutter::DlBlendMode::kOverlay},
1493  {"Darken", flutter::DlBlendMode::kDarken},
1494  {"Lighten", flutter::DlBlendMode::kLighten},
1495  {"ColorDodge", flutter::DlBlendMode::kColorDodge},
1496  {"ColorBurn", flutter::DlBlendMode::kColorBurn},
1497  {"HardLight", flutter::DlBlendMode::kHardLight},
1498  {"SoftLight", flutter::DlBlendMode::kSoftLight},
1499  {"Difference", flutter::DlBlendMode::kDifference},
1500  {"Exclusion", flutter::DlBlendMode::kExclusion},
1501  {"Multiply", flutter::DlBlendMode::kMultiply},
1502  {"Hue", flutter::DlBlendMode::kHue},
1503  {"Saturation", flutter::DlBlendMode::kSaturation},
1504  {"Color", flutter::DlBlendMode::kColor},
1505  {"Luminosity", flutter::DlBlendMode::kLuminosity},
1506  };
1507  assert(blends.size() ==
1508  static_cast<size_t>(flutter::DlBlendMode::kLastMode) + 1);
1509  for (const auto& [name, mode] : blends) {
1510  blend_mode_names.push_back(name);
1511  blend_mode_values.push_back(mode);
1512  }
1513  }
1514 
1515  auto callback = [&]() {
1516  static int current_blend_index = 3;
1517  static float dst_alpha = 1;
1518  static float src_alpha = 1;
1519  static float color0[4] = {1.0f, 0.0f, 0.0f, 1.0f};
1520  static float color1[4] = {0.0f, 1.0f, 0.0f, 1.0f};
1521  static float color2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
1522  static float src_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
1523 
1524  ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1525  {
1526  ImGui::ListBox("Blending mode", &current_blend_index,
1527  blend_mode_names.data(), blend_mode_names.size());
1528  ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
1529  ImGui::ColorEdit4("Color A", color0);
1530  ImGui::ColorEdit4("Color B", color1);
1531  ImGui::ColorEdit4("Color C", color2);
1532  ImGui::ColorEdit4("Source Color", src_color);
1533  ImGui::SliderFloat("Destination alpha", &dst_alpha, 0, 1);
1534  }
1535  ImGui::End();
1536 
1537  std::vector<DlPoint> positions = {DlPoint(100, 300), //
1538  DlPoint(200, 100), //
1539  DlPoint(300, 300)};
1540  std::vector<flutter::DlColor> colors = {
1541  toColor(color0).modulateOpacity(dst_alpha),
1542  toColor(color1).modulateOpacity(dst_alpha),
1543  toColor(color2).modulateOpacity(dst_alpha)};
1544 
1545  auto vertices = flutter::DlVertices::Make(
1546  flutter::DlVertexMode::kTriangles, 3, positions.data(),
1547  /*texture_coordinates=*/nullptr, colors.data());
1548 
1549  flutter::DisplayListBuilder builder;
1550  flutter::DlPaint paint;
1551 
1552  paint.setColor(toColor(src_color).modulateOpacity(src_alpha));
1553  builder.DrawVertices(vertices, blend_mode_values[current_blend_index],
1554  paint);
1555  return builder.Build();
1556  };
1557 
1558  ASSERT_TRUE(OpenPlaygroundHere(callback));
1559 }
1560 
1561 TEST_P(DisplayListTest, DrawPaintIgnoresMaskFilter) {
1562  flutter::DisplayListBuilder builder;
1563  builder.DrawPaint(flutter::DlPaint().setColor(flutter::DlColor::kWhite()));
1564 
1565  auto filter = flutter::DlBlurMaskFilter(flutter::DlBlurStyle::kNormal, 10.0f);
1566  builder.DrawCircle(DlPoint(300, 300), 200,
1567  flutter::DlPaint().setMaskFilter(&filter));
1568 
1569  std::vector<flutter::DlColor> colors = {flutter::DlColor::kGreen(),
1570  flutter::DlColor::kGreen()};
1571  const float stops[2] = {0.0, 1.0};
1572  auto linear = flutter::DlColorSource::MakeLinear(
1573  {100.0, 100.0}, {300.0, 300.0}, 2, colors.data(), stops,
1574  flutter::DlTileMode::kRepeat);
1575  flutter::DlPaint blend_paint =
1576  flutter::DlPaint() //
1577  .setColorSource(linear) //
1578  .setBlendMode(flutter::DlBlendMode::kScreen);
1579  builder.DrawPaint(blend_paint);
1580 
1581  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1582 }
1583 
1584 TEST_P(DisplayListTest, DrawMaskBlursThatMightUseSaveLayers) {
1585  flutter::DisplayListBuilder builder;
1586  builder.DrawColor(flutter::DlColor::kWhite(), flutter::DlBlendMode::kSrc);
1587  Vector2 scale = GetContentScale();
1588  builder.Scale(scale.x, scale.y);
1589 
1590  builder.Save();
1591  // We need a small transform op to avoid a deferred save
1592  builder.Translate(1.0f, 1.0f);
1593  auto solid_filter =
1594  flutter::DlBlurMaskFilter::Make(flutter::DlBlurStyle::kSolid, 5.0f);
1595  flutter::DlPaint solid_alpha_paint =
1596  flutter::DlPaint() //
1597  .setMaskFilter(solid_filter) //
1598  .setColor(flutter::DlColor::kBlue()) //
1599  .setAlpha(0x7f);
1600  for (int x = 1; x <= 4; x++) {
1601  for (int y = 1; y <= 4; y++) {
1602  builder.DrawRect(DlRect::MakeXYWH(x * 100, y * 100, 80, 80),
1603  solid_alpha_paint);
1604  }
1605  }
1606  builder.Restore();
1607 
1608  builder.Save();
1609  builder.Translate(500.0f, 0.0f);
1610  auto normal_filter =
1611  flutter::DlBlurMaskFilter::Make(flutter::DlBlurStyle::kNormal, 5.0f);
1612  auto rotate_if = flutter::DlMatrixImageFilter::Make(
1613  Matrix::MakeRotationZ(Degrees(10)), flutter::DlImageSampling::kLinear);
1614  flutter::DlPaint normal_if_paint =
1615  flutter::DlPaint() //
1616  .setMaskFilter(solid_filter) //
1617  .setImageFilter(rotate_if) //
1618  .setColor(flutter::DlColor::kGreen()) //
1619  .setAlpha(0x7f);
1620  for (int x = 1; x <= 4; x++) {
1621  for (int y = 1; y <= 4; y++) {
1622  builder.DrawRect(DlRect::MakeXYWH(x * 100, y * 100, 80, 80),
1623  normal_if_paint);
1624  }
1625  }
1626  builder.Restore();
1627 
1628  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1629 }
1630 
1631 } // namespace testing
1632 } // 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:327
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
std::vector< Point > points