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