Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
5#include "flutter/impeller/aiks/aiks_unittests.h"
6
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
22namespace impeller {
23namespace testing {
24
25namespace {
26void CanRenderLinearGradient(AiksTest* aiks_test, Entity::TileMode tile_mode) {
27 Canvas canvas;
28 canvas.Scale(aiks_test->GetContentScale());
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
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
45TEST_P(AiksTest, CanRenderLinearGradientClamp) {
46 CanRenderLinearGradient(this, Entity::TileMode::kClamp);
47}
48TEST_P(AiksTest, CanRenderLinearGradientRepeat) {
49 CanRenderLinearGradient(this, Entity::TileMode::kRepeat);
50}
51TEST_P(AiksTest, CanRenderLinearGradientMirror) {
52 CanRenderLinearGradient(this, Entity::TileMode::kMirror);
53}
54TEST_P(AiksTest, CanRenderLinearGradientDecal) {
55 CanRenderLinearGradient(this, Entity::TileMode::kDecal);
56}
57
58TEST_P(AiksTest, CanRenderLinearGradientDecalWithColorFilter) {
59 Canvas canvas;
60 canvas.Scale(GetContentScale());
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;
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
102TEST_P(AiksTest, CanRenderLinearGradientWithDitheringDisabled) {
104}
105
106TEST_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
129TEST_P(AiksTest, CanRenderRadialGradientWithDitheringDisabled) {
131}
132
133TEST_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
158TEST_P(AiksTest, CanRenderSweepGradientWithDitheringDisabled) {
160}
161
162TEST_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
187TEST_P(AiksTest, CanRenderConicalGradientWithDitheringDisabled) {
189}
190
191TEST_P(AiksTest, CanRenderConicalGradientWithDitheringEnabled) {
193}
194
195namespace {
196void 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.
217TEST_P(AiksTest, CanRenderLinearGradientWithOverlappingStopsClamp) {
218 CanRenderLinearGradientWithOverlappingStops(this, Entity::TileMode::kClamp);
219}
220
221namespace {
222void 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
257TEST_P(AiksTest, CanRenderLinearGradientManyColorsClamp) {
258 CanRenderLinearGradientManyColors(this, Entity::TileMode::kClamp);
259}
260TEST_P(AiksTest, CanRenderLinearGradientManyColorsRepeat) {
261 CanRenderLinearGradientManyColors(this, Entity::TileMode::kRepeat);
262}
263TEST_P(AiksTest, CanRenderLinearGradientManyColorsMirror) {
264 CanRenderLinearGradientManyColors(this, Entity::TileMode::kMirror);
265}
266TEST_P(AiksTest, CanRenderLinearGradientManyColorsDecal) {
267 CanRenderLinearGradientManyColors(this, Entity::TileMode::kDecal);
268}
269
270namespace {
271void 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.
296TEST_P(AiksTest, CanRenderLinearGradientWayManyColorsClamp) {
297 CanRenderLinearGradientWayManyColors(this, Entity::TileMode::kClamp);
298}
299
300TEST_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
353TEST_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
378TEST_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
423TEST_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
482namespace {
483void 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
502TEST_P(AiksTest, CanRenderSweepGradientClamp) {
503 CanRenderSweepGradient(this, Entity::TileMode::kClamp);
504}
505TEST_P(AiksTest, CanRenderSweepGradientRepeat) {
506 CanRenderSweepGradient(this, Entity::TileMode::kRepeat);
507}
508TEST_P(AiksTest, CanRenderSweepGradientMirror) {
509 CanRenderSweepGradient(this, Entity::TileMode::kMirror);
510}
511TEST_P(AiksTest, CanRenderSweepGradientDecal) {
512 CanRenderSweepGradient(this, Entity::TileMode::kDecal);
513}
514
515namespace {
516void 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
549TEST_P(AiksTest, CanRenderSweepGradientManyColorsClamp) {
550 CanRenderSweepGradientManyColors(this, Entity::TileMode::kClamp);
551}
552TEST_P(AiksTest, CanRenderSweepGradientManyColorsRepeat) {
553 CanRenderSweepGradientManyColors(this, Entity::TileMode::kRepeat);
554}
555TEST_P(AiksTest, CanRenderSweepGradientManyColorsMirror) {
556 CanRenderSweepGradientManyColors(this, Entity::TileMode::kMirror);
557}
558TEST_P(AiksTest, CanRenderSweepGradientManyColorsDecal) {
559 CanRenderSweepGradientManyColors(this, Entity::TileMode::kDecal);
560}
561
562TEST_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
604TEST_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
654TEST_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
SkColor4f color
#define APPLY_COLOR_FILTER_GRADIENT_TEST(name)
bool OpenPlaygroundHere(Picture picture)
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
const Matrix & GetCurrentTransform() const
Definition canvas.cc:296
void ClipPath(const Path &path, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition canvas.cc:587
virtual bool Restore()
Definition canvas.cc:257
void DrawPaint(const Paint &paint)
Definition canvas.cc:350
void Scale(const Vector2 &scale)
Definition canvas.cc:313
Picture EndRecordingAsPicture()
Definition canvas.cc:802
void DrawPath(const Path &path, const Paint &paint)
Definition canvas.cc:341
virtual void Save(uint32_t total_content_depth=kMaxDepth)
Definition canvas.cc:184
void DrawRect(const Rect &rect, const Paint &paint)
Definition canvas.cc:493
void Translate(const Vector3 &offset)
Definition canvas.cc:309
void DrawCircle(const Point &center, Scalar radius, const Paint &paint)
Definition canvas.cc:564
static std::shared_ptr< ColorFilter > MakeBlend(BlendMode blend_mode, Color color)
static ColorSource MakeLinearGradient(Point start_point, Point end_point, std::vector< Color > colors, std::vector< Scalar > stops, Entity::TileMode tile_mode, Matrix effect_transform)
static ColorSource MakeRadialGradient(Point center, Scalar radius, std::vector< Color > colors, std::vector< Scalar > stops, Entity::TileMode tile_mode, Matrix effect_transform)
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)
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)
@ kNormal
Blurred inside and outside.
Path TakePath(FillType fill=FillType::kNonZero)
PathBuilder & MoveTo(Point point, bool relative=false)
PathBuilder & AddCircle(const Point &center, Scalar radius)
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition path.h:51
Point GetContentScale() const
const Paint & paint
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
PODArray< SkColor > colors
Definition SkRecords.h:276
AiksPlayground AiksTest
static void CanRenderLinearGradientWithDithering(AiksTest *aiks_test, bool use_dithering)
static void CanRenderRadialGradientWithDithering(AiksTest *aiks_test, bool use_dithering)
static void CanRenderSweepGradientWithDithering(AiksTest *aiks_test, bool use_dithering)
static void CanRenderConicalGradientWithDithering(AiksTest *aiks_test, bool use_dithering)
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer)
Point Vector2
Definition point.h:320
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:316
void Close(PathBuilder *builder)
const Scalar scale
static constexpr Color White()
Definition color.h:256
static constexpr Color Red()
Definition color.h:264
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition color.h:154
static constexpr Color Green()
Definition color.h:266
A 4x4 matrix using column-major storage.
Definition matrix.h:37
Matrix Invert() const
Definition matrix.cc:97
FilterContents::BlurStyle style
Definition paint.h:40
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition sigma.h:32
constexpr Type GetDistance(const TPoint &p) const
Definition point.h:200
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129