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