| Flutter Impeller |

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"
17 #include "impeller/aiks/canvas.h"
18 #include "impeller/aiks/color_filter.h"
19 #include "impeller/aiks/image.h"
20 #include "impeller/aiks/image_filter.h"
21 #include "impeller/aiks/testing/context_spy.h"
22 #include "impeller/core/device_buffer.h"
23 #include "impeller/entity/contents/solid_color_contents.h"
24 #include "impeller/geometry/color.h"
25 #include "impeller/geometry/constants.h"
26 #include "impeller/geometry/geometry_asserts.h"
27 #include "impeller/geometry/matrix.h"
28 #include "impeller/geometry/path.h"
29 #include "impeller/geometry/path_builder.h"
30 #include "impeller/geometry/rect.h"
31 #include "impeller/geometry/size.h"
32 #include "impeller/playground/widgets.h"
33 #include "impeller/renderer/command_buffer.h"
34 #include "impeller/renderer/snapshot.h"
35 #include "impeller/typographer/backends/skia/text_frame_skia.h"
36 #include "impeller/typographer/backends/stb/text_frame_stb.h"
37 #include "impeller/typographer/backends/stb/typeface_stb.h"
38 #include "impeller/typographer/backends/stb/typographer_context_stb.h"
39 #include "third_party/imgui/imgui.h"
40 #include "third_party/skia/include/core/SkFontMgr.h"
41 #include "txt/platform.h"
42
43 namespace impeller {
44 namespace testing {
45
46 INSTANTIATE_PLAYGROUND_SUITE(AiksTest);
47
48 TEST_P(AiksTest, CanvasCTMCanBeUpdated) {
49 Canvas canvas;
50 Matrix identity;
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
57 TEST_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
73 TEST_P(AiksTest, CanRenderColoredRect) {
74 Canvas canvas;
75 Paint paint;
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
84 TEST_P(AiksTest, CanRenderImage) {
85 Canvas canvas;
86 Paint paint;
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
93 TEST_P(AiksTest, CanRenderInvertedImageWithColorFilter) {
94 Canvas canvas;
95 Paint paint;
96 auto image = std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
97 paint.color = Color::Red();
98 paint.color_filter =
99 ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow());
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
106 TEST_P(AiksTest, CanRenderColorFilterWithInvertColors) {
107 Canvas canvas;
108 Paint paint;
109 paint.color = Color::Red();
110 paint.color_filter =
111 ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow());
112 paint.invert_colors = true;
113
114 canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint);
115 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
116 }
117
118 TEST_P(AiksTest, CanRenderColorFilterWithInvertColorsDrawPaint) {
119 Canvas canvas;
120 Paint paint;
121 paint.color = Color::Red();
122 paint.color_filter =
123 ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow());
124 paint.invert_colors = true;
125
126 canvas.DrawPaint(paint);
127 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
128 }
129
130 namespace {
131 bool 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
148 void 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;
167 paint.style = Paint::Style::kStroke;
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);
181 paint.style = Paint::Style::kFill;
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);
190 paint.style = Paint::Style::kFill;
191 canvas.DrawPath(path_builder.TakePath(), paint);
192 }
193
194 ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
195 }
196 } // namespace
197
198 TEST_P(AiksTest, CanRenderTiledTextureClamp) {
199 CanRenderTiledTexture(this, Entity::TileMode::kClamp);
200 }
201
202 TEST_P(AiksTest, CanRenderTiledTextureRepeat) {
203 CanRenderTiledTexture(this, Entity::TileMode::kRepeat);
204 }
205
206 TEST_P(AiksTest, CanRenderTiledTextureMirror) {
207 CanRenderTiledTexture(this, Entity::TileMode::kMirror);
208 }
209
210 TEST_P(AiksTest, CanRenderTiledTextureDecal) {
211 CanRenderTiledTexture(this, Entity::TileMode::kDecal);
212 }
213
214 TEST_P(AiksTest, CanRenderTiledTextureClampWithTranslate) {
215 CanRenderTiledTexture(this, Entity::TileMode::kClamp,
216 Matrix::MakeTranslation({172.f, 172.f, 0.f}));
217 }
218
219 TEST_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
234 TEST_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
301 paint.color_source = ColorSource::MakeRadialGradient(
302 {500, 600}, 75, std::move(gradient_colors), std::move(stops),
303 Entity::TileMode::kMirror, {});
304 draw(paint, 0, 300);
305
306 paint.color_source = ColorSource::MakeImage(
307 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
308 Matrix::MakeTranslation({0, 0}));
309 draw(paint, 300, 0);
310
311 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
312 }
313
314 TEST_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
332 TEST_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
343 paint.color_source = ColorSource::MakeLinearGradient(
344 {0, 0}, {100, 100}, std::move(colors), std::move(stops),
345 Entity::TileMode::kRepeat, {});
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
359 TEST_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);
369 paint.color = Color::BlackTransparent();
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.
385 TEST_P(AiksTest, CanEmptyPictureConvertToImage) {
386 Canvas recorder_canvas;
387
388 Canvas canvas;
389 AiksContext renderer(GetContext(), nullptr);
390 Paint paint;
391 paint.color = Color::BlackTransparent();
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
404 TEST_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
445 TEST_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
456 TEST_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
468 TEST_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
494 TEST_P(AiksTest,
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
521 TEST_P(AiksTest, CanRenderRoundedRectWithNonUniformRadii) {
522 Canvas canvas;
523
524 Paint paint;
525 paint.color = Color::Red();
526
527 PathBuilder::RoundingRadii radii;
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
542 struct TextRenderOptions {
543 bool stroke = false;
544 Scalar font_size = 50;
545 Color color = Color::Yellow();
546 Point position = Vector2(100, 200);
547 std::optional<Paint::MaskBlurDescriptor> mask_blur_descriptor;
548 };
549
550 bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
551 Canvas& canvas,
552 const std::string& text,
553 const std::string_view& font_fixture,
554 TextRenderOptions options = {}) {
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 }
570 sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
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.
578 auto frame = MakeTextFrameFromTextBlobSkia(blob);
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 =
585 options.stroke ? Paint::Style::kStroke : Paint::Style::kFill;
586 canvas.DrawTextFrame(frame, options.position, text_paint);
587 return true;
588 }
589
590 bool RenderTextInCanvasSTB(const std::shared_ptr<Context>& context,
591 Canvas& canvas,
592 const std::string& text,
593 const std::string& font_fixture,
594 TextRenderOptions options = {}) {
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
620 TEST_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
629 TEST_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
641 TEST_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
651 TEST_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
661 TEST_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
672 TEST_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 );
703 if (!RenderTextInCanvasSkia(
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
718 TEST_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
728 static constexpr std::string_view kFontFixture =
729 #if FML_OS_MACOSX
730 "Apple Color Emoji.ttc";
731 #else
732 "NotoColorEmoji.ttf";
733 #endif
734
735 TEST_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
744 TEST_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,
750 TextRenderOptions{.color = Color::Blue(),
751 .mask_blur_descriptor = Paint::MaskBlurDescriptor{
752 .style = FilterContents::BlurStyle::kNormal,
753 .sigma = Sigma(4)}}));
754 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
755 }
756
757 TEST_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
767 TEST_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
789 TEST_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;
798 sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
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);
818 auto frame = MakeTextFrameFromTextBlobSkia(blob);
819 canvas.DrawTextFrame(frame, Point(), text_paint);
820 }
821 canvas.Restore();
822 }
823
824 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
825 }
826
827 TEST_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
843 TEST_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
850 TEST_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.
859 TEST_P(AiksTest, FormatWideGamut) {
860 EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
861 PixelFormat::kB10G10R10A10XR);
862 }
863
864 TEST_P(AiksTest, FormatSRGB) {
865 PixelFormat pixel_format =
866 GetContext()->GetCapabilities()->GetDefaultColorFormat();
867 EXPECT_TRUE(pixel_format == PixelFormat::kR8G8B8A8UNormInt ||
868 pixel_format == PixelFormat::kB8G8R8A8UNormInt)
869 << "pixel format: " << PixelFormatToString(pixel_format);
870 }
871
872 TEST_P(AiksTest, TransformMultipliesCorrectly) {
873 Canvas canvas;
874 ASSERT_MATRIX_NEAR(canvas.GetCurrentTransform(), Matrix());
875
876 // clang-format off
877 canvas.Translate(Vector3(100, 200));
878 ASSERT_MATRIX_NEAR(
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));
886 ASSERT_MATRIX_NEAR(
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));
894 ASSERT_MATRIX_NEAR(
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));
902 ASSERT_MATRIX_NEAR(
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
911 TEST_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(),
919 Color::Crimson(),
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
957 paint.color_source = ColorSource::MakeRadialGradient(
958 {500, 600}, 75, std::move(gradient_colors), std::move(stops),
959 Entity::TileMode::kMirror, {});
960 canvas.DrawCircle({500, 600}, 100, paint);
961
962 paint.color_source = ColorSource::MakeImage(
963 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
964 Matrix::MakeTranslation({700, 200}));
965 canvas.DrawCircle({800, 300}, 100, paint);
966
967 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
968 }
969
970 TEST_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(),
978 Color::Crimson(),
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
995 paint.style = Paint::Style::kStroke;
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
1021 paint.color_source = ColorSource::MakeRadialGradient(
1022 {500, 600}, 75, std::move(gradient_colors), std::move(stops),
1023 Entity::TileMode::kMirror, {});
1024 draw(canvas, {500, 600}, 5, 10, 10);
1025
1026 paint.color_source = ColorSource::MakeImage(
1027 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
1028 Matrix::MakeTranslation({700, 200}));
1029 draw(canvas, {800, 300}, 5, 10, 10);
1030
1031 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1032 }
1033
1034 TEST_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(),
1042 Color::Crimson(),
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
1090 paint.color_source = ColorSource::MakeRadialGradient(
1091 {300, 650}, 75, std::move(gradient_colors), std::move(stops),
1092 Entity::TileMode::kMirror, {});
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(
1097 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
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
1105 TEST_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(),
1113 Color::Crimson(),
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);
1153 paint.color_source = ColorSource::MakeRadialGradient(
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);
1162 paint.color_source = ColorSource::MakeRadialGradient(
1163 {200, 650}, 75, std::move(gradient_colors), std::move(stops),
1164 Entity::TileMode::kMirror, {});
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(
1170 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
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(
1180 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
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
1188 TEST_P(AiksTest, SolidColorCirclesOvalsRRectsMaskBlurCorrectly) {
1189 Canvas canvas;
1190 canvas.Scale(GetContentScale());
1191 Paint paint;
1192 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
1193 .style = FilterContents::BlurStyle::kNormal,
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
1249 TEST_P(AiksTest, FastEllipticalRRectMaskBlursRenderCorrectly) {
1250 Canvas canvas;
1251 canvas.Scale(GetContentScale());
1252 Paint paint;
1253 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
1254 .style = FilterContents::BlurStyle::kNormal,
1255 .sigma = Sigma{1},
1256 };
1257
1258 canvas.DrawPaint({.color = Color::White()});
1259
1260 paint.color = Color::Blue();
1261 for (int i = 0; i < 5; i++) {
1262 Scalar y = i * 125;
1263 Scalar y_radius = i * 15;
1264 for (int j = 0; j < 5; j++) {
1265 Scalar x = j * 125;
1266 Scalar x_radius = j * 15;
1267 canvas.DrawRRect(Rect::MakeXYWH(x + 50, y + 50, 100.0f, 100.0f),
1268 {x_radius, y_radius}, paint);
1269 }
1270 }
1271
1272 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1273 }
1274
1275 TEST_P(AiksTest, FilledRoundRectPathsRenderCorrectly) {
1276 Canvas canvas;
1277 canvas.Scale(GetContentScale());
1278 Paint paint;
1279 const int color_count = 3;
1280 Color colors[color_count] = {
1281 Color::Blue(),
1282 Color::Green(),
1283 Color::Crimson(),
1284 };
1285
1286 paint.color = Color::White();
1287 canvas.DrawPaint(paint);
1288
1289 auto draw_rrect_as_path = [&canvas](const Rect& rect, const Size& radii,
1290 const Paint& paint) {
1291 PathBuilder builder = PathBuilder();
1292 builder.AddRoundedRect(rect, radii);
1293 canvas.DrawPath(builder.TakePath(), paint);
1294 };
1295
1296 int c_index = 0;
1297 for (int i = 0; i < 4; i++) {
1298 for (int j = 0; j < 4; j++) {
1299 paint.color = colors[(c_index++) % color_count];
1300 draw_rrect_as_path(Rect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80),
1301 Size(i * 5 + 10, j * 5 + 10), paint);
1302 }
1303 }
1304 paint.color = colors[(c_index++) % color_count];
1305 draw_rrect_as_path(Rect::MakeXYWH(10, 420, 380, 80), Size(40, 40), paint);
1306 paint.color = colors[(c_index++) % color_count];
1307 draw_rrect_as_path(Rect::MakeXYWH(410, 20, 80, 380), Size(40, 40), paint);
1308
1309 std::vector<Color> gradient_colors = {
1310 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1311 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1312 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1313 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1314 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1315 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1316 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1317 std::vector<Scalar> stops = {
1318 0.0,
1319 (1.0 / 6.0) * 1,
1320 (1.0 / 6.0) * 2,
1321 (1.0 / 6.0) * 3,
1322 (1.0 / 6.0) * 4,
1323 (1.0 / 6.0) * 5,
1324 1.0,
1325 };
1326 auto texture = CreateTextureForFixture("airplane.jpg",
1327 /*enable_mipmapping=*/true);
1328
1329 paint.color = Color::White().WithAlpha(0.1);
1330 paint.color_source = ColorSource::MakeRadialGradient(
1331 {550, 550}, 75, gradient_colors, stops, Entity::TileMode::kMirror, {});
1332 for (int i = 1; i <= 10; i++) {
1333 int j = 11 - i;
1334 draw_rrect_as_path(Rect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1335 550 + i * 20, 550 + j * 20),
1336 Size(i * 10, j * 10), paint);
1337 }
1338 paint.color = Color::White().WithAlpha(0.5);
1339 paint.color_source = ColorSource::MakeRadialGradient(
1340 {200, 650}, 75, std::move(gradient_colors), std::move(stops),
1341 Entity::TileMode::kMirror, {});
1342 draw_rrect_as_path(Rect::MakeLTRB(100, 610, 300, 690), Size(40, 40), paint);
1343 draw_rrect_as_path(Rect::MakeLTRB(160, 550, 240, 750), Size(40, 40), paint);
1344
1345 paint.color = Color::White().WithAlpha(0.1);
1346 paint.color_source = ColorSource::MakeImage(
1347 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
1348 Matrix::MakeTranslation({520, 20}));
1349 for (int i = 1; i <= 10; i++) {
1350 int j = 11 - i;
1351 draw_rrect_as_path(Rect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1352 720 + i * 20, 220 + j * 20),
1353 Size(i * 10, j * 10), paint);
1354 }
1355 paint.color = Color::White().WithAlpha(0.5);
1356 paint.color_source = ColorSource::MakeImage(
1357 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
1358 Matrix::MakeTranslation({800, 300}));
1359 draw_rrect_as_path(Rect::MakeLTRB(800, 410, 1000, 490), Size(40, 40), paint);
1360 draw_rrect_as_path(Rect::MakeLTRB(860, 350, 940, 550), Size(40, 40), paint);
1361
1362 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1363 }
1364
1365 TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
1366 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1367 Canvas canvas;
1368 canvas.Scale(GetContentScale());
1369
1370 Paint alpha;
1371 alpha.color = Color::Red().WithAlpha(0.5);
1372
1373 auto current = Point{25, 25};
1374 const auto offset = Point{25, 25};
1375 const auto size = Size(100, 100);
1376
1377 static PlaygroundPoint point_a(Point(40, 40), 10, Color::White());
1378 static PlaygroundPoint point_b(Point(160, 160), 10, Color::White());
1379 auto [b0, b1] = DrawPlaygroundLine(point_a, point_b);
1380 auto bounds = Rect::MakeLTRB(b0.x, b0.y, b1.x, b1.y);
1381
1382 canvas.DrawRect(bounds, Paint{.color = Color::Yellow(),
1383 .stroke_width = 5.0f,
1384 .style = Paint::Style::kStroke});
1385
1386 canvas.SaveLayer(alpha, bounds);
1387
1388 canvas.DrawRect(Rect::MakeOriginSize(current, size),
1389 Paint{.color = Color::Red()});
1390 canvas.DrawRect(Rect::MakeOriginSize(current += offset, size),
1391 Paint{.color = Color::Green()});
1392 canvas.DrawRect(Rect::MakeOriginSize(current += offset, size),
1393 Paint{.color = Color::Blue()});
1394
1395 canvas.Restore();
1396
1397 return canvas.EndRecordingAsPicture();
1398 };
1399
1400 ASSERT_TRUE(OpenPlaygroundHere(callback));
1401 }
1402
1403 TEST_P(AiksTest, SaveLayerDrawsBehindSubsequentEntities) {
1404 // Compare with https://fiddle.skia.org/c/9e03de8567ffb49e7e83f53b64bcf636
1405 Canvas canvas;
1406 Paint paint;
1407
1408 paint.color = Color::Black();
1409 Rect rect = Rect::MakeXYWH(25, 25, 25, 25);
1410 canvas.DrawRect(rect, paint);
1411
1412 canvas.Translate({10, 10});
1413 canvas.SaveLayer({});
1414
1415 paint.color = Color::Green();
1416 canvas.DrawRect(rect, paint);
1417
1418 canvas.Restore();
1419
1420 canvas.Translate({10, 10});
1421 paint.color = Color::Red();
1422 canvas.DrawRect(rect, paint);
1423
1424 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1425 }
1426
1427 TEST_P(AiksTest, SiblingSaveLayerBoundsAreRespected) {
1428 Canvas canvas;
1429 Paint paint;
1430 Rect rect = Rect::MakeXYWH(0, 0, 1000, 1000);
1431
1432 // Black, green, and red squares offset by [10, 10].
1433 {
1434 canvas.SaveLayer({}, Rect::MakeXYWH(25, 25, 25, 25));
1435 paint.color = Color::Black();
1436 canvas.DrawRect(rect, paint);
1437 canvas.Restore();
1438 }
1439
1440 {
1441 canvas.SaveLayer({}, Rect::MakeXYWH(35, 35, 25, 25));
1442 paint.color = Color::Green();
1443 canvas.DrawRect(rect, paint);
1444 canvas.Restore();
1445 }
1446
1447 {
1448 canvas.SaveLayer({}, Rect::MakeXYWH(45, 45, 25, 25));
1449 paint.color = Color::Red();
1450 canvas.DrawRect(rect, paint);
1451 canvas.Restore();
1452 }
1453
1454 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1455 }
1456
1457 TEST_P(AiksTest, CanRenderClippedLayers) {
1458 Canvas canvas;
1459
1460 canvas.DrawPaint({.color = Color::White()});
1461
1462 // Draw a green circle on the screen.
1463 {
1464 // Increase the clip depth for the savelayer to contend with.
1465 canvas.ClipPath(PathBuilder{}.AddCircle({100, 100}, 50).TakePath());
1466
1467 canvas.SaveLayer({}, Rect::MakeXYWH(50, 50, 100, 100));
1468
1469 // Fill the layer with white.
1470 canvas.DrawRect(Rect::MakeSize(Size{400, 400}), {.color = Color::White()});
1471 // Fill the layer with green, but do so with a color blend that can't be
1472 // collapsed into the parent pass.
1473 // TODO(jonahwilliams): this blend mode was changed from color burn to
1474 // hardlight to work around https://github.com/flutter/flutter/issues/136554
1475 // .
1476 canvas.DrawRect(
1477 Rect::MakeSize(Size{400, 400}),
1478 {.color = Color::Green(), .blend_mode = BlendMode::kHardLight});
1479 }
1480
1481 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1482 }
1483
1484 TEST_P(AiksTest, SaveLayerFiltersScaleWithTransform) {
1485 Canvas canvas;
1486 canvas.Scale(GetContentScale());
1487 canvas.Translate(Vector2(100, 100));
1488
1489 auto texture = std::make_shared<Image>(CreateTextureForFixture("boston.jpg"));
1490 auto draw_image_layer = [&canvas, &texture](const Paint& paint) {
1491 canvas.SaveLayer(paint);
1492 canvas.DrawImage(texture, {}, Paint{});
1493 canvas.Restore();
1494 };
1495
1496 Paint effect_paint;
1497 effect_paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
1498 .style = FilterContents::BlurStyle::kNormal,
1499 .sigma = Sigma{6},
1500 };
1501 draw_image_layer(effect_paint);
1502
1503 canvas.Translate(Vector2(300, 300));
1504 canvas.Scale(Vector2(3, 3));
1505 draw_image_layer(effect_paint);
1506
1507 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1508 }
1509
1510 #if IMPELLER_ENABLE_3D
1511 TEST_P(AiksTest, SceneColorSource) {
1512 // Load up the scene.
1513 auto mapping =
1514 flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb.ipscene");
1515 ASSERT_NE(mapping, nullptr);
1516
1517 std::shared_ptr<scene::Node> gltf_scene = scene::Node::MakeFromFlatbuffer(
1518 *mapping, *GetContext()->GetResourceAllocator());
1519 ASSERT_NE(gltf_scene, nullptr);
1520
1521 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1522 Paint paint;
1523
1524 static Scalar distance = 2;
1525 static Scalar y_pos = 0;
1526 static Scalar fov = 45;
1527 if (AiksTest::ImGuiBegin("Controls", nullptr,
1528 ImGuiWindowFlags_AlwaysAutoResize)) {
1529 ImGui::SliderFloat("Distance", &distance, 0, 4);
1530 ImGui::SliderFloat("Y", &y_pos, -3, 3);
1531 ImGui::SliderFloat("FOV", &fov, 1, 180);
1532 ImGui::End();
1533 }
1534
1535 Scalar angle = GetSecondsElapsed();
1536 auto camera_position =
1537 Vector3(distance * std::sin(angle), y_pos, -distance * std::cos(angle));
1538
1539 paint.color_source = ColorSource::MakeScene(
1540 gltf_scene,
1541 Matrix::MakePerspective(Degrees(fov), GetWindowSize(), 0.1, 1000) *
1542 Matrix::MakeLookAt(camera_position, {0, 0, 0}, {0, 1, 0}));
1543
1544 Canvas canvas;
1545 canvas.DrawPaint(Paint{.color = Color::MakeRGBA8(0xf9, 0xf9, 0xf9, 0xff)});
1546 canvas.Scale(GetContentScale());
1547 canvas.DrawPaint(paint);
1548 return canvas.EndRecordingAsPicture();
1549 };
1550
1551 ASSERT_TRUE(OpenPlaygroundHere(callback));
1552 }
1553 #endif // IMPELLER_ENABLE_3D
1554
1555 TEST_P(AiksTest, PaintWithFilters) {
1556 // validate that a paint with a color filter "HasFilters", no other filters
1557 // impact this setting.
1558 Paint paint;
1559
1560 ASSERT_FALSE(paint.HasColorFilter());
1561
1562 paint.color_filter =
1563 ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Blue());
1564
1565 ASSERT_TRUE(paint.HasColorFilter());
1566
1567 paint.image_filter = ImageFilter::MakeBlur(Sigma(1.0), Sigma(1.0),
1568 FilterContents::BlurStyle::kNormal,
1569 Entity::TileMode::kClamp);
1570
1571 ASSERT_TRUE(paint.HasColorFilter());
1572
1573 paint.mask_blur_descriptor = {};
1574
1575 ASSERT_TRUE(paint.HasColorFilter());
1576
1577 paint.color_filter = nullptr;
1578
1579 ASSERT_FALSE(paint.HasColorFilter());
1580 }
1581
1582 TEST_P(AiksTest, DrawPaintAbsorbsClears) {
1583 Canvas canvas;
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
1588 Picture picture = canvas.EndRecordingAsPicture();
1589 auto expected = Color::Red().Blend(Color::CornflowerBlue().WithAlpha(0.75),
1590 BlendMode::kSourceOver);
1591 ASSERT_EQ(picture.pass->GetClearColor(), expected);
1592
1593 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1594 std::shared_ptr<Context> real_context = GetContext();
1595 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1596 AiksContext renderer(mock_context, nullptr);
1597 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1598
1599 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1600 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1601 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1602 }
1603
1604 // This is important to enforce with texture reuse, since cached textures need
1605 // to be cleared before reuse.
1606 TEST_P(AiksTest,
1607 ParentSaveLayerCreatesRenderPassWhenChildBackdropFilterIsPresent) {
1608 Canvas canvas;
1609 canvas.SaveLayer({}, std::nullopt, ImageFilter::MakeMatrix(Matrix(), {}));
1610 canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1611 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1612 .blend_mode = BlendMode::kSourceOver});
1613 canvas.Restore();
1614
1615 Picture picture = canvas.EndRecordingAsPicture();
1616
1617 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1618 std::shared_ptr<Context> real_context = GetContext();
1619 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1620 AiksContext renderer(mock_context, nullptr);
1621 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1622
1623 ASSERT_EQ(spy->render_passes_.size(),
1624 GetBackend() == PlaygroundBackend::kOpenGLES ? 4llu : 3llu);
1625 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1626 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1627 }
1628
1629 TEST_P(AiksTest, DrawRectAbsorbsClears) {
1630 Canvas canvas;
1631 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1632 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1633 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1634 {.color = Color::CornflowerBlue().WithAlpha(0.75),
1635 .blend_mode = BlendMode::kSourceOver});
1636
1637 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1638 Picture picture = canvas.EndRecordingAsPicture();
1639 std::shared_ptr<Context> real_context = GetContext();
1640 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1641 AiksContext renderer(mock_context, nullptr);
1642 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1643
1644 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1645 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1646 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1647 }
1648
1649 TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRRect) {
1650 Canvas canvas;
1651 canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), {5.0, 5.0},
1652 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1653 canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), {5.0, 5.0},
1654 {.color = Color::CornflowerBlue().WithAlpha(0.75),
1655 .blend_mode = BlendMode::kSourceOver});
1656
1657 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1658 Picture picture = canvas.EndRecordingAsPicture();
1659 std::shared_ptr<Context> real_context = GetContext();
1660 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1661 AiksContext renderer(mock_context, nullptr);
1662 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1663
1664 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1665 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1666 ASSERT_EQ(render_pass->GetCommands().size(), 2llu);
1667 }
1668
1669 TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRotation) {
1670 Canvas canvas;
1671 canvas.Translate(Vector3(150.0, 150.0, 0.0));
1672 canvas.Rotate(Degrees(45.0));
1673 canvas.Translate(Vector3(-150.0, -150.0, 0.0));
1674 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1675 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1676
1677 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1678 Picture picture = canvas.EndRecordingAsPicture();
1679 std::shared_ptr<Context> real_context = GetContext();
1680 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1681 AiksContext renderer(mock_context, nullptr);
1682 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1683
1684 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1685 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1686 ASSERT_EQ(render_pass->GetCommands().size(), 1llu);
1687 }
1688
1689 TEST_P(AiksTest, DrawRectAbsorbsClearsNegative) {
1690 Canvas canvas;
1691 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1692 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1693 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1694 {.color = Color::CornflowerBlue().WithAlpha(0.75),
1695 .blend_mode = BlendMode::kSourceOver});
1696
1697 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1698 Picture picture = canvas.EndRecordingAsPicture();
1699 std::shared_ptr<Context> real_context = GetContext();
1700 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1701 AiksContext renderer(mock_context, nullptr);
1702 std::shared_ptr<Image> image = picture.ToImage(renderer, {301, 301});
1703
1704 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1705 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1706 ASSERT_EQ(render_pass->GetCommands().size(), 2llu);
1707 }
1708
1709 TEST_P(AiksTest, ClipRectElidesNoOpClips) {
1710 Canvas canvas(Rect::MakeXYWH(0, 0, 100, 100));
1711 canvas.ClipRect(Rect::MakeXYWH(0, 0, 100, 100));
1712 canvas.ClipRect(Rect::MakeXYWH(-100, -100, 300, 300));
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
1717 Picture picture = canvas.EndRecordingAsPicture();
1718 auto expected = Color::Red().Blend(Color::CornflowerBlue().WithAlpha(0.75),
1719 BlendMode::kSourceOver);
1720 ASSERT_EQ(picture.pass->GetClearColor(), expected);
1721
1722 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1723 std::shared_ptr<Context> real_context = GetContext();
1724 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1725 AiksContext renderer(mock_context, nullptr);
1726 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1727
1728 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1729 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1730 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1731 }
1732
1733 TEST_P(AiksTest, ClearColorOptimizationDoesNotApplyForBackdropFilters) {
1734 Canvas canvas;
1735 canvas.SaveLayer({}, std::nullopt,
1736 ImageFilter::MakeBlur(Sigma(3), Sigma(3),
1737 FilterContents::BlurStyle::kNormal,
1738 Entity::TileMode::kClamp));
1739 canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1740 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1741 .blend_mode = BlendMode::kSourceOver});
1742 canvas.Restore();
1743
1744 Picture picture = canvas.EndRecordingAsPicture();
1745
1746 std::optional<Color> actual_color;
1747 bool found_subpass = false;
1748 picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool {
1749 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1750 actual_color = subpass->get()->GetClearColor();
1751 found_subpass = true;
1752 }
1753 // Fail if the first element isn't a subpass.
1754 return true;
1755 });
1756
1757 EXPECT_TRUE(found_subpass);
1758 EXPECT_FALSE(actual_color.has_value());
1759 }
1760
1761 TEST_P(AiksTest, CollapsedDrawPaintInSubpass) {
1762 Canvas canvas;
1763 canvas.DrawPaint(
1764 {.color = Color::Yellow(), .blend_mode = BlendMode::kSource});
1765 canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
1766 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1767 .blend_mode = BlendMode::kSourceOver});
1768
1769 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1770 }
1771
1772 TEST_P(AiksTest, CollapsedDrawPaintInSubpassBackdropFilter) {
1773 // Bug: https://github.com/flutter/flutter/issues/131576
1774 Canvas canvas;
1775 canvas.DrawPaint(
1776 {.color = Color::Yellow(), .blend_mode = BlendMode::kSource});
1777 canvas.SaveLayer({}, {},
1778 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
1779 FilterContents::BlurStyle::kNormal,
1780 Entity::TileMode::kDecal));
1781 canvas.DrawPaint(
1782 {.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSourceOver});
1783
1784 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1785 }
1786
1787 TEST_P(AiksTest, ColorMatrixFilterSubpassCollapseOptimization) {
1788 Canvas canvas;
1789
1790 canvas.SaveLayer({
1791 .color_filter =
1792 ColorFilter::MakeMatrix({.array =
1793 {
1794 -1.0, 0, 0, 1.0, 0, //
1795 0, -1.0, 0, 1.0, 0, //
1796 0, 0, -1.0, 1.0, 0, //
1797 1.0, 1.0, 1.0, 1.0, 0 //
1798 }}),
1799 });
1800
1801 canvas.Translate({500, 300, 0});
1802 canvas.Rotate(Radians(2 * kPi / 3));
1803 canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
1804
1805 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1806 }
1807
1808 TEST_P(AiksTest, LinearToSrgbFilterSubpassCollapseOptimization) {
1809 Canvas canvas;
1810
1811 canvas.SaveLayer({
1812 .color_filter = ColorFilter::MakeLinearToSrgb(),
1813 });
1814
1815 canvas.Translate({500, 300, 0});
1816 canvas.Rotate(Radians(2 * kPi / 3));
1817 canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
1818
1819 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1820 }
1821
1822 TEST_P(AiksTest, SrgbToLinearFilterSubpassCollapseOptimization) {
1823 Canvas canvas;
1824
1825 canvas.SaveLayer({
1826 .color_filter = ColorFilter::MakeSrgbToLinear(),
1827 });
1828
1829 canvas.Translate({500, 300, 0});
1830 canvas.Rotate(Radians(2 * kPi / 3));
1831 canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
1832
1833 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1834 }
1835
1836 TEST_P(AiksTest, TranslucentSaveLayerDrawsCorrectly) {
1837 Canvas canvas;
1838
1839 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1840
1841 canvas.SaveLayer({.color = Color::Black().WithAlpha(0.5)});
1842 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1843 canvas.Restore();
1844
1845 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1846 }
1847
1848 TEST_P(AiksTest, TranslucentSaveLayerWithBlendColorFilterDrawsCorrectly) {
1849 Canvas canvas;
1850
1851 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1852
1853 canvas.SaveLayer({
1854 .color = Color::Black().WithAlpha(0.5),
1855 .color_filter =
1856 ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red()),
1857 });
1858 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1859 canvas.Restore();
1860
1861 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1862 }
1863
1864 TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterDrawsCorrectly) {
1865 Canvas canvas;
1866
1867 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1868
1869 canvas.SaveLayer({
1870 .color = Color::Black().WithAlpha(0.5),
1871 .image_filter = ImageFilter::MakeFromColorFilter(
1872 *ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red())),
1873 });
1874
1875 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1876 canvas.Restore();
1877
1878 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1879 }
1880
1881 TEST_P(AiksTest, TranslucentSaveLayerWithColorAndImageFilterDrawsCorrectly) {
1882 Canvas canvas;
1883
1884 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1885
1886 canvas.SaveLayer({
1887 .color = Color::Black().WithAlpha(0.5),
1888 .color_filter =
1889 ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red()),
1890 });
1891
1892 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1893 canvas.Restore();
1894
1895 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1896 }
1897
1898 TEST_P(AiksTest, ImageFilteredSaveLayerWithUnboundedContents) {
1899 Canvas canvas;
1900 canvas.Scale(GetContentScale());
1901
1902 auto test = [&canvas](const std::shared_ptr<ImageFilter>& filter) {
1903 auto DrawLine = [&canvas](const Point& p0, const Point& p1,
1904 const Paint& p) {
1905 auto path = PathBuilder{}
1906 .AddLine(p0, p1)
1907 .SetConvexity(Convexity::kConvex)
1908 .TakePath();
1909 Paint paint = p;
1910 paint.style = Paint::Style::kStroke;
1911 canvas.DrawPath(path, paint);
1912 };
1913 // Registration marks for the edge of the SaveLayer
1914 DrawLine(Point(75, 100), Point(225, 100), {.color = Color::White()});
1915 DrawLine(Point(75, 200), Point(225, 200), {.color = Color::White()});
1916 DrawLine(Point(100, 75), Point(100, 225), {.color = Color::White()});
1917 DrawLine(Point(200, 75), Point(200, 225), {.color = Color::White()});
1918
1919 canvas.SaveLayer({.image_filter = filter},
1920 Rect::MakeLTRB(100, 100, 200, 200));
1921 {
1922 // DrawPaint to verify correct behavior when the contents are unbounded.
1923 canvas.DrawPaint({.color = Color::Yellow()});
1924
1925 // Contrasting rectangle to see interior blurring
1926 canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
1927 {.color = Color::Blue()});
1928 }
1929 canvas.Restore();
1930 };
1931
1932 test(ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0},
1933 FilterContents::BlurStyle::kNormal,
1934 Entity::TileMode::kDecal));
1935
1936 canvas.Translate({200.0, 0.0});
1937
1938 test(ImageFilter::MakeDilate(Radius{10.0}, Radius{10.0}));
1939
1940 canvas.Translate({200.0, 0.0});
1941
1942 test(ImageFilter::MakeErode(Radius{10.0}, Radius{10.0}));
1943
1944 canvas.Translate({-400.0, 200.0});
1945
1946 auto rotate_filter =
1947 ImageFilter::MakeMatrix(Matrix::MakeTranslation({150, 150}) *
1948 Matrix::MakeRotationZ(Degrees{10.0}) *
1949 Matrix::MakeTranslation({-150, -150}),
1950 SamplerDescriptor{});
1951 test(rotate_filter);
1952
1953 canvas.Translate({200.0, 0.0});
1954
1955 auto rgb_swap_filter = ImageFilter::MakeFromColorFilter(
1956 *ColorFilter::MakeMatrix({.array = {
1957 0, 1, 0, 0, 0, //
1958 0, 0, 1, 0, 0, //
1959 1, 0, 0, 0, 0, //
1960 0, 0, 0, 1, 0 //
1961 }}));
1962 test(rgb_swap_filter);
1963
1964 canvas.Translate({200.0, 0.0});
1965
1966 test(ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter));
1967
1968 canvas.Translate({-400.0, 200.0});
1969
1970 test(ImageFilter::MakeLocalMatrix(Matrix::MakeTranslation({25.0, 25.0}),
1971 *rotate_filter));
1972
1973 canvas.Translate({200.0, 0.0});
1974
1975 test(ImageFilter::MakeLocalMatrix(Matrix::MakeTranslation({25.0, 25.0}),
1976 *rgb_swap_filter));
1977
1978 canvas.Translate({200.0, 0.0});
1979
1980 test(ImageFilter::MakeLocalMatrix(
1981 Matrix::MakeTranslation({25.0, 25.0}),
1982 *ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter)));
1983
1984 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1985 }
1986
1987 TEST_P(AiksTest, ImageFilteredUnboundedSaveLayerWithUnboundedContents) {
1988 Canvas canvas;
1989 canvas.Scale(GetContentScale());
1990
1991 auto blur_filter = ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0},
1992 FilterContents::BlurStyle::kNormal,
1993 Entity::TileMode::kDecal);
1994
1995 canvas.SaveLayer({.image_filter = blur_filter}, std::nullopt);
1996 {
1997 // DrawPaint to verify correct behavior when the contents are unbounded.
1998 canvas.DrawPaint({.color = Color::Yellow()});
1999
2000 // Contrasting rectangle to see interior blurring
2001 canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
2002 {.color = Color::Blue()});
2003 }
2004 canvas.Restore();
2005
2006 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2007 }
2008
2009 TEST_P(AiksTest, TranslucentSaveLayerImageDrawsCorrectly) {
2010 Canvas canvas;
2011
2012 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2013 canvas.DrawImage(image, {100, 100}, {});
2014
2015 canvas.SaveLayer({.color = Color::Black().WithAlpha(0.5)});
2016 canvas.DrawImage(image, {100, 500}, {});
2017 canvas.Restore();
2018
2019 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2020 }
2021
2022 TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixColorFilterDrawsCorrectly) {
2023 Canvas canvas;
2024
2025 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2026 canvas.DrawImage(image, {100, 100}, {});
2027
2028 canvas.SaveLayer({
2029 .color = Color::Black().WithAlpha(0.5),
2030 .color_filter = ColorFilter::MakeMatrix({.array =
2031 {
2032 1, 0, 0, 0, 0, //
2033 0, 1, 0, 0, 0, //
2034 0, 0, 1, 0, 0, //
2035 0, 0, 0, 2, 0 //
2036 }}),
2037 });
2038 canvas.DrawImage(image, {100, 500}, {});
2039 canvas.Restore();
2040
2041 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2042 }
2043
2044 TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly) {
2045 Canvas canvas;
2046
2047 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2048 canvas.DrawImage(image, {100, 100}, {});
2049
2050 canvas.SaveLayer({
2051 .color = Color::Black().WithAlpha(0.5),
2052 .image_filter = ImageFilter::MakeFromColorFilter(
2053 *ColorFilter::MakeMatrix({.array =
2054 {
2055 1, 0, 0, 0, 0, //
2056 0, 1, 0, 0, 0, //
2057 0, 0, 1, 0, 0, //
2058 0, 0, 0, 2, 0 //
2059 }})),
2060 });
2061 canvas.DrawImage(image, {100, 500}, {});
2062 canvas.Restore();
2063
2064 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2065 }
2066
2067 TEST_P(AiksTest,
2068 TranslucentSaveLayerWithColorFilterAndImageFilterDrawsCorrectly) {
2069 Canvas canvas;
2070
2071 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2072 canvas.DrawImage(image, {100, 100}, {});
2073
2074 canvas.SaveLayer({
2075 .color = Color::Black().WithAlpha(0.5),
2076 .image_filter = ImageFilter::MakeFromColorFilter(
2077 *ColorFilter::MakeMatrix({.array =
2078 {
2079 1, 0, 0, 0, 0, //
2080 0, 1, 0, 0, 0, //
2081 0, 0.2, 1, 0, 0, //
2082 0, 0, 0, 0.5, 0 //
2083 }})),
2084 .color_filter =
2085 ColorFilter::MakeBlend(BlendMode::kModulate, Color::Green()),
2086 });
2087 canvas.DrawImage(image, {100, 500}, {});
2088 canvas.Restore();
2089
2090 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2091 }
2092
2093 TEST_P(AiksTest, TranslucentSaveLayerWithAdvancedBlendModeDrawsCorrectly) {
2094 Canvas canvas;
2095 canvas.DrawRect(Rect::MakeXYWH(0, 0, 400, 400), {.color = Color::Red()});
2096 canvas.SaveLayer({
2097 .color = Color::Black().WithAlpha(0.5),
2098 .blend_mode = BlendMode::kLighten,
2099 });
2100 canvas.DrawCircle({200, 200}, 100, {.color = Color::Green()});
2101 canvas.Restore();
2102 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2103 }
2104
2105 /// This is a regression check for https://github.com/flutter/engine/pull/41129
2106 /// The entire screen is green if successful. If failing, no frames will render,
2107 /// or the entire screen will be transparent black.
2108 TEST_P(AiksTest, CanRenderTinyOverlappingSubpasses) {
2109 Canvas canvas;
2110 canvas.DrawPaint({.color = Color::Red()});
2111
2112 // Draw two overlapping subpixel circles.
2113 canvas.SaveLayer({});
2114 canvas.DrawCircle({100, 100}, 0.1, {.color = Color::Yellow()});
2115 canvas.Restore();
2116 canvas.SaveLayer({});
2117 canvas.DrawCircle({100, 100}, 0.1, {.color = Color::Yellow()});
2118 canvas.Restore();
2119
2120 canvas.DrawPaint({.color = Color::Green()});
2121
2122 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2123 }
2124
2125 TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) {
2126 Canvas canvas;
2127 canvas.Scale(Vector2(1.618, 1.618));
2128 canvas.DrawCircle(Point(), 10,
2129 {
2130 .color = Color::CornflowerBlue(),
2131 .blend_mode = BlendMode::kSourceOver,
2132 });
2133 Picture picture = canvas.EndRecordingAsPicture();
2134
2135 // Extract the SolidColorSource.
2136 // Entity entity;
2137 std::vector<Entity> entity;
2138 std::shared_ptr<SolidColorContents> contents;
2139 picture.pass->IterateAllEntities([e = &entity, &contents](Entity& entity) {
2140 if (ScalarNearlyEqual(entity.GetTransform().GetScale().x, 1.618f)) {
2141 contents =
2142 std::static_pointer_cast<SolidColorContents>(entity.GetContents());
2143 e->emplace_back(entity.Clone());
2144 return false;
2145 }
2146 return true;
2147 });
2148
2149 ASSERT_TRUE(entity.size() >= 1);
2150 ASSERT_TRUE(contents->IsOpaque());
2151 ASSERT_EQ(entity[0].GetBlendMode(), BlendMode::kSource);
2152 }
2153
2154 TEST_P(AiksTest, CanRenderDestructiveSaveLayer) {
2155 Canvas canvas;
2156
2157 canvas.DrawPaint({.color = Color::Red()});
2158 // Draw an empty savelayer with a destructive blend mode, which will replace
2159 // the entire red screen with fully transparent black, except for the green
2160 // circle drawn within the layer.
2161 canvas.SaveLayer({.blend_mode = BlendMode::kSource});
2162 canvas.DrawCircle({300, 300}, 100, {.color = Color::Green()});
2163 canvas.Restore();
2164
2165 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2166 }
2167
2168 // Regression test for https://github.com/flutter/flutter/issues/126701 .
2169 TEST_P(AiksTest, CanRenderClippedRuntimeEffects) {
2170 auto runtime_stages =
2171 OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
2172
2173 auto runtime_stage =
2174 runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
2175 ASSERT_TRUE(runtime_stage);
2176 ASSERT_TRUE(runtime_stage->IsDirty());
2177
2178 struct FragUniforms {
2179 Vector2 iResolution;
2180 Scalar iTime;
2181 } frag_uniforms = {.iResolution = Vector2(400, 400), .iTime = 100.0};
2182 auto uniform_data = std::make_shared<std::vector<uint8_t>>();
2183 uniform_data->resize(sizeof(FragUniforms));
2184 memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
2185
2186 std::vector<RuntimeEffectContents::TextureInput> texture_inputs;
2187
2188 Paint paint;
2189 paint.color_source = ColorSource::MakeRuntimeEffect(
2190 runtime_stage, uniform_data, texture_inputs);
2191
2192 Canvas canvas;
2193 canvas.Save();
2194 canvas.ClipRRect(Rect::MakeXYWH(0, 0, 400, 400), {10.0, 10.0},
2195 Entity::ClipOperation::kIntersect);
2196 canvas.DrawRect(Rect::MakeXYWH(0, 0, 400, 400), paint);
2197 canvas.Restore();
2198
2199 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2200 }
2201
2202 TEST_P(AiksTest, DrawPaintTransformsBounds) {
2203 auto runtime_stages = OpenAssetAsRuntimeStage("gradient.frag.iplr");
2204 auto runtime_stage =
2205 runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
2206 ASSERT_TRUE(runtime_stage);
2207 ASSERT_TRUE(runtime_stage->IsDirty());
2208
2209 struct FragUniforms {
2210 Size size;
2211 } frag_uniforms = {.size = Size::MakeWH(400, 400)};
2212 auto uniform_data = std::make_shared<std::vector<uint8_t>>();
2213 uniform_data->resize(sizeof(FragUniforms));
2214 memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
2215
2216 std::vector<RuntimeEffectContents::TextureInput> texture_inputs;
2217
2218 Paint paint;
2219 paint.color_source = ColorSource::MakeRuntimeEffect(
2220 runtime_stage, uniform_data, texture_inputs);
2221
2222 Canvas canvas;
2223 canvas.Save();
2224 canvas.Scale(GetContentScale());
2225 canvas.DrawPaint(paint);
2226 canvas.Restore();
2227
2228 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2229 }
2230
2231 TEST_P(AiksTest, CanDrawPoints) {
2232 std::vector<Point> points = {
2233 {0, 0}, //
2234 {100, 100}, //
2235 {100, 0}, //
2236 {0, 100}, //
2237 {0, 0}, //
2238 {48, 48}, //
2239 {52, 52}, //
2240 };
2241 std::vector<PointStyle> caps = {
2242 PointStyle::kRound,
2243 PointStyle::kSquare,
2244 };
2245 Paint paint;
2246 paint.color = Color::Yellow().WithAlpha(0.5);
2247
2248 Paint background;
2249 background.color = Color::Black();
2250
2251 Canvas canvas;
2252 canvas.DrawPaint(background);
2253 canvas.Translate({200, 200});
2254 canvas.DrawPoints(points, 10, paint, PointStyle::kRound);
2255 canvas.Translate({150, 0});
2256 canvas.DrawPoints(points, 10, paint, PointStyle::kSquare);
2257
2258 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2259 }
2260
2261 TEST_P(AiksTest, CanDrawPointsWithTextureMap) {
2262 auto texture = CreateTextureForFixture("table_mountain_nx.png",
2263 /*enable_mipmapping=*/true);
2264
2265 std::vector<Point> points = {
2266 {0, 0}, //
2267 {100, 100}, //
2268 {100, 0}, //
2269 {0, 100}, //
2270 {0, 0}, //
2271 {48, 48}, //
2272 {52, 52}, //
2273 };
2274 std::vector<PointStyle> caps = {
2275 PointStyle::kRound,
2276 PointStyle::kSquare,
2277 };
2278 Paint paint;
2279 paint.color_source = ColorSource::MakeImage(texture, Entity::TileMode::kClamp,
2280 Entity::TileMode::kClamp, {}, {});
2281
2282 Canvas canvas;
2283 canvas.Translate({200, 200});
2284 canvas.DrawPoints(points, 100, paint, PointStyle::kRound);
2285 canvas.Translate({150, 0});
2286 canvas.DrawPoints(points, 100, paint, PointStyle::kSquare);
2287
2288 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2289 }
2290
2291 // This currently renders solid blue, as the support for text color sources was
2292 // moved into DLDispatching. Path data requires the SkTextBlobs which are not
2293 // used in impeller::TextFrames.
2294 TEST_P(AiksTest, TextForegroundShaderWithTransform) {
2295 auto mapping = flutter::testing::OpenFixtureAsSkData("Roboto-Regular.ttf");
2296 ASSERT_NE(mapping, nullptr);
2297
2298 Scalar font_size = 100;
2299 sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
2300 SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
2301
2302 Paint text_paint;
2303 text_paint.color = Color::Blue();
2304
2305 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
2306 Color{0.1294, 0.5882, 0.9529, 1.0}};
2307 std::vector<Scalar> stops = {
2308 0.0,
2309 1.0,
2310 };
2311 text_paint.color_source = ColorSource::MakeLinearGradient(
2312 {0, 0}, {100, 100}, std::move(colors), std::move(stops),
2313 Entity::TileMode::kRepeat, {});
2314
2315 Canvas canvas;
2316 canvas.Translate({100, 100});
2317 canvas.Rotate(Radians(kPi / 4));
2318
2319 auto blob = SkTextBlob::MakeFromString("Hello", sk_font);
2320 ASSERT_NE(blob, nullptr);
2321 auto frame = MakeTextFrameFromTextBlobSkia(blob);
2322 canvas.DrawTextFrame(frame, Point(), text_paint);
2323
2324 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2325 }
2326
2327 TEST_P(AiksTest, MatrixSaveLayerFilter) {
2328 Canvas canvas;
2329 canvas.DrawPaint({.color = Color::Black()});
2330 canvas.SaveLayer({}, std::nullopt);
2331 {
2332 canvas.DrawCircle(Point(200, 200), 100,
2333 {.color = Color::Green().WithAlpha(0.5),
2334 .blend_mode = BlendMode::kPlus});
2335 // Should render a second circle, centered on the bottom-right-most edge of
2336 // the circle.
2337 canvas.SaveLayer({.image_filter = ImageFilter::MakeMatrix(
2338 Matrix::MakeTranslation(Vector2(1, 1) *
2339 (200 + 100 * k1OverSqrt2)) *
2340 Matrix::MakeScale(Vector2(1, 1) * 0.5) *
2341 Matrix::MakeTranslation(Vector2(-200, -200)),
2342 SamplerDescriptor{})},
2343 std::nullopt);
2344 canvas.DrawCircle(Point(200, 200), 100,
2345 {.color = Color::Green().WithAlpha(0.5),
2346 .blend_mode = BlendMode::kPlus});
2347 canvas.Restore();
2348 }
2349 canvas.Restore();
2350
2351 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2352 }
2353
2354 TEST_P(AiksTest, MatrixBackdropFilter) {
2355 Canvas canvas;
2356 canvas.DrawPaint({.color = Color::Black()});
2357 canvas.SaveLayer({}, std::nullopt);
2358 {
2359 canvas.DrawCircle(Point(200, 200), 100,
2360 {.color = Color::Green().WithAlpha(0.5),
2361 .blend_mode = BlendMode::kPlus});
2362 // Should render a second circle, centered on the bottom-right-most edge of
2363 // the circle.
2364 canvas.SaveLayer(
2365 {}, std::nullopt,
2366 ImageFilter::MakeMatrix(
2367 Matrix::MakeTranslation(Vector2(1, 1) * (100 + 100 * k1OverSqrt2)) *
2368 Matrix::MakeScale(Vector2(1, 1) * 0.5) *
2369 Matrix::MakeTranslation(Vector2(-100, -100)),
2370 SamplerDescriptor{}));
2371 canvas.Restore();
2372 }
2373 canvas.Restore();
2374
2375 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2376 }
2377
2378 TEST_P(AiksTest, SolidColorApplyColorFilter) {
2379 auto contents = SolidColorContents();
2380 contents.SetColor(Color::CornflowerBlue().WithAlpha(0.75));
2381 auto result = contents.ApplyColorFilter([](const Color& color) {
2382 return color.Blend(Color::LimeGreen().WithAlpha(0.75), BlendMode::kScreen);
2383 });
2384 ASSERT_TRUE(result);
2385 ASSERT_COLOR_NEAR(contents.GetColor(),
2386 Color(0.424452, 0.828743, 0.79105, 0.9375));
2387 }
2388
2389 TEST_P(AiksTest, DrawScaledTextWithPerspectiveNoSaveLayer) {
2390 Canvas canvas;
2391 canvas.Transform(Matrix(1.0, 0.0, 0.0, 0.0, //
2392 0.0, 1.0, 0.0, 0.0, //
2393 0.0, 0.0, 1.0, 0.01, //
2394 0.0, 0.0, 0.0, 1.0) * //
2395 Matrix::MakeRotationY({Degrees{10}}));
2396
2397 ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
2398 "Roboto-Regular.ttf"));
2399
2400 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2401 }
2402
2403 TEST_P(AiksTest, DrawScaledTextWithPerspectiveSaveLayer) {
2404 Canvas canvas;
2405 Paint save_paint;
2406 canvas.SaveLayer(save_paint);
2407 canvas.Transform(Matrix(1.0, 0.0, 0.0, 0.0, //
2408 0.0, 1.0, 0.0, 0.0, //
2409 0.0, 0.0, 1.0, 0.01, //
2410 0.0, 0.0, 0.0, 1.0) * //
2411 Matrix::MakeRotationY({Degrees{10}}));
2412
2413 ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
2414 "Roboto-Regular.ttf"));
2415 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2416 }
2417
2418 TEST_P(AiksTest, PipelineBlendSingleParameter) {
2419 Canvas canvas;
2420
2421 // Should render a green square in the middle of a blue circle.
2422 canvas.SaveLayer({});
2423 {
2424 canvas.Translate(Point(100, 100));
2425 canvas.DrawCircle(Point(200, 200), 200, {.color = Color::Blue()});
2426 canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
2427 canvas.DrawCircle(Point(200, 200), 200,
2428 {
2429 .color = Color::Green(),
2430 .blend_mode = BlendMode::kSourceOver,
2431 .image_filter = ImageFilter::MakeFromColorFilter(
2432 *ColorFilter::MakeBlend(BlendMode::kDestination,
2433 Color::White())),
2434 });
2435 canvas.Restore();
2436 }
2437
2438 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2439 }
2440
2441 // Regression test for https://github.com/flutter/flutter/issues/134678.
2442 TEST_P(AiksTest, ReleasesTextureOnTeardown) {
2443 auto context = MakeContext();
2444 std::weak_ptr<Texture> weak_texture;
2445
2446 {
2447 auto texture = CreateTextureForFixture("table_mountain_nx.png");
2448
2449 Canvas canvas;
2450 canvas.Scale(GetContentScale());
2451 canvas.Translate({100.0f, 100.0f, 0});
2452
2453 Paint paint;
2454 paint.color_source = ColorSource::MakeImage(
2455 texture, Entity::TileMode::kClamp, Entity::TileMode::kClamp, {}, {});
2456 canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
2457
2458 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2459 }
2460
2461 // See https://github.com/flutter/flutter/issues/134751.
2462 //
2463 // If the fence waiter was working this may not be released by the end of the
2464 // scope above. Adding a manual shutdown so that future changes to the fence
2465 // waiter will not flake this test.
2466 context->Shutdown();
2467
2468 // The texture should be released by now.
2469 ASSERT_TRUE(weak_texture.expired()) << "When the texture is no longer in use "
2470 "by the backend, it should be "
2471 "released.";
2472 }
2473
2474 TEST_P(AiksTest, MatrixImageFilterMagnify) {
2475 Scalar scale = 2.0;
2476 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
2477 if (AiksTest::ImGuiBegin("Controls", nullptr,
2478 ImGuiWindowFlags_AlwaysAutoResize)) {
2479 ImGui::SliderFloat("Scale", &scale, 1, 2);
2480 ImGui::End();
2481 }
2482 Canvas canvas;
2483 canvas.Scale(GetContentScale());
2484 auto image =
2485 std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2486 canvas.Translate({600, -200});
2487 canvas.SaveLayer({
2488 .image_filter = std::make_shared<MatrixImageFilter>(
2489 Matrix::MakeScale({scale, scale, 1}), SamplerDescriptor{}),
2490 });
2491 canvas.DrawImage(image, {0, 0},
2492 Paint{.color = Color::White().WithAlpha(0.5)});
2493 canvas.Restore();
2494 return canvas.EndRecordingAsPicture();
2495 };
2496
2497 ASSERT_TRUE(OpenPlaygroundHere(callback));
2498 }
2499
2500 // Render a white circle at the top left corner of the screen.
2501 TEST_P(AiksTest, MatrixImageFilterDoesntCullWhenTranslatedFromOffscreen) {
2502 Canvas canvas;
2503 canvas.Scale(GetContentScale());
2504 canvas.Translate({100, 100});
2505 // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
2506 // +300 translation applied by a SaveLayer image filter.
2507 canvas.SaveLayer({
2508 .image_filter = std::make_shared<MatrixImageFilter>(
2509 Matrix::MakeTranslation({300, 0}), SamplerDescriptor{}),
2510 });
2511 canvas.DrawCircle({-300, 0}, 100, {.color = Color::Green()});
2512 canvas.Restore();
2513
2514 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2515 }
2516
2517 // Render a white circle at the top left corner of the screen.
2518 TEST_P(AiksTest,
2519 MatrixImageFilterDoesntCullWhenScaledAndTranslatedFromOffscreen) {
2520 Canvas canvas;
2521 canvas.Scale(GetContentScale());
2522 canvas.Translate({100, 100});
2523 // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
2524 // +300 translation applied by a SaveLayer image filter.
2525 canvas.SaveLayer({
2526 .image_filter = std::make_shared<MatrixImageFilter>(
2527 Matrix::MakeTranslation({300, 0}) * Matrix::MakeScale({2, 2, 2}),
2528 SamplerDescriptor{}),
2529 });
2530 canvas.DrawCircle({-150, 0}, 50, {.color = Color::Green()});
2531 canvas.Restore();
2532
2533 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2534 }
2535
2536 // This should be solid red, if you see a little red box this is broken.
2537 TEST_P(AiksTest, ClearColorOptimizationWhenSubpassIsBiggerThanParentPass) {
2538 SetWindowSize({400, 400});
2539 Canvas canvas;
2540 canvas.Scale(GetContentScale());
2541 canvas.DrawRect(Rect::MakeLTRB(200, 200, 300, 300), {.color = Color::Red()});
2542 canvas.SaveLayer({
2543 .image_filter = std::make_shared<MatrixImageFilter>(
2544 Matrix::MakeScale({2, 2, 1}), SamplerDescriptor{}),
2545 });
2546 // Draw a rectangle that would fully cover the parent pass size, but not
2547 // the subpass that it is rendered in.
2548 canvas.DrawRect(Rect::MakeLTRB(0, 0, 400, 400), {.color = Color::Green()});
2549 // Draw a bigger rectangle to force the subpass to be bigger.
2550 canvas.DrawRect(Rect::MakeLTRB(0, 0, 800, 800), {.color = Color::Red()});
2551 canvas.Restore();
2552
2553 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2554 }
2555
2556 TEST_P(AiksTest, EmptySaveLayerIgnoresPaint) {
2557 Canvas canvas;
2558 canvas.Scale(GetContentScale());
2559 canvas.DrawPaint(Paint{.color = Color::Red()});
2560 canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
2561 canvas.SaveLayer(Paint{.color = Color::Blue()});
2562 canvas.Restore();
2563 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2564 }
2565
2566 TEST_P(AiksTest, EmptySaveLayerRendersWithClear) {
2567 Canvas canvas;
2568 canvas.Scale(GetContentScale());
2569 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2570 canvas.DrawImage(image, {10, 10}, {});
2571 canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
2572 canvas.SaveLayer(Paint{.blend_mode = BlendMode::kClear});
2573 canvas.Restore();
2574 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2575 }
2576
2577 TEST_P(AiksTest, SubpassWithClearColorOptimization) {
2578 Canvas canvas;
2579
2580 // Use a non-srcOver blend mode to ensure that we don't detect this as an
2581 // opacity peephole optimization.
2582 canvas.SaveLayer(
2583 {.color = Color::Blue().WithAlpha(0.5), .blend_mode = BlendMode::kSource},
2584 Rect::MakeLTRB(0, 0, 200, 200));
2585 canvas.DrawPaint(
2586 {.color = Color::BlackTransparent(), .blend_mode = BlendMode::kSource});
2587 canvas.Restore();
2588
2589 canvas.SaveLayer(
2590 {.color = Color::Blue(), .blend_mode = BlendMode::kDestinationOver});
2591 canvas.Restore();
2592
2593 // This playground should appear blank on CI since we are only drawing
2594 // transparent black. If the clear color optimization is broken, the texture
2595 // will be filled with NaNs and may produce a magenta texture on macOS or iOS.
2596 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2597 }
2598
2599 TEST_P(AiksTest, ImageColorSourceEffectTransform) {
2600 // Compare with https://fiddle.skia.org/c/6cdc5aefb291fda3833b806ca347a885
2601
2602 Canvas canvas;
2603 auto texture = CreateTextureForFixture("monkey.png");
2604
2605 canvas.DrawPaint({.color = Color::White()});
2606
2607 // Translation
2608 {
2609 Paint paint;
2610 paint.color_source = ColorSource::MakeImage(
2611 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
2612 Matrix::MakeTranslation({50, 50}));
2613 canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint);
2614 }
2615
2616 // Rotation/skew
2617 {
2618 canvas.Save();
2619 canvas.Rotate(Degrees(45));
2620 Paint paint;
2621 paint.color_source = ColorSource::MakeImage(
2622 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
2623 Matrix(1, -1, 0, 0, //
2624 1, 1, 0, 0, //
2625 0, 0, 1, 0, //
2626 0, 0, 0, 1) //
2627 );
2628 canvas.DrawRect(Rect::MakeLTRB(100, 0, 200, 100), paint);
2629 canvas.Restore();
2630 }
2631
2632 // Scale
2633 {
2634 canvas.Translate(Vector2(100, 0));
2635 canvas.Scale(Vector2(100, 100));
2636 Paint paint;
2637 paint.color_source = ColorSource::MakeImage(
2638 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
2639 Matrix::MakeScale(Vector2(0.005, 0.005)));
2640 canvas.DrawRect(Rect::MakeLTRB(0, 0, 1, 1), paint);
2641 }
2642
2643 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2644 }
2645
2646 TEST_P(AiksTest, CorrectClipDepthAssignedToEntities) {
2647 Canvas canvas; // Depth 1 (base pass)
2648 canvas.DrawRRect(Rect::MakeLTRB(0, 0, 100, 100), {10, 10}, {}); // Depth 2
2649 canvas.Save();
2650 {
2651 canvas.ClipRRect(Rect::MakeLTRB(0, 0, 50, 50), {10, 10}, {}); // Depth 4
2652 canvas.SaveLayer({}); // Depth 4
2653 {
2654 canvas.DrawRRect(Rect::MakeLTRB(0, 0, 50, 50), {10, 10}, {}); // Depth 3
2655 }
2656 canvas.Restore(); // Restore the savelayer.
2657 }
2658 canvas.Restore(); // Depth 5 -- this will no longer append a restore entity
2659 // once we switch to the clip depth approach.
2660
2661 auto picture = canvas.EndRecordingAsPicture();
2662
2663 std::vector<uint32_t> expected = {
2664 2, // DrawRRect
2665 4, // ClipRRect -- Has a depth value equal to the max depth of all the
2666 // content it affect. In this case, the SaveLayer and all
2667 // its contents are affected.
2668 4, // SaveLayer -- The SaveLayer is drawn to the parent pass after its
2669 // contents are rendered, so it should have a depth value
2670 // greater than all its contents.
2671 3, // DrawRRect
2672 5, // Restore (no longer necessary when clipping on the depth buffer)
2673 };
2674
2675 std::vector<uint32_t> actual;
2676
2677 picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool {
2678 if (auto* subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
2679 actual.push_back(subpass->get()->GetClipDepth());
2680 }
2681 if (Entity* entity = std::get_if<Entity>(&element)) {
2682 actual.push_back(entity->GetClipDepth());
2683 }
2684 return true;
2685 });
2686
2687 ASSERT_EQ(actual.size(), expected.size());
2688 for (size_t i = 0; i < expected.size(); i++) {
2689 EXPECT_EQ(expected[i], actual[i]) << "Index: " << i;
2690 }
2691 }
2692
2693 TEST_P(AiksTest, CanDrawPerspectiveTransformWithClips) {
2694 // Avoiding `GetSecondsElapsed()` to reduce risk of golden flakiness.
2695 int time = 0;
2696 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
2697 Canvas canvas;
2698
2699 canvas.Save();
2700 {
2701 canvas.Translate({300, 300});
2702
2703 // 1. Draw/restore a clip before drawing the image, which will get drawn
2704 // to the depth buffer behind the image.
2705 canvas.Save();
2706 {
2707 canvas.DrawPaint({.color = Color::Green()});
2708 canvas.ClipRect(Rect::MakeLTRB(-180, -180, 180, 180),
2709 Entity::ClipOperation::kDifference);
2710 canvas.DrawPaint({.color = Color::Black()});
2711 }
2712 canvas.Restore(); // Restore rectangle difference clip.
2713
2714 canvas.Save();
2715 {
2716 // 2. Draw an oval clip that applies to the image, which will get drawn
2717 // in front of the image on the depth buffer.
2718 canvas.ClipOval(Rect::MakeLTRB(-200, -200, 200, 200));
2719
2720 // 3. Draw the rotating image with a perspective transform.
2721 canvas.Transform(
2722 Matrix(1.0, 0.0, 0.0, 0.0, //
2723 0.0, 1.0, 0.0, 0.0, //
2724 0.0, 0.0, 1.0, 0.003, //
2725 0.0, 0.0, 0.0, 1.0) * //
2726 Matrix::MakeRotationY({Radians{-1.0f + (time++ / 60.0f)}}));
2727 auto image =
2728 std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2729 canvas.DrawImage(image, -Point(image->GetSize()) / 2, {});
2730 }
2731 canvas.Restore(); // Restore oval intersect clip.
2732
2733 // 4. Draw a semi-translucent blue circle atop all previous draws.
2734 canvas.DrawCircle({}, 230, {.color = Color::Blue().WithAlpha(0.4)});
2735 }
2736 canvas.Restore(); // Restore translation.
2737
2738 return canvas.EndRecordingAsPicture();
2739 };
2740 ASSERT_TRUE(OpenPlaygroundHere(callback));
2741 }
2742
2743 TEST_P(AiksTest, CanRenderClippedBackdropFilter) {
2744 Canvas canvas;
2745 Paint paint;
2746
2747 canvas.Scale(GetContentScale());
2748
2749 // Draw something interesting in the background.
2750 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
2751 Color{0.1294, 0.5882, 0.9529, 1.0}};
2752 std::vector<Scalar> stops = {
2753 0.0,
2754 1.0,
2755 };
2756 paint.color_source = ColorSource::MakeLinearGradient(
2757 {0, 0}, {100, 100}, std::move(colors), std::move(stops),
2758 Entity::TileMode::kRepeat, {});
2759 canvas.DrawPaint(paint);
2760
2761 Rect clip_rect = Rect::MakeLTRB(50, 50, 400, 300);
2762
2763 // Draw a clipped SaveLayer, where the clip coverage and SaveLayer size are
2764 // the same.
2765 canvas.ClipRRect(clip_rect, Size(100, 100),
2766 Entity::ClipOperation::kIntersect);
2767 canvas.SaveLayer({}, clip_rect,
2768 ImageFilter::MakeFromColorFilter(*ColorFilter::MakeBlend(
2769 BlendMode::kExclusion, Color::Red())));
2770 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2771 }
2772
2773 TEST_P(AiksTest, MipmapGenerationWorksCorrectly) {
2774 TextureDescriptor texture_descriptor;
2775 texture_descriptor.size = ISize{1024, 1024};
2776 texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
2777 texture_descriptor.storage_mode = StorageMode::kHostVisible;
2778 texture_descriptor.mip_count = texture_descriptor.size.MipCount();
2779
2780 std::vector<uint8_t> bytes(4194304);
2781 bool alternate = false;
2782 for (auto i = 0u; i < 4194304; i += 4) {
2783 if (alternate) {
2784 bytes[i] = 255;
2785 bytes[i + 1] = 0;
2786 bytes[i + 2] = 0;
2787 bytes[i + 3] = 255;
2788 } else {
2789 bytes[i] = 0;
2790 bytes[i + 1] = 255;
2791 bytes[i + 2] = 0;
2792 bytes[i + 3] = 255;
2793 }
2794 alternate = !alternate;
2795 }
2796
2797 ASSERT_EQ(texture_descriptor.GetByteSizeOfBaseMipLevel(), bytes.size());
2798 auto mapping = std::make_shared<fml::NonOwnedMapping>(
2799 bytes.data(), // data
2800 texture_descriptor.GetByteSizeOfBaseMipLevel() // size
2801 );
2802 auto texture =
2803 GetContext()->GetResourceAllocator()->CreateTexture(texture_descriptor);
2804
2805 auto device_buffer =
2806 GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
2807 auto command_buffer = GetContext()->CreateCommandBuffer();
2808 auto blit_pass = command_buffer->CreateBlitPass();
2809
2810 blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(device_buffer)),
2811 texture);
2812 blit_pass->GenerateMipmap(texture);
2813 EXPECT_TRUE(blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()));
2814 EXPECT_TRUE(GetContext()->GetCommandQueue()->Submit({command_buffer}).ok());
2815
2816 auto image = std::make_shared<Image>(texture);
2817
2818 Canvas canvas;
2819 canvas.DrawImageRect(image, Rect::MakeSize(texture->GetSize()),
2820 Rect::MakeLTRB(0, 0, 100, 100), {});
2821
2822 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2823 }
2824
2825 // https://github.com/flutter/flutter/issues/146648
2826 TEST_P(AiksTest, StrokedPathWithMoveToThenCloseDrawnCorrectly) {
2827 Path path = PathBuilder{}
2828 .MoveTo({0, 400})
2829 .LineTo({0, 0})
2830 .LineTo({400, 0})
2831 // MoveTo implicitly adds a contour, ensure that close doesn't
2832 // add another nearly-empty contour.
2833 .MoveTo({0, 400})
2834 .Close()
2835 .TakePath();
2836
2837 Canvas canvas;
2838 canvas.Translate({50, 50, 0});
2839 canvas.DrawPath(path, {
2840 .color = Color::Blue(),
2841 .stroke_width = 10,
2842 .stroke_cap = Cap::kRound,
2843 .style = Paint::Style::kStroke,
2844 });
2845 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2846 }
2847
2848 TEST_P(AiksTest, CanRenderTextWithLargePerspectiveTransform) {
2849 // Verifies that text scales are clamped to work around
2850 // https://github.com/flutter/flutter/issues/136112 .
2851
2852 Canvas canvas;
2853 Paint save_paint;
2854 canvas.SaveLayer(save_paint);
2855 canvas.Transform(Matrix(2000, 0, 0, 0, //
2856 0, 2000, 0, 0, //
2857 0, 0, -1, 9000, //
2858 0, 0, -1, 7000 //
2859 ));
2860
2861 ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
2862 "Roboto-Regular.ttf"));
2863 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2864 }
2865
2866 TEST_P(AiksTest, SetContentsWithRegion) {
2867 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
2868
2869 // Replace part of the texture with a red rectangle.
2870 std::vector<uint8_t> bytes(100 * 100 * 4);
2871 for (auto i = 0u; i < bytes.size(); i += 4) {
2872 bytes[i] = 255;
2873 bytes[i + 1] = 0;
2874 bytes[i + 2] = 0;
2875 bytes[i + 3] = 255;
2876 }
2877 auto mapping =
2878 std::make_shared<fml::NonOwnedMapping>(bytes.data(), bytes.size());
2879 auto device_buffer =
2880 GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
2881 auto cmd_buffer = GetContext()->CreateCommandBuffer();
2882 auto blit_pass = cmd_buffer->CreateBlitPass();
2883 blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), bridge,
2884 IRect::MakeLTRB(50, 50, 150, 150));
2885
2886 auto did_submit =
2887 blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()) &&
2888 GetContext()->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok();
2889 ASSERT_TRUE(did_submit);
2890
2891 auto image = std::make_shared<Image>(bridge);
2892
2893 Canvas canvas;
2894 canvas.DrawImage(image, {0, 0}, {});
2895
2896 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2897 }
2898
2899 } // namespace testing
2900 } // namespace impeller
2901
2902 // █████████████████████████████████████████████████████████████████████████████
2903 // █ NOTICE: Before adding new tests to this file consider adding it to one of
2904 // █ the subdivisions of AiksTest to avoid having one massive file.
2905 // █
2906 // █ Subdivisions:
2907 // █ - aiks_blend_unittests.cc
2908 // █ - aiks_blur_unittests.cc
2909 // █ - aiks_gradient_unittests.cc
2910 // █ - aiks_path_unittests.cc
2911 // █████████████████████████████████████████████████████████████████████████████
impeller::DeviceBuffer::AsBufferView
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
Definition: device_buffer.cc:18
impeller::Canvas::DrawPoints
void DrawPoints(std::vector< Point > points, Scalar radius, const Paint &paint, PointStyle point_style)
Definition: canvas.cc:733
impeller::Color::Blue
static constexpr Color Blue()
Definition: color.h:278
typeface_stb.h
impeller::Entity::ClipOperation::kIntersect
@ kIntersect
path.h
impeller::AiksPlayground
Definition: aiks_playground.h:17
ASSERT_COLOR_NEAR
#define ASSERT_COLOR_NEAR(a, b)
Definition: geometry_asserts.h:192
impeller::Entity::TileMode::kClamp
@ kClamp
impeller::k1OverSqrt2
constexpr float k1OverSqrt2
Definition: constants.h:50
impeller::Canvas::EndRecordingAsPicture
Picture EndRecordingAsPicture()
Definition: canvas.cc:804
impeller::Picture::pass
std::unique_ptr< EntityPass > pass
Definition: picture.h:18
impeller::Canvas::ClipOval
void ClipOval(const Rect &bounds, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:620
impeller::Cap::kRound
@ kRound
impeller::Canvas::DrawRRect
void DrawRRect(const Rect &rect, const Size &corner_radii, const Paint &paint)
Definition: canvas.cc:540
impeller::ImageFilter::MakeDilate
static std::shared_ptr< ImageFilter > MakeDilate(Radius radius_x, Radius radius_y)
Definition: image_filter.cc:29
impeller::Entity::ClipOperation::kDifference
@ kDifference
impeller::PixelFormatToString
constexpr const char * PixelFormatToString(PixelFormat format)
Definition: formats.h:140
impeller::Scalar
float Scalar
Definition: scalar.h:18
image_filter.h
impeller::AiksContext
Definition: aiks_context.h:19
impeller::Canvas::DrawImageRect
void DrawImageRect(const std::shared_ptr< Image > &image, Rect source, Rect dest, const Paint &paint, SamplerDescriptor sampler={}, SourceRectConstraint src_rect_constraint=SourceRectConstraint::kFast)
Definition: canvas.cc:766
impeller::PixelFormat::kB10G10R10A10XR
@ kB10G10R10A10XR
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:274
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:46
geometry_asserts.h
impeller::Paint::Style::kStroke
@ kStroke
aiks_unittests.h
impeller::ColorSource::MakeLinearGradient
static ColorSource MakeLinearGradient(Point start_point, Point end_point, std::vector< Color > colors, std::vector< Scalar > stops, Entity::TileMode tile_mode, Matrix effect_transform)
Definition: color_source.cc:166
impeller::Paint
Definition: paint.h:23
impeller::testing::AiksTest
AiksPlayground AiksTest
Definition: aiks_unittests.h:17
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::Canvas::Skew
void Skew(Scalar sx, Scalar sy)
Definition: canvas.cc:323
impeller::Color::MakeRGBA8
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:154
solid_color_contents.h
impeller::Color
Definition: color.h:124
impeller::testing::TextRenderOptions::color
Color color
Definition: aiks_unittests.cc:545
device_buffer.h
font_size
SkScalar font_size
Definition: dl_golden_blur_unittests.cc:22
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:41
impeller::Paint::color
Color color
Definition: paint.h:68
impeller::Entity::TileMode::kDecal
@ kDecal
impeller::BlendMode::kSource
@ kSource
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
impeller::Canvas::DrawTextFrame
virtual void DrawTextFrame(const std::shared_ptr< TextFrame > &text_frame, Point position, const Paint &paint)
Definition: canvas.cc:884
impeller::BlendMode::kDestination
@ kDestination
impeller::Color::Purple
static constexpr Color Purple()
Definition: color.h:744
impeller::Canvas
Definition: canvas.h:60
impeller::PathBuilder
Definition: path_builder.h:14
impeller::Paint::MaskBlurDescriptor::style
FilterContents::BlurStyle style
Definition: paint.h:49
impeller::PointStyle::kRound
@ kRound
Points are drawn as squares.
impeller::testing::TextRenderOptions::mask_blur_descriptor
std::optional< Paint::MaskBlurDescriptor > mask_blur_descriptor
Definition: aiks_unittests.cc:547
impeller::TextureDescriptor::mip_count
size_t mip_count
Definition: texture_descriptor.h:43
impeller::Vector2
Point Vector2
Definition: point.h:326
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:198
impeller::kPi
constexpr float kPi
Definition: constants.h:26
impeller::ImageFilter::MakeBlur
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
impeller::BlendMode::kDestinationOver
@ kDestinationOver
impeller::BlendMode::kPlus
@ kPlus
impeller::Paint::MaskBlurDescriptor
Definition: paint.h:48
impeller::Color::CornflowerBlue
static constexpr Color CornflowerBlue()
Definition: color.h:344
impeller::testing::TextRenderOptions
Definition: aiks_unittests.cc:542
impeller::PathBuilder::AddRoundedRect
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
Definition: path_builder.cc:150
impeller::Color::Yellow
static constexpr Color Yellow()
Definition: color.h:844
impeller::ColorSource::MakeImage
static ColorSource MakeImage(std::shared_ptr< Texture > texture, Entity::TileMode x_tile_mode, Entity::TileMode y_tile_mode, SamplerDescriptor sampler_descriptor, Matrix effect_transform)
Definition: color_source.cc:225
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
impeller::FilterContents::BlurStyle::kNormal
@ kNormal
Blurred inside and outside.
impeller::PathBuilder::SetConvexity
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:85
impeller::StorageMode::kHostVisible
@ kHostVisible
impeller::Entity::TileMode::kRepeat
@ kRepeat
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Paint::color_source
ColorSource color_source
Definition: paint.h:69
impeller::PlaygroundBackendToRuntimeStageBackend
constexpr RuntimeStageBackend PlaygroundBackendToRuntimeStageBackend(PlaygroundBackend backend)
Definition: playground.h:33
impeller::Vector3::x
Scalar x
Definition: vector.h:23
stroke_width
const Scalar stroke_width
Definition: stroke_path_geometry.cc:304
impeller::Canvas::DrawRect
void DrawRect(const Rect &rect, const Paint &paint)
Definition: canvas.cc:495
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:311
impeller::MoveTo
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:20
impeller::Canvas::GetCurrentTransform
const Matrix & GetCurrentTransform() const
Definition: canvas.cc:298
impeller::BlendMode::kModulate
@ kModulate
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:117
impeller::PathBuilder::RoundingRadii::bottom_right
Point bottom_right
Definition: path_builder.h:109
impeller::testing::TextRenderOptions::font_size
Scalar font_size
Definition: aiks_unittests.cc:544
impeller::testing::RenderTextInCanvasSTB
bool RenderTextInCanvasSTB(const std::shared_ptr< Context > &context, Canvas &canvas, const std::string &text, const std::string &font_fixture, TextRenderOptions options={})
Definition: aiks_unittests.cc:590
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:32
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
impeller::Matrix::MakeLookAt
static constexpr Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:532
impeller::Canvas::DrawImage
void DrawImage(const std::shared_ptr< Image > &image, Point offset, const Paint &paint, SamplerDescriptor sampler={})
Definition: canvas.cc:752
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:506
impeller::testing::TEST_P
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer)
Definition: aiks_blend_unittests.cc:21
impeller::ImageFilter::MakeFromColorFilter
static std::shared_ptr< ImageFilter > MakeFromColorFilter(const ColorFilter &color_filter)
Definition: image_filter.cc:52
impeller::Entity::TileMode::kMirror
@ kMirror
impeller::MakeTextFrameSTB
std::shared_ptr< TextFrame > MakeTextFrameSTB(const std::shared_ptr< TypefaceSTB > &typeface_stb, Font::Metrics metrics, const std::string &text)
Definition: text_frame_stb.cc:11
path_builder.h
impeller::SamplerDescriptor
Definition: sampler_descriptor.h:15
impeller::PathBuilder::RoundingRadii
Definition: path_builder.h:105
impeller::testing::INSTANTIATE_PLAYGROUND_SUITE
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
matrix.h
impeller::Entity
Definition: entity.h:20
impeller::saturated::distance
SI distance
Definition: saturated_math.h:57
impeller::BlendMode::kLighten
@ kLighten
text_frame_skia.h
impeller::Picture
Definition: picture.h:17
impeller::TSize< Scalar >
impeller::PointStyle::kSquare
@ kSquare
Points are drawn as circles.
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
impeller::k2Pi
constexpr float k2Pi
Definition: constants.h:29
impeller::Canvas::Scale
void Scale(const Vector2 &scale)
Definition: canvas.cc:315
impeller::BlendMode::kHardLight
@ kHardLight
impeller::Radius
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:48
impeller::Matrix::GetScale
constexpr Vector3 GetScale() const
Definition: matrix.h:311
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:52
impeller::ColorFilter::MakeBlend
static std::shared_ptr< ColorFilter > MakeBlend(BlendMode blend_mode, Color color)
Definition: color_filter.cc:23
widgets.h
impeller::BlendMode::kClear
@ kClear
text_frame_stb.h
impeller::EntityPass::Element
std::variant< Entity, std::unique_ptr< EntityPass > > Element
Definition: entity_pass.h:54
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:280
impeller::ImageFilter::MakeLocalMatrix
static std::shared_ptr< ImageFilter > MakeLocalMatrix(const Matrix &matrix, const ImageFilter &internal_filter)
Definition: image_filter.cc:57
impeller::Paint::Style::kFill
@ kFill
impeller::SolidColorContents
Definition: solid_color_contents.h:17
impeller::TRect< Scalar >::MakeOriginSize
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:140
impeller::testing::TextRenderOptions::stroke
bool stroke
Definition: aiks_unittests.cc:543
impeller::Canvas::DrawCircle
void DrawCircle(const Point &center, Scalar radius, const Paint &paint)
Definition: canvas.cc:566
impeller::TypographerContextSTB::Make
static std::unique_ptr< TypographerContext > Make()
Definition: typographer_context_stb.cc:32
impeller::Entity::GetContents
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:94
impeller::Color::White
static constexpr Color White()
Definition: color.h:266
impeller::Sigma
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
impeller::PathBuilder::RoundingRadii::top_left
Point top_left
Definition: path_builder.h:106
impeller::Canvas::Restore
virtual bool Restore()
Definition: canvas.cc:257
impeller::Radians
Definition: scalar.h:38
impeller::ImageFilter::MakeErode
static std::shared_ptr< ImageFilter > MakeErode(Radius radius_x, Radius radius_y)
Definition: image_filter.cc:34
impeller::PathBuilder::AddLine
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
Definition: path_builder.cc:424
canvas.h
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:276
impeller::Canvas::DrawPath
void DrawPath(const Path &path, const Paint &paint)
Definition: canvas.cc:343
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:22
impeller::Canvas::DrawPaint
void DrawPaint(const Paint &paint)
Definition: canvas.cc:352
impeller::Canvas::GetSaveCount
size_t GetSaveCount() const
Definition: canvas.cc:331
ASSERT_MATRIX_NEAR
#define ASSERT_MATRIX_NEAR(a, b)
Definition: geometry_asserts.h:189
impeller::testing::TextRenderOptions::position
Point position
Definition: aiks_unittests.cc:546
impeller::Entity::TileMode
TileMode
Definition: entity.h:42
impeller::MakeTextFrameFromTextBlobSkia
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
Definition: text_frame_skia.cc:61
impeller::ColorSource::MakeRadialGradient
static ColorSource MakeRadialGradient(Point center, Scalar radius, std::vector< Color > colors, std::vector< Scalar > stops, Entity::TileMode tile_mode, Matrix effect_transform)
Definition: color_source.cc:196
impeller::ImageFilter::MakeMatrix
static std::shared_ptr< ImageFilter > MakeMatrix(const Matrix &matrix, SamplerDescriptor sampler_descriptor)
Definition: image_filter.cc:39
impeller::ColorFilter::MakeMatrix
static std::shared_ptr< ColorFilter > MakeMatrix(ColorMatrix color_matrix)
Definition: color_filter.cc:28
impeller::Canvas::ClipRRect
void ClipRRect(const Rect &rect, const Size &corner_radii, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:640
impeller::Close
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:42
impeller::ImageFilter::MakeCompose
static std::shared_ptr< ImageFilter > MakeCompose(const ImageFilter &inner, const ImageFilter &outer)
Definition: image_filter.cc:46
impeller::Picture::ToImage
std::shared_ptr< Image > ToImage(AiksContext &context, ISize size) const
Definition: picture.cc:31
impeller::Color::MediumTurquoise
static constexpr Color MediumTurquoise()
Definition: color.h:648
command_buffer.h
impeller::ColorSource::MakeRuntimeEffect
static ColorSource MakeRuntimeEffect(std::shared_ptr< RuntimeStage > runtime_stage, std::shared_ptr< std::vector< uint8_t >> uniform_data, std::vector< RuntimeEffectContents::TextureInput > texture_inputs)
Definition: color_source.cc:238
impeller::Canvas::DrawOval
void DrawOval(const Rect &rect, const Paint &paint)
Definition: canvas.cc:514
impeller::BlendMode::kExclusion
@ kExclusion
impeller::testing::RenderTextInCanvasSkia
bool RenderTextInCanvasSkia(const std::shared_ptr< Context > &context, Canvas &canvas, const std::string &text, const std::string_view &font_fixture, TextRenderOptions options={})
Definition: aiks_unittests.cc:550
impeller::PlaygroundBackend::kOpenGLES
@ kOpenGLES
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:213
impeller::Canvas::Rotate
void Rotate(Radians radians)
Definition: canvas.cc:327
image.h
impeller::LineTo
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
impeller::ColorFilter::MakeLinearToSrgb
static std::shared_ptr< ColorFilter > MakeLinearToSrgb()
Definition: color_filter.cc:36
impeller::Color::Orange
static constexpr Color Orange()
Definition: color.h:692
impeller::PlaygroundPoint
Definition: widgets.h:17
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
impeller::PathBuilder::RoundingRadii::top_right
Point top_right
Definition: path_builder.h:108
constants.h
impeller::TextureDescriptor::GetByteSizeOfBaseMipLevel
constexpr size_t GetByteSizeOfBaseMipLevel() const
Definition: texture_descriptor.h:48
snapshot.h
rect.h
impeller::Entity::Clone
Entity Clone() const
Definition: entity.cc:191
impeller::TPoint< Scalar >
impeller::Canvas::Transform
void Transform(const Matrix &transform)
Definition: canvas.cc:294
impeller::PathBuilder::MoveTo
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:33
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:272
impeller::testing::kFontFixture
static constexpr std::string_view kFontFixture
Definition: aiks_unittests.cc:728
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:268
impeller::PathBuilder::RoundingRadii::bottom_left
Point bottom_left
Definition: path_builder.h:107
impeller::Canvas::Save
virtual void Save(uint32_t total_content_depth=kMaxDepth)
Definition: canvas.cc:184
scale
const Scalar scale
Definition: stroke_path_geometry.cc:308
impeller::BlendMode::kScreen
@ kScreen
paint
const Paint & paint
Definition: color_source.cc:38
impeller::ScalarNearlyEqual
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:30
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:69
impeller::Degrees
Definition: scalar.h:46
color.h
impeller::Color::LimeGreen
static constexpr Color LimeGreen()
Definition: color.h:604
color_filter.h
color
DlColor color
Definition: dl_golden_blur_unittests.cc:23
impeller::PathBuilder::AddCircle
PathBuilder & AddCircle(const Point &center, Scalar radius)
Definition: path_builder.cc:135
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:39
typographer_context_stb.h
impeller::Canvas::ClipRect
void ClipRect(const Rect &rect, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:599
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:38
impeller::ColorFilter::MakeSrgbToLinear
static std::shared_ptr< ColorFilter > MakeSrgbToLinear()
Definition: color_filter.cc:32
impeller::TSize::MipCount
constexpr size_t MipCount() const
Definition: size.h:115
impeller::Convexity::kConvex
@ kConvex
impeller::scene::Node::MakeFromFlatbuffer
static std::shared_ptr< Node > MakeFromFlatbuffer(const fml::Mapping &ipscene_mapping, Allocator &allocator)
Definition: node.cc:47
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller::TSize< Scalar >::MakeWH
static constexpr TSize MakeWH(Type width, Type height)
Definition: size.h:34
impeller
Definition: aiks_blend_unittests.cc:18
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::Paint::mask_blur_descriptor
std::optional< MaskBlurDescriptor > mask_blur_descriptor
Definition: paint.h:81
impeller::BlendMode::kMultiply
@ kMultiply
impeller::Canvas::SaveLayer
virtual void SaveLayer(const Paint &paint, std::optional< Rect > bounds=std::nullopt, const std::shared_ptr< ImageFilter > &backdrop_filter=nullptr, ContentBoundsPromise bounds_promise=ContentBoundsPromise::kUnknown, uint32_t total_content_depth=kMaxDepth, bool can_distribute_opacity=false)
Definition: canvas.cc:842
impeller::TRect
Definition: rect.h:122
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Vector3
Definition: vector.h:20
size.h
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::Paint::blend_mode
BlendMode blend_mode
Definition: paint.h:76
impeller::Color::Crimson
static constexpr Color Crimson()
Definition: color.h:352
impeller::DrawPlaygroundLine
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
impeller::TPoint< Scalar >::MakeXY
static constexpr TPoint< Type > MakeXY(Type x, Type y)
Definition: point.h:46
impeller::Canvas::Translate
void Translate(const Vector3 &offset)
Definition: canvas.cc:311
impeller::Canvas::ClipPath
void ClipPath(const Path &path, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:589
impeller::Color::Blend
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