Flutter Impeller
aiks_dl_gradient_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 "display_list/display_list.h"
6 #include "display_list/dl_blend_mode.h"
7 #include "display_list/dl_tile_mode.h"
8 #include "display_list/effects/dl_color_filter.h"
9 #include "display_list/effects/dl_color_source.h"
10 #include "display_list/effects/dl_mask_filter.h"
12 
13 #include "flutter/display_list/dl_builder.h"
14 #include "flutter/display_list/dl_color.h"
15 #include "flutter/display_list/dl_paint.h"
16 #include "flutter/testing/testing.h"
18 #include "include/core/SkPath.h"
19 #include "include/core/SkRRect.h"
20 #include "include/core/SkRect.h"
21 
22 using namespace flutter;
23 ////////////////////////////////////////////////////////////////////////////////
24 // This is for tests of Canvas that are interested the results of rendering
25 // gradients.
26 ////////////////////////////////////////////////////////////////////////////////
27 
28 namespace impeller {
29 namespace testing {
30 
31 namespace {
32 
33 /// Test body for linear gradient tile mode tests (ex.
34 /// CanRenderLinearGradientClamp).
35 void CanRenderLinearGradient(AiksTest* aiks_test, DlTileMode tile_mode) {
36  DisplayListBuilder builder;
37  Point scale = aiks_test->GetContentScale();
38  builder.Scale(scale.x, scale.y);
39  DlPaint paint;
40  builder.Translate(100.0f, 0);
41 
42  std::vector<DlColor> colors = {
43  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
44  DlColor(Color{0.1294, 0.5882, 0.9529, 0.0}.ToARGB())};
45  std::vector<Scalar> stops = {0.0, 1.0};
46 
47  auto gradient = DlColorSource::MakeLinear(
48  {0, 0}, {200, 200}, 2, colors.data(), stops.data(), tile_mode);
49  paint.setColorSource(gradient);
50  paint.setColor(DlColor::kWhite());
51  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
52  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
53 }
54 
55 Matrix ToMatrix(const SkMatrix& m) {
56  return Matrix{
57  // clang-format off
58  m[0], m[3], 0, m[6],
59  m[1], m[4], 0, m[7],
60  0, 0, 1, 0,
61  m[2], m[5], 0, m[8],
62  // clang-format on
63  };
64 }
65 } // namespace
66 
67 TEST_P(AiksTest, CanRenderLinearGradientClamp) {
68  CanRenderLinearGradient(this, DlTileMode::kClamp);
69 }
70 TEST_P(AiksTest, CanRenderLinearGradientRepeat) {
71  CanRenderLinearGradient(this, DlTileMode::kRepeat);
72 }
73 TEST_P(AiksTest, CanRenderLinearGradientMirror) {
74  CanRenderLinearGradient(this, DlTileMode::kMirror);
75 }
76 TEST_P(AiksTest, CanRenderLinearGradientDecal) {
77  CanRenderLinearGradient(this, DlTileMode::kDecal);
78 }
79 
80 TEST_P(AiksTest, CanRenderLinearGradientDecalWithColorFilter) {
81  DisplayListBuilder builder;
82  Point scale = GetContentScale();
83  builder.Scale(scale.x, scale.y);
84  DlPaint paint;
85  builder.Translate(100.0f, 0);
86 
87  std::vector<DlColor> colors = {
88  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
89  DlColor(Color{0.1294, 0.5882, 0.9529, 0.0}.ToARGB())};
90  std::vector<Scalar> stops = {0.0, 1.0};
91 
92  paint.setColorSource(DlColorSource::MakeLinear(
93  {0, 0}, {200, 200}, 2, colors.data(), stops.data(), DlTileMode::kDecal));
94  // Overlay the gradient with 25% green. This should appear as the entire
95  // rectangle being drawn with 25% green, including the border area outside the
96  // decal gradient.
97  paint.setColorFilter(DlColorFilter::MakeBlend(DlColor::kGreen().withAlpha(64),
98  DlBlendMode::kSrcOver));
99  paint.setColor(DlColor::kWhite());
100  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
101  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
102 }
103 
105  DisplayListBuilder builder;
106  DlPaint paint;
107  builder.Translate(100.0, 100.0);
108 
109  // 0xffcccccc --> 0xff333333, taken from
110  // https://github.com/flutter/flutter/issues/118073#issue-1521699748
111  std::vector<DlColor> colors = {DlColor(0xFFCCCCCC), DlColor(0xFF333333)};
112  std::vector<Scalar> stops = {0.0, 1.0};
113 
114  paint.setColorSource(DlColorSource::MakeLinear(
115  {0, 0}, {800, 500}, 2, colors.data(), stops.data(), DlTileMode::kClamp));
116  builder.DrawRect(SkRect::MakeXYWH(0, 0, 800, 500), paint);
117  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
118 }
119 
120 TEST_P(AiksTest, CanRenderLinearGradientWithDitheringEnabled) {
122 }
123 
125  DisplayListBuilder builder;
126  DlPaint paint;
127  builder.Translate(100.0, 100.0);
128 
129  // #FFF -> #000
130  std::vector<DlColor> colors = {DlColor(Color{1.0, 1.0, 1.0, 1.0}.ToARGB()),
131  DlColor(Color{0.0, 0.0, 0.0, 1.0}.ToARGB())};
132  std::vector<Scalar> stops = {0.0, 1.0};
133 
134  paint.setColorSource(DlColorSource::MakeRadial(
135  {600, 600}, 600, 2, colors.data(), stops.data(), DlTileMode::kClamp));
136  builder.DrawRect(SkRect::MakeXYWH(0, 0, 1200, 1200), paint);
137  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
138 }
139 
140 TEST_P(AiksTest, CanRenderRadialGradientWithDitheringEnabled) {
142 }
143 
145  DisplayListBuilder builder;
146  builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
147  DlPaint paint;
148  builder.Translate(100.0, 100.0);
149 
150  // #FFF -> #000
151  std::vector<DlColor> colors = {DlColor(Color{1.0, 1.0, 1.0, 1.0}.ToARGB()),
152  DlColor(Color{0.0, 0.0, 0.0, 1.0}.ToARGB())};
153  std::vector<Scalar> stops = {0.0, 1.0};
154 
155  paint.setColorSource(DlColorSource::MakeSweep(
156  {100, 100}, /*start=*/45, /*end=*/135, 2, colors.data(), stops.data(),
157  DlTileMode::kMirror));
158 
159  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
160  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
161 }
162 
163 TEST_P(AiksTest, CanRenderSweepGradientWithDitheringEnabled) {
165 }
166 
168  DisplayListBuilder builder;
169  builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
170  DlPaint paint;
171  builder.Translate(100.0, 100.0);
172 
173  // #FFF -> #000
174  std::vector<DlColor> colors = {DlColor(Color{1.0, 1.0, 1.0, 1.0}.ToARGB()),
175  DlColor(Color{0.0, 0.0, 0.0, 1.0}.ToARGB())};
176  std::vector<Scalar> stops = {0.0, 1.0};
177 
178  paint.setColorSource(DlColorSource::MakeConical({0, 1}, 0, {100, 100}, 100, 2,
179  colors.data(), stops.data(),
180  DlTileMode::kMirror));
181 
182  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
183  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
184 }
185 
186 TEST_P(AiksTest, CanRenderConicalGradientWithDitheringEnabled) {
188 }
189 
190 namespace {
191 void CanRenderLinearGradientWithOverlappingStops(AiksTest* aiks_test,
192  DlTileMode tile_mode) {
193  DisplayListBuilder builder;
194  DlPaint paint;
195  builder.Translate(100.0, 100.0);
196 
197  std::vector<DlColor> colors = {
198  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
199  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
200  DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB()),
201  DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())};
202  std::vector<Scalar> stops = {0.0, 0.5, 0.5, 1.0};
203 
204  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {500, 500},
205  stops.size(), colors.data(),
206  stops.data(), tile_mode));
207 
208  paint.setColor(DlColor::kWhite());
209  builder.DrawRect(SkRect::MakeXYWH(0, 0, 500, 500), paint);
210  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
211 }
212 } // namespace
213 
214 // Only clamp is necessary. All tile modes are the same output.
215 TEST_P(AiksTest, CanRenderLinearGradientWithOverlappingStopsClamp) {
216  CanRenderLinearGradientWithOverlappingStops(this, DlTileMode::kClamp);
217 }
218 
219 namespace {
220 void CanRenderGradientWithIncompleteStops(AiksTest* aiks_test,
221  DlColorSourceType type) {
222  const DlTileMode tile_modes[4] = {
223  DlTileMode::kClamp,
224  DlTileMode::kRepeat,
225  DlTileMode::kMirror,
226  DlTileMode::kDecal,
227  };
228  const DlScalar test_size = 250;
229  const DlScalar test_border = 25;
230  const DlScalar gradient_size = 50;
231  const DlScalar quadrant_size = test_size + test_border * 2;
232 
233  DisplayListBuilder builder;
234  builder.DrawRect(DlRect::MakeWH(quadrant_size * 2, quadrant_size * 2),
235  DlPaint().setColor(DlColor::kDarkGrey()));
236 
237  for (int quadrant = 0; quadrant < 4; quadrant++) {
238  builder.Save();
239  builder.Translate((quadrant & 1) * quadrant_size + test_border,
240  (quadrant >> 1) * quadrant_size + test_border);
241 
242  if (type == DlColorSourceType::kLinearGradient) {
243  // Alignment lines for the gradient edges/repeats/mirrors/etc.
244  // (rendered under the gradient so as not to obscure it)
245  DlPoint center = DlPoint(test_size, test_size) * 0.5;
246  DlScalar ten_percent = gradient_size * 0.1;
247  for (int i = gradient_size / 2; i <= test_size / 2; i += gradient_size) {
248  auto draw_at = [=](DlCanvas& canvas, DlScalar offset, DlColor color) {
249  DlPaint line_paint;
250  line_paint.setColor(color);
251  // strokewidth of 2 straddles the dividing line
252  line_paint.setStrokeWidth(2.0f);
253  line_paint.setDrawStyle(DlDrawStyle::kStroke);
254 
255  DlPoint along(offset, offset);
256  DlScalar across_distance = test_size / 2 + 10 - offset;
257  DlPoint across(across_distance, -across_distance);
258 
259  canvas.DrawLine(center - along - across, //
260  center - along + across, //
261  line_paint);
262  canvas.DrawLine(center + along - across, //
263  center + along + across, //
264  line_paint);
265  };
266  // White line is at the edge of the gradient
267  // Grey lines are where the 0.1 and 0.9 color stops land
268  draw_at(builder, i - ten_percent, DlColor::kMidGrey());
269  draw_at(builder, i, DlColor::kWhite());
270  draw_at(builder, i + ten_percent, DlColor::kMidGrey());
271  }
272  }
273 
274  std::vector<DlColor> colors = {
275  DlColor::kGreen(),
276  DlColor::kPurple(),
277  DlColor::kOrange(),
278  DlColor::kBlue(),
279  };
280  std::vector<Scalar> stops = {0.1, 0.3, 0.7, 0.9};
281 
282  DlPaint paint;
283  switch (type) {
284  case DlColorSourceType::kLinearGradient:
285  paint.setColorSource(DlColorSource::MakeLinear(
286  {test_size / 2 - gradient_size / 2,
287  test_size / 2 - gradient_size / 2},
288  {test_size / 2 + gradient_size / 2,
289  test_size / 2 + gradient_size / 2},
290  stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
291  break;
292  case DlColorSourceType::kRadialGradient:
293  paint.setColorSource(DlColorSource::MakeRadial(
294  {test_size / 2, test_size / 2}, gradient_size, //
295  stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
296  break;
297  case DlColorSourceType::kConicalGradient:
298  paint.setColorSource(DlColorSource::MakeConical(
299  {test_size / 2, test_size / 2}, 0,
300  {test_size / 2 + 20, test_size / 2 - 10}, gradient_size,
301  stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
302  break;
303  case DlColorSourceType::kSweepGradient:
304  paint.setColorSource(DlColorSource::MakeSweep(
305  {test_size / 2, test_size / 2}, 0, 45, //
306  stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
307  break;
308  default:
309  FML_UNREACHABLE();
310  }
311 
312  builder.DrawRect(SkRect::MakeXYWH(0, 0, test_size, test_size), paint);
313  builder.Restore();
314  }
315 
316  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
317 }
318 } // namespace
319 
320 TEST_P(AiksTest, CanRenderLinearGradientWithIncompleteStops) {
321  CanRenderGradientWithIncompleteStops(this,
322  DlColorSourceType::kLinearGradient);
323 }
324 TEST_P(AiksTest, CanRenderRadialGradientWithIncompleteStops) {
325  CanRenderGradientWithIncompleteStops(this,
326  DlColorSourceType::kRadialGradient);
327 }
328 TEST_P(AiksTest, CanRenderConicalGradientWithIncompleteStops) {
329  CanRenderGradientWithIncompleteStops(this,
330  DlColorSourceType::kConicalGradient);
331 }
332 TEST_P(AiksTest, CanRenderSweepGradientWithIncompleteStops) {
333  CanRenderGradientWithIncompleteStops(this, DlColorSourceType::kSweepGradient);
334 }
335 
336 namespace {
337 void CanRenderLinearGradientManyColors(AiksTest* aiks_test,
338  DlTileMode tile_mode) {
339  DisplayListBuilder builder;
340  builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
341  DlPaint paint;
342  builder.Translate(100, 100);
343 
344  std::vector<DlColor> colors = {
345  DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()),
346  DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()),
347  DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
348  DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()),
349  DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()),
350  DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
351  DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())};
352  std::vector<Scalar> stops = {
353  0.0,
354  (1.0 / 6.0) * 1,
355  (1.0 / 6.0) * 2,
356  (1.0 / 6.0) * 3,
357  (1.0 / 6.0) * 4,
358  (1.0 / 6.0) * 5,
359  1.0,
360  };
361 
362  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {200, 200},
363  stops.size(), colors.data(),
364  stops.data(), tile_mode));
365 
366  paint.setColor(DlColor::kWhite());
367  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
368  builder.Restore();
369  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
370 }
371 } // namespace
372 
373 TEST_P(AiksTest, CanRenderLinearGradientManyColorsClamp) {
374  CanRenderLinearGradientManyColors(this, DlTileMode::kClamp);
375 }
376 TEST_P(AiksTest, CanRenderLinearGradientManyColorsRepeat) {
377  CanRenderLinearGradientManyColors(this, DlTileMode::kRepeat);
378 }
379 TEST_P(AiksTest, CanRenderLinearGradientManyColorsMirror) {
380  CanRenderLinearGradientManyColors(this, DlTileMode::kMirror);
381 }
382 TEST_P(AiksTest, CanRenderLinearGradientManyColorsDecal) {
383  CanRenderLinearGradientManyColors(this, DlTileMode::kDecal);
384 }
385 
386 namespace {
387 void CanRenderLinearGradientWayManyColors(AiksTest* aiks_test,
388  DlTileMode tile_mode) {
389  DisplayListBuilder builder;
390  DlPaint paint;
391  builder.Translate(100.0, 100.0);
392  auto color = DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB());
393  std::vector<DlColor> colors;
394  std::vector<Scalar> stops;
395  auto current_stop = 0.0;
396  for (int i = 0; i < 2000; i++) {
397  colors.push_back(color);
398  stops.push_back(current_stop);
399  current_stop += 1 / 2000.0;
400  }
401  stops[2000 - 1] = 1.0;
402 
403  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {200, 200},
404  stops.size(), colors.data(),
405  stops.data(), tile_mode));
406 
407  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
408  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
409 }
410 } // namespace
411 
412 // Only test clamp on purpose since they all look the same.
413 TEST_P(AiksTest, CanRenderLinearGradientWayManyColorsClamp) {
414  CanRenderLinearGradientWayManyColors(this, DlTileMode::kClamp);
415 }
416 
417 TEST_P(AiksTest, CanRenderLinearGradientManyColorsUnevenStops) {
418  auto callback = [&]() -> sk_sp<DisplayList> {
419  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
420  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
421  DlTileMode::kMirror, DlTileMode::kDecal};
422 
423  static int selected_tile_mode = 0;
424  static Matrix matrix;
425  if (AiksTest::ImGuiBegin("Controls", nullptr,
426  ImGuiWindowFlags_AlwaysAutoResize)) {
427  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
428  sizeof(tile_mode_names) / sizeof(char*));
429  std::string label = "##1";
430  for (int i = 0; i < 4; i++) {
431  ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
432  &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0);
433  label[2]++;
434  }
435  ImGui::End();
436  }
437 
438  DisplayListBuilder builder;
439  DlPaint paint;
440  builder.Translate(100.0, 100.0);
441  auto tile_mode = tile_modes[selected_tile_mode];
442 
443  std::vector<DlColor> colors = {
444  DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()),
445  DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()),
446  DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
447  DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()),
448  DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()),
449  DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
450  DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())};
451  std::vector<Scalar> stops = {
452  0.0, 2.0 / 62.0, 4.0 / 62.0, 8.0 / 62.0, 16.0 / 62.0, 32.0 / 62.0, 1.0,
453  };
454 
455  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {200, 200},
456  stops.size(), colors.data(),
457  stops.data(), tile_mode));
458 
459  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
460  return builder.Build();
461  };
462  ASSERT_TRUE(OpenPlaygroundHere(callback));
463 }
464 
465 TEST_P(AiksTest, CanRenderLinearGradientMaskBlur) {
466  DisplayListBuilder builder;
467 
468  std::vector<DlColor> colors = {
469  DlColor::kRed(), DlColor::kWhite(), DlColor::kRed(), DlColor::kWhite(),
470  DlColor::kRed(), DlColor::kWhite(), DlColor::kRed(), DlColor::kWhite(),
471  DlColor::kRed(), DlColor::kWhite(), DlColor::kRed()};
472  std::vector<Scalar> stops = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5,
473  0.6, 0.7, 0.8, 0.9, 1.0};
474 
475  DlPaint paint;
476  paint.setColor(DlColor::kWhite());
477  paint.setColorSource(DlColorSource::MakeLinear(
478  {200, 200}, {400, 400}, stops.size(), colors.data(), stops.data(),
479  DlTileMode::kClamp));
480  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20));
481 
482  builder.DrawCircle(SkPoint{300, 300}, 200, paint);
483  builder.DrawRect(SkRect::MakeLTRB(100, 300, 500, 600), paint);
484 
485  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
486 }
487 
488 TEST_P(AiksTest, CanRenderRadialGradient) {
489  auto callback = [&]() -> sk_sp<DisplayList> {
490  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
491  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
492  DlTileMode::kMirror, DlTileMode::kDecal};
493 
494  static int selected_tile_mode = 0;
495  static Matrix matrix;
496  if (AiksTest::ImGuiBegin("Controls", nullptr,
497  ImGuiWindowFlags_AlwaysAutoResize)) {
498  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
499  sizeof(tile_mode_names) / sizeof(char*));
500  std::string label = "##1";
501  for (int i = 0; i < 4; i++) {
502  ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
503  &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0);
504  label[2]++;
505  }
506  ImGui::End();
507  }
508 
509  DisplayListBuilder builder;
510  DlPaint paint;
511  builder.Translate(100.0, 100.0);
512  auto tile_mode = tile_modes[selected_tile_mode];
513 
514  std::vector<DlColor> colors = {
515  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
516  DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())};
517  std::vector<Scalar> stops = {0.0, 1.0};
518 
519  paint.setColorSource(DlColorSource::MakeRadial(
520  {100, 100}, 100, 2, colors.data(), stops.data(), tile_mode));
521 
522  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
523  return builder.Build();
524  };
525  ASSERT_TRUE(OpenPlaygroundHere(callback));
526 }
527 
528 TEST_P(AiksTest, CanRenderRadialGradientManyColors) {
529  auto callback = [&]() -> sk_sp<DisplayList> {
530  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
531  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
532  DlTileMode::kMirror, DlTileMode::kDecal};
533 
534  static int selected_tile_mode = 0;
535  static Matrix matrix = {
536  1, 0, 0, 0, //
537  0, 1, 0, 0, //
538  0, 0, 1, 0, //
539  0, 0, 0, 1 //
540  };
541  if (AiksTest::ImGuiBegin("Controls", nullptr,
542  ImGuiWindowFlags_AlwaysAutoResize)) {
543  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
544  sizeof(tile_mode_names) / sizeof(char*));
545  std::string label = "##1";
546  for (int i = 0; i < 4; i++) {
547  ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
548  &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0);
549  label[2]++;
550  }
551  ImGui::End();
552  }
553 
554  DisplayListBuilder builder;
555  DlPaint paint;
556  builder.Translate(100.0, 100.0);
557  auto tile_mode = tile_modes[selected_tile_mode];
558 
559  std::vector<DlColor> colors = {
560  DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()),
561  DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()),
562  DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
563  DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()),
564  DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()),
565  DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
566  DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())};
567  std::vector<Scalar> stops = {
568  0.0,
569  (1.0 / 6.0) * 1,
570  (1.0 / 6.0) * 2,
571  (1.0 / 6.0) * 3,
572  (1.0 / 6.0) * 4,
573  (1.0 / 6.0) * 5,
574  1.0,
575  };
576 
577  paint.setColorSource(DlColorSource::MakeRadial(
578  {100, 100}, 100, stops.size(), colors.data(), stops.data(), tile_mode));
579 
580  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
581  return builder.Build();
582  };
583  ASSERT_TRUE(OpenPlaygroundHere(callback));
584 }
585 
586 namespace {
587 void CanRenderSweepGradient(AiksTest* aiks_test, DlTileMode tile_mode) {
588  DisplayListBuilder builder;
589  builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
590  DlPaint paint;
591  builder.Translate(100, 100);
592 
593  std::vector<DlColor> colors = {
594  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
595  DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())};
596  std::vector<Scalar> stops = {0.0, 1.0};
597 
598  paint.setColorSource(DlColorSource::MakeSweep(
599  {100, 100}, /*start=*/45, /*end=*/135, /*stop_count=*/2, colors.data(),
600  stops.data(), tile_mode));
601 
602  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
603  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
604 }
605 } // namespace
606 
607 TEST_P(AiksTest, CanRenderSweepGradientClamp) {
608  CanRenderSweepGradient(this, DlTileMode::kClamp);
609 }
610 TEST_P(AiksTest, CanRenderSweepGradientRepeat) {
611  CanRenderSweepGradient(this, DlTileMode::kRepeat);
612 }
613 TEST_P(AiksTest, CanRenderSweepGradientMirror) {
614  CanRenderSweepGradient(this, DlTileMode::kMirror);
615 }
616 TEST_P(AiksTest, CanRenderSweepGradientDecal) {
617  CanRenderSweepGradient(this, DlTileMode::kDecal);
618 }
619 
620 namespace {
621 void CanRenderSweepGradientManyColors(AiksTest* aiks_test,
622  DlTileMode tile_mode) {
623  DisplayListBuilder builder;
624  DlPaint paint;
625  builder.Translate(100.0, 100.0);
626 
627  std::vector<DlColor> colors = {
628  DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()),
629  DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()),
630  DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
631  DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()),
632  DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()),
633  DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
634  DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())};
635  std::vector<Scalar> stops = {
636  0.0,
637  (1.0 / 6.0) * 1,
638  (1.0 / 6.0) * 2,
639  (1.0 / 6.0) * 3,
640  (1.0 / 6.0) * 4,
641  (1.0 / 6.0) * 5,
642  1.0,
643  };
644 
645  paint.setColorSource(DlColorSource::MakeSweep({100, 100}, 45, 135,
646  stops.size(), colors.data(),
647  stops.data(), tile_mode));
648 
649  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
650  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
651 }
652 } // namespace
653 
654 TEST_P(AiksTest, CanRenderSweepGradientManyColorsClamp) {
655  CanRenderSweepGradientManyColors(this, DlTileMode::kClamp);
656 }
657 TEST_P(AiksTest, CanRenderSweepGradientManyColorsRepeat) {
658  CanRenderSweepGradientManyColors(this, DlTileMode::kRepeat);
659 }
660 TEST_P(AiksTest, CanRenderSweepGradientManyColorsMirror) {
661  CanRenderSweepGradientManyColors(this, DlTileMode::kMirror);
662 }
663 TEST_P(AiksTest, CanRenderSweepGradientManyColorsDecal) {
664  CanRenderSweepGradientManyColors(this, DlTileMode::kDecal);
665 }
666 
667 TEST_P(AiksTest, CanRenderConicalGradient) {
668  Scalar size = 256;
669  DisplayListBuilder builder;
670  DlPaint paint;
671  paint.setColor(DlColor::kWhite());
672  builder.DrawRect(SkRect::MakeXYWH(0, 0, size * 3, size * 3), paint);
673  std::vector<DlColor> colors = {
674  DlColor(Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF).ToARGB()),
675  DlColor(Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF).ToARGB()),
676  DlColor(Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF).ToARGB()),
677  DlColor(Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF).ToARGB())};
678  std::vector<Scalar> stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0};
679  std::array<std::tuple<DlPoint, float, DlPoint, float>, 8> array{
680  std::make_tuple(DlPoint(size / 2.f, size / 2.f), 0.f,
681  DlPoint(size / 2.f, size / 2.f), size / 2.f),
682  std::make_tuple(DlPoint(size / 2.f, size / 2.f), size / 4.f,
683  DlPoint(size / 2.f, size / 2.f), size / 2.f),
684  std::make_tuple(DlPoint(size / 4.f, size / 4.f), 0.f,
685  DlPoint(size / 2.f, size / 2.f), size / 2.f),
686  std::make_tuple(DlPoint(size / 4.f, size / 4.f), size / 2.f,
687  DlPoint(size / 2.f, size / 2.f), 0),
688  std::make_tuple(DlPoint(size / 4.f, size / 4.f), size / 4.f,
689  DlPoint(size / 2.f, size / 2.f), size / 2.f),
690  std::make_tuple(DlPoint(size / 4.f, size / 4.f), size / 16.f,
691  DlPoint(size / 2.f, size / 2.f), size / 8.f),
692  std::make_tuple(DlPoint(size / 4.f, size / 4.f), size / 8.f,
693  DlPoint(size / 2.f, size / 2.f), size / 16.f),
694  std::make_tuple(DlPoint(size / 8.f, size / 8.f), size / 8.f,
695  DlPoint(size / 2.f, size / 2.f), size / 8.f),
696  };
697  for (int i = 0; i < 8; i++) {
698  builder.Save();
699  builder.Translate((i % 3) * size, i / 3 * size);
700  paint.setColorSource(DlColorSource::MakeConical(
701  std::get<2>(array[i]), std::get<3>(array[i]), std::get<0>(array[i]),
702  std::get<1>(array[i]), stops.size(), colors.data(), stops.data(),
703  DlTileMode::kClamp));
704  builder.DrawRect(SkRect::MakeXYWH(0, 0, size, size), paint);
705  builder.Restore();
706  }
707  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
708 }
709 
710 TEST_P(AiksTest, CanRenderGradientDecalWithBackground) {
711  std::vector<DlColor> colors = {
712  DlColor(Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF).ToARGB()),
713  DlColor(Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF).ToARGB()),
714  DlColor(Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF).ToARGB()),
715  DlColor(Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF).ToARGB())};
716  std::vector<Scalar> stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0};
717 
718  std::array<std::shared_ptr<DlColorSource>, 3> color_sources = {
719  DlColorSource::MakeLinear({0, 0}, {100, 100}, stops.size(), colors.data(),
720  stops.data(), DlTileMode::kDecal),
721  DlColorSource::MakeRadial({100, 100}, 100, stops.size(), colors.data(),
722  stops.data(), DlTileMode::kDecal),
723  DlColorSource::MakeSweep({100, 100}, 45, 135, stops.size(), colors.data(),
724  stops.data(), DlTileMode::kDecal),
725  };
726 
727  DisplayListBuilder builder;
728  DlPaint paint;
729  paint.setColor(DlColor::kWhite());
730  builder.DrawRect(SkRect::MakeLTRB(0, 0, 605, 205), paint);
731  for (int i = 0; i < 3; i++) {
732  builder.Save();
733  builder.Translate(i * 200.0f, 0);
734  paint.setColorSource(color_sources[i]);
735  builder.DrawRect(SkRect::MakeLTRB(0, 0, 200, 200), paint);
736  builder.Restore();
737  }
738  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
739 }
740 
741 TEST_P(AiksTest, GradientStrokesRenderCorrectly) {
742  // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
743  auto callback = [&]() -> sk_sp<DisplayList> {
744  static float scale = 3;
745  static bool add_circle_clip = true;
746  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
747  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
748  DlTileMode::kMirror, DlTileMode::kDecal};
749  static int selected_tile_mode = 0;
750  static float alpha = 1;
751 
752  if (AiksTest::ImGuiBegin("Controls", nullptr,
753  ImGuiWindowFlags_AlwaysAutoResize)) {
754  ImGui::SliderFloat("Scale", &scale, 0, 6);
755  ImGui::Checkbox("Circle clip", &add_circle_clip);
756  ImGui::SliderFloat("Alpha", &alpha, 0, 1);
757  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
758  sizeof(tile_mode_names) / sizeof(char*));
759  ImGui::End();
760  }
761 
762  DisplayListBuilder builder;
763  builder.Scale(GetContentScale().x, GetContentScale().y);
764  DlPaint paint;
765  paint.setColor(DlColor::kWhite());
766  builder.DrawPaint(paint);
767 
768  paint.setDrawStyle(DlDrawStyle::kStroke);
769  paint.setColor(DlColor::kWhite().withAlpha(alpha * 255));
770  paint.setStrokeWidth(10);
771  auto tile_mode = tile_modes[selected_tile_mode];
772 
773  std::vector<DlColor> colors = {
774  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
775  DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())};
776  std::vector<Scalar> stops = {0.0, 1.0};
777 
778  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {50, 50},
779  stops.size(), colors.data(),
780  stops.data(), tile_mode));
781 
782  SkPath path;
783  path.moveTo(20, 20);
784  path.quadTo({60, 20}, {60, 60});
785  path.close();
786  path.moveTo(60, 20);
787  path.quadTo({60, 60}, {20, 60});
788 
789  builder.Scale(scale, scale);
790 
791  if (add_circle_clip) {
792  static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20,
793  Color::Red());
794  static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20,
795  Color::Red());
796  auto [handle_a, handle_b] =
797  DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b);
798 
799  SkMatrix screen_to_canvas;
800  if (!builder.GetTransform().invert(&screen_to_canvas)) {
801  return nullptr;
802  }
803  Matrix ip_matrix = ToMatrix(screen_to_canvas);
804  Point point_a = ip_matrix * handle_a * GetContentScale();
805  Point point_b = ip_matrix * handle_b * GetContentScale();
806 
807  Point middle = (point_a + point_b) / 2;
808  auto radius = point_a.GetDistance(middle);
809  SkPath circle;
810  circle.addCircle(middle.x, middle.y, radius);
811  builder.ClipPath(circle);
812  }
813 
814  for (auto join :
815  {DlStrokeJoin::kBevel, DlStrokeJoin::kRound, DlStrokeJoin::kMiter}) {
816  paint.setStrokeJoin(join);
817  for (auto cap :
818  {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
819  paint.setStrokeCap(cap);
820  builder.DrawPath(path, paint);
821  builder.Translate(80, 0);
822  }
823  builder.Translate(-240, 60);
824  }
825 
826  return builder.Build();
827  };
828 
829  ASSERT_TRUE(OpenPlaygroundHere(callback));
830 }
831 
832 // Draws two gradients that should look identical (except that one is an RRECT).
833 TEST_P(AiksTest, FastGradientTestHorizontal) {
834  DisplayListBuilder builder;
835  DlPaint paint;
836  builder.Translate(100.0f, 0);
837 
838  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue(),
839  DlColor::kGreen()};
840  std::vector<Scalar> stops = {0.0, 0.1, 1.0};
841 
842  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {300, 0}, stops.size(),
843  colors.data(), stops.data(),
844  DlTileMode::kClamp));
845 
846  paint.setColor(DlColor::kWhite());
847  builder.DrawRect(SkRect::MakeXYWH(0, 0, 300, 300), paint);
848  builder.Translate(400, 0);
849  builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 300, 300), 4, 4),
850  paint);
851 
852  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
853 }
854 
855 // Draws two gradients that should look identical (except that one is an RRECT).
856 TEST_P(AiksTest, FastGradientTestVertical) {
857  DisplayListBuilder builder;
858  DlPaint paint;
859  builder.Translate(100.0f, 0);
860 
861  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue(),
862  DlColor::kGreen()};
863  std::vector<Scalar> stops = {0.0, 0.1, 1.0};
864 
865  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {0, 300}, stops.size(),
866  colors.data(), stops.data(),
867  DlTileMode::kClamp));
868 
869  paint.setColor(DlColor::kWhite());
870  builder.DrawRect(SkRect::MakeXYWH(0, 0, 300, 300), paint);
871  builder.Translate(400, 0);
872  builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 300, 300), 4, 4),
873  paint);
874 
875  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
876 }
877 
878 // Draws two gradients that should look identical (except that one is an RRECT).
879 TEST_P(AiksTest, FastGradientTestHorizontalReversed) {
880  DisplayListBuilder builder;
881  DlPaint paint;
882  builder.Translate(100.0f, 0);
883 
884  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue(),
885  DlColor::kGreen()};
886  std::vector<Scalar> stops = {0.0, 0.1, 1.0};
887 
888  paint.setColorSource(DlColorSource::MakeLinear({300, 0}, {0, 0}, stops.size(),
889  colors.data(), stops.data(),
890  DlTileMode::kClamp));
891 
892  paint.setColor(DlColor::kWhite());
893  builder.DrawRect(SkRect::MakeXYWH(0, 0, 300, 300), paint);
894  builder.Translate(400, 0);
895  builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 300, 300), 4, 4),
896  paint);
897 
898  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
899 }
900 
901 // Draws two gradients that should look identical (except that one is an RRECT).
902 TEST_P(AiksTest, FastGradientTestVerticalReversed) {
903  DisplayListBuilder builder;
904  DlPaint paint;
905  builder.Translate(100.0f, 0);
906 
907  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue(),
908  DlColor::kGreen()};
909  std::vector<Scalar> stops = {0.0, 0.1, 1.0};
910 
911  paint.setColorSource(DlColorSource::MakeLinear({0, 300}, {0, 0}, stops.size(),
912  colors.data(), stops.data(),
913  DlTileMode::kClamp));
914 
915  paint.setColor(DlColor::kWhite());
916  builder.DrawRect(SkRect::MakeXYWH(0, 0, 300, 300), paint);
917  builder.Translate(400, 0);
918  builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 300, 300), 4, 4),
919  paint);
920 
921  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
922 }
923 
924 TEST_P(AiksTest, VerifyNonOptimizedGradient) {
925  DisplayListBuilder builder;
926  DlPaint paint;
927  builder.Translate(100.0f, 0);
928 
929  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue(),
930  DlColor::kGreen()};
931  std::vector<Scalar> stops = {0.0, 0.1, 1.0};
932 
933  // Inset the start and end point to verify that we do not apply
934  // the fast gradient condition.
935  paint.setColorSource(
936  DlColorSource::MakeLinear({0, 150}, {0, 100}, stops.size(), colors.data(),
937  stops.data(), DlTileMode::kRepeat));
938 
939  paint.setColor(DlColor::kWhite());
940  builder.DrawRect(SkRect::MakeXYWH(0, 0, 300, 300), paint);
941  builder.Translate(400, 0);
942  builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 300, 300), 4, 4),
943  paint);
944 
945  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
946 }
947 
948 } // namespace testing
949 } // namespace impeller
GLenum type
bool OpenPlaygroundHere(const AiksDlPlaygroundCallback &callback)
Point GetContentScale() const
Definition: playground.cc:189
int32_t x
Matrix ToMatrix(const SkMatrix &m)
AiksPlayground AiksTest
static void CanRenderLinearGradientWithDithering(AiksTest *aiks_test)
static void CanRenderRadialGradientWithDithering(AiksTest *aiks_test)
static void CanRenderConicalGradientWithDithering(AiksTest *aiks_test)
static void CanRenderSweepGradientWithDithering(AiksTest *aiks_test)
TEST_P(AiksTest, VerifyNonOptimizedGradient)
float Scalar
Definition: scalar.h:18
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
TPoint< Scalar > Point
Definition: point.h:327
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
flutter::DlScalar DlScalar
Definition: dl_dispatcher.h:23
const Scalar scale
SeparatedVector2 offset
constexpr uint32_t ToARGB() const
Convert to ARGB 32 bit color.
Definition: color.h:258
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
Vector4 vec[4]
Definition: matrix.h:41
constexpr Type GetDistance(const TPoint &p) const
Definition: point.h:200