Flutter Engine
The Flutter Engine
aiks_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
7#include <array>
8#include <cmath>
9#include <cstdlib>
10#include <memory>
11#include <tuple>
12#include <utility>
13#include <vector>
14
15#include "flutter/testing/testing.h"
16#include "gtest/gtest.h"
19#include "impeller/aiks/image.h"
39#include "third_party/imgui/imgui.h"
41#include "txt/platform.h"
42
43namespace impeller {
44namespace testing {
45
47
48TEST_P(AiksTest, CanvasCTMCanBeUpdated) {
49 Canvas canvas;
51 ASSERT_MATRIX_NEAR(canvas.GetCurrentTransform(), identity);
52 canvas.Translate(Size{100, 100});
53 ASSERT_MATRIX_NEAR(canvas.GetCurrentTransform(),
54 Matrix::MakeTranslation({100.0, 100.0, 0.0}));
55}
56
57TEST_P(AiksTest, CanvasCanPushPopCTM) {
58 Canvas canvas;
59 ASSERT_EQ(canvas.GetSaveCount(), 1u);
60 ASSERT_EQ(canvas.Restore(), false);
61
62 canvas.Translate(Size{100, 100});
63 canvas.Save();
64 ASSERT_EQ(canvas.GetSaveCount(), 2u);
65 ASSERT_MATRIX_NEAR(canvas.GetCurrentTransform(),
66 Matrix::MakeTranslation({100.0, 100.0, 0.0}));
67 ASSERT_TRUE(canvas.Restore());
68 ASSERT_EQ(canvas.GetSaveCount(), 1u);
69 ASSERT_MATRIX_NEAR(canvas.GetCurrentTransform(),
70 Matrix::MakeTranslation({100.0, 100.0, 0.0}));
71}
72
73TEST_P(AiksTest, CanRenderColoredRect) {
74 Canvas canvas;
76 paint.color = Color::Blue();
77 canvas.DrawPath(PathBuilder{}
78 .AddRect(Rect::MakeXYWH(100.0, 100.0, 100.0, 100.0))
79 .TakePath(),
80 paint);
81 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
82}
83
84TEST_P(AiksTest, CanRenderImage) {
85 Canvas canvas;
87 auto image = std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
88 paint.color = Color::Red();
89 canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint);
90 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
91}
92
93TEST_P(AiksTest, CanRenderInvertedImageWithColorFilter) {
94 Canvas canvas;
96 auto image = std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
97 paint.color = Color::Red();
98 paint.color_filter =
100 paint.invert_colors = true;
101
102 canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint);
103 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
104}
105
106TEST_P(AiksTest, CanRenderColorFilterWithInvertColors) {
107 Canvas canvas;
108 Paint paint;
109 paint.color = Color::Red();
110 paint.color_filter =
112 paint.invert_colors = true;
113
114 canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint);
115 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
116}
117
118TEST_P(AiksTest, CanRenderColorFilterWithInvertColorsDrawPaint) {
119 Canvas canvas;
120 Paint paint;
121 paint.color = Color::Red();
122 paint.color_filter =
124 paint.invert_colors = true;
125
126 canvas.DrawPaint(paint);
127 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
128}
129
130namespace {
131bool GenerateMipmap(const std::shared_ptr<Context>& context,
132 std::shared_ptr<Texture> texture,
133 std::string label) {
134 auto buffer = context->CreateCommandBuffer();
135 if (!buffer) {
136 return false;
137 }
138 auto pass = buffer->CreateBlitPass();
139 if (!pass) {
140 return false;
141 }
142 pass->GenerateMipmap(std::move(texture), std::move(label));
143
144 pass->EncodeCommands(context->GetResourceAllocator());
145 return context->GetCommandQueue()->Submit({buffer}).ok();
146}
147
148void CanRenderTiledTexture(AiksTest* aiks_test,
149 Entity::TileMode tile_mode,
150 Matrix local_matrix = {}) {
151 auto context = aiks_test->GetContext();
152 ASSERT_TRUE(context);
153 auto texture = aiks_test->CreateTextureForFixture("table_mountain_nx.png",
154 /*enable_mipmapping=*/true);
155 GenerateMipmap(context, texture, "table_mountain_nx");
156 Canvas canvas;
157 canvas.Scale(aiks_test->GetContentScale());
158 canvas.Translate({100.0f, 100.0f, 0});
159 Paint paint;
160 paint.color_source =
161 ColorSource::MakeImage(texture, tile_mode, tile_mode, {}, local_matrix);
162 paint.color = Color(1, 1, 1, 1);
163 canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
164
165 // Should not change the image.
166 constexpr auto stroke_width = 64;
168 paint.stroke_width = stroke_width;
169 if (tile_mode == Entity::TileMode::kDecal) {
170 canvas.DrawRect(Rect::MakeXYWH(stroke_width, stroke_width, 600, 600),
171 paint);
172 } else {
173 canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
174 }
175
176 {
177 // Should not change the image.
178 PathBuilder path_builder;
179 path_builder.AddCircle({150, 150}, 150);
180 path_builder.AddRoundedRect(Rect::MakeLTRB(300, 300, 600, 600), 10);
182 canvas.DrawPath(path_builder.TakePath(), paint);
183 }
184
185 {
186 // Should not change the image. Tests the Convex short-cut code.
187 PathBuilder path_builder;
188 path_builder.AddCircle({150, 450}, 150);
189 path_builder.SetConvexity(Convexity::kConvex);
191 canvas.DrawPath(path_builder.TakePath(), paint);
192 }
193
194 ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
195}
196} // namespace
197
198TEST_P(AiksTest, CanRenderTiledTextureClamp) {
199 CanRenderTiledTexture(this, Entity::TileMode::kClamp);
200}
201
202TEST_P(AiksTest, CanRenderTiledTextureRepeat) {
203 CanRenderTiledTexture(this, Entity::TileMode::kRepeat);
204}
205
206TEST_P(AiksTest, CanRenderTiledTextureMirror) {
207 CanRenderTiledTexture(this, Entity::TileMode::kMirror);
208}
209
210TEST_P(AiksTest, CanRenderTiledTextureDecal) {
211 CanRenderTiledTexture(this, Entity::TileMode::kDecal);
212}
213
214TEST_P(AiksTest, CanRenderTiledTextureClampWithTranslate) {
215 CanRenderTiledTexture(this, Entity::TileMode::kClamp,
216 Matrix::MakeTranslation({172.f, 172.f, 0.f}));
217}
218
219TEST_P(AiksTest, CanRenderImageRect) {
220 Canvas canvas;
221 Paint paint;
222 auto image = std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
223 Size image_half_size = Size(image->GetSize()) * 0.5;
224
225 // Render the bottom right quarter of the source image in a stretched rect.
226 auto source_rect = Rect::MakeSize(image_half_size);
227 source_rect = source_rect.Shift(Point(image_half_size));
228
229 canvas.DrawImageRect(image, source_rect, Rect::MakeXYWH(100, 100, 600, 600),
230 paint);
231 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
232}
233
234TEST_P(AiksTest, CanRenderSimpleClips) {
235 Canvas canvas;
236 canvas.Scale(GetContentScale());
237 Paint paint;
238
239 paint.color = Color::White();
240 canvas.DrawPaint(paint);
241
242 auto draw = [&canvas](const Paint& paint, Scalar x, Scalar y) {
243 canvas.Save();
244 canvas.Translate({x, y});
245 {
246 canvas.Save();
247 canvas.ClipRect(Rect::MakeLTRB(50, 50, 150, 150));
248 canvas.DrawPaint(paint);
249 canvas.Restore();
250 }
251 {
252 canvas.Save();
253 canvas.ClipOval(Rect::MakeLTRB(200, 50, 300, 150));
254 canvas.DrawPaint(paint);
255 canvas.Restore();
256 }
257 {
258 canvas.Save();
259 canvas.ClipRRect(Rect::MakeLTRB(50, 200, 150, 300), {20, 20});
260 canvas.DrawPaint(paint);
261 canvas.Restore();
262 }
263 {
264 canvas.Save();
265 canvas.ClipRRect(Rect::MakeLTRB(200, 230, 300, 270), {20, 20});
266 canvas.DrawPaint(paint);
267 canvas.Restore();
268 }
269 {
270 canvas.Save();
271 canvas.ClipRRect(Rect::MakeLTRB(230, 200, 270, 300), {20, 20});
272 canvas.DrawPaint(paint);
273 canvas.Restore();
274 }
275 canvas.Restore();
276 };
277
278 paint.color = Color::Blue();
279 draw(paint, 0, 0);
280
281 std::vector<Color> gradient_colors = {
282 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
283 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
284 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
285 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
286 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
287 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
288 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
289 std::vector<Scalar> stops = {
290 0.0,
291 (1.0 / 6.0) * 1,
292 (1.0 / 6.0) * 2,
293 (1.0 / 6.0) * 3,
294 (1.0 / 6.0) * 4,
295 (1.0 / 6.0) * 5,
296 1.0,
297 };
298 auto texture = CreateTextureForFixture("airplane.jpg",
299 /*enable_mipmapping=*/true);
300
302 {500, 600}, 75, std::move(gradient_colors), std::move(stops),
304 draw(paint, 0, 300);
305
306 paint.color_source = ColorSource::MakeImage(
309 draw(paint, 300, 0);
310
311 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
312}
313
314TEST_P(AiksTest, CanSaveLayerStandalone) {
315 Canvas canvas;
316
317 Paint red;
318 red.color = Color::Red();
319
320 Paint alpha;
321 alpha.color = Color::Red().WithAlpha(0.5);
322
323 canvas.SaveLayer(alpha);
324
325 canvas.DrawCircle({125, 125}, 125, red);
326
327 canvas.Restore();
328
329 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
330}
331
332TEST_P(AiksTest, CanRenderDifferentShapesWithSameColorSource) {
333 Canvas canvas;
334 Paint paint;
335
336 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
337 Color{0.1294, 0.5882, 0.9529, 1.0}};
338 std::vector<Scalar> stops = {
339 0.0,
340 1.0,
341 };
342
344 {0, 0}, {100, 100}, std::move(colors), std::move(stops),
346
347 canvas.Save();
348 canvas.Translate({100, 100, 0});
349 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), paint);
350 canvas.Restore();
351
352 canvas.Save();
353 canvas.Translate({100, 400, 0});
354 canvas.DrawCircle({100, 100}, 100, paint);
355 canvas.Restore();
356 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
357}
358
359TEST_P(AiksTest, CanPictureConvertToImage) {
360 Canvas recorder_canvas;
361 Paint paint;
362 paint.color = Color{0.9568, 0.2627, 0.2118, 1.0};
363 recorder_canvas.DrawRect(Rect::MakeXYWH(100.0, 100.0, 600, 600), paint);
364 paint.color = Color{0.1294, 0.5882, 0.9529, 1.0};
365 recorder_canvas.DrawRect(Rect::MakeXYWH(200.0, 200.0, 600, 600), paint);
366
367 Canvas canvas;
368 AiksContext renderer(GetContext(), nullptr);
370 canvas.DrawPaint(paint);
371 Picture picture = recorder_canvas.EndRecordingAsPicture();
372 auto image = picture.ToImage(renderer, ISize{1000, 1000});
373 if (image) {
374 canvas.DrawImage(image, Point(), Paint());
375 paint.color = Color{0.1, 0.1, 0.1, 0.2};
376 canvas.DrawRect(Rect::MakeSize(ISize{1000, 1000}), paint);
377 }
378
379 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
380}
381
382// Regression test for https://github.com/flutter/flutter/issues/142358 .
383// Without a change to force render pass construction the image is left in an
384// undefined layout and triggers a validation error.
385TEST_P(AiksTest, CanEmptyPictureConvertToImage) {
386 Canvas recorder_canvas;
387
388 Canvas canvas;
389 AiksContext renderer(GetContext(), nullptr);
390 Paint paint;
392 canvas.DrawPaint(paint);
393 Picture picture = recorder_canvas.EndRecordingAsPicture();
394 auto image = picture.ToImage(renderer, ISize{1000, 1000});
395 if (image) {
396 canvas.DrawImage(image, Point(), Paint());
397 paint.color = Color{0.1, 0.1, 0.1, 0.2};
398 canvas.DrawRect(Rect::MakeSize(ISize{1000, 1000}), paint);
399 }
400
401 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
402}
403
404TEST_P(AiksTest, CoordinateConversionsAreCorrect) {
405 Canvas canvas;
406
407 // Render a texture directly.
408 {
409 Paint paint;
410 auto image =
411 std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
412 paint.color = Color::Red();
413
414 canvas.Save();
415 canvas.Translate({100, 200, 0});
416 canvas.Scale(Vector2{0.5, 0.5});
417 canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint);
418 canvas.Restore();
419 }
420
421 // Render an offscreen rendered texture.
422 {
423 Paint red;
424 red.color = Color::Red();
425 Paint green;
426 green.color = Color::Green();
427 Paint blue;
428 blue.color = Color::Blue();
429
430 Paint alpha;
431 alpha.color = Color::Red().WithAlpha(0.5);
432
433 canvas.SaveLayer(alpha);
434
435 canvas.DrawRect(Rect::MakeXYWH(000, 000, 100, 100), red);
436 canvas.DrawRect(Rect::MakeXYWH(020, 020, 100, 100), green);
437 canvas.DrawRect(Rect::MakeXYWH(040, 040, 100, 100), blue);
438
439 canvas.Restore();
440 }
441
442 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
443}
444
445TEST_P(AiksTest, CanPerformFullScreenMSAA) {
446 Canvas canvas;
447
448 Paint red;
449 red.color = Color::Red();
450
451 canvas.DrawCircle({250, 250}, 125, red);
452
453 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
454}
455
456TEST_P(AiksTest, CanPerformSkew) {
457 Canvas canvas;
458
459 Paint red;
460 red.color = Color::Red();
461
462 canvas.Skew(2, 5);
463 canvas.DrawRect(Rect::MakeXYWH(0, 0, 100, 100), red);
464
465 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
466}
467
468TEST_P(AiksTest, CanPerformSaveLayerWithBounds) {
469 Canvas canvas;
470
471 Paint red;
472 red.color = Color::Red();
473
474 Paint green;
475 green.color = Color::Green();
476
477 Paint blue;
478 blue.color = Color::Blue();
479
480 Paint save;
481 save.color = Color::Black();
482
483 canvas.SaveLayer(save, Rect::MakeXYWH(0, 0, 50, 50));
484
485 canvas.DrawRect(Rect::MakeXYWH(0, 0, 100, 100), red);
486 canvas.DrawRect(Rect::MakeXYWH(10, 10, 100, 100), green);
487 canvas.DrawRect(Rect::MakeXYWH(20, 20, 100, 100), blue);
488
489 canvas.Restore();
490
491 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
492}
493
495 CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) {
496 Canvas canvas;
497
498 Paint red;
499 red.color = Color::Red();
500
501 Paint green;
502 green.color = Color::Green();
503
504 Paint blue;
505 blue.color = Color::Blue();
506
507 Paint save;
508 save.color = Color::Black().WithAlpha(0.5);
509
510 canvas.SaveLayer(save, Rect::MakeXYWH(0, 0, 100000, 100000));
511
512 canvas.DrawRect(Rect::MakeXYWH(0, 0, 100, 100), red);
513 canvas.DrawRect(Rect::MakeXYWH(10, 10, 100, 100), green);
514 canvas.DrawRect(Rect::MakeXYWH(20, 20, 100, 100), blue);
515
516 canvas.Restore();
517
518 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
519}
520
521TEST_P(AiksTest, CanRenderRoundedRectWithNonUniformRadii) {
522 Canvas canvas;
523
524 Paint paint;
525 paint.color = Color::Red();
526
528 radii.top_left = {50, 25};
529 radii.top_right = {25, 50};
530 radii.bottom_right = {50, 25};
531 radii.bottom_left = {25, 50};
532
533 auto path = PathBuilder{}
534 .AddRoundedRect(Rect::MakeXYWH(100, 100, 500, 500), radii)
535 .TakePath();
536
537 canvas.DrawPath(path, paint);
538
539 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
540}
541
543 bool stroke = false;
546 Point position = Vector2(100, 200);
547 std::optional<Paint::MaskBlurDescriptor> mask_blur_descriptor;
548};
549
550bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
551 Canvas& canvas,
552 const std::string& text,
553 const std::string_view& font_fixture,
555 // Draw the baseline.
556 canvas.DrawRect(
557 Rect::MakeXYWH(options.position.x - 50, options.position.y, 900, 10),
558 Paint{.color = Color::Aqua().WithAlpha(0.25)});
559
560 // Mark the point at which the text is drawn.
561 canvas.DrawCircle(options.position, 5.0,
562 Paint{.color = Color::Red().WithAlpha(0.25)});
563
564 // Construct the text blob.
565 auto c_font_fixture = std::string(font_fixture);
566 auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
567 if (!mapping) {
568 return false;
569 }
571 SkFont sk_font(font_mgr->makeFromData(mapping), options.font_size);
572 auto blob = SkTextBlob::MakeFromString(text.c_str(), sk_font);
573 if (!blob) {
574 return false;
575 }
576
577 // Create the Impeller text frame and draw it at the designated baseline.
579
580 Paint text_paint;
581 text_paint.color = options.color;
582 text_paint.mask_blur_descriptor = options.mask_blur_descriptor;
583 text_paint.stroke_width = 1;
584 text_paint.style =
586 canvas.DrawTextFrame(frame, options.position, text_paint);
587 return true;
588}
589
590bool RenderTextInCanvasSTB(const std::shared_ptr<Context>& context,
591 Canvas& canvas,
592 const std::string& text,
593 const std::string& font_fixture,
595 // Draw the baseline.
596 canvas.DrawRect(
597 Rect::MakeXYWH(options.position.x - 50, options.position.y, 900, 10),
598 Paint{.color = Color::Aqua().WithAlpha(0.25)});
599
600 // Mark the point at which the text is drawn.
601 canvas.DrawCircle(options.position, 5.0,
602 Paint{.color = Color::Red().WithAlpha(0.25)});
603
604 // Construct the text blob.
605 auto mapping = flutter::testing::OpenFixtureAsMapping(font_fixture.c_str());
606 if (!mapping) {
607 return false;
608 }
609 auto typeface_stb = std::make_shared<TypefaceSTB>(std::move(mapping));
610
611 auto frame = MakeTextFrameSTB(
612 typeface_stb, Font::Metrics{.point_size = options.font_size}, text);
613
614 Paint text_paint;
615 text_paint.color = options.color;
616 canvas.DrawTextFrame(frame, options.position, text_paint);
617 return true;
618}
619
620TEST_P(AiksTest, CanRenderTextFrame) {
621 Canvas canvas;
622 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
623 ASSERT_TRUE(RenderTextInCanvasSkia(
624 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
625 "Roboto-Regular.ttf"));
626 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
627}
628
629TEST_P(AiksTest, CanRenderStrokedTextFrame) {
630 Canvas canvas;
631 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
632 ASSERT_TRUE(RenderTextInCanvasSkia(
633 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
634 "Roboto-Regular.ttf",
635 {
636 .stroke = true,
637 }));
638 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
639}
640
641TEST_P(AiksTest, CanRenderTextFrameWithHalfScaling) {
642 Canvas canvas;
643 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
644 canvas.Scale({0.5, 0.5, 1});
645 ASSERT_TRUE(RenderTextInCanvasSkia(
646 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
647 "Roboto-Regular.ttf"));
648 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
649}
650
651TEST_P(AiksTest, CanRenderTextFrameWithFractionScaling) {
652 Canvas canvas;
653 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
654 canvas.Scale({2.625, 2.625, 1});
655 ASSERT_TRUE(RenderTextInCanvasSkia(
656 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
657 "Roboto-Regular.ttf"));
658 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
659}
660
661TEST_P(AiksTest, CanRenderTextFrameSTB) {
662 Canvas canvas;
663 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
664 ASSERT_TRUE(RenderTextInCanvasSTB(
665 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
666 "Roboto-Regular.ttf"));
667
668 SetTypographerContext(TypographerContextSTB::Make());
669 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
670}
671
672TEST_P(AiksTest, TextFrameSubpixelAlignment) {
673 // "Random" numbers between 0 and 1. Hardcoded to avoid flakiness in goldens.
674 std::array<Scalar, 20> phase_offsets = {
675 7.82637e-06, 0.131538, 0.755605, 0.45865, 0.532767,
676 0.218959, 0.0470446, 0.678865, 0.679296, 0.934693,
677 0.383502, 0.519416, 0.830965, 0.0345721, 0.0534616,
678 0.5297, 0.671149, 0.00769819, 0.383416, 0.0668422};
679 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
680 static float font_size = 20;
681 static float phase_variation = 0.2;
682 static float speed = 0.5;
683 static float magnitude = 100;
684 if (AiksTest::ImGuiBegin("Controls", nullptr,
685 ImGuiWindowFlags_AlwaysAutoResize)) {
686 ImGui::SliderFloat("Font size", &font_size, 5, 50);
687 ImGui::SliderFloat("Phase variation", &phase_variation, 0, 1);
688 ImGui::SliderFloat("Oscillation speed", &speed, 0, 2);
689 ImGui::SliderFloat("Oscillation magnitude", &magnitude, 0, 300);
690 ImGui::End();
691 }
692
693 Canvas canvas;
694 canvas.Scale(GetContentScale());
695
696 for (size_t i = 0; i < phase_offsets.size(); i++) {
697 auto position =
698 Point(200 + magnitude *
699 std::sin((-phase_offsets[i] * k2Pi * phase_variation +
700 GetSecondsElapsed() * speed)), //
701 200 + i * font_size * 1.1 //
702 );
704 GetContext(), canvas,
705 "the quick brown fox jumped over "
706 "the lazy dog!.?",
707 "Roboto-Regular.ttf",
708 {.font_size = font_size, .position = position})) {
709 return std::nullopt;
710 }
711 }
712 return canvas.EndRecordingAsPicture();
713 };
714
715 ASSERT_TRUE(OpenPlaygroundHere(callback));
716}
717
718TEST_P(AiksTest, CanRenderItalicizedText) {
719 Canvas canvas;
720 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
721
722 ASSERT_TRUE(RenderTextInCanvasSkia(
723 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
724 "HomemadeApple.ttf"));
725 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
726}
727
728static constexpr std::string_view kFontFixture =
729#if FML_OS_MACOSX
730 "Apple Color Emoji.ttc";
731#else
732 "NotoColorEmoji.ttf";
733#endif
734
735TEST_P(AiksTest, CanRenderEmojiTextFrame) {
736 Canvas canvas;
737 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
738
739 ASSERT_TRUE(RenderTextInCanvasSkia(
740 GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture));
741 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
742}
743
744TEST_P(AiksTest, CanRenderEmojiTextFrameWithBlur) {
745 Canvas canvas;
746 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
747
748 ASSERT_TRUE(RenderTextInCanvasSkia(
749 GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture,
751 .mask_blur_descriptor = Paint::MaskBlurDescriptor{
753 .sigma = Sigma(4)}}));
754 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
755}
756
757TEST_P(AiksTest, CanRenderEmojiTextFrameWithAlpha) {
758 Canvas canvas;
759 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
760
761 ASSERT_TRUE(RenderTextInCanvasSkia(
762 GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture,
763 {.color = Color::Black().WithAlpha(0.5)}));
764 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
765}
766
767TEST_P(AiksTest, CanRenderTextInSaveLayer) {
768 Canvas canvas;
769 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
770
771 canvas.Translate({100, 100});
772 canvas.Scale(Vector2{0.5, 0.5});
773
774 // Blend the layer with the parent pass using kClear to expose the coverage.
775 canvas.SaveLayer({.blend_mode = BlendMode::kClear});
776 ASSERT_TRUE(RenderTextInCanvasSkia(
777 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
778 "Roboto-Regular.ttf"));
779 canvas.Restore();
780
781 // Render the text again over the cleared coverage rect.
782 ASSERT_TRUE(RenderTextInCanvasSkia(
783 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
784 "Roboto-Regular.ttf"));
785
786 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
787}
788
789TEST_P(AiksTest, CanRenderTextOutsideBoundaries) {
790 Canvas canvas;
791 canvas.Translate({200, 150});
792
793 // Construct the text blob.
794 auto mapping = flutter::testing::OpenFixtureAsSkData("wtf.otf");
795 ASSERT_NE(mapping, nullptr);
796
797 Scalar font_size = 80;
799 SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
800
801 Paint text_paint;
802 text_paint.color = Color::Blue().WithAlpha(0.8);
803
804 struct {
805 Point position;
806 const char* text;
807 } text[] = {{Point(0, 0), "0F0F0F0"},
808 {Point(1, 2), "789"},
809 {Point(1, 3), "456"},
810 {Point(1, 4), "123"},
811 {Point(0, 6), "0F0F0F0"}};
812 for (auto& t : text) {
813 canvas.Save();
814 canvas.Translate(t.position * Point(font_size * 2, font_size * 1.1));
815 {
816 auto blob = SkTextBlob::MakeFromString(t.text, sk_font);
817 ASSERT_NE(blob, nullptr);
819 canvas.DrawTextFrame(frame, Point(), text_paint);
820 }
821 canvas.Restore();
822 }
823
824 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
825}
826
827TEST_P(AiksTest, TextRotated) {
828 Canvas canvas;
829 canvas.Scale(GetContentScale());
830 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
831
832 canvas.Transform(Matrix(0.25, -0.3, 0, -0.002, //
833 0, 0.5, 0, 0, //
834 0, 0, 0.3, 0, //
835 100, 100, 0, 1.3));
836 ASSERT_TRUE(RenderTextInCanvasSkia(
837 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
838 "Roboto-Regular.ttf"));
839
840 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
841}
842
843TEST_P(AiksTest, CanDrawPaint) {
844 Canvas canvas;
845 canvas.Scale(Vector2(0.2, 0.2));
846 canvas.DrawPaint({.color = Color::MediumTurquoise()});
847 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
848}
849
850TEST_P(AiksTest, CanDrawPaintMultipleTimes) {
851 Canvas canvas;
852 canvas.Scale(Vector2(0.2, 0.2));
853 canvas.DrawPaint({.color = Color::MediumTurquoise()});
854 canvas.DrawPaint({.color = Color::Color::OrangeRed().WithAlpha(0.5)});
855 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
856}
857
858// This makes sure the WideGamut named tests use 16bit float pixel format.
859TEST_P(AiksTest, FormatWideGamut) {
860 EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
862}
863
864TEST_P(AiksTest, FormatSRGB) {
865 PixelFormat pixel_format =
866 GetContext()->GetCapabilities()->GetDefaultColorFormat();
868 pixel_format == PixelFormat::kB8G8R8A8UNormInt)
869 << "pixel format: " << PixelFormatToString(pixel_format);
870}
871
872TEST_P(AiksTest, TransformMultipliesCorrectly) {
873 Canvas canvas;
874 ASSERT_MATRIX_NEAR(canvas.GetCurrentTransform(), Matrix());
875
876 // clang-format off
877 canvas.Translate(Vector3(100, 200));
879 canvas.GetCurrentTransform(),
880 Matrix( 1, 0, 0, 0,
881 0, 1, 0, 0,
882 0, 0, 1, 0,
883 100, 200, 0, 1));
884
885 canvas.Rotate(Radians(kPiOver2));
887 canvas.GetCurrentTransform(),
888 Matrix( 0, 1, 0, 0,
889 -1, 0, 0, 0,
890 0, 0, 1, 0,
891 100, 200, 0, 1));
892
893 canvas.Scale(Vector3(2, 3));
895 canvas.GetCurrentTransform(),
896 Matrix( 0, 2, 0, 0,
897 -3, 0, 0, 0,
898 0, 0, 0, 0,
899 100, 200, 0, 1));
900
901 canvas.Translate(Vector3(100, 200));
903 canvas.GetCurrentTransform(),
904 Matrix( 0, 2, 0, 0,
905 -3, 0, 0, 0,
906 0, 0, 0, 0,
907 -500, 400, 0, 1));
908 // clang-format on
909}
910
911TEST_P(AiksTest, FilledCirclesRenderCorrectly) {
912 Canvas canvas;
913 canvas.Scale(GetContentScale());
914 Paint paint;
915 const int color_count = 3;
916 Color colors[color_count] = {
917 Color::Blue(),
918 Color::Green(),
920 };
921
922 paint.color = Color::White();
923 canvas.DrawPaint(paint);
924
925 int c_index = 0;
926 int radius = 600;
927 while (radius > 0) {
928 paint.color = colors[(c_index++) % color_count];
929 canvas.DrawCircle({10, 10}, radius, paint);
930 if (radius > 30) {
931 radius -= 10;
932 } else {
933 radius -= 2;
934 }
935 }
936
937 std::vector<Color> gradient_colors = {
938 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
939 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
940 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
941 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
942 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
943 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
944 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
945 std::vector<Scalar> stops = {
946 0.0,
947 (1.0 / 6.0) * 1,
948 (1.0 / 6.0) * 2,
949 (1.0 / 6.0) * 3,
950 (1.0 / 6.0) * 4,
951 (1.0 / 6.0) * 5,
952 1.0,
953 };
954 auto texture = CreateTextureForFixture("airplane.jpg",
955 /*enable_mipmapping=*/true);
956
958 {500, 600}, 75, std::move(gradient_colors), std::move(stops),
960 canvas.DrawCircle({500, 600}, 100, paint);
961
962 paint.color_source = ColorSource::MakeImage(
964 Matrix::MakeTranslation({700, 200}));
965 canvas.DrawCircle({800, 300}, 100, paint);
966
967 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
968}
969
970TEST_P(AiksTest, StrokedCirclesRenderCorrectly) {
971 Canvas canvas;
972 canvas.Scale(GetContentScale());
973 Paint paint;
974 const int color_count = 3;
975 Color colors[color_count] = {
976 Color::Blue(),
977 Color::Green(),
979 };
980
981 paint.color = Color::White();
982 canvas.DrawPaint(paint);
983
984 int c_index = 0;
985
986 auto draw = [&paint, &colors, &c_index](Canvas& canvas, Point center,
987 Scalar r, Scalar dr, int n) {
988 for (int i = 0; i < n; i++) {
989 paint.color = colors[(c_index++) % color_count];
990 canvas.DrawCircle(center, r, paint);
991 r += dr;
992 }
993 };
994
996 paint.stroke_width = 1;
997 draw(canvas, {10, 10}, 2, 2, 14); // r = [2, 28], covers [1,29]
998 paint.stroke_width = 5;
999 draw(canvas, {10, 10}, 35, 10, 56); // r = [35, 585], covers [30,590]
1000
1001 std::vector<Color> gradient_colors = {
1002 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1003 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1004 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1005 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1006 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1007 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1008 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1009 std::vector<Scalar> stops = {
1010 0.0,
1011 (1.0 / 6.0) * 1,
1012 (1.0 / 6.0) * 2,
1013 (1.0 / 6.0) * 3,
1014 (1.0 / 6.0) * 4,
1015 (1.0 / 6.0) * 5,
1016 1.0,
1017 };
1018 auto texture = CreateTextureForFixture("airplane.jpg",
1019 /*enable_mipmapping=*/true);
1020
1022 {500, 600}, 75, std::move(gradient_colors), std::move(stops),
1024 draw(canvas, {500, 600}, 5, 10, 10);
1025
1026 paint.color_source = ColorSource::MakeImage(
1028 Matrix::MakeTranslation({700, 200}));
1029 draw(canvas, {800, 300}, 5, 10, 10);
1030
1031 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1032}
1033
1034TEST_P(AiksTest, FilledEllipsesRenderCorrectly) {
1035 Canvas canvas;
1036 canvas.Scale(GetContentScale());
1037 Paint paint;
1038 const int color_count = 3;
1039 Color colors[color_count] = {
1040 Color::Blue(),
1041 Color::Green(),
1043 };
1044
1045 paint.color = Color::White();
1046 canvas.DrawPaint(paint);
1047
1048 int c_index = 0;
1049 int long_radius = 600;
1050 int short_radius = 600;
1051 while (long_radius > 0 && short_radius > 0) {
1052 paint.color = colors[(c_index++) % color_count];
1053 canvas.DrawOval(Rect::MakeXYWH(10 - long_radius, 10 - short_radius,
1054 long_radius * 2, short_radius * 2),
1055 paint);
1056 canvas.DrawOval(Rect::MakeXYWH(1000 - short_radius, 750 - long_radius,
1057 short_radius * 2, long_radius * 2),
1058 paint);
1059 if (short_radius > 30) {
1060 short_radius -= 10;
1061 long_radius -= 5;
1062 } else {
1063 short_radius -= 2;
1064 long_radius -= 1;
1065 }
1066 }
1067
1068 std::vector<Color> gradient_colors = {
1069 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1070 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1071 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1072 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1073 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1074 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1075 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1076 std::vector<Scalar> stops = {
1077 0.0,
1078 (1.0 / 6.0) * 1,
1079 (1.0 / 6.0) * 2,
1080 (1.0 / 6.0) * 3,
1081 (1.0 / 6.0) * 4,
1082 (1.0 / 6.0) * 5,
1083 1.0,
1084 };
1085 auto texture = CreateTextureForFixture("airplane.jpg",
1086 /*enable_mipmapping=*/true);
1087
1088 paint.color = Color::White().WithAlpha(0.5);
1089
1091 {300, 650}, 75, std::move(gradient_colors), std::move(stops),
1093 canvas.DrawOval(Rect::MakeXYWH(200, 625, 200, 50), paint);
1094 canvas.DrawOval(Rect::MakeXYWH(275, 550, 50, 200), paint);
1095
1096 paint.color_source = ColorSource::MakeImage(
1098 Matrix::MakeTranslation({610, 15}));
1099 canvas.DrawOval(Rect::MakeXYWH(610, 90, 200, 50), paint);
1100 canvas.DrawOval(Rect::MakeXYWH(685, 15, 50, 200), paint);
1101
1102 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1103}
1104
1105TEST_P(AiksTest, FilledRoundRectsRenderCorrectly) {
1106 Canvas canvas;
1107 canvas.Scale(GetContentScale());
1108 Paint paint;
1109 const int color_count = 3;
1110 Color colors[color_count] = {
1111 Color::Blue(),
1112 Color::Green(),
1114 };
1115
1116 paint.color = Color::White();
1117 canvas.DrawPaint(paint);
1118
1119 int c_index = 0;
1120 for (int i = 0; i < 4; i++) {
1121 for (int j = 0; j < 4; j++) {
1122 paint.color = colors[(c_index++) % color_count];
1123 canvas.DrawRRect(Rect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80),
1124 Size(i * 5 + 10, j * 5 + 10), paint);
1125 }
1126 }
1127 paint.color = colors[(c_index++) % color_count];
1128 canvas.DrawRRect(Rect::MakeXYWH(10, 420, 380, 80), Size(40, 40), paint);
1129 paint.color = colors[(c_index++) % color_count];
1130 canvas.DrawRRect(Rect::MakeXYWH(410, 20, 80, 380), Size(40, 40), paint);
1131
1132 std::vector<Color> gradient_colors = {
1133 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1134 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1135 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1136 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1137 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1138 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1139 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1140 std::vector<Scalar> stops = {
1141 0.0,
1142 (1.0 / 6.0) * 1,
1143 (1.0 / 6.0) * 2,
1144 (1.0 / 6.0) * 3,
1145 (1.0 / 6.0) * 4,
1146 (1.0 / 6.0) * 5,
1147 1.0,
1148 };
1149 auto texture = CreateTextureForFixture("airplane.jpg",
1150 /*enable_mipmapping=*/true);
1151
1152 paint.color = Color::White().WithAlpha(0.1);
1154 {550, 550}, 75, gradient_colors, stops, Entity::TileMode::kMirror, {});
1155 for (int i = 1; i <= 10; i++) {
1156 int j = 11 - i;
1157 canvas.DrawRRect(Rect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1158 550 + i * 20, 550 + j * 20),
1159 Size(i * 10, j * 10), paint);
1160 }
1161 paint.color = Color::White().WithAlpha(0.5);
1163 {200, 650}, 75, std::move(gradient_colors), std::move(stops),
1165 canvas.DrawRRect(Rect::MakeLTRB(100, 610, 300, 690), Size(40, 40), paint);
1166 canvas.DrawRRect(Rect::MakeLTRB(160, 550, 240, 750), Size(40, 40), paint);
1167
1168 paint.color = Color::White().WithAlpha(0.1);
1169 paint.color_source = ColorSource::MakeImage(
1171 Matrix::MakeTranslation({520, 20}));
1172 for (int i = 1; i <= 10; i++) {
1173 int j = 11 - i;
1174 canvas.DrawRRect(Rect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1175 720 + i * 20, 220 + j * 20),
1176 Size(i * 10, j * 10), paint);
1177 }
1178 paint.color = Color::White().WithAlpha(0.5);
1179 paint.color_source = ColorSource::MakeImage(
1181 Matrix::MakeTranslation({800, 300}));
1182 canvas.DrawRRect(Rect::MakeLTRB(800, 410, 1000, 490), Size(40, 40), paint);
1183 canvas.DrawRRect(Rect::MakeLTRB(860, 350, 940, 550), Size(40, 40), paint);
1184
1185 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1186}
1187
1188TEST_P(AiksTest, SolidColorCirclesOvalsRRectsMaskBlurCorrectly) {
1189 Canvas canvas;
1190 canvas.Scale(GetContentScale());
1191 Paint paint;
1192 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
1194 .sigma = Sigma{1},
1195 };
1196
1197 canvas.DrawPaint({.color = Color::White()});
1198
1199 paint.color = Color::Crimson();
1200 Scalar y = 100.0f;
1201 for (int i = 0; i < 5; i++) {
1202 Scalar x = (i + 1) * 100;
1203 Scalar radius = x / 10.0f;
1204 canvas.DrawRect(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1205 radius, 60.0f - radius),
1206 paint);
1207 }
1208
1209 paint.color = Color::Blue();
1210 y += 100.0f;
1211 for (int i = 0; i < 5; i++) {
1212 Scalar x = (i + 1) * 100;
1213 Scalar radius = x / 10.0f;
1214 canvas.DrawCircle({x + 25, y + 25}, radius, paint);
1215 }
1216
1217 paint.color = Color::Green();
1218 y += 100.0f;
1219 for (int i = 0; i < 5; i++) {
1220 Scalar x = (i + 1) * 100;
1221 Scalar radius = x / 10.0f;
1222 canvas.DrawOval(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1223 radius, 60.0f - radius),
1224 paint);
1225 }
1226
1227 paint.color = Color::Purple();
1228 y += 100.0f;
1229 for (int i = 0; i < 5; i++) {
1230 Scalar x = (i + 1) * 100;
1231 Scalar radius = x / 20.0f;
1232 canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
1233 {radius, radius}, //
1234 paint);
1235 }
1236
1237 paint.color = Color::Orange();
1238 y += 100.0f;
1239 for (int i = 0; i < 5; i++) {
1240 Scalar x = (i + 1) * 100;
1241 Scalar radius = x / 20.0f;
1242 canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
1243 {radius, 5.0f}, paint);
1244 }
1245
1246 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1247}
1248
1249TEST_P(AiksTest, FilledRoundRectPathsRenderCorrectly) {
1250 Canvas canvas;
1251 canvas.Scale(GetContentScale());
1252 Paint paint;
1253 const int color_count = 3;
1254 Color colors[color_count] = {
1255 Color::Blue(),
1256 Color::Green(),
1258 };
1259
1260 paint.color = Color::White();
1261 canvas.DrawPaint(paint);
1262
1263 auto draw_rrect_as_path = [&canvas](const Rect& rect, const Size& radii,
1264 const Paint& paint) {
1266 builder.AddRoundedRect(rect, radii);
1267 canvas.DrawPath(builder.TakePath(), paint);
1268 };
1269
1270 int c_index = 0;
1271 for (int i = 0; i < 4; i++) {
1272 for (int j = 0; j < 4; j++) {
1273 paint.color = colors[(c_index++) % color_count];
1274 draw_rrect_as_path(Rect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80),
1275 Size(i * 5 + 10, j * 5 + 10), paint);
1276 }
1277 }
1278 paint.color = colors[(c_index++) % color_count];
1279 draw_rrect_as_path(Rect::MakeXYWH(10, 420, 380, 80), Size(40, 40), paint);
1280 paint.color = colors[(c_index++) % color_count];
1281 draw_rrect_as_path(Rect::MakeXYWH(410, 20, 80, 380), Size(40, 40), paint);
1282
1283 std::vector<Color> gradient_colors = {
1284 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1285 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1286 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1287 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1288 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1289 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1290 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1291 std::vector<Scalar> stops = {
1292 0.0,
1293 (1.0 / 6.0) * 1,
1294 (1.0 / 6.0) * 2,
1295 (1.0 / 6.0) * 3,
1296 (1.0 / 6.0) * 4,
1297 (1.0 / 6.0) * 5,
1298 1.0,
1299 };
1300 auto texture = CreateTextureForFixture("airplane.jpg",
1301 /*enable_mipmapping=*/true);
1302
1303 paint.color = Color::White().WithAlpha(0.1);
1305 {550, 550}, 75, gradient_colors, stops, Entity::TileMode::kMirror, {});
1306 for (int i = 1; i <= 10; i++) {
1307 int j = 11 - i;
1308 draw_rrect_as_path(Rect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1309 550 + i * 20, 550 + j * 20),
1310 Size(i * 10, j * 10), paint);
1311 }
1312 paint.color = Color::White().WithAlpha(0.5);
1314 {200, 650}, 75, std::move(gradient_colors), std::move(stops),
1316 draw_rrect_as_path(Rect::MakeLTRB(100, 610, 300, 690), Size(40, 40), paint);
1317 draw_rrect_as_path(Rect::MakeLTRB(160, 550, 240, 750), Size(40, 40), paint);
1318
1319 paint.color = Color::White().WithAlpha(0.1);
1320 paint.color_source = ColorSource::MakeImage(
1322 Matrix::MakeTranslation({520, 20}));
1323 for (int i = 1; i <= 10; i++) {
1324 int j = 11 - i;
1325 draw_rrect_as_path(Rect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1326 720 + i * 20, 220 + j * 20),
1327 Size(i * 10, j * 10), paint);
1328 }
1329 paint.color = Color::White().WithAlpha(0.5);
1330 paint.color_source = ColorSource::MakeImage(
1332 Matrix::MakeTranslation({800, 300}));
1333 draw_rrect_as_path(Rect::MakeLTRB(800, 410, 1000, 490), Size(40, 40), paint);
1334 draw_rrect_as_path(Rect::MakeLTRB(860, 350, 940, 550), Size(40, 40), paint);
1335
1336 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1337}
1338
1339TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
1340 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1341 Canvas canvas;
1342 canvas.Scale(GetContentScale());
1343
1344 Paint alpha;
1345 alpha.color = Color::Red().WithAlpha(0.5);
1346
1347 auto current = Point{25, 25};
1348 const auto offset = Point{25, 25};
1349 const auto size = Size(100, 100);
1350
1351 static PlaygroundPoint point_a(Point(40, 40), 10, Color::White());
1352 static PlaygroundPoint point_b(Point(160, 160), 10, Color::White());
1353 auto [b0, b1] = DrawPlaygroundLine(point_a, point_b);
1354 auto bounds = Rect::MakeLTRB(b0.x, b0.y, b1.x, b1.y);
1355
1356 canvas.DrawRect(bounds, Paint{.color = Color::Yellow(),
1357 .stroke_width = 5.0f,
1358 .style = Paint::Style::kStroke});
1359
1360 canvas.SaveLayer(alpha, bounds);
1361
1362 canvas.DrawRect(Rect::MakeOriginSize(current, size),
1363 Paint{.color = Color::Red()});
1364 canvas.DrawRect(Rect::MakeOriginSize(current += offset, size),
1365 Paint{.color = Color::Green()});
1366 canvas.DrawRect(Rect::MakeOriginSize(current += offset, size),
1367 Paint{.color = Color::Blue()});
1368
1369 canvas.Restore();
1370
1371 return canvas.EndRecordingAsPicture();
1372 };
1373
1374 ASSERT_TRUE(OpenPlaygroundHere(callback));
1375}
1376
1377TEST_P(AiksTest, SaveLayerDrawsBehindSubsequentEntities) {
1378 // Compare with https://fiddle.skia.org/c/9e03de8567ffb49e7e83f53b64bcf636
1379 Canvas canvas;
1380 Paint paint;
1381
1382 paint.color = Color::Black();
1383 Rect rect = Rect::MakeXYWH(25, 25, 25, 25);
1384 canvas.DrawRect(rect, paint);
1385
1386 canvas.Translate({10, 10});
1387 canvas.SaveLayer({});
1388
1389 paint.color = Color::Green();
1390 canvas.DrawRect(rect, paint);
1391
1392 canvas.Restore();
1393
1394 canvas.Translate({10, 10});
1395 paint.color = Color::Red();
1396 canvas.DrawRect(rect, paint);
1397
1398 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1399}
1400
1401TEST_P(AiksTest, SiblingSaveLayerBoundsAreRespected) {
1402 Canvas canvas;
1403 Paint paint;
1404 Rect rect = Rect::MakeXYWH(0, 0, 1000, 1000);
1405
1406 // Black, green, and red squares offset by [10, 10].
1407 {
1408 canvas.SaveLayer({}, Rect::MakeXYWH(25, 25, 25, 25));
1409 paint.color = Color::Black();
1410 canvas.DrawRect(rect, paint);
1411 canvas.Restore();
1412 }
1413
1414 {
1415 canvas.SaveLayer({}, Rect::MakeXYWH(35, 35, 25, 25));
1416 paint.color = Color::Green();
1417 canvas.DrawRect(rect, paint);
1418 canvas.Restore();
1419 }
1420
1421 {
1422 canvas.SaveLayer({}, Rect::MakeXYWH(45, 45, 25, 25));
1423 paint.color = Color::Red();
1424 canvas.DrawRect(rect, paint);
1425 canvas.Restore();
1426 }
1427
1428 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1429}
1430
1431TEST_P(AiksTest, CanRenderClippedLayers) {
1432 Canvas canvas;
1433
1434 canvas.DrawPaint({.color = Color::White()});
1435
1436 // Draw a green circle on the screen.
1437 {
1438 // Increase the clip depth for the savelayer to contend with.
1439 canvas.ClipPath(PathBuilder{}.AddCircle({100, 100}, 50).TakePath());
1440
1441 canvas.SaveLayer({}, Rect::MakeXYWH(50, 50, 100, 100));
1442
1443 // Fill the layer with white.
1444 canvas.DrawRect(Rect::MakeSize(Size{400, 400}), {.color = Color::White()});
1445 // Fill the layer with green, but do so with a color blend that can't be
1446 // collapsed into the parent pass.
1447 // TODO(jonahwilliams): this blend mode was changed from color burn to
1448 // hardlight to work around https://github.com/flutter/flutter/issues/136554
1449 // .
1450 canvas.DrawRect(
1451 Rect::MakeSize(Size{400, 400}),
1452 {.color = Color::Green(), .blend_mode = BlendMode::kHardLight});
1453 }
1454
1455 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1456}
1457
1458TEST_P(AiksTest, SaveLayerFiltersScaleWithTransform) {
1459 Canvas canvas;
1460 canvas.Scale(GetContentScale());
1461 canvas.Translate(Vector2(100, 100));
1462
1463 auto texture = std::make_shared<Image>(CreateTextureForFixture("boston.jpg"));
1464 auto draw_image_layer = [&canvas, &texture](const Paint& paint) {
1465 canvas.SaveLayer(paint);
1466 canvas.DrawImage(texture, {}, Paint{});
1467 canvas.Restore();
1468 };
1469
1470 Paint effect_paint;
1471 effect_paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
1473 .sigma = Sigma{6},
1474 };
1475 draw_image_layer(effect_paint);
1476
1477 canvas.Translate(Vector2(300, 300));
1478 canvas.Scale(Vector2(3, 3));
1479 draw_image_layer(effect_paint);
1480
1481 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1482}
1483
1484#if IMPELLER_ENABLE_3D
1485TEST_P(AiksTest, SceneColorSource) {
1486 // Load up the scene.
1487 auto mapping =
1488 flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb.ipscene");
1489 ASSERT_NE(mapping, nullptr);
1490
1491 std::shared_ptr<scene::Node> gltf_scene = scene::Node::MakeFromFlatbuffer(
1492 *mapping, *GetContext()->GetResourceAllocator());
1493 ASSERT_NE(gltf_scene, nullptr);
1494
1495 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1496 Paint paint;
1497
1498 static Scalar distance = 2;
1499 static Scalar y_pos = 0;
1500 static Scalar fov = 45;
1501 if (AiksTest::ImGuiBegin("Controls", nullptr,
1502 ImGuiWindowFlags_AlwaysAutoResize)) {
1503 ImGui::SliderFloat("Distance", &distance, 0, 4);
1504 ImGui::SliderFloat("Y", &y_pos, -3, 3);
1505 ImGui::SliderFloat("FOV", &fov, 1, 180);
1506 ImGui::End();
1507 }
1508
1509 Scalar angle = GetSecondsElapsed();
1510 auto camera_position =
1511 Vector3(distance * std::sin(angle), y_pos, -distance * std::cos(angle));
1512
1513 paint.color_source = ColorSource::MakeScene(
1514 gltf_scene,
1515 Matrix::MakePerspective(Degrees(fov), GetWindowSize(), 0.1, 1000) *
1516 Matrix::MakeLookAt(camera_position, {0, 0, 0}, {0, 1, 0}));
1517
1518 Canvas canvas;
1519 canvas.DrawPaint(Paint{.color = Color::MakeRGBA8(0xf9, 0xf9, 0xf9, 0xff)});
1520 canvas.Scale(GetContentScale());
1521 canvas.DrawPaint(paint);
1522 return canvas.EndRecordingAsPicture();
1523 };
1524
1525 ASSERT_TRUE(OpenPlaygroundHere(callback));
1526}
1527#endif // IMPELLER_ENABLE_3D
1528
1529TEST_P(AiksTest, PaintWithFilters) {
1530 // validate that a paint with a color filter "HasFilters", no other filters
1531 // impact this setting.
1532 Paint paint;
1533
1534 ASSERT_FALSE(paint.HasColorFilter());
1535
1536 paint.color_filter =
1538
1539 ASSERT_TRUE(paint.HasColorFilter());
1540
1541 paint.image_filter = ImageFilter::MakeBlur(Sigma(1.0), Sigma(1.0),
1544
1545 ASSERT_TRUE(paint.HasColorFilter());
1546
1547 paint.mask_blur_descriptor = {};
1548
1549 ASSERT_TRUE(paint.HasColorFilter());
1550
1551 paint.color_filter = nullptr;
1552
1553 ASSERT_FALSE(paint.HasColorFilter());
1554}
1555
1556TEST_P(AiksTest, DrawPaintAbsorbsClears) {
1557 Canvas canvas;
1558 canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1559 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1560 .blend_mode = BlendMode::kSourceOver});
1561
1562 Picture picture = canvas.EndRecordingAsPicture();
1563 auto expected = Color::Red().Blend(Color::CornflowerBlue().WithAlpha(0.75),
1565 ASSERT_EQ(picture.pass->GetClearColor(), expected);
1566
1567 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1568 std::shared_ptr<Context> real_context = GetContext();
1569 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1571 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1572
1573 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1574 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1575 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1576}
1577
1578// This is important to enforce with texture reuse, since cached textures need
1579// to be cleared before reuse.
1581 ParentSaveLayerCreatesRenderPassWhenChildBackdropFilterIsPresent) {
1582 Canvas canvas;
1583 canvas.SaveLayer({}, std::nullopt, ImageFilter::MakeMatrix(Matrix(), {}));
1584 canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1585 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1586 .blend_mode = BlendMode::kSourceOver});
1587 canvas.Restore();
1588
1589 Picture picture = canvas.EndRecordingAsPicture();
1590
1591 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1592 std::shared_ptr<Context> real_context = GetContext();
1593 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1595 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1596
1597 ASSERT_EQ(spy->render_passes_.size(),
1598 GetBackend() == PlaygroundBackend::kOpenGLES ? 4llu : 3llu);
1599 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1600 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1601}
1602
1603TEST_P(AiksTest, DrawRectAbsorbsClears) {
1604 Canvas canvas;
1605 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1606 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1607 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1608 {.color = Color::CornflowerBlue().WithAlpha(0.75),
1609 .blend_mode = BlendMode::kSourceOver});
1610
1611 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1612 Picture picture = canvas.EndRecordingAsPicture();
1613 std::shared_ptr<Context> real_context = GetContext();
1614 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1616 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1617
1618 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1619 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1620 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1621}
1622
1623TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRRect) {
1624 Canvas canvas;
1625 canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), {5.0, 5.0},
1626 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1627 canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), {5.0, 5.0},
1628 {.color = Color::CornflowerBlue().WithAlpha(0.75),
1629 .blend_mode = BlendMode::kSourceOver});
1630
1631 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1632 Picture picture = canvas.EndRecordingAsPicture();
1633 std::shared_ptr<Context> real_context = GetContext();
1634 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1636 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1637
1638 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1639 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1640 ASSERT_EQ(render_pass->GetCommands().size(), 2llu);
1641}
1642
1643TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRotation) {
1644 Canvas canvas;
1645 canvas.Translate(Vector3(150.0, 150.0, 0.0));
1646 canvas.Rotate(Degrees(45.0));
1647 canvas.Translate(Vector3(-150.0, -150.0, 0.0));
1648 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1649 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1650
1651 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1652 Picture picture = canvas.EndRecordingAsPicture();
1653 std::shared_ptr<Context> real_context = GetContext();
1654 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1656 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1657
1658 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1659 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1660 ASSERT_EQ(render_pass->GetCommands().size(), 1llu);
1661}
1662
1663TEST_P(AiksTest, DrawRectAbsorbsClearsNegative) {
1664 Canvas canvas;
1665 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1666 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1667 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1668 {.color = Color::CornflowerBlue().WithAlpha(0.75),
1669 .blend_mode = BlendMode::kSourceOver});
1670
1671 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1672 Picture picture = canvas.EndRecordingAsPicture();
1673 std::shared_ptr<Context> real_context = GetContext();
1674 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1676 std::shared_ptr<Image> image = picture.ToImage(renderer, {301, 301});
1677
1678 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1679 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1680 ASSERT_EQ(render_pass->GetCommands().size(), 2llu);
1681}
1682
1683TEST_P(AiksTest, ClipRectElidesNoOpClips) {
1684 Canvas canvas(Rect::MakeXYWH(0, 0, 100, 100));
1685 canvas.ClipRect(Rect::MakeXYWH(0, 0, 100, 100));
1686 canvas.ClipRect(Rect::MakeXYWH(-100, -100, 300, 300));
1687 canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1688 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1689 .blend_mode = BlendMode::kSourceOver});
1690
1691 Picture picture = canvas.EndRecordingAsPicture();
1692 auto expected = Color::Red().Blend(Color::CornflowerBlue().WithAlpha(0.75),
1694 ASSERT_EQ(picture.pass->GetClearColor(), expected);
1695
1696 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1697 std::shared_ptr<Context> real_context = GetContext();
1698 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1700 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1701
1702 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1703 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1704 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1705}
1706
1707TEST_P(AiksTest, ClearColorOptimizationDoesNotApplyForBackdropFilters) {
1708 Canvas canvas;
1709 canvas.SaveLayer({}, std::nullopt,
1713 canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1714 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1715 .blend_mode = BlendMode::kSourceOver});
1716 canvas.Restore();
1717
1718 Picture picture = canvas.EndRecordingAsPicture();
1719
1720 std::optional<Color> actual_color;
1721 bool found_subpass = false;
1722 picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool {
1723 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1724 actual_color = subpass->get()->GetClearColor();
1725 found_subpass = true;
1726 }
1727 // Fail if the first element isn't a subpass.
1728 return true;
1729 });
1730
1731 EXPECT_TRUE(found_subpass);
1732 EXPECT_FALSE(actual_color.has_value());
1733}
1734
1735TEST_P(AiksTest, CollapsedDrawPaintInSubpass) {
1736 Canvas canvas;
1737 canvas.DrawPaint(
1738 {.color = Color::Yellow(), .blend_mode = BlendMode::kSource});
1739 canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
1740 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1741 .blend_mode = BlendMode::kSourceOver});
1742
1743 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1744}
1745
1746TEST_P(AiksTest, CollapsedDrawPaintInSubpassBackdropFilter) {
1747 // Bug: https://github.com/flutter/flutter/issues/131576
1748 Canvas canvas;
1749 canvas.DrawPaint(
1750 {.color = Color::Yellow(), .blend_mode = BlendMode::kSource});
1751 canvas.SaveLayer({}, {},
1752 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
1755 canvas.DrawPaint(
1756 {.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSourceOver});
1757
1758 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1759}
1760
1761TEST_P(AiksTest, ColorMatrixFilterSubpassCollapseOptimization) {
1762 Canvas canvas;
1763
1764 canvas.SaveLayer({
1765 .color_filter =
1766 ColorFilter::MakeMatrix({.array =
1767 {
1768 -1.0, 0, 0, 1.0, 0, //
1769 0, -1.0, 0, 1.0, 0, //
1770 0, 0, -1.0, 1.0, 0, //
1771 1.0, 1.0, 1.0, 1.0, 0 //
1772 }}),
1773 });
1774
1775 canvas.Translate({500, 300, 0});
1776 canvas.Rotate(Radians(2 * kPi / 3));
1777 canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
1778
1779 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1780}
1781
1782TEST_P(AiksTest, LinearToSrgbFilterSubpassCollapseOptimization) {
1783 Canvas canvas;
1784
1785 canvas.SaveLayer({
1786 .color_filter = ColorFilter::MakeLinearToSrgb(),
1787 });
1788
1789 canvas.Translate({500, 300, 0});
1790 canvas.Rotate(Radians(2 * kPi / 3));
1791 canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
1792
1793 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1794}
1795
1796TEST_P(AiksTest, SrgbToLinearFilterSubpassCollapseOptimization) {
1797 Canvas canvas;
1798
1799 canvas.SaveLayer({
1800 .color_filter = ColorFilter::MakeSrgbToLinear(),
1801 });
1802
1803 canvas.Translate({500, 300, 0});
1804 canvas.Rotate(Radians(2 * kPi / 3));
1805 canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
1806
1807 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1808}
1809
1810TEST_P(AiksTest, TranslucentSaveLayerDrawsCorrectly) {
1811 Canvas canvas;
1812
1813 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1814
1815 canvas.SaveLayer({.color = Color::Black().WithAlpha(0.5)});
1816 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1817 canvas.Restore();
1818
1819 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1820}
1821
1822TEST_P(AiksTest, TranslucentSaveLayerWithBlendColorFilterDrawsCorrectly) {
1823 Canvas canvas;
1824
1825 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1826
1827 canvas.SaveLayer({
1828 .color = Color::Black().WithAlpha(0.5),
1829 .color_filter =
1831 });
1832 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1833 canvas.Restore();
1834
1835 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1836}
1837
1838TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterDrawsCorrectly) {
1839 Canvas canvas;
1840
1841 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1842
1843 canvas.SaveLayer({
1844 .color = Color::Black().WithAlpha(0.5),
1845 .image_filter = ImageFilter::MakeFromColorFilter(
1847 });
1848
1849 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1850 canvas.Restore();
1851
1852 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1853}
1854
1855TEST_P(AiksTest, TranslucentSaveLayerWithColorAndImageFilterDrawsCorrectly) {
1856 Canvas canvas;
1857
1858 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1859
1860 canvas.SaveLayer({
1861 .color = Color::Black().WithAlpha(0.5),
1862 .color_filter =
1864 });
1865
1866 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1867 canvas.Restore();
1868
1869 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1870}
1871
1872TEST_P(AiksTest, ImageFilteredSaveLayerWithUnboundedContents) {
1873 Canvas canvas;
1874 canvas.Scale(GetContentScale());
1875
1876 auto test = [&canvas](const std::shared_ptr<ImageFilter>& filter) {
1877 auto DrawLine = [&canvas](const Point& p0, const Point& p1,
1878 const Paint& p) {
1879 auto path = PathBuilder{}
1880 .AddLine(p0, p1)
1882 .TakePath();
1883 Paint paint = p;
1885 canvas.DrawPath(path, paint);
1886 };
1887 // Registration marks for the edge of the SaveLayer
1888 DrawLine(Point(75, 100), Point(225, 100), {.color = Color::White()});
1889 DrawLine(Point(75, 200), Point(225, 200), {.color = Color::White()});
1890 DrawLine(Point(100, 75), Point(100, 225), {.color = Color::White()});
1891 DrawLine(Point(200, 75), Point(200, 225), {.color = Color::White()});
1892
1893 canvas.SaveLayer({.image_filter = filter},
1894 Rect::MakeLTRB(100, 100, 200, 200));
1895 {
1896 // DrawPaint to verify correct behavior when the contents are unbounded.
1897 canvas.DrawPaint({.color = Color::Yellow()});
1898
1899 // Contrasting rectangle to see interior blurring
1900 canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
1901 {.color = Color::Blue()});
1902 }
1903 canvas.Restore();
1904 };
1905
1909
1910 canvas.Translate({200.0, 0.0});
1911
1913
1914 canvas.Translate({200.0, 0.0});
1915
1917
1918 canvas.Translate({-400.0, 200.0});
1919
1920 auto rotate_filter =
1923 Matrix::MakeTranslation({-150, -150}),
1925 test(rotate_filter);
1926
1927 canvas.Translate({200.0, 0.0});
1928
1929 auto rgb_swap_filter = ImageFilter::MakeFromColorFilter(
1930 *ColorFilter::MakeMatrix({.array = {
1931 0, 1, 0, 0, 0, //
1932 0, 0, 1, 0, 0, //
1933 1, 0, 0, 0, 0, //
1934 0, 0, 0, 1, 0 //
1935 }}));
1936 test(rgb_swap_filter);
1937
1938 canvas.Translate({200.0, 0.0});
1939
1940 test(ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter));
1941
1942 canvas.Translate({-400.0, 200.0});
1943
1945 *rotate_filter));
1946
1947 canvas.Translate({200.0, 0.0});
1948
1950 *rgb_swap_filter));
1951
1952 canvas.Translate({200.0, 0.0});
1953
1955 Matrix::MakeTranslation({25.0, 25.0}),
1956 *ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter)));
1957
1958 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1959}
1960
1961TEST_P(AiksTest, ImageFilteredUnboundedSaveLayerWithUnboundedContents) {
1962 Canvas canvas;
1963 canvas.Scale(GetContentScale());
1964
1965 auto blur_filter = ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0},
1968
1969 canvas.SaveLayer({.image_filter = blur_filter}, std::nullopt);
1970 {
1971 // DrawPaint to verify correct behavior when the contents are unbounded.
1972 canvas.DrawPaint({.color = Color::Yellow()});
1973
1974 // Contrasting rectangle to see interior blurring
1975 canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
1976 {.color = Color::Blue()});
1977 }
1978 canvas.Restore();
1979
1980 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1981}
1982
1983TEST_P(AiksTest, TranslucentSaveLayerImageDrawsCorrectly) {
1984 Canvas canvas;
1985
1986 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
1987 canvas.DrawImage(image, {100, 100}, {});
1988
1989 canvas.SaveLayer({.color = Color::Black().WithAlpha(0.5)});
1990 canvas.DrawImage(image, {100, 500}, {});
1991 canvas.Restore();
1992
1993 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1994}
1995
1996TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixColorFilterDrawsCorrectly) {
1997 Canvas canvas;
1998
1999 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2000 canvas.DrawImage(image, {100, 100}, {});
2001
2002 canvas.SaveLayer({
2003 .color = Color::Black().WithAlpha(0.5),
2004 .color_filter = ColorFilter::MakeMatrix({.array =
2005 {
2006 1, 0, 0, 0, 0, //
2007 0, 1, 0, 0, 0, //
2008 0, 0, 1, 0, 0, //
2009 0, 0, 0, 2, 0 //
2010 }}),
2011 });
2012 canvas.DrawImage(image, {100, 500}, {});
2013 canvas.Restore();
2014
2015 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2016}
2017
2018TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly) {
2019 Canvas canvas;
2020
2021 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2022 canvas.DrawImage(image, {100, 100}, {});
2023
2024 canvas.SaveLayer({
2025 .color = Color::Black().WithAlpha(0.5),
2026 .image_filter = ImageFilter::MakeFromColorFilter(
2027 *ColorFilter::MakeMatrix({.array =
2028 {
2029 1, 0, 0, 0, 0, //
2030 0, 1, 0, 0, 0, //
2031 0, 0, 1, 0, 0, //
2032 0, 0, 0, 2, 0 //
2033 }})),
2034 });
2035 canvas.DrawImage(image, {100, 500}, {});
2036 canvas.Restore();
2037
2038 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2039}
2040
2042 TranslucentSaveLayerWithColorFilterAndImageFilterDrawsCorrectly) {
2043 Canvas canvas;
2044
2045 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2046 canvas.DrawImage(image, {100, 100}, {});
2047
2048 canvas.SaveLayer({
2049 .color = Color::Black().WithAlpha(0.5),
2050 .image_filter = ImageFilter::MakeFromColorFilter(
2051 *ColorFilter::MakeMatrix({.array =
2052 {
2053 1, 0, 0, 0, 0, //
2054 0, 1, 0, 0, 0, //
2055 0, 0.2, 1, 0, 0, //
2056 0, 0, 0, 0.5, 0 //
2057 }})),
2058 .color_filter =
2060 });
2061 canvas.DrawImage(image, {100, 500}, {});
2062 canvas.Restore();
2063
2064 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2065}
2066
2067TEST_P(AiksTest, TranslucentSaveLayerWithAdvancedBlendModeDrawsCorrectly) {
2068 Canvas canvas;
2069 canvas.DrawRect(Rect::MakeXYWH(0, 0, 400, 400), {.color = Color::Red()});
2070 canvas.SaveLayer({
2071 .color = Color::Black().WithAlpha(0.5),
2072 .blend_mode = BlendMode::kLighten,
2073 });
2074 canvas.DrawCircle({200, 200}, 100, {.color = Color::Green()});
2075 canvas.Restore();
2076 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2077}
2078
2079/// This is a regression check for https://github.com/flutter/engine/pull/41129
2080/// The entire screen is green if successful. If failing, no frames will render,
2081/// or the entire screen will be transparent black.
2082TEST_P(AiksTest, CanRenderTinyOverlappingSubpasses) {
2083 Canvas canvas;
2084 canvas.DrawPaint({.color = Color::Red()});
2085
2086 // Draw two overlapping subpixel circles.
2087 canvas.SaveLayer({});
2088 canvas.DrawCircle({100, 100}, 0.1, {.color = Color::Yellow()});
2089 canvas.Restore();
2090 canvas.SaveLayer({});
2091 canvas.DrawCircle({100, 100}, 0.1, {.color = Color::Yellow()});
2092 canvas.Restore();
2093
2094 canvas.DrawPaint({.color = Color::Green()});
2095
2096 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2097}
2098
2099TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) {
2100 Canvas canvas;
2101 canvas.Scale(Vector2(1.618, 1.618));
2102 canvas.DrawCircle(Point(), 10,
2103 {
2104 .color = Color::CornflowerBlue(),
2105 .blend_mode = BlendMode::kSourceOver,
2106 });
2107 Picture picture = canvas.EndRecordingAsPicture();
2108
2109 // Extract the SolidColorSource.
2110 // Entity entity;
2111 std::vector<Entity> entity;
2112 std::shared_ptr<SolidColorContents> contents;
2113 picture.pass->IterateAllEntities([e = &entity, &contents](Entity& entity) {
2114 if (ScalarNearlyEqual(entity.GetTransform().GetScale().x, 1.618f)) {
2115 contents =
2116 std::static_pointer_cast<SolidColorContents>(entity.GetContents());
2117 e->emplace_back(entity.Clone());
2118 return false;
2119 }
2120 return true;
2121 });
2122
2123 ASSERT_TRUE(entity.size() >= 1);
2124 ASSERT_TRUE(contents->IsOpaque());
2125 ASSERT_EQ(entity[0].GetBlendMode(), BlendMode::kSource);
2126}
2127
2128TEST_P(AiksTest, CanRenderDestructiveSaveLayer) {
2129 Canvas canvas;
2130
2131 canvas.DrawPaint({.color = Color::Red()});
2132 // Draw an empty savelayer with a destructive blend mode, which will replace
2133 // the entire red screen with fully transparent black, except for the green
2134 // circle drawn within the layer.
2135 canvas.SaveLayer({.blend_mode = BlendMode::kSource});
2136 canvas.DrawCircle({300, 300}, 100, {.color = Color::Green()});
2137 canvas.Restore();
2138
2139 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2140}
2141
2142// Regression test for https://github.com/flutter/flutter/issues/126701 .
2143TEST_P(AiksTest, CanRenderClippedRuntimeEffects) {
2144 auto runtime_stages =
2145 OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
2146
2147 auto runtime_stage =
2148 runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
2149 ASSERT_TRUE(runtime_stage);
2150 ASSERT_TRUE(runtime_stage->IsDirty());
2151
2152 struct FragUniforms {
2153 Vector2 iResolution;
2154 Scalar iTime;
2155 } frag_uniforms = {.iResolution = Vector2(400, 400), .iTime = 100.0};
2156 auto uniform_data = std::make_shared<std::vector<uint8_t>>();
2157 uniform_data->resize(sizeof(FragUniforms));
2158 memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
2159
2160 std::vector<RuntimeEffectContents::TextureInput> texture_inputs;
2161
2162 Paint paint;
2164 runtime_stage, uniform_data, texture_inputs);
2165
2166 Canvas canvas;
2167 canvas.Save();
2168 canvas.ClipRRect(Rect::MakeXYWH(0, 0, 400, 400), {10.0, 10.0},
2170 canvas.DrawRect(Rect::MakeXYWH(0, 0, 400, 400), paint);
2171 canvas.Restore();
2172
2173 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2174}
2175
2176TEST_P(AiksTest, DrawPaintTransformsBounds) {
2177 auto runtime_stages = OpenAssetAsRuntimeStage("gradient.frag.iplr");
2178 auto runtime_stage =
2179 runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
2180 ASSERT_TRUE(runtime_stage);
2181 ASSERT_TRUE(runtime_stage->IsDirty());
2182
2183 struct FragUniforms {
2184 Size size;
2185 } frag_uniforms = {.size = Size::MakeWH(400, 400)};
2186 auto uniform_data = std::make_shared<std::vector<uint8_t>>();
2187 uniform_data->resize(sizeof(FragUniforms));
2188 memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
2189
2190 std::vector<RuntimeEffectContents::TextureInput> texture_inputs;
2191
2192 Paint paint;
2194 runtime_stage, uniform_data, texture_inputs);
2195
2196 Canvas canvas;
2197 canvas.Save();
2198 canvas.Scale(GetContentScale());
2199 canvas.DrawPaint(paint);
2200 canvas.Restore();
2201
2202 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2203}
2204
2205TEST_P(AiksTest, CanDrawPoints) {
2206 std::vector<Point> points = {
2207 {0, 0}, //
2208 {100, 100}, //
2209 {100, 0}, //
2210 {0, 100}, //
2211 {0, 0}, //
2212 {48, 48}, //
2213 {52, 52}, //
2214 };
2215 std::vector<PointStyle> caps = {
2218 };
2219 Paint paint;
2220 paint.color = Color::Yellow().WithAlpha(0.5);
2221
2222 Paint background;
2223 background.color = Color::Black();
2224
2225 Canvas canvas;
2226 canvas.DrawPaint(background);
2227 canvas.Translate({200, 200});
2228 canvas.DrawPoints(points, 10, paint, PointStyle::kRound);
2229 canvas.Translate({150, 0});
2230 canvas.DrawPoints(points, 10, paint, PointStyle::kSquare);
2231
2232 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2233}
2234
2235TEST_P(AiksTest, CanDrawPointsWithTextureMap) {
2236 auto texture = CreateTextureForFixture("table_mountain_nx.png",
2237 /*enable_mipmapping=*/true);
2238
2239 std::vector<Point> points = {
2240 {0, 0}, //
2241 {100, 100}, //
2242 {100, 0}, //
2243 {0, 100}, //
2244 {0, 0}, //
2245 {48, 48}, //
2246 {52, 52}, //
2247 };
2248 std::vector<PointStyle> caps = {
2251 };
2252 Paint paint;
2254 Entity::TileMode::kClamp, {}, {});
2255
2256 Canvas canvas;
2257 canvas.Translate({200, 200});
2258 canvas.DrawPoints(points, 100, paint, PointStyle::kRound);
2259 canvas.Translate({150, 0});
2260 canvas.DrawPoints(points, 100, paint, PointStyle::kSquare);
2261
2262 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2263}
2264
2265// This currently renders solid blue, as the support for text color sources was
2266// moved into DLDispatching. Path data requires the SkTextBlobs which are not
2267// used in impeller::TextFrames.
2268TEST_P(AiksTest, TextForegroundShaderWithTransform) {
2269 auto mapping = flutter::testing::OpenFixtureAsSkData("Roboto-Regular.ttf");
2270 ASSERT_NE(mapping, nullptr);
2271
2272 Scalar font_size = 100;
2274 SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
2275
2276 Paint text_paint;
2277 text_paint.color = Color::Blue();
2278
2279 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
2280 Color{0.1294, 0.5882, 0.9529, 1.0}};
2281 std::vector<Scalar> stops = {
2282 0.0,
2283 1.0,
2284 };
2285 text_paint.color_source = ColorSource::MakeLinearGradient(
2286 {0, 0}, {100, 100}, std::move(colors), std::move(stops),
2288
2289 Canvas canvas;
2290 canvas.Translate({100, 100});
2291 canvas.Rotate(Radians(kPi / 4));
2292
2293 auto blob = SkTextBlob::MakeFromString("Hello", sk_font);
2294 ASSERT_NE(blob, nullptr);
2296 canvas.DrawTextFrame(frame, Point(), text_paint);
2297
2298 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2299}
2300
2301TEST_P(AiksTest, MatrixSaveLayerFilter) {
2302 Canvas canvas;
2303 canvas.DrawPaint({.color = Color::Black()});
2304 canvas.SaveLayer({}, std::nullopt);
2305 {
2306 canvas.DrawCircle(Point(200, 200), 100,
2307 {.color = Color::Green().WithAlpha(0.5),
2308 .blend_mode = BlendMode::kPlus});
2309 // Should render a second circle, centered on the bottom-right-most edge of
2310 // the circle.
2311 canvas.SaveLayer({.image_filter = ImageFilter::MakeMatrix(
2313 (200 + 100 * k1OverSqrt2)) *
2314 Matrix::MakeScale(Vector2(1, 1) * 0.5) *
2315 Matrix::MakeTranslation(Vector2(-200, -200)),
2317 std::nullopt);
2318 canvas.DrawCircle(Point(200, 200), 100,
2319 {.color = Color::Green().WithAlpha(0.5),
2320 .blend_mode = BlendMode::kPlus});
2321 canvas.Restore();
2322 }
2323 canvas.Restore();
2324
2325 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2326}
2327
2328TEST_P(AiksTest, MatrixBackdropFilter) {
2329 Canvas canvas;
2330 canvas.DrawPaint({.color = Color::Black()});
2331 canvas.SaveLayer({}, std::nullopt);
2332 {
2333 canvas.DrawCircle(Point(200, 200), 100,
2334 {.color = Color::Green().WithAlpha(0.5),
2335 .blend_mode = BlendMode::kPlus});
2336 // Should render a second circle, centered on the bottom-right-most edge of
2337 // the circle.
2338 canvas.SaveLayer(
2339 {}, std::nullopt,
2341 Matrix::MakeTranslation(Vector2(1, 1) * (100 + 100 * k1OverSqrt2)) *
2342 Matrix::MakeScale(Vector2(1, 1) * 0.5) *
2343 Matrix::MakeTranslation(Vector2(-100, -100)),
2345 canvas.Restore();
2346 }
2347 canvas.Restore();
2348
2349 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2350}
2351
2352TEST_P(AiksTest, SolidColorApplyColorFilter) {
2353 auto contents = SolidColorContents();
2354 contents.SetColor(Color::CornflowerBlue().WithAlpha(0.75));
2355 auto result = contents.ApplyColorFilter([](const Color& color) {
2356 return color.Blend(Color::LimeGreen().WithAlpha(0.75), BlendMode::kScreen);
2357 });
2358 ASSERT_TRUE(result);
2359 ASSERT_COLOR_NEAR(contents.GetColor(),
2360 Color(0.424452, 0.828743, 0.79105, 0.9375));
2361}
2362
2363TEST_P(AiksTest, DrawScaledTextWithPerspectiveNoSaveLayer) {
2364 Canvas canvas;
2365 canvas.Transform(Matrix(1.0, 0.0, 0.0, 0.0, //
2366 0.0, 1.0, 0.0, 0.0, //
2367 0.0, 0.0, 1.0, 0.01, //
2368 0.0, 0.0, 0.0, 1.0) * //
2370
2371 ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
2372 "Roboto-Regular.ttf"));
2373
2374 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2375}
2376
2377TEST_P(AiksTest, DrawScaledTextWithPerspectiveSaveLayer) {
2378 Canvas canvas;
2379 Paint save_paint;
2380 canvas.SaveLayer(save_paint);
2381 canvas.Transform(Matrix(1.0, 0.0, 0.0, 0.0, //
2382 0.0, 1.0, 0.0, 0.0, //
2383 0.0, 0.0, 1.0, 0.01, //
2384 0.0, 0.0, 0.0, 1.0) * //
2386
2387 ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
2388 "Roboto-Regular.ttf"));
2389 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2390}
2391
2392TEST_P(AiksTest, PipelineBlendSingleParameter) {
2393 Canvas canvas;
2394
2395 // Should render a green square in the middle of a blue circle.
2396 canvas.SaveLayer({});
2397 {
2398 canvas.Translate(Point(100, 100));
2399 canvas.DrawCircle(Point(200, 200), 200, {.color = Color::Blue()});
2400 canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
2401 canvas.DrawCircle(Point(200, 200), 200,
2402 {
2403 .color = Color::Green(),
2404 .blend_mode = BlendMode::kSourceOver,
2405 .image_filter = ImageFilter::MakeFromColorFilter(
2407 Color::White())),
2408 });
2409 canvas.Restore();
2410 }
2411
2412 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2413}
2414
2415// Regression test for https://github.com/flutter/flutter/issues/134678.
2416TEST_P(AiksTest, ReleasesTextureOnTeardown) {
2417 auto context = MakeContext();
2418 std::weak_ptr<Texture> weak_texture;
2419
2420 {
2421 auto texture = CreateTextureForFixture("table_mountain_nx.png");
2422
2423 Canvas canvas;
2424 canvas.Scale(GetContentScale());
2425 canvas.Translate({100.0f, 100.0f, 0});
2426
2427 Paint paint;
2428 paint.color_source = ColorSource::MakeImage(
2430 canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
2431
2432 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2433 }
2434
2435 // See https://github.com/flutter/flutter/issues/134751.
2436 //
2437 // If the fence waiter was working this may not be released by the end of the
2438 // scope above. Adding a manual shutdown so that future changes to the fence
2439 // waiter will not flake this test.
2440 context->Shutdown();
2441
2442 // The texture should be released by now.
2443 ASSERT_TRUE(weak_texture.expired()) << "When the texture is no longer in use "
2444 "by the backend, it should be "
2445 "released.";
2446}
2447
2448TEST_P(AiksTest, MatrixImageFilterMagnify) {
2449 Scalar scale = 2.0;
2450 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
2451 if (AiksTest::ImGuiBegin("Controls", nullptr,
2452 ImGuiWindowFlags_AlwaysAutoResize)) {
2453 ImGui::SliderFloat("Scale", &scale, 1, 2);
2454 ImGui::End();
2455 }
2456 Canvas canvas;
2457 canvas.Scale(GetContentScale());
2458 auto image =
2459 std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2460 canvas.Translate({600, -200});
2461 canvas.SaveLayer({
2462 .image_filter = std::make_shared<MatrixImageFilter>(
2464 });
2465 canvas.DrawImage(image, {0, 0},
2466 Paint{.color = Color::White().WithAlpha(0.5)});
2467 canvas.Restore();
2468 return canvas.EndRecordingAsPicture();
2469 };
2470
2471 ASSERT_TRUE(OpenPlaygroundHere(callback));
2472}
2473
2474// Render a white circle at the top left corner of the screen.
2475TEST_P(AiksTest, MatrixImageFilterDoesntCullWhenTranslatedFromOffscreen) {
2476 Canvas canvas;
2477 canvas.Scale(GetContentScale());
2478 canvas.Translate({100, 100});
2479 // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
2480 // +300 translation applied by a SaveLayer image filter.
2481 canvas.SaveLayer({
2482 .image_filter = std::make_shared<MatrixImageFilter>(
2484 });
2485 canvas.DrawCircle({-300, 0}, 100, {.color = Color::Green()});
2486 canvas.Restore();
2487
2488 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2489}
2490
2491// Render a white circle at the top left corner of the screen.
2493 MatrixImageFilterDoesntCullWhenScaledAndTranslatedFromOffscreen) {
2494 Canvas canvas;
2495 canvas.Scale(GetContentScale());
2496 canvas.Translate({100, 100});
2497 // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
2498 // +300 translation applied by a SaveLayer image filter.
2499 canvas.SaveLayer({
2500 .image_filter = std::make_shared<MatrixImageFilter>(
2501 Matrix::MakeTranslation({300, 0}) * Matrix::MakeScale({2, 2, 2}),
2503 });
2504 canvas.DrawCircle({-150, 0}, 50, {.color = Color::Green()});
2505 canvas.Restore();
2506
2507 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2508}
2509
2510// This should be solid red, if you see a little red box this is broken.
2511TEST_P(AiksTest, ClearColorOptimizationWhenSubpassIsBiggerThanParentPass) {
2512 SetWindowSize({400, 400});
2513 Canvas canvas;
2514 canvas.Scale(GetContentScale());
2515 canvas.DrawRect(Rect::MakeLTRB(200, 200, 300, 300), {.color = Color::Red()});
2516 canvas.SaveLayer({
2517 .image_filter = std::make_shared<MatrixImageFilter>(
2518 Matrix::MakeScale({2, 2, 1}), SamplerDescriptor{}),
2519 });
2520 // Draw a rectangle that would fully cover the parent pass size, but not
2521 // the subpass that it is rendered in.
2522 canvas.DrawRect(Rect::MakeLTRB(0, 0, 400, 400), {.color = Color::Green()});
2523 // Draw a bigger rectangle to force the subpass to be bigger.
2524 canvas.DrawRect(Rect::MakeLTRB(0, 0, 800, 800), {.color = Color::Red()});
2525 canvas.Restore();
2526
2527 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2528}
2529
2530TEST_P(AiksTest, EmptySaveLayerIgnoresPaint) {
2531 Canvas canvas;
2532 canvas.Scale(GetContentScale());
2533 canvas.DrawPaint(Paint{.color = Color::Red()});
2534 canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
2535 canvas.SaveLayer(Paint{.color = Color::Blue()});
2536 canvas.Restore();
2537 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2538}
2539
2540TEST_P(AiksTest, EmptySaveLayerRendersWithClear) {
2541 Canvas canvas;
2542 canvas.Scale(GetContentScale());
2543 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2544 canvas.DrawImage(image, {10, 10}, {});
2545 canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
2546 canvas.SaveLayer(Paint{.blend_mode = BlendMode::kClear});
2547 canvas.Restore();
2548 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2549}
2550
2551TEST_P(AiksTest, SubpassWithClearColorOptimization) {
2552 Canvas canvas;
2553
2554 // Use a non-srcOver blend mode to ensure that we don't detect this as an
2555 // opacity peephole optimization.
2556 canvas.SaveLayer(
2557 {.color = Color::Blue().WithAlpha(0.5), .blend_mode = BlendMode::kSource},
2558 Rect::MakeLTRB(0, 0, 200, 200));
2559 canvas.DrawPaint(
2560 {.color = Color::BlackTransparent(), .blend_mode = BlendMode::kSource});
2561 canvas.Restore();
2562
2563 canvas.SaveLayer(
2564 {.color = Color::Blue(), .blend_mode = BlendMode::kDestinationOver});
2565 canvas.Restore();
2566
2567 // This playground should appear blank on CI since we are only drawing
2568 // transparent black. If the clear color optimization is broken, the texture
2569 // will be filled with NaNs and may produce a magenta texture on macOS or iOS.
2570 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2571}
2572
2573TEST_P(AiksTest, ImageColorSourceEffectTransform) {
2574 // Compare with https://fiddle.skia.org/c/6cdc5aefb291fda3833b806ca347a885
2575
2576 Canvas canvas;
2577 auto texture = CreateTextureForFixture("monkey.png");
2578
2579 canvas.DrawPaint({.color = Color::White()});
2580
2581 // Translation
2582 {
2583 Paint paint;
2584 paint.color_source = ColorSource::MakeImage(
2586 Matrix::MakeTranslation({50, 50}));
2587 canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint);
2588 }
2589
2590 // Rotation/skew
2591 {
2592 canvas.Save();
2593 canvas.Rotate(Degrees(45));
2594 Paint paint;
2595 paint.color_source = ColorSource::MakeImage(
2597 Matrix(1, -1, 0, 0, //
2598 1, 1, 0, 0, //
2599 0, 0, 1, 0, //
2600 0, 0, 0, 1) //
2601 );
2602 canvas.DrawRect(Rect::MakeLTRB(100, 0, 200, 100), paint);
2603 canvas.Restore();
2604 }
2605
2606 // Scale
2607 {
2608 canvas.Translate(Vector2(100, 0));
2609 canvas.Scale(Vector2(100, 100));
2610 Paint paint;
2611 paint.color_source = ColorSource::MakeImage(
2613 Matrix::MakeScale(Vector2(0.005, 0.005)));
2614 canvas.DrawRect(Rect::MakeLTRB(0, 0, 1, 1), paint);
2615 }
2616
2617 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2618}
2619
2620TEST_P(AiksTest, CorrectClipDepthAssignedToEntities) {
2621 Canvas canvas; // Depth 1 (base pass)
2622 canvas.DrawRRect(Rect::MakeLTRB(0, 0, 100, 100), {10, 10}, {}); // Depth 2
2623 canvas.Save();
2624 {
2625 canvas.ClipRRect(Rect::MakeLTRB(0, 0, 50, 50), {10, 10}, {}); // Depth 4
2626 canvas.SaveLayer({}); // Depth 4
2627 {
2628 canvas.DrawRRect(Rect::MakeLTRB(0, 0, 50, 50), {10, 10}, {}); // Depth 3
2629 }
2630 canvas.Restore(); // Restore the savelayer.
2631 }
2632 canvas.Restore(); // Depth 5 -- this will no longer append a restore entity
2633 // once we switch to the clip depth approach.
2634
2635 auto picture = canvas.EndRecordingAsPicture();
2636
2637 std::vector<uint32_t> expected = {
2638 2, // DrawRRect
2639 4, // ClipRRect -- Has a depth value equal to the max depth of all the
2640 // content it affect. In this case, the SaveLayer and all
2641 // its contents are affected.
2642 4, // SaveLayer -- The SaveLayer is drawn to the parent pass after its
2643 // contents are rendered, so it should have a depth value
2644 // greater than all its contents.
2645 3, // DrawRRect
2646 5, // Restore (no longer necessary when clipping on the depth buffer)
2647 };
2648
2649 std::vector<uint32_t> actual;
2650
2651 picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool {
2652 if (auto* subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
2653 actual.push_back(subpass->get()->GetClipDepth());
2654 }
2655 if (Entity* entity = std::get_if<Entity>(&element)) {
2656 actual.push_back(entity->GetClipDepth());
2657 }
2658 return true;
2659 });
2660
2661 ASSERT_EQ(actual.size(), expected.size());
2662 for (size_t i = 0; i < expected.size(); i++) {
2663 EXPECT_EQ(expected[i], actual[i]) << "Index: " << i;
2664 }
2665}
2666
2667TEST_P(AiksTest, CanDrawPerspectiveTransformWithClips) {
2668 // Avoiding `GetSecondsElapsed()` to reduce risk of golden flakiness.
2669 int time = 0;
2670 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
2671 Canvas canvas;
2672
2673 canvas.Save();
2674 {
2675 canvas.Translate({300, 300});
2676
2677 // 1. Draw/restore a clip before drawing the image, which will get drawn
2678 // to the depth buffer behind the image.
2679 canvas.Save();
2680 {
2681 canvas.DrawPaint({.color = Color::Green()});
2682 canvas.ClipRect(Rect::MakeLTRB(-180, -180, 180, 180),
2684 canvas.DrawPaint({.color = Color::Black()});
2685 }
2686 canvas.Restore(); // Restore rectangle difference clip.
2687
2688 canvas.Save();
2689 {
2690 // 2. Draw an oval clip that applies to the image, which will get drawn
2691 // in front of the image on the depth buffer.
2692 canvas.ClipOval(Rect::MakeLTRB(-200, -200, 200, 200));
2693
2694 // 3. Draw the rotating image with a perspective transform.
2695 canvas.Transform(
2696 Matrix(1.0, 0.0, 0.0, 0.0, //
2697 0.0, 1.0, 0.0, 0.0, //
2698 0.0, 0.0, 1.0, 0.003, //
2699 0.0, 0.0, 0.0, 1.0) * //
2700 Matrix::MakeRotationY({Radians{-1.0f + (time++ / 60.0f)}}));
2701 auto image =
2702 std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2703 canvas.DrawImage(image, -Point(image->GetSize()) / 2, {});
2704 }
2705 canvas.Restore(); // Restore oval intersect clip.
2706
2707 // 4. Draw a semi-translucent blue circle atop all previous draws.
2708 canvas.DrawCircle({}, 230, {.color = Color::Blue().WithAlpha(0.4)});
2709 }
2710 canvas.Restore(); // Restore translation.
2711
2712 return canvas.EndRecordingAsPicture();
2713 };
2714 ASSERT_TRUE(OpenPlaygroundHere(callback));
2715}
2716
2717TEST_P(AiksTest, CanRenderClippedBackdropFilter) {
2718 Canvas canvas;
2719 Paint paint;
2720
2721 canvas.Scale(GetContentScale());
2722
2723 // Draw something interesting in the background.
2724 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
2725 Color{0.1294, 0.5882, 0.9529, 1.0}};
2726 std::vector<Scalar> stops = {
2727 0.0,
2728 1.0,
2729 };
2731 {0, 0}, {100, 100}, std::move(colors), std::move(stops),
2733 canvas.DrawPaint(paint);
2734
2735 Rect clip_rect = Rect::MakeLTRB(50, 50, 400, 300);
2736
2737 // Draw a clipped SaveLayer, where the clip coverage and SaveLayer size are
2738 // the same.
2739 canvas.ClipRRect(clip_rect, Size(100, 100),
2741 canvas.SaveLayer({}, clip_rect,
2744 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2745}
2746
2747TEST_P(AiksTest, MipmapGenerationWorksCorrectly) {
2748 TextureDescriptor texture_descriptor;
2749 texture_descriptor.size = ISize{1024, 1024};
2750 texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
2751 texture_descriptor.storage_mode = StorageMode::kHostVisible;
2752 texture_descriptor.mip_count = texture_descriptor.size.MipCount();
2753
2754 std::vector<uint8_t> bytes(4194304);
2755 bool alternate = false;
2756 for (auto i = 0u; i < 4194304; i += 4) {
2757 if (alternate) {
2758 bytes[i] = 255;
2759 bytes[i + 1] = 0;
2760 bytes[i + 2] = 0;
2761 bytes[i + 3] = 255;
2762 } else {
2763 bytes[i] = 0;
2764 bytes[i + 1] = 255;
2765 bytes[i + 2] = 0;
2766 bytes[i + 3] = 255;
2767 }
2768 alternate = !alternate;
2769 }
2770
2771 ASSERT_EQ(texture_descriptor.GetByteSizeOfBaseMipLevel(), bytes.size());
2772 auto mapping = std::make_shared<fml::NonOwnedMapping>(
2773 bytes.data(), // data
2774 texture_descriptor.GetByteSizeOfBaseMipLevel() // size
2775 );
2776 auto texture =
2777 GetContext()->GetResourceAllocator()->CreateTexture(texture_descriptor);
2778
2779 auto device_buffer =
2780 GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
2781 auto command_buffer = GetContext()->CreateCommandBuffer();
2782 auto blit_pass = command_buffer->CreateBlitPass();
2783
2784 blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(device_buffer)),
2785 texture);
2786 blit_pass->GenerateMipmap(texture);
2787 EXPECT_TRUE(blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()));
2788 EXPECT_TRUE(GetContext()->GetCommandQueue()->Submit({command_buffer}).ok());
2789
2790 auto image = std::make_shared<Image>(texture);
2791
2792 Canvas canvas;
2793 canvas.DrawImageRect(image, Rect::MakeSize(texture->GetSize()),
2794 Rect::MakeLTRB(0, 0, 100, 100), {});
2795
2796 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2797}
2798
2799// https://github.com/flutter/flutter/issues/146648
2800TEST_P(AiksTest, StrokedPathWithMoveToThenCloseDrawnCorrectly) {
2802 .MoveTo({0, 400})
2803 .LineTo({0, 0})
2804 .LineTo({400, 0})
2805 // MoveTo implicitly adds a contour, ensure that close doesn't
2806 // add another nearly-empty contour.
2807 .MoveTo({0, 400})
2808 .Close()
2809 .TakePath();
2810
2811 Canvas canvas;
2812 canvas.Translate({50, 50, 0});
2813 canvas.DrawPath(path, {
2814 .color = Color::Blue(),
2815 .stroke_width = 10,
2816 .stroke_cap = Cap::kRound,
2817 .style = Paint::Style::kStroke,
2818 });
2819 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2820}
2821
2822TEST_P(AiksTest, CanRenderTextWithLargePerspectiveTransform) {
2823 // Verifies that text scales are clamped to work around
2824 // https://github.com/flutter/flutter/issues/136112 .
2825
2826 Canvas canvas;
2827 Paint save_paint;
2828 canvas.SaveLayer(save_paint);
2829 canvas.Transform(Matrix(2000, 0, 0, 0, //
2830 0, 2000, 0, 0, //
2831 0, 0, -1, 9000, //
2832 0, 0, -1, 7000 //
2833 ));
2834
2835 ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
2836 "Roboto-Regular.ttf"));
2837 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2838}
2839
2840TEST_P(AiksTest, SetContentsWithRegion) {
2841 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
2842
2843 // Replace part of the texture with a red rectangle.
2844 std::vector<uint8_t> bytes(100 * 100 * 4);
2845 for (auto i = 0u; i < bytes.size(); i += 4) {
2846 bytes[i] = 255;
2847 bytes[i + 1] = 0;
2848 bytes[i + 2] = 0;
2849 bytes[i + 3] = 255;
2850 }
2851 auto mapping =
2852 std::make_shared<fml::NonOwnedMapping>(bytes.data(), bytes.size());
2853 auto device_buffer =
2854 GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
2855 auto cmd_buffer = GetContext()->CreateCommandBuffer();
2856 auto blit_pass = cmd_buffer->CreateBlitPass();
2857 blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), bridge,
2858 IRect::MakeLTRB(50, 50, 150, 150));
2859
2860 auto did_submit =
2861 blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()) &&
2862 GetContext()->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok();
2863 ASSERT_TRUE(did_submit);
2864
2865 auto image = std::make_shared<Image>(bridge);
2866
2867 Canvas canvas;
2868 canvas.DrawImage(image, {0, 0}, {});
2869
2870 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2871}
2872
2873} // namespace testing
2874} // namespace impeller
2875
2876// █████████████████████████████████████████████████████████████████████████████
2877// █ NOTICE: Before adding new tests to this file consider adding it to one of
2878// █ the subdivisions of AiksTest to avoid having one massive file.
2879// █
2880// █ Subdivisions:
2881// █ - aiks_blend_unittests.cc
2882// █ - aiks_blur_unittests.cc
2883// █ - aiks_gradient_unittests.cc
2884// █ - aiks_path_unittests.cc
2885// █████████████████████████████████████████████████████████████████████████████
const char * options
#define test(name)
static const int points[]
static double magnitude(double a)
static bool ok(int result)
static SkBlendMode GetBlendMode(SkSVGFeBlend::Mode mode)
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition: aaclip.cpp:27
sk_sp< SkTypeface > makeFromData(sk_sp< SkData >, int ttcIndex=0) const
Definition: SkFontMgr.cpp:120
Definition: SkFont.h:35
static sk_sp< SkTextBlob > MakeFromString(const char *string, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.h:115
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
static std::shared_ptr< ColorFilter > MakeMatrix(ColorMatrix color_matrix)
Definition: color_filter.cc:28
static std::shared_ptr< ColorFilter > MakeBlend(BlendMode blend_mode, Color color)
Definition: color_filter.cc:23
static std::shared_ptr< ColorFilter > MakeLinearToSrgb()
Definition: color_filter.cc:36
static std::shared_ptr< ColorFilter > MakeSrgbToLinear()
Definition: color_filter.cc:32
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 MakeImage(std::shared_ptr< Texture > texture, Entity::TileMode x_tile_mode, Entity::TileMode y_tile_mode, SamplerDescriptor sampler_descriptor, Matrix effect_transform)
static ColorSource MakeRuntimeEffect(std::shared_ptr< RuntimeStage > runtime_stage, std::shared_ptr< std::vector< uint8_t > > uniform_data, std::vector< RuntimeEffectContents::TextureInput > texture_inputs)
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
std::variant< Entity, std::unique_ptr< EntityPass > > Element
Definition: entity_pass.h:54
@ kNormal
Blurred inside and outside.
static std::shared_ptr< ImageFilter > MakeMatrix(const Matrix &matrix, SamplerDescriptor sampler_descriptor)
Definition: image_filter.cc:39
static std::shared_ptr< ImageFilter > MakeLocalMatrix(const Matrix &matrix, const ImageFilter &internal_filter)
Definition: image_filter.cc:57
static std::shared_ptr< ImageFilter > MakeErode(Radius radius_x, Radius radius_y)
Definition: image_filter.cc:34
static std::shared_ptr< ImageFilter > MakeBlur(Sigma sigma_x, Sigma sigma_y, FilterContents::BlurStyle blur_style, Entity::TileMode tile_mode)
Definition: image_filter.cc:20
static std::shared_ptr< ImageFilter > MakeDilate(Radius radius_x, Radius radius_y)
Definition: image_filter.cc:29
static std::shared_ptr< ImageFilter > MakeFromColorFilter(const ColorFilter &color_filter)
Definition: image_filter.cc:52
static std::shared_ptr< ImageFilter > MakeCompose(const ImageFilter &inner, const ImageFilter &outer)
Definition: image_filter.cc:46
PathBuilder & AddRect(Rect rect)
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:22
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:33
PathBuilder & AddCircle(const Point &center, Scalar radius)
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:85
static std::unique_ptr< TypographerContext > Make()
static std::shared_ptr< Node > MakeFromFlatbuffer(const fml::Mapping &ipscene_mapping, Allocator &allocator)
Definition: node.cc:47
static std::shared_ptr< ContextSpy > Make()
Definition: context_spy.cc:23
const Paint & paint
Definition: color_source.cc:38
DlColor color
SkScalar font_size
double frame
Definition: examples.cpp:31
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
GAsyncResult * result
#define ASSERT_MATRIX_NEAR(a, b)
#define ASSERT_COLOR_NEAR(a, b)
std::u16string text
static MockContext mock_context
Definition: mock_epoxy.cc:51
FlTexture * texture
double y
double x
SK_API GrDirectContext * GetContext(const SkImage *src)
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< const SkImage > image
Definition: SkRecords.h:269
sk_sp< const SkPicture > picture
Definition: SkRecords.h:299
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
PODArray< SkColor > colors
Definition: SkRecords.h:276
SK_API sk_sp< SkShader > Color(SkColor)
sk_sp< SkData > OpenFixtureAsSkData(const std::string &fixture_name)
Opens a fixture of the given file name and returns a Skia SkData holding its contents.
Definition: testing.cc:64
std::unique_ptr< fml::Mapping > OpenFixtureAsMapping(const std::string &fixture_name)
Opens a fixture of the given file name and returns a mapping to its contents.
Definition: testing.cc:59
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
AiksPlayground AiksTest
bool RenderTextInCanvasSTB(const std::shared_ptr< Context > &context, Canvas &canvas, const std::string &text, const std::string &font_fixture, TextRenderOptions options={})
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer)
static constexpr std::string_view kFontFixture
bool RenderTextInCanvasSkia(const std::shared_ptr< Context > &context, Canvas &canvas, const std::string &text, const std::string_view &font_fixture, TextRenderOptions options={})
constexpr float k2Pi
Definition: constants.h:29
Point Vector2
Definition: point.h:326
constexpr float kPi
Definition: constants.h:26
float Scalar
Definition: scalar.h:18
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
std::shared_ptr< TextFrame > MakeTextFrameSTB(const std::shared_ptr< TypefaceSTB > &typeface_stb, Font::Metrics metrics, const std::string &text)
constexpr RuntimeStageBackend PlaygroundBackendToRuntimeStageBackend(PlaygroundBackend backend)
Definition: playground.h:33
@ kRound
Points are drawn as squares.
@ kSquare
Points are drawn as circles.
TPoint< Scalar > Point
Definition: point.h:322
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
constexpr float kPiOver2
Definition: constants.h:32
TSize< Scalar > Size
Definition: size.h:137
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:20
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:30
constexpr float k1OverSqrt2
Definition: constants.h:50
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
constexpr const char * PixelFormatToString(PixelFormat format)
Definition: formats.h:140
SK_API sk_sp< PrecompileColorFilter > Matrix()
sk_sp< SkFontMgr > GetDefaultFontManager(uint32_t font_initialization_data)
Definition: platform.cc:17
static double time(int loops, Benchmark *bench, Target *target)
Definition: nanobench.cpp:394
const Scalar stroke_width
const Scalar scale
SeparatedVector2 offset
static constexpr Color Crimson()
Definition: color.h:352
static constexpr Color LimeGreen()
Definition: color.h:604
static constexpr Color BlackTransparent()
Definition: color.h:272
static constexpr Color Black()
Definition: color.h:268
static constexpr Color CornflowerBlue()
Definition: color.h:344
static constexpr Color MediumTurquoise()
Definition: color.h:648
static constexpr Color White()
Definition: color.h:266
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:280
static constexpr Color Orange()
Definition: color.h:692
static constexpr Color Purple()
Definition: color.h:744
static constexpr Color Red()
Definition: color.h:274
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:154
static constexpr Color Yellow()
Definition: color.h:844
Color Blend(Color source, BlendMode blend_mode) const
Blends an unpremultiplied destination color into a given unpremultiplied source color to form a new u...
Definition: color.cc:234
static constexpr Color Blue()
Definition: color.h:278
static constexpr Color Green()
Definition: color.h:276
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:506
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:198
static constexpr Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:532
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:213
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
FilterContents::BlurStyle style
Definition: paint.h:49
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:48
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
static constexpr TPoint< Type > MakeXY(Type x, Type y)
Definition: point.h:46
static constexpr TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:140
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
static constexpr TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
constexpr size_t MipCount() const
Definition: size.h:115
static constexpr TSize MakeWH(Type width, Type height)
Definition: size.h:34
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
constexpr size_t GetByteSizeOfBaseMipLevel() const
std::optional< Paint::MaskBlurDescriptor > mask_blur_descriptor
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678