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