Flutter Impeller
aiks_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 
6 
7 #include "impeller/aiks/canvas.h"
15 #include "third_party/imgui/imgui.h"
16 
17 ////////////////////////////////////////////////////////////////////////////////
18 // This is for tests of Canvas that are interested the results of rendering
19 // gradients.
20 ////////////////////////////////////////////////////////////////////////////////
21 
22 namespace impeller {
23 namespace testing {
24 
25 namespace {
26 void CanRenderLinearGradient(AiksTest* aiks_test, Entity::TileMode tile_mode) {
27  Canvas canvas;
28  canvas.Scale(aiks_test->GetContentScale());
29  Paint paint;
30  canvas.Translate({100.0f, 0, 0});
31 
32  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
33  Color{0.1294, 0.5882, 0.9529, 0.0}};
34  std::vector<Scalar> stops = {0.0, 1.0};
35 
36  paint.color_source = ColorSource::MakeLinearGradient(
37  {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
38 
39  paint.color = Color(1.0, 1.0, 1.0, 1.0);
40  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
41  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
42 }
43 } // namespace
44 
45 TEST_P(AiksTest, CanRenderLinearGradientClamp) {
46  CanRenderLinearGradient(this, Entity::TileMode::kClamp);
47 }
48 TEST_P(AiksTest, CanRenderLinearGradientRepeat) {
49  CanRenderLinearGradient(this, Entity::TileMode::kRepeat);
50 }
51 TEST_P(AiksTest, CanRenderLinearGradientMirror) {
52  CanRenderLinearGradient(this, Entity::TileMode::kMirror);
53 }
54 TEST_P(AiksTest, CanRenderLinearGradientDecal) {
55  CanRenderLinearGradient(this, Entity::TileMode::kDecal);
56 }
57 
58 TEST_P(AiksTest, CanRenderLinearGradientDecalWithColorFilter) {
59  Canvas canvas;
60  canvas.Scale(GetContentScale());
61  Paint paint;
62  canvas.Translate({100.0f, 0, 0});
63 
64  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
65  Color{0.1294, 0.5882, 0.9529, 0.0}};
66  std::vector<Scalar> stops = {0.0, 1.0};
67 
69  {0, 0}, {200, 200}, std::move(colors), std::move(stops),
71  // Overlay the gradient with 25% green. This should appear as the entire
72  // rectangle being drawn with 25% green, including the border area outside the
73  // decal gradient.
75  Color::Green().WithAlpha(0.25));
76 
77  paint.color = Color(1.0, 1.0, 1.0, 1.0);
78  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
79  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
80 }
81 
83  bool use_dithering) {
84  Canvas canvas;
85  Paint paint;
86  canvas.Translate({100.0, 100.0, 0});
87 
88  // 0xffcccccc --> 0xff333333, taken from
89  // https://github.com/flutter/flutter/issues/118073#issue-1521699748
90  std::vector<Color> colors = {Color{0.8, 0.8, 0.8, 1.0},
91  Color{0.2, 0.2, 0.2, 1.0}};
92  std::vector<Scalar> stops = {0.0, 1.0};
93 
95  {0, 0}, {800, 500}, std::move(colors), std::move(stops),
97  paint.dither = use_dithering;
98  canvas.DrawRect(Rect::MakeXYWH(0, 0, 800, 500), paint);
99  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
100 }
101 
102 TEST_P(AiksTest, CanRenderLinearGradientWithDitheringDisabled) {
104 }
105 
106 TEST_P(AiksTest, CanRenderLinearGradientWithDitheringEnabled) {
108 } // namespace
109 
111  bool use_dithering) {
112  Canvas canvas;
113  Paint paint;
114  canvas.Translate({100.0, 100.0, 0});
115 
116  // #FFF -> #000
117  std::vector<Color> colors = {Color{1.0, 1.0, 1.0, 1.0},
118  Color{0.0, 0.0, 0.0, 1.0}};
119  std::vector<Scalar> stops = {0.0, 1.0};
120 
122  {600, 600}, 600, std::move(colors), std::move(stops),
124  paint.dither = use_dithering;
125  canvas.DrawRect(Rect::MakeXYWH(0, 0, 1200, 1200), paint);
126  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
127 }
128 
129 TEST_P(AiksTest, CanRenderRadialGradientWithDitheringDisabled) {
131 }
132 
133 TEST_P(AiksTest, CanRenderRadialGradientWithDitheringEnabled) {
135 }
136 
138  bool use_dithering) {
139  Canvas canvas;
140  canvas.Scale(aiks_test->GetContentScale());
141  Paint paint;
142  canvas.Translate({100.0, 100.0, 0});
143 
144  // #FFF -> #000
145  std::vector<Color> colors = {Color{1.0, 1.0, 1.0, 1.0},
146  Color{0.0, 0.0, 0.0, 1.0}};
147  std::vector<Scalar> stops = {0.0, 1.0};
148 
150  {100, 100}, Degrees(45), Degrees(135), std::move(colors),
151  std::move(stops), Entity::TileMode::kMirror, {});
152  paint.dither = use_dithering;
153 
154  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
155  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
156 }
157 
158 TEST_P(AiksTest, CanRenderSweepGradientWithDitheringDisabled) {
160 }
161 
162 TEST_P(AiksTest, CanRenderSweepGradientWithDitheringEnabled) {
164 }
165 
167  bool use_dithering) {
168  Canvas canvas;
169  canvas.Scale(aiks_test->GetContentScale());
170  Paint paint;
171  canvas.Translate({100.0, 100.0, 0});
172 
173  // #FFF -> #000
174  std::vector<Color> colors = {Color{1.0, 1.0, 1.0, 1.0},
175  Color{0.0, 0.0, 0.0, 1.0}};
176  std::vector<Scalar> stops = {0.0, 1.0};
177 
179  {100, 100}, 100, std::move(colors), std::move(stops), {0, 1}, 0,
181  paint.dither = use_dithering;
182 
183  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
184  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
185 }
186 
187 TEST_P(AiksTest, CanRenderConicalGradientWithDitheringDisabled) {
189 }
190 
191 TEST_P(AiksTest, CanRenderConicalGradientWithDitheringEnabled) {
193 }
194 
195 namespace {
196 void CanRenderLinearGradientWithOverlappingStops(AiksTest* aiks_test,
197  Entity::TileMode tile_mode) {
198  Canvas canvas;
199  Paint paint;
200  canvas.Translate({100.0, 100.0, 0});
201 
202  std::vector<Color> colors = {
203  Color{0.9568, 0.2627, 0.2118, 1.0}, Color{0.9568, 0.2627, 0.2118, 1.0},
204  Color{0.1294, 0.5882, 0.9529, 1.0}, Color{0.1294, 0.5882, 0.9529, 1.0}};
205  std::vector<Scalar> stops = {0.0, 0.5, 0.5, 1.0};
206 
208  {0, 0}, {500, 500}, std::move(colors), std::move(stops), tile_mode, {});
209 
210  paint.color = Color(1.0, 1.0, 1.0, 1.0);
211  canvas.DrawRect(Rect::MakeXYWH(0, 0, 500, 500), paint);
212  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
213 }
214 } // namespace
215 
216 // Only clamp is necessary. All tile modes are the same output.
217 TEST_P(AiksTest, CanRenderLinearGradientWithOverlappingStopsClamp) {
218  CanRenderLinearGradientWithOverlappingStops(this, Entity::TileMode::kClamp);
219 }
220 
221 namespace {
222 void CanRenderLinearGradientManyColors(AiksTest* aiks_test,
223  Entity::TileMode tile_mode) {
224  Canvas canvas;
225  canvas.Scale(aiks_test->GetContentScale());
226  Paint paint;
227  canvas.Translate({100, 100, 0});
228 
229  std::vector<Color> colors = {
230  Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
231  Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
232  Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
233  Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
234  Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
235  Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
236  Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
237  std::vector<Scalar> stops = {
238  0.0,
239  (1.0 / 6.0) * 1,
240  (1.0 / 6.0) * 2,
241  (1.0 / 6.0) * 3,
242  (1.0 / 6.0) * 4,
243  (1.0 / 6.0) * 5,
244  1.0,
245  };
246 
248  {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
249 
250  paint.color = Color(1.0, 1.0, 1.0, 1.0);
251  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
252  canvas.Restore();
253  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
254 }
255 } // namespace
256 
257 TEST_P(AiksTest, CanRenderLinearGradientManyColorsClamp) {
258  CanRenderLinearGradientManyColors(this, Entity::TileMode::kClamp);
259 }
260 TEST_P(AiksTest, CanRenderLinearGradientManyColorsRepeat) {
261  CanRenderLinearGradientManyColors(this, Entity::TileMode::kRepeat);
262 }
263 TEST_P(AiksTest, CanRenderLinearGradientManyColorsMirror) {
264  CanRenderLinearGradientManyColors(this, Entity::TileMode::kMirror);
265 }
266 TEST_P(AiksTest, CanRenderLinearGradientManyColorsDecal) {
267  CanRenderLinearGradientManyColors(this, Entity::TileMode::kDecal);
268 }
269 
270 namespace {
271 void CanRenderLinearGradientWayManyColors(AiksTest* aiks_test,
272  Entity::TileMode tile_mode) {
273  Canvas canvas;
274  Paint paint;
275  canvas.Translate({100.0, 100.0, 0});
276  auto color = Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0};
277  std::vector<Color> colors;
278  std::vector<Scalar> stops;
279  auto current_stop = 0.0;
280  for (int i = 0; i < 2000; i++) {
281  colors.push_back(color);
282  stops.push_back(current_stop);
283  current_stop += 1 / 2000.0;
284  }
285  stops[2000 - 1] = 1.0;
286 
288  {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
289 
290  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
291  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
292 }
293 } // namespace
294 
295 // Only test clamp on purpose since they all look the same.
296 TEST_P(AiksTest, CanRenderLinearGradientWayManyColorsClamp) {
297  CanRenderLinearGradientWayManyColors(this, Entity::TileMode::kClamp);
298 }
299 
300 TEST_P(AiksTest, CanRenderLinearGradientManyColorsUnevenStops) {
301  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
302  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
303  const Entity::TileMode tile_modes[] = {
306 
307  static int selected_tile_mode = 0;
308  static Matrix matrix = {
309  1, 0, 0, 0, //
310  0, 1, 0, 0, //
311  0, 0, 1, 0, //
312  0, 0, 0, 1 //
313  };
314  if (AiksTest::ImGuiBegin("Controls", nullptr,
315  ImGuiWindowFlags_AlwaysAutoResize)) {
316  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
317  sizeof(tile_mode_names) / sizeof(char*));
318  std::string label = "##1";
319  for (int i = 0; i < 4; i++) {
320  ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
321  &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0);
322  label[2]++;
323  }
324  ImGui::End();
325  }
326 
327  Canvas canvas;
328  Paint paint;
329  canvas.Translate({100.0, 100.0, 0});
330  auto tile_mode = tile_modes[selected_tile_mode];
331 
332  std::vector<Color> colors = {
333  Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
334  Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
335  Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
336  Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
337  Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
338  Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
339  Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
340  std::vector<Scalar> stops = {
341  0.0, 2.0 / 62.0, 4.0 / 62.0, 8.0 / 62.0, 16.0 / 62.0, 32.0 / 62.0, 1.0,
342  };
343 
345  {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
346 
347  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
348  return canvas.EndRecordingAsPicture();
349  };
350  ASSERT_TRUE(OpenPlaygroundHere(callback));
351 }
352 
353 TEST_P(AiksTest, CanRenderLinearGradientMaskBlur) {
354  Canvas canvas;
355 
356  Paint paint = {
357  .color = Color::White(),
358  .color_source = ColorSource::MakeLinearGradient(
359  {200, 200}, {400, 400},
363  {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0},
365  .mask_blur_descriptor =
368  .sigma = Sigma(20),
369  },
370  };
371 
372  canvas.DrawCircle({300, 300}, 200, paint);
373  canvas.DrawRect(Rect::MakeLTRB(100, 300, 500, 600), paint);
374 
375  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
376 }
377 
378 TEST_P(AiksTest, CanRenderRadialGradient) {
379  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
380  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
381  const Entity::TileMode tile_modes[] = {
384 
385  static int selected_tile_mode = 0;
386  static Matrix matrix = {
387  1, 0, 0, 0, //
388  0, 1, 0, 0, //
389  0, 0, 1, 0, //
390  0, 0, 0, 1 //
391  };
392  if (AiksTest::ImGuiBegin("Controls", nullptr,
393  ImGuiWindowFlags_AlwaysAutoResize)) {
394  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
395  sizeof(tile_mode_names) / sizeof(char*));
396  std::string label = "##1";
397  for (int i = 0; i < 4; i++) {
398  ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
399  &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0);
400  label[2]++;
401  }
402  ImGui::End();
403  }
404 
405  Canvas canvas;
406  Paint paint;
407  canvas.Translate({100.0, 100.0, 0});
408  auto tile_mode = tile_modes[selected_tile_mode];
409 
410  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
411  Color{0.1294, 0.5882, 0.9529, 1.0}};
412  std::vector<Scalar> stops = {0.0, 1.0};
413 
415  {100, 100}, 100, std::move(colors), std::move(stops), tile_mode, {});
416 
417  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
418  return canvas.EndRecordingAsPicture();
419  };
420  ASSERT_TRUE(OpenPlaygroundHere(callback));
421 }
422 
423 TEST_P(AiksTest, CanRenderRadialGradientManyColors) {
424  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
425  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
426  const Entity::TileMode tile_modes[] = {
429 
430  static int selected_tile_mode = 0;
431  static Matrix matrix = {
432  1, 0, 0, 0, //
433  0, 1, 0, 0, //
434  0, 0, 1, 0, //
435  0, 0, 0, 1 //
436  };
437  if (AiksTest::ImGuiBegin("Controls", nullptr,
438  ImGuiWindowFlags_AlwaysAutoResize)) {
439  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
440  sizeof(tile_mode_names) / sizeof(char*));
441  std::string label = "##1";
442  for (int i = 0; i < 4; i++) {
443  ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
444  &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0);
445  label[2]++;
446  }
447  ImGui::End();
448  }
449 
450  Canvas canvas;
451  Paint paint;
452  canvas.Translate({100.0, 100.0, 0});
453  auto tile_mode = tile_modes[selected_tile_mode];
454 
455  std::vector<Color> colors = {
456  Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
457  Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
458  Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
459  Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
460  Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
461  Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
462  Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
463  std::vector<Scalar> stops = {
464  0.0,
465  (1.0 / 6.0) * 1,
466  (1.0 / 6.0) * 2,
467  (1.0 / 6.0) * 3,
468  (1.0 / 6.0) * 4,
469  (1.0 / 6.0) * 5,
470  1.0,
471  };
472 
474  {100, 100}, 100, std::move(colors), std::move(stops), tile_mode, {});
475 
476  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
477  return canvas.EndRecordingAsPicture();
478  };
479  ASSERT_TRUE(OpenPlaygroundHere(callback));
480 }
481 
482 namespace {
483 void CanRenderSweepGradient(AiksTest* aiks_test, Entity::TileMode tile_mode) {
484  Canvas canvas;
485  canvas.Scale(aiks_test->GetContentScale());
486  Paint paint;
487  canvas.Translate({100, 100, 0});
488 
489  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
490  Color{0.1294, 0.5882, 0.9529, 1.0}};
491  std::vector<Scalar> stops = {0.0, 1.0};
492 
494  {100, 100}, Degrees(45), Degrees(135), std::move(colors),
495  std::move(stops), tile_mode, {});
496 
497  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
498  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
499 }
500 } // namespace
501 
502 TEST_P(AiksTest, CanRenderSweepGradientClamp) {
503  CanRenderSweepGradient(this, Entity::TileMode::kClamp);
504 }
505 TEST_P(AiksTest, CanRenderSweepGradientRepeat) {
506  CanRenderSweepGradient(this, Entity::TileMode::kRepeat);
507 }
508 TEST_P(AiksTest, CanRenderSweepGradientMirror) {
509  CanRenderSweepGradient(this, Entity::TileMode::kMirror);
510 }
511 TEST_P(AiksTest, CanRenderSweepGradientDecal) {
512  CanRenderSweepGradient(this, Entity::TileMode::kDecal);
513 }
514 
515 namespace {
516 void CanRenderSweepGradientManyColors(AiksTest* aiks_test,
517  Entity::TileMode tile_mode) {
518  Canvas canvas;
519  Paint paint;
520  canvas.Translate({100.0, 100.0, 0});
521 
522  std::vector<Color> colors = {
523  Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
524  Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
525  Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
526  Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
527  Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
528  Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
529  Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
530  std::vector<Scalar> stops = {
531  0.0,
532  (1.0 / 6.0) * 1,
533  (1.0 / 6.0) * 2,
534  (1.0 / 6.0) * 3,
535  (1.0 / 6.0) * 4,
536  (1.0 / 6.0) * 5,
537  1.0,
538  };
539 
541  {100, 100}, Degrees(45), Degrees(135), std::move(colors),
542  std::move(stops), tile_mode, {});
543 
544  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
545  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
546 }
547 } // namespace
548 
549 TEST_P(AiksTest, CanRenderSweepGradientManyColorsClamp) {
550  CanRenderSweepGradientManyColors(this, Entity::TileMode::kClamp);
551 }
552 TEST_P(AiksTest, CanRenderSweepGradientManyColorsRepeat) {
553  CanRenderSweepGradientManyColors(this, Entity::TileMode::kRepeat);
554 }
555 TEST_P(AiksTest, CanRenderSweepGradientManyColorsMirror) {
556  CanRenderSweepGradientManyColors(this, Entity::TileMode::kMirror);
557 }
558 TEST_P(AiksTest, CanRenderSweepGradientManyColorsDecal) {
559  CanRenderSweepGradientManyColors(this, Entity::TileMode::kDecal);
560 }
561 
562 TEST_P(AiksTest, CanRenderConicalGradient) {
563  Scalar size = 256;
564  Canvas canvas;
565  Paint paint;
566  paint.color = Color::White();
567  canvas.DrawRect(Rect::MakeXYWH(0, 0, size * 3, size * 3), paint);
568  std::vector<Color> colors = {Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF),
569  Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF),
570  Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF),
571  Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF)};
572  std::vector<Scalar> stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0};
573  std::array<std::tuple<Point, float, Point, float>, 8> array{
574  std::make_tuple(Point{size / 2.f, size / 2.f}, 0.f,
575  Point{size / 2.f, size / 2.f}, size / 2.f),
576  std::make_tuple(Point{size / 2.f, size / 2.f}, size / 4.f,
577  Point{size / 2.f, size / 2.f}, size / 2.f),
578  std::make_tuple(Point{size / 4.f, size / 4.f}, 0.f,
579  Point{size / 2.f, size / 2.f}, size / 2.f),
580  std::make_tuple(Point{size / 4.f, size / 4.f}, size / 2.f,
581  Point{size / 2.f, size / 2.f}, 0),
582  std::make_tuple(Point{size / 4.f, size / 4.f}, size / 4.f,
583  Point{size / 2.f, size / 2.f}, size / 2.f),
584  std::make_tuple(Point{size / 4.f, size / 4.f}, size / 16.f,
585  Point{size / 2.f, size / 2.f}, size / 8.f),
586  std::make_tuple(Point{size / 4.f, size / 4.f}, size / 8.f,
587  Point{size / 2.f, size / 2.f}, size / 16.f),
588  std::make_tuple(Point{size / 8.f, size / 8.f}, size / 8.f,
589  Point{size / 2.f, size / 2.f}, size / 8.f),
590  };
591  for (int i = 0; i < 8; i++) {
592  canvas.Save();
593  canvas.Translate({(i % 3) * size, i / 3 * size, 0});
595  std::get<0>(array[i]), std::get<1>(array[i]), colors, stops,
596  std::get<2>(array[i]), std::get<3>(array[i]), Entity::TileMode::kClamp,
597  {});
598  canvas.DrawRect(Rect::MakeXYWH(0, 0, size, size), paint);
599  canvas.Restore();
600  }
601  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
602 }
603 
604 TEST_P(AiksTest, CanRenderGradientDecalWithBackground) {
605  std::vector<Color> colors = {Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF),
606  Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF),
607  Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF),
608  Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF)};
609  std::vector<Scalar> stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0};
610 
611  std::array<ColorSource, 3> color_sources = {
612  ColorSource::MakeLinearGradient({0, 0}, {100, 100}, colors, stops,
614  ColorSource::MakeRadialGradient({100, 100}, 100, colors, stops,
616  ColorSource::MakeSweepGradient({100, 100}, Degrees(45), Degrees(135),
617  colors, stops, Entity::TileMode::kDecal,
618  {}),
619  };
620 
621  Canvas canvas;
622  Paint paint;
623  paint.color = Color::White();
624  canvas.DrawRect(Rect::MakeLTRB(0, 0, 605, 205), paint);
625  for (int i = 0; i < 3; i++) {
626  canvas.Save();
627  canvas.Translate({i * 200.0f, 0, 0});
628  paint.color_source = color_sources[i];
629  canvas.DrawRect(Rect::MakeLTRB(0, 0, 200, 200), paint);
630  canvas.Restore();
631  }
632  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
633 }
634 
635 #define APPLY_COLOR_FILTER_GRADIENT_TEST(name) \
636  TEST_P(AiksTest, name##GradientApplyColorFilter) { \
637  auto contents = name##GradientContents(); \
638  contents.SetColors({Color::CornflowerBlue().WithAlpha(0.75)}); \
639  auto result = contents.ApplyColorFilter([](const Color& color) { \
640  return color.Blend(Color::LimeGreen().WithAlpha(0.75), \
641  BlendMode::kScreen); \
642  }); \
643  ASSERT_TRUE(result); \
644  \
645  std::vector<Color> expected = {Color(0.433247, 0.879523, 0.825324, 0.75)}; \
646  ASSERT_COLORS_NEAR(contents.GetColors(), expected); \
647  }
648 
653 
654 TEST_P(AiksTest, GradientStrokesRenderCorrectly) {
655  // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
656  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
657  static float scale = 3;
658  static bool add_circle_clip = true;
659  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
660  const Entity::TileMode tile_modes[] = {
663  static int selected_tile_mode = 0;
664  static float alpha = 1;
665 
666  if (AiksTest::ImGuiBegin("Controls", nullptr,
667  ImGuiWindowFlags_AlwaysAutoResize)) {
668  ImGui::SliderFloat("Scale", &scale, 0, 6);
669  ImGui::Checkbox("Circle clip", &add_circle_clip);
670  ImGui::SliderFloat("Alpha", &alpha, 0, 1);
671  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
672  sizeof(tile_mode_names) / sizeof(char*));
673  ImGui::End();
674  }
675 
676  Canvas canvas;
677  canvas.Scale(GetContentScale());
678  Paint paint;
679  paint.color = Color::White();
680  canvas.DrawPaint(paint);
681 
683  paint.color = Color(1.0, 1.0, 1.0, alpha);
684  paint.stroke_width = 10;
685  auto tile_mode = tile_modes[selected_tile_mode];
686 
687  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
688  Color{0.1294, 0.5882, 0.9529, 1.0}};
689  std::vector<Scalar> stops = {0.0, 1.0};
690 
692  {0, 0}, {50, 50}, std::move(colors), std::move(stops), tile_mode, {});
693 
694  Path path = PathBuilder{}
695  .MoveTo({20, 20})
696  .QuadraticCurveTo({60, 20}, {60, 60})
697  .Close()
698  .MoveTo({60, 20})
699  .QuadraticCurveTo({60, 60}, {20, 60})
700  .TakePath();
701 
702  canvas.Scale(Vector2(scale, scale));
703 
704  if (add_circle_clip) {
705  static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20,
706  Color::Red());
707  static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20,
708  Color::Red());
709  auto [handle_a, handle_b] =
710  DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b);
711 
712  auto screen_to_canvas = canvas.GetCurrentTransform().Invert();
713  Point point_a = screen_to_canvas * handle_a * GetContentScale();
714  Point point_b = screen_to_canvas * handle_b * GetContentScale();
715 
716  Point middle = (point_a + point_b) / 2;
717  auto radius = point_a.GetDistance(middle);
718  canvas.ClipPath(PathBuilder{}.AddCircle(middle, radius).TakePath());
719  }
720 
721  for (auto join : {Join::kBevel, Join::kRound, Join::kMiter}) {
722  paint.stroke_join = join;
723  for (auto cap : {Cap::kButt, Cap::kSquare, Cap::kRound}) {
724  paint.stroke_cap = cap;
725  canvas.DrawPath(path, paint);
726  canvas.Translate({80, 0});
727  }
728  canvas.Translate({-240, 60});
729  }
730 
731  return canvas.EndRecordingAsPicture();
732  };
733 
734  ASSERT_TRUE(OpenPlaygroundHere(callback));
735 }
736 
737 } // namespace testing
738 } // namespace impeller
impeller::Paint::stroke_cap
Cap stroke_cap
Definition: paint.h:60
impeller::AiksPlayground
Definition: aiks_playground.h:18
impeller::Entity::TileMode::kClamp
@ kClamp
impeller::Canvas::EndRecordingAsPicture
Picture EndRecordingAsPicture()
Definition: canvas.cc:756
impeller::Cap::kRound
@ kRound
impeller::Cap::kSquare
@ kSquare
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::AiksContext
Definition: aiks_context.h:20
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:264
impeller::testing::CanRenderSweepGradientWithDithering
static void CanRenderSweepGradientWithDithering(AiksTest *aiks_test, bool use_dithering)
Definition: aiks_gradient_unittests.cc:137
geometry_asserts.h
impeller::Paint::Style::kStroke
@ kStroke
aiks_unittests.h
impeller::ColorSource::MakeLinearGradient
static ColorSource MakeLinearGradient(Point start_point, Point end_point, std::vector< Color > colors, std::vector< Scalar > stops, Entity::TileMode tile_mode, Matrix effect_transform)
Definition: color_source.cc:45
impeller::Paint
Definition: paint.h:23
impeller::testing::AiksTest
AiksPlayground AiksTest
Definition: aiks_unittests.h:17
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::Color::MakeRGBA8
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:154
impeller::Color
Definition: color.h:124
impeller::Paint::color
Color color
Definition: paint.h:55
impeller::Entity::TileMode::kDecal
@ kDecal
impeller::testing::CanRenderConicalGradientWithDithering
static void CanRenderConicalGradientWithDithering(AiksTest *aiks_test, bool use_dithering)
Definition: aiks_gradient_unittests.cc:166
impeller::Canvas
Definition: canvas.h:58
impeller::PathBuilder
Definition: path_builder.h:14
impeller::Paint::MaskBlurDescriptor::style
FilterContents::BlurStyle style
Definition: paint.h:40
impeller::Vector2
Point Vector2
Definition: point.h:320
impeller::Paint::MaskBlurDescriptor
Definition: paint.h:39
impeller::ColorSource::MakeSweepGradient
static ColorSource MakeSweepGradient(Point center, Degrees start_angle, Degrees end_angle, std::vector< Color > colors, std::vector< Scalar > stops, Entity::TileMode tile_mode, Matrix effect_transform)
Definition: color_source.cc:138
impeller::FilterContents::BlurStyle::kNormal
@ kNormal
Blurred inside and outside.
sweep_gradient_contents.h
impeller::Cap::kButt
@ kButt
impeller::Entity::TileMode::kRepeat
@ kRepeat
impeller::Paint::color_source
ColorSource color_source
Definition: paint.h:56
impeller::Canvas::DrawRect
void DrawRect(const Rect &rect, const Paint &paint)
Definition: canvas.cc:441
impeller::Canvas::GetCurrentTransform
const Matrix & GetCurrentTransform() const
Definition: canvas.cc:247
impeller::Join::kMiter
@ kMiter
impeller::Matrix::vec
Vector4 vec[4]
Definition: matrix.h:41
impeller::Entity::TileMode::kMirror
@ kMirror
path_builder.h
impeller::Paint::color_filter
std::shared_ptr< ColorFilter > color_filter
Definition: paint.h:68
impeller::Point
TPoint< Scalar > Point
Definition: point.h:316
impeller::Canvas::Scale
void Scale(const Vector2 &scale)
Definition: canvas.cc:264
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:51
impeller::ColorSource::MakeConicalGradient
static ColorSource MakeConicalGradient(Point center, Scalar radius, std::vector< Color > colors, std::vector< Scalar > stops, Point focus_center, Scalar focus_radius, Entity::TileMode tile_mode, Matrix effect_transform)
Definition: color_source.cc:74
impeller::ColorFilter::MakeBlend
static std::shared_ptr< ColorFilter > MakeBlend(BlendMode blend_mode, Color color)
Definition: color_filter.cc:23
widgets.h
impeller::Canvas::Save
void Save()
Definition: canvas.cc:136
conical_gradient_contents.h
impeller::Paint::style
Style style
Definition: paint.h:63
impeller::Canvas::DrawCircle
void DrawCircle(const Point &center, Scalar radius, const Paint &paint)
Definition: canvas.cc:515
impeller::testing::CanRenderRadialGradientWithDithering
static void CanRenderRadialGradientWithDithering(AiksTest *aiks_test, bool use_dithering)
Definition: aiks_gradient_unittests.cc:110
impeller::Color::White
static constexpr Color White()
Definition: color.h:256
impeller::Sigma
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
impeller::Canvas::Restore
bool Restore()
Definition: canvas.cc:208
impeller::testing::CanRenderLinearGradientWithDithering
static void CanRenderLinearGradientWithDithering(AiksTest *aiks_test, bool use_dithering)
Definition: aiks_gradient_unittests.cc:82
canvas.h
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:266
impeller::Canvas::DrawPath
void DrawPath(const Path &path, const Paint &paint)
Definition: canvas.cc:292
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:22
impeller::Canvas::DrawPaint
void DrawPaint(const Paint &paint)
Definition: canvas.cc:302
impeller::Join::kRound
@ kRound
impeller::Entity::TileMode
TileMode
Definition: entity.h:42
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:97
impeller::ColorSource::MakeRadialGradient
static ColorSource MakeRadialGradient(Point center, Scalar radius, std::vector< Color > colors, std::vector< Scalar > stops, Entity::TileMode tile_mode, Matrix effect_transform)
Definition: color_source.cc:108
impeller::Close
void Close(PathBuilder *builder)
Definition: tessellator.cc:36
impeller::AiksPlayground::OpenPlaygroundHere
bool OpenPlaygroundHere(Picture picture)
Definition: aiks_playground.cc:30
impeller::Join::kBevel
@ kBevel
impeller::Playground::GetContentScale
Point GetContentScale() const
Definition: playground.cc:186
impeller::PlaygroundPoint
Definition: widgets.h:17
impeller::testing::TEST_P
TEST_P(AiksTest, CanRenderMaskBlurHugeSigma)
Definition: aiks_blur_unittests.cc:23
impeller::TPoint< Scalar >
impeller::PathBuilder::MoveTo
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:33
scale
const Scalar scale
Definition: stroke_path_geometry.cc:297
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:59
impeller::Degrees
Definition: scalar.h:46
impeller::PathBuilder::AddCircle
PathBuilder & AddCircle(const Point &center, Scalar radius)
Definition: path_builder.cc:130
radial_gradient_contents.h
impeller::TPoint::GetDistance
constexpr Type GetDistance(const TPoint &p) const
Definition: point.h:200
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::testing::APPLY_COLOR_FILTER_GRADIENT_TEST
APPLY_COLOR_FILTER_GRADIENT_TEST(Linear)
impeller
Definition: aiks_blur_unittests.cc:20
impeller::Paint::dither
bool dither
Definition: paint.h:57
impeller::Paint::stroke_width
Scalar stroke_width
Definition: paint.h:59
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::DrawPlaygroundLine
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
linear_gradient_contents.h
impeller::Paint::stroke_join
Join stroke_join
Definition: paint.h:61
impeller::Canvas::Translate
void Translate(const Vector3 &offset)
Definition: canvas.cc:260
impeller::Canvas::ClipPath
void ClipPath(const Path &path, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:539