| 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, CanRenderTextFrameWithInvertedTransform) {
630 Canvas canvas;
631 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
632
633 canvas.Translate({1000, 0, 0});
634 canvas.Scale({-1, 1, 1});
635 ASSERT_TRUE(RenderTextInCanvasSkia(
636 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
637 "Roboto-Regular.ttf"));
638 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
639 }
640
641 TEST_P(AiksTest, CanRenderStrokedTextFrame) {
642 Canvas canvas;
643 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
644 ASSERT_TRUE(RenderTextInCanvasSkia(
645 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
646 "Roboto-Regular.ttf",
647 {
648 .stroke = true,
649 }));
650 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
651 }
652
653 TEST_P(AiksTest, CanRenderTextFrameWithHalfScaling) {
654 Canvas canvas;
655 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
656 canvas.Scale({0.5, 0.5, 1});
657 ASSERT_TRUE(RenderTextInCanvasSkia(
658 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
659 "Roboto-Regular.ttf"));
660 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
661 }
662
663 TEST_P(AiksTest, CanRenderTextFrameWithFractionScaling) {
664 Canvas canvas;
665 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
666 canvas.Scale({2.625, 2.625, 1});
667 ASSERT_TRUE(RenderTextInCanvasSkia(
668 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
669 "Roboto-Regular.ttf"));
670 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
671 }
672
673 TEST_P(AiksTest, CanRenderTextFrameSTB) {
674 Canvas canvas;
675 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
676 ASSERT_TRUE(RenderTextInCanvasSTB(
677 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
678 "Roboto-Regular.ttf"));
679
680 SetTypographerContext(TypographerContextSTB::Make());
681 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
682 }
683
684 TEST_P(AiksTest, TextFrameSubpixelAlignment) {
685 // "Random" numbers between 0 and 1. Hardcoded to avoid flakiness in goldens.
686 std::array<Scalar, 20> phase_offsets = {
687 7.82637e-06, 0.131538, 0.755605, 0.45865, 0.532767,
688 0.218959, 0.0470446, 0.678865, 0.679296, 0.934693,
689 0.383502, 0.519416, 0.830965, 0.0345721, 0.0534616,
690 0.5297, 0.671149, 0.00769819, 0.383416, 0.0668422};
691 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
692 static float font_size = 20;
693 static float phase_variation = 0.2;
694 static float speed = 0.5;
695 static float magnitude = 100;
696 if (AiksTest::ImGuiBegin("Controls", nullptr,
697 ImGuiWindowFlags_AlwaysAutoResize)) {
698 ImGui::SliderFloat("Font size", &font_size, 5, 50);
699 ImGui::SliderFloat("Phase variation", &phase_variation, 0, 1);
700 ImGui::SliderFloat("Oscillation speed", &speed, 0, 2);
701 ImGui::SliderFloat("Oscillation magnitude", &magnitude, 0, 300);
702 ImGui::End();
703 }
704
705 Canvas canvas;
706 canvas.Scale(GetContentScale());
707
708 for (size_t i = 0; i < phase_offsets.size(); i++) {
709 auto position =
710 Point(200 + magnitude *
711 std::sin((-phase_offsets[i] * k2Pi * phase_variation +
712 GetSecondsElapsed() * speed)), //
713 200 + i * font_size * 1.1 //
714 );
715 if (!RenderTextInCanvasSkia(
716 GetContext(), canvas,
717 "the quick brown fox jumped over "
718 "the lazy dog!.?",
719 "Roboto-Regular.ttf",
720 {.font_size = font_size, .position = position})) {
721 return std::nullopt;
722 }
723 }
724 return canvas.EndRecordingAsPicture();
725 };
726
727 ASSERT_TRUE(OpenPlaygroundHere(callback));
728 }
729
730 TEST_P(AiksTest, CanRenderItalicizedText) {
731 Canvas canvas;
732 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
733
734 ASSERT_TRUE(RenderTextInCanvasSkia(
735 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
736 "HomemadeApple.ttf"));
737 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
738 }
739
740 static constexpr std::string_view kFontFixture =
741 #if FML_OS_MACOSX
742 "Apple Color Emoji.ttc";
743 #else
744 "NotoColorEmoji.ttf";
745 #endif
746
747 TEST_P(AiksTest, CanRenderEmojiTextFrame) {
748 Canvas canvas;
749 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
750
751 ASSERT_TRUE(RenderTextInCanvasSkia(
752 GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture));
753 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
754 }
755
756 TEST_P(AiksTest, CanRenderEmojiTextFrameWithBlur) {
757 Canvas canvas;
758 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
759
760 ASSERT_TRUE(RenderTextInCanvasSkia(
761 GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture,
762 TextRenderOptions{.color = Color::Blue(),
763 .mask_blur_descriptor = Paint::MaskBlurDescriptor{
764 .style = FilterContents::BlurStyle::kNormal,
765 .sigma = Sigma(4)}}));
766 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
767 }
768
769 TEST_P(AiksTest, CanRenderEmojiTextFrameWithAlpha) {
770 Canvas canvas;
771 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
772
773 ASSERT_TRUE(RenderTextInCanvasSkia(
774 GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture,
775 {.color = Color::Black().WithAlpha(0.5)}));
776 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
777 }
778
779 TEST_P(AiksTest, CanRenderTextInSaveLayer) {
780 Canvas canvas;
781 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
782
783 canvas.Translate({100, 100});
784 canvas.Scale(Vector2{0.5, 0.5});
785
786 // Blend the layer with the parent pass using kClear to expose the coverage.
787 canvas.SaveLayer({.blend_mode = BlendMode::kClear});
788 ASSERT_TRUE(RenderTextInCanvasSkia(
789 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
790 "Roboto-Regular.ttf"));
791 canvas.Restore();
792
793 // Render the text again over the cleared coverage rect.
794 ASSERT_TRUE(RenderTextInCanvasSkia(
795 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
796 "Roboto-Regular.ttf"));
797
798 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
799 }
800
801 TEST_P(AiksTest, CanRenderTextOutsideBoundaries) {
802 Canvas canvas;
803 canvas.Translate({200, 150});
804
805 // Construct the text blob.
806 auto mapping = flutter::testing::OpenFixtureAsSkData("wtf.otf");
807 ASSERT_NE(mapping, nullptr);
808
809 Scalar font_size = 80;
810 sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
811 SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
812
813 Paint text_paint;
814 text_paint.color = Color::Blue().WithAlpha(0.8);
815
816 struct {
817 Point position;
818 const char* text;
819 } text[] = {{Point(0, 0), "0F0F0F0"},
820 {Point(1, 2), "789"},
821 {Point(1, 3), "456"},
822 {Point(1, 4), "123"},
823 {Point(0, 6), "0F0F0F0"}};
824 for (auto& t : text) {
825 canvas.Save();
826 canvas.Translate(t.position * Point(font_size * 2, font_size * 1.1));
827 {
828 auto blob = SkTextBlob::MakeFromString(t.text, sk_font);
829 ASSERT_NE(blob, nullptr);
830 auto frame = MakeTextFrameFromTextBlobSkia(blob);
831 canvas.DrawTextFrame(frame, Point(), text_paint);
832 }
833 canvas.Restore();
834 }
835
836 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
837 }
838
839 TEST_P(AiksTest, TextRotated) {
840 Canvas canvas;
841 canvas.Scale(GetContentScale());
842 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
843
844 canvas.Transform(Matrix(0.25, -0.3, 0, -0.002, //
845 0, 0.5, 0, 0, //
846 0, 0, 0.3, 0, //
847 100, 100, 0, 1.3));
848 ASSERT_TRUE(RenderTextInCanvasSkia(
849 GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
850 "Roboto-Regular.ttf"));
851
852 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
853 }
854
855 TEST_P(AiksTest, CanDrawPaint) {
856 Canvas canvas;
857 canvas.Scale(Vector2(0.2, 0.2));
858 canvas.DrawPaint({.color = Color::MediumTurquoise()});
859 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
860 }
861
862 TEST_P(AiksTest, CanDrawPaintMultipleTimes) {
863 Canvas canvas;
864 canvas.Scale(Vector2(0.2, 0.2));
865 canvas.DrawPaint({.color = Color::MediumTurquoise()});
866 canvas.DrawPaint({.color = Color::Color::OrangeRed().WithAlpha(0.5)});
867 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
868 }
869
870 // This makes sure the WideGamut named tests use 16bit float pixel format.
871 TEST_P(AiksTest, FormatWideGamut) {
872 EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
873 PixelFormat::kB10G10R10A10XR);
874 }
875
876 TEST_P(AiksTest, FormatSRGB) {
877 PixelFormat pixel_format =
878 GetContext()->GetCapabilities()->GetDefaultColorFormat();
879 EXPECT_TRUE(pixel_format == PixelFormat::kR8G8B8A8UNormInt ||
880 pixel_format == PixelFormat::kB8G8R8A8UNormInt)
881 << "pixel format: " << PixelFormatToString(pixel_format);
882 }
883
884 TEST_P(AiksTest, TransformMultipliesCorrectly) {
885 Canvas canvas;
886 ASSERT_MATRIX_NEAR(canvas.GetCurrentTransform(), Matrix());
887
888 // clang-format off
889 canvas.Translate(Vector3(100, 200));
890 ASSERT_MATRIX_NEAR(
891 canvas.GetCurrentTransform(),
892 Matrix( 1, 0, 0, 0,
893 0, 1, 0, 0,
894 0, 0, 1, 0,
895 100, 200, 0, 1));
896
897 canvas.Rotate(Radians(kPiOver2));
898 ASSERT_MATRIX_NEAR(
899 canvas.GetCurrentTransform(),
900 Matrix( 0, 1, 0, 0,
901 -1, 0, 0, 0,
902 0, 0, 1, 0,
903 100, 200, 0, 1));
904
905 canvas.Scale(Vector3(2, 3));
906 ASSERT_MATRIX_NEAR(
907 canvas.GetCurrentTransform(),
908 Matrix( 0, 2, 0, 0,
909 -3, 0, 0, 0,
910 0, 0, 0, 0,
911 100, 200, 0, 1));
912
913 canvas.Translate(Vector3(100, 200));
914 ASSERT_MATRIX_NEAR(
915 canvas.GetCurrentTransform(),
916 Matrix( 0, 2, 0, 0,
917 -3, 0, 0, 0,
918 0, 0, 0, 0,
919 -500, 400, 0, 1));
920 // clang-format on
921 }
922
923 TEST_P(AiksTest, FilledCirclesRenderCorrectly) {
924 Canvas canvas;
925 canvas.Scale(GetContentScale());
926 Paint paint;
927 const int color_count = 3;
928 Color colors[color_count] = {
929 Color::Blue(),
930 Color::Green(),
931 Color::Crimson(),
932 };
933
934 paint.color = Color::White();
935 canvas.DrawPaint(paint);
936
937 int c_index = 0;
938 int radius = 600;
939 while (radius > 0) {
940 paint.color = colors[(c_index++) % color_count];
941 canvas.DrawCircle({10, 10}, radius, paint);
942 if (radius > 30) {
943 radius -= 10;
944 } else {
945 radius -= 2;
946 }
947 }
948
949 std::vector<Color> gradient_colors = {
950 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
951 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
952 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
953 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
954 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
955 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
956 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
957 std::vector<Scalar> stops = {
958 0.0,
959 (1.0 / 6.0) * 1,
960 (1.0 / 6.0) * 2,
961 (1.0 / 6.0) * 3,
962 (1.0 / 6.0) * 4,
963 (1.0 / 6.0) * 5,
964 1.0,
965 };
966 auto texture = CreateTextureForFixture("airplane.jpg",
967 /*enable_mipmapping=*/true);
968
969 paint.color_source = ColorSource::MakeRadialGradient(
970 {500, 600}, 75, std::move(gradient_colors), std::move(stops),
971 Entity::TileMode::kMirror, {});
972 canvas.DrawCircle({500, 600}, 100, paint);
973
974 paint.color_source = ColorSource::MakeImage(
975 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
976 Matrix::MakeTranslation({700, 200}));
977 canvas.DrawCircle({800, 300}, 100, paint);
978
979 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
980 }
981
982 TEST_P(AiksTest, StrokedCirclesRenderCorrectly) {
983 Canvas canvas;
984 canvas.Scale(GetContentScale());
985 Paint paint;
986 const int color_count = 3;
987 Color colors[color_count] = {
988 Color::Blue(),
989 Color::Green(),
990 Color::Crimson(),
991 };
992
993 paint.color = Color::White();
994 canvas.DrawPaint(paint);
995
996 int c_index = 0;
997
998 auto draw = [&paint, &colors, &c_index](Canvas& canvas, Point center,
999 Scalar r, Scalar dr, int n) {
1000 for (int i = 0; i < n; i++) {
1001 paint.color = colors[(c_index++) % color_count];
1002 canvas.DrawCircle(center, r, paint);
1003 r += dr;
1004 }
1005 };
1006
1007 paint.style = Paint::Style::kStroke;
1008 paint.stroke_width = 1;
1009 draw(canvas, {10, 10}, 2, 2, 14); // r = [2, 28], covers [1,29]
1010 paint.stroke_width = 5;
1011 draw(canvas, {10, 10}, 35, 10, 56); // r = [35, 585], covers [30,590]
1012
1013 std::vector<Color> gradient_colors = {
1014 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1015 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1016 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1017 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1018 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1019 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1020 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1021 std::vector<Scalar> stops = {
1022 0.0,
1023 (1.0 / 6.0) * 1,
1024 (1.0 / 6.0) * 2,
1025 (1.0 / 6.0) * 3,
1026 (1.0 / 6.0) * 4,
1027 (1.0 / 6.0) * 5,
1028 1.0,
1029 };
1030 auto texture = CreateTextureForFixture("airplane.jpg",
1031 /*enable_mipmapping=*/true);
1032
1033 paint.color_source = ColorSource::MakeRadialGradient(
1034 {500, 600}, 75, std::move(gradient_colors), std::move(stops),
1035 Entity::TileMode::kMirror, {});
1036 draw(canvas, {500, 600}, 5, 10, 10);
1037
1038 paint.color_source = ColorSource::MakeImage(
1039 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
1040 Matrix::MakeTranslation({700, 200}));
1041 draw(canvas, {800, 300}, 5, 10, 10);
1042
1043 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1044 }
1045
1046 TEST_P(AiksTest, FilledEllipsesRenderCorrectly) {
1047 Canvas canvas;
1048 canvas.Scale(GetContentScale());
1049 Paint paint;
1050 const int color_count = 3;
1051 Color colors[color_count] = {
1052 Color::Blue(),
1053 Color::Green(),
1054 Color::Crimson(),
1055 };
1056
1057 paint.color = Color::White();
1058 canvas.DrawPaint(paint);
1059
1060 int c_index = 0;
1061 int long_radius = 600;
1062 int short_radius = 600;
1063 while (long_radius > 0 && short_radius > 0) {
1064 paint.color = colors[(c_index++) % color_count];
1065 canvas.DrawOval(Rect::MakeXYWH(10 - long_radius, 10 - short_radius,
1066 long_radius * 2, short_radius * 2),
1067 paint);
1068 canvas.DrawOval(Rect::MakeXYWH(1000 - short_radius, 750 - long_radius,
1069 short_radius * 2, long_radius * 2),
1070 paint);
1071 if (short_radius > 30) {
1072 short_radius -= 10;
1073 long_radius -= 5;
1074 } else {
1075 short_radius -= 2;
1076 long_radius -= 1;
1077 }
1078 }
1079
1080 std::vector<Color> gradient_colors = {
1081 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1082 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1083 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1084 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1085 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1086 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1087 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1088 std::vector<Scalar> stops = {
1089 0.0,
1090 (1.0 / 6.0) * 1,
1091 (1.0 / 6.0) * 2,
1092 (1.0 / 6.0) * 3,
1093 (1.0 / 6.0) * 4,
1094 (1.0 / 6.0) * 5,
1095 1.0,
1096 };
1097 auto texture = CreateTextureForFixture("airplane.jpg",
1098 /*enable_mipmapping=*/true);
1099
1100 paint.color = Color::White().WithAlpha(0.5);
1101
1102 paint.color_source = ColorSource::MakeRadialGradient(
1103 {300, 650}, 75, std::move(gradient_colors), std::move(stops),
1104 Entity::TileMode::kMirror, {});
1105 canvas.DrawOval(Rect::MakeXYWH(200, 625, 200, 50), paint);
1106 canvas.DrawOval(Rect::MakeXYWH(275, 550, 50, 200), paint);
1107
1108 paint.color_source = ColorSource::MakeImage(
1109 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
1110 Matrix::MakeTranslation({610, 15}));
1111 canvas.DrawOval(Rect::MakeXYWH(610, 90, 200, 50), paint);
1112 canvas.DrawOval(Rect::MakeXYWH(685, 15, 50, 200), paint);
1113
1114 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1115 }
1116
1117 TEST_P(AiksTest, FilledRoundRectsRenderCorrectly) {
1118 Canvas canvas;
1119 canvas.Scale(GetContentScale());
1120 Paint paint;
1121 const int color_count = 3;
1122 Color colors[color_count] = {
1123 Color::Blue(),
1124 Color::Green(),
1125 Color::Crimson(),
1126 };
1127
1128 paint.color = Color::White();
1129 canvas.DrawPaint(paint);
1130
1131 int c_index = 0;
1132 for (int i = 0; i < 4; i++) {
1133 for (int j = 0; j < 4; j++) {
1134 paint.color = colors[(c_index++) % color_count];
1135 canvas.DrawRRect(Rect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80),
1136 Size(i * 5 + 10, j * 5 + 10), paint);
1137 }
1138 }
1139 paint.color = colors[(c_index++) % color_count];
1140 canvas.DrawRRect(Rect::MakeXYWH(10, 420, 380, 80), Size(40, 40), paint);
1141 paint.color = colors[(c_index++) % color_count];
1142 canvas.DrawRRect(Rect::MakeXYWH(410, 20, 80, 380), Size(40, 40), paint);
1143
1144 std::vector<Color> gradient_colors = {
1145 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1146 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1147 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1148 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1149 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1150 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1151 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1152 std::vector<Scalar> stops = {
1153 0.0,
1154 (1.0 / 6.0) * 1,
1155 (1.0 / 6.0) * 2,
1156 (1.0 / 6.0) * 3,
1157 (1.0 / 6.0) * 4,
1158 (1.0 / 6.0) * 5,
1159 1.0,
1160 };
1161 auto texture = CreateTextureForFixture("airplane.jpg",
1162 /*enable_mipmapping=*/true);
1163
1164 paint.color = Color::White().WithAlpha(0.1);
1165 paint.color_source = ColorSource::MakeRadialGradient(
1166 {550, 550}, 75, gradient_colors, stops, Entity::TileMode::kMirror, {});
1167 for (int i = 1; i <= 10; i++) {
1168 int j = 11 - i;
1169 canvas.DrawRRect(Rect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1170 550 + i * 20, 550 + j * 20),
1171 Size(i * 10, j * 10), paint);
1172 }
1173 paint.color = Color::White().WithAlpha(0.5);
1174 paint.color_source = ColorSource::MakeRadialGradient(
1175 {200, 650}, 75, std::move(gradient_colors), std::move(stops),
1176 Entity::TileMode::kMirror, {});
1177 canvas.DrawRRect(Rect::MakeLTRB(100, 610, 300, 690), Size(40, 40), paint);
1178 canvas.DrawRRect(Rect::MakeLTRB(160, 550, 240, 750), Size(40, 40), paint);
1179
1180 paint.color = Color::White().WithAlpha(0.1);
1181 paint.color_source = ColorSource::MakeImage(
1182 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
1183 Matrix::MakeTranslation({520, 20}));
1184 for (int i = 1; i <= 10; i++) {
1185 int j = 11 - i;
1186 canvas.DrawRRect(Rect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1187 720 + i * 20, 220 + j * 20),
1188 Size(i * 10, j * 10), paint);
1189 }
1190 paint.color = Color::White().WithAlpha(0.5);
1191 paint.color_source = ColorSource::MakeImage(
1192 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
1193 Matrix::MakeTranslation({800, 300}));
1194 canvas.DrawRRect(Rect::MakeLTRB(800, 410, 1000, 490), Size(40, 40), paint);
1195 canvas.DrawRRect(Rect::MakeLTRB(860, 350, 940, 550), Size(40, 40), paint);
1196
1197 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1198 }
1199
1200 TEST_P(AiksTest, SolidColorCirclesOvalsRRectsMaskBlurCorrectly) {
1201 Canvas canvas;
1202 canvas.Scale(GetContentScale());
1203 Paint paint;
1204 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
1205 .style = FilterContents::BlurStyle::kNormal,
1206 .sigma = Sigma{1},
1207 };
1208
1209 canvas.DrawPaint({.color = Color::White()});
1210
1211 paint.color = Color::Crimson();
1212 Scalar y = 100.0f;
1213 for (int i = 0; i < 5; i++) {
1214 Scalar x = (i + 1) * 100;
1215 Scalar radius = x / 10.0f;
1216 canvas.DrawRect(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1217 radius, 60.0f - radius),
1218 paint);
1219 }
1220
1221 paint.color = Color::Blue();
1222 y += 100.0f;
1223 for (int i = 0; i < 5; i++) {
1224 Scalar x = (i + 1) * 100;
1225 Scalar radius = x / 10.0f;
1226 canvas.DrawCircle({x + 25, y + 25}, radius, paint);
1227 }
1228
1229 paint.color = Color::Green();
1230 y += 100.0f;
1231 for (int i = 0; i < 5; i++) {
1232 Scalar x = (i + 1) * 100;
1233 Scalar radius = x / 10.0f;
1234 canvas.DrawOval(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1235 radius, 60.0f - radius),
1236 paint);
1237 }
1238
1239 paint.color = Color::Purple();
1240 y += 100.0f;
1241 for (int i = 0; i < 5; i++) {
1242 Scalar x = (i + 1) * 100;
1243 Scalar radius = x / 20.0f;
1244 canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
1245 {radius, radius}, //
1246 paint);
1247 }
1248
1249 paint.color = Color::Orange();
1250 y += 100.0f;
1251 for (int i = 0; i < 5; i++) {
1252 Scalar x = (i + 1) * 100;
1253 Scalar radius = x / 20.0f;
1254 canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
1255 {radius, 5.0f}, paint);
1256 }
1257
1258 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1259 }
1260
1261 TEST_P(AiksTest, FastEllipticalRRectMaskBlursRenderCorrectly) {
1262 Canvas canvas;
1263 canvas.Scale(GetContentScale());
1264 Paint paint;
1265 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
1266 .style = FilterContents::BlurStyle::kNormal,
1267 .sigma = Sigma{1},
1268 };
1269
1270 canvas.DrawPaint({.color = Color::White()});
1271
1272 paint.color = Color::Blue();
1273 for (int i = 0; i < 5; i++) {
1274 Scalar y = i * 125;
1275 Scalar y_radius = i * 15;
1276 for (int j = 0; j < 5; j++) {
1277 Scalar x = j * 125;
1278 Scalar x_radius = j * 15;
1279 canvas.DrawRRect(Rect::MakeXYWH(x + 50, y + 50, 100.0f, 100.0f),
1280 {x_radius, y_radius}, paint);
1281 }
1282 }
1283
1284 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1285 }
1286
1287 TEST_P(AiksTest, FilledRoundRectPathsRenderCorrectly) {
1288 Canvas canvas;
1289 canvas.Scale(GetContentScale());
1290 Paint paint;
1291 const int color_count = 3;
1292 Color colors[color_count] = {
1293 Color::Blue(),
1294 Color::Green(),
1295 Color::Crimson(),
1296 };
1297
1298 paint.color = Color::White();
1299 canvas.DrawPaint(paint);
1300
1301 auto draw_rrect_as_path = [&canvas](const Rect& rect, const Size& radii,
1302 const Paint& paint) {
1303 PathBuilder builder = PathBuilder();
1304 builder.AddRoundedRect(rect, radii);
1305 canvas.DrawPath(builder.TakePath(), paint);
1306 };
1307
1308 int c_index = 0;
1309 for (int i = 0; i < 4; i++) {
1310 for (int j = 0; j < 4; j++) {
1311 paint.color = colors[(c_index++) % color_count];
1312 draw_rrect_as_path(Rect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80),
1313 Size(i * 5 + 10, j * 5 + 10), paint);
1314 }
1315 }
1316 paint.color = colors[(c_index++) % color_count];
1317 draw_rrect_as_path(Rect::MakeXYWH(10, 420, 380, 80), Size(40, 40), paint);
1318 paint.color = colors[(c_index++) % color_count];
1319 draw_rrect_as_path(Rect::MakeXYWH(410, 20, 80, 380), Size(40, 40), paint);
1320
1321 std::vector<Color> gradient_colors = {
1322 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1323 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1324 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1325 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1326 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1327 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1328 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1329 std::vector<Scalar> stops = {
1330 0.0,
1331 (1.0 / 6.0) * 1,
1332 (1.0 / 6.0) * 2,
1333 (1.0 / 6.0) * 3,
1334 (1.0 / 6.0) * 4,
1335 (1.0 / 6.0) * 5,
1336 1.0,
1337 };
1338 auto texture = CreateTextureForFixture("airplane.jpg",
1339 /*enable_mipmapping=*/true);
1340
1341 paint.color = Color::White().WithAlpha(0.1);
1342 paint.color_source = ColorSource::MakeRadialGradient(
1343 {550, 550}, 75, gradient_colors, stops, Entity::TileMode::kMirror, {});
1344 for (int i = 1; i <= 10; i++) {
1345 int j = 11 - i;
1346 draw_rrect_as_path(Rect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1347 550 + i * 20, 550 + j * 20),
1348 Size(i * 10, j * 10), paint);
1349 }
1350 paint.color = Color::White().WithAlpha(0.5);
1351 paint.color_source = ColorSource::MakeRadialGradient(
1352 {200, 650}, 75, std::move(gradient_colors), std::move(stops),
1353 Entity::TileMode::kMirror, {});
1354 draw_rrect_as_path(Rect::MakeLTRB(100, 610, 300, 690), Size(40, 40), paint);
1355 draw_rrect_as_path(Rect::MakeLTRB(160, 550, 240, 750), Size(40, 40), paint);
1356
1357 paint.color = Color::White().WithAlpha(0.1);
1358 paint.color_source = ColorSource::MakeImage(
1359 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
1360 Matrix::MakeTranslation({520, 20}));
1361 for (int i = 1; i <= 10; i++) {
1362 int j = 11 - i;
1363 draw_rrect_as_path(Rect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1364 720 + i * 20, 220 + j * 20),
1365 Size(i * 10, j * 10), paint);
1366 }
1367 paint.color = Color::White().WithAlpha(0.5);
1368 paint.color_source = ColorSource::MakeImage(
1369 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
1370 Matrix::MakeTranslation({800, 300}));
1371 draw_rrect_as_path(Rect::MakeLTRB(800, 410, 1000, 490), Size(40, 40), paint);
1372 draw_rrect_as_path(Rect::MakeLTRB(860, 350, 940, 550), Size(40, 40), paint);
1373
1374 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1375 }
1376
1377 TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
1378 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1379 Canvas canvas;
1380 canvas.Scale(GetContentScale());
1381
1382 Paint alpha;
1383 alpha.color = Color::Red().WithAlpha(0.5);
1384
1385 auto current = Point{25, 25};
1386 const auto offset = Point{25, 25};
1387 const auto size = Size(100, 100);
1388
1389 static PlaygroundPoint point_a(Point(40, 40), 10, Color::White());
1390 static PlaygroundPoint point_b(Point(160, 160), 10, Color::White());
1391 auto [b0, b1] = DrawPlaygroundLine(point_a, point_b);
1392 auto bounds = Rect::MakeLTRB(b0.x, b0.y, b1.x, b1.y);
1393
1394 canvas.DrawRect(bounds, Paint{.color = Color::Yellow(),
1395 .stroke_width = 5.0f,
1396 .style = Paint::Style::kStroke});
1397
1398 canvas.SaveLayer(alpha, bounds);
1399
1400 canvas.DrawRect(Rect::MakeOriginSize(current, size),
1401 Paint{.color = Color::Red()});
1402 canvas.DrawRect(Rect::MakeOriginSize(current += offset, size),
1403 Paint{.color = Color::Green()});
1404 canvas.DrawRect(Rect::MakeOriginSize(current += offset, size),
1405 Paint{.color = Color::Blue()});
1406
1407 canvas.Restore();
1408
1409 return canvas.EndRecordingAsPicture();
1410 };
1411
1412 ASSERT_TRUE(OpenPlaygroundHere(callback));
1413 }
1414
1415 TEST_P(AiksTest, SaveLayerDrawsBehindSubsequentEntities) {
1416 // Compare with https://fiddle.skia.org/c/9e03de8567ffb49e7e83f53b64bcf636
1417 Canvas canvas;
1418 Paint paint;
1419
1420 paint.color = Color::Black();
1421 Rect rect = Rect::MakeXYWH(25, 25, 25, 25);
1422 canvas.DrawRect(rect, paint);
1423
1424 canvas.Translate({10, 10});
1425 canvas.SaveLayer({});
1426
1427 paint.color = Color::Green();
1428 canvas.DrawRect(rect, paint);
1429
1430 canvas.Restore();
1431
1432 canvas.Translate({10, 10});
1433 paint.color = Color::Red();
1434 canvas.DrawRect(rect, paint);
1435
1436 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1437 }
1438
1439 TEST_P(AiksTest, SiblingSaveLayerBoundsAreRespected) {
1440 Canvas canvas;
1441 Paint paint;
1442 Rect rect = Rect::MakeXYWH(0, 0, 1000, 1000);
1443
1444 // Black, green, and red squares offset by [10, 10].
1445 {
1446 canvas.SaveLayer({}, Rect::MakeXYWH(25, 25, 25, 25));
1447 paint.color = Color::Black();
1448 canvas.DrawRect(rect, paint);
1449 canvas.Restore();
1450 }
1451
1452 {
1453 canvas.SaveLayer({}, Rect::MakeXYWH(35, 35, 25, 25));
1454 paint.color = Color::Green();
1455 canvas.DrawRect(rect, paint);
1456 canvas.Restore();
1457 }
1458
1459 {
1460 canvas.SaveLayer({}, Rect::MakeXYWH(45, 45, 25, 25));
1461 paint.color = Color::Red();
1462 canvas.DrawRect(rect, paint);
1463 canvas.Restore();
1464 }
1465
1466 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1467 }
1468
1469 TEST_P(AiksTest, CanRenderClippedLayers) {
1470 Canvas canvas;
1471
1472 canvas.DrawPaint({.color = Color::White()});
1473
1474 // Draw a green circle on the screen.
1475 {
1476 // Increase the clip depth for the savelayer to contend with.
1477 canvas.ClipPath(PathBuilder{}.AddCircle({100, 100}, 50).TakePath());
1478
1479 canvas.SaveLayer({}, Rect::MakeXYWH(50, 50, 100, 100));
1480
1481 // Fill the layer with white.
1482 canvas.DrawRect(Rect::MakeSize(Size{400, 400}), {.color = Color::White()});
1483 // Fill the layer with green, but do so with a color blend that can't be
1484 // collapsed into the parent pass.
1485 // TODO(jonahwilliams): this blend mode was changed from color burn to
1486 // hardlight to work around https://github.com/flutter/flutter/issues/136554
1487 // .
1488 canvas.DrawRect(
1489 Rect::MakeSize(Size{400, 400}),
1490 {.color = Color::Green(), .blend_mode = BlendMode::kHardLight});
1491 }
1492
1493 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1494 }
1495
1496 TEST_P(AiksTest, SaveLayerFiltersScaleWithTransform) {
1497 Canvas canvas;
1498 canvas.Scale(GetContentScale());
1499 canvas.Translate(Vector2(100, 100));
1500
1501 auto texture = std::make_shared<Image>(CreateTextureForFixture("boston.jpg"));
1502 auto draw_image_layer = [&canvas, &texture](const Paint& paint) {
1503 canvas.SaveLayer(paint);
1504 canvas.DrawImage(texture, {}, Paint{});
1505 canvas.Restore();
1506 };
1507
1508 Paint effect_paint;
1509 effect_paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
1510 .style = FilterContents::BlurStyle::kNormal,
1511 .sigma = Sigma{6},
1512 };
1513 draw_image_layer(effect_paint);
1514
1515 canvas.Translate(Vector2(300, 300));
1516 canvas.Scale(Vector2(3, 3));
1517 draw_image_layer(effect_paint);
1518
1519 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1520 }
1521
1522 #if IMPELLER_ENABLE_3D
1523 TEST_P(AiksTest, SceneColorSource) {
1524 // Load up the scene.
1525 auto mapping =
1526 flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb.ipscene");
1527 ASSERT_NE(mapping, nullptr);
1528
1529 std::shared_ptr<scene::Node> gltf_scene = scene::Node::MakeFromFlatbuffer(
1530 *mapping, *GetContext()->GetResourceAllocator());
1531 ASSERT_NE(gltf_scene, nullptr);
1532
1533 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1534 Paint paint;
1535
1536 static Scalar distance = 2;
1537 static Scalar y_pos = 0;
1538 static Scalar fov = 45;
1539 if (AiksTest::ImGuiBegin("Controls", nullptr,
1540 ImGuiWindowFlags_AlwaysAutoResize)) {
1541 ImGui::SliderFloat("Distance", &distance, 0, 4);
1542 ImGui::SliderFloat("Y", &y_pos, -3, 3);
1543 ImGui::SliderFloat("FOV", &fov, 1, 180);
1544 ImGui::End();
1545 }
1546
1547 Scalar angle = GetSecondsElapsed();
1548 auto camera_position =
1549 Vector3(distance * std::sin(angle), y_pos, -distance * std::cos(angle));
1550
1551 paint.color_source = ColorSource::MakeScene(
1552 gltf_scene,
1553 Matrix::MakePerspective(Degrees(fov), GetWindowSize(), 0.1, 1000) *
1554 Matrix::MakeLookAt(camera_position, {0, 0, 0}, {0, 1, 0}));
1555
1556 Canvas canvas;
1557 canvas.DrawPaint(Paint{.color = Color::MakeRGBA8(0xf9, 0xf9, 0xf9, 0xff)});
1558 canvas.Scale(GetContentScale());
1559 canvas.DrawPaint(paint);
1560 return canvas.EndRecordingAsPicture();
1561 };
1562
1563 ASSERT_TRUE(OpenPlaygroundHere(callback));
1564 }
1565 #endif // IMPELLER_ENABLE_3D
1566
1567 TEST_P(AiksTest, PaintWithFilters) {
1568 // validate that a paint with a color filter "HasFilters", no other filters
1569 // impact this setting.
1570 Paint paint;
1571
1572 ASSERT_FALSE(paint.HasColorFilter());
1573
1574 paint.color_filter =
1575 ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Blue());
1576
1577 ASSERT_TRUE(paint.HasColorFilter());
1578
1579 paint.image_filter = ImageFilter::MakeBlur(Sigma(1.0), Sigma(1.0),
1580 FilterContents::BlurStyle::kNormal,
1581 Entity::TileMode::kClamp);
1582
1583 ASSERT_TRUE(paint.HasColorFilter());
1584
1585 paint.mask_blur_descriptor = {};
1586
1587 ASSERT_TRUE(paint.HasColorFilter());
1588
1589 paint.color_filter = nullptr;
1590
1591 ASSERT_FALSE(paint.HasColorFilter());
1592 }
1593
1594 TEST_P(AiksTest, DrawPaintAbsorbsClears) {
1595 Canvas canvas;
1596 canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1597 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1598 .blend_mode = BlendMode::kSourceOver});
1599
1600 Picture picture = canvas.EndRecordingAsPicture();
1601 auto expected = Color::Red().Blend(Color::CornflowerBlue().WithAlpha(0.75),
1602 BlendMode::kSourceOver);
1603 ASSERT_EQ(picture.pass->GetClearColor(), expected);
1604
1605 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1606 std::shared_ptr<Context> real_context = GetContext();
1607 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1608 AiksContext renderer(mock_context, nullptr);
1609 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1610
1611 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1612 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1613 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1614 }
1615
1616 // This is important to enforce with texture reuse, since cached textures need
1617 // to be cleared before reuse.
1618 TEST_P(AiksTest,
1619 ParentSaveLayerCreatesRenderPassWhenChildBackdropFilterIsPresent) {
1620 Canvas canvas;
1621 canvas.SaveLayer({}, std::nullopt, ImageFilter::MakeMatrix(Matrix(), {}));
1622 canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1623 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1624 .blend_mode = BlendMode::kSourceOver});
1625 canvas.Restore();
1626
1627 Picture picture = canvas.EndRecordingAsPicture();
1628
1629 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1630 std::shared_ptr<Context> real_context = GetContext();
1631 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1632 AiksContext renderer(mock_context, nullptr);
1633 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1634
1635 ASSERT_EQ(spy->render_passes_.size(),
1636 GetBackend() == PlaygroundBackend::kOpenGLES ? 4llu : 3llu);
1637 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1638 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1639 }
1640
1641 TEST_P(AiksTest, DrawRectAbsorbsClears) {
1642 Canvas canvas;
1643 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1644 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1645 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1646 {.color = Color::CornflowerBlue().WithAlpha(0.75),
1647 .blend_mode = BlendMode::kSourceOver});
1648
1649 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1650 Picture picture = canvas.EndRecordingAsPicture();
1651 std::shared_ptr<Context> real_context = GetContext();
1652 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1653 AiksContext renderer(mock_context, nullptr);
1654 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1655
1656 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1657 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1658 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1659 }
1660
1661 TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRRect) {
1662 Canvas canvas;
1663 canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), {5.0, 5.0},
1664 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1665 canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), {5.0, 5.0},
1666 {.color = Color::CornflowerBlue().WithAlpha(0.75),
1667 .blend_mode = BlendMode::kSourceOver});
1668
1669 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1670 Picture picture = canvas.EndRecordingAsPicture();
1671 std::shared_ptr<Context> real_context = GetContext();
1672 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1673 AiksContext renderer(mock_context, nullptr);
1674 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1675
1676 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1677 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1678 ASSERT_EQ(render_pass->GetCommands().size(), 2llu);
1679 }
1680
1681 TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRotation) {
1682 Canvas canvas;
1683 canvas.Translate(Vector3(150.0, 150.0, 0.0));
1684 canvas.Rotate(Degrees(45.0));
1685 canvas.Translate(Vector3(-150.0, -150.0, 0.0));
1686 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1687 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1688
1689 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1690 Picture picture = canvas.EndRecordingAsPicture();
1691 std::shared_ptr<Context> real_context = GetContext();
1692 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1693 AiksContext renderer(mock_context, nullptr);
1694 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1695
1696 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1697 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1698 ASSERT_EQ(render_pass->GetCommands().size(), 1llu);
1699 }
1700
1701 TEST_P(AiksTest, DrawRectAbsorbsClearsNegative) {
1702 Canvas canvas;
1703 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1704 {.color = Color::Red(), .blend_mode = BlendMode::kSource});
1705 canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
1706 {.color = Color::CornflowerBlue().WithAlpha(0.75),
1707 .blend_mode = BlendMode::kSourceOver});
1708
1709 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1710 Picture picture = canvas.EndRecordingAsPicture();
1711 std::shared_ptr<Context> real_context = GetContext();
1712 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1713 AiksContext renderer(mock_context, nullptr);
1714 std::shared_ptr<Image> image = picture.ToImage(renderer, {301, 301});
1715
1716 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1717 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1718 ASSERT_EQ(render_pass->GetCommands().size(), 2llu);
1719 }
1720
1721 TEST_P(AiksTest, ClipRectElidesNoOpClips) {
1722 Canvas canvas(Rect::MakeXYWH(0, 0, 100, 100));
1723 canvas.ClipRect(Rect::MakeXYWH(0, 0, 100, 100));
1724 canvas.ClipRect(Rect::MakeXYWH(-100, -100, 300, 300));
1725 canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1726 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1727 .blend_mode = BlendMode::kSourceOver});
1728
1729 Picture picture = canvas.EndRecordingAsPicture();
1730 auto expected = Color::Red().Blend(Color::CornflowerBlue().WithAlpha(0.75),
1731 BlendMode::kSourceOver);
1732 ASSERT_EQ(picture.pass->GetClearColor(), expected);
1733
1734 std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1735 std::shared_ptr<Context> real_context = GetContext();
1736 std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1737 AiksContext renderer(mock_context, nullptr);
1738 std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1739
1740 ASSERT_EQ(spy->render_passes_.size(), 1llu);
1741 std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1742 ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1743 }
1744
1745 TEST_P(AiksTest, ClearColorOptimizationDoesNotApplyForBackdropFilters) {
1746 Canvas canvas;
1747 canvas.SaveLayer({}, std::nullopt,
1748 ImageFilter::MakeBlur(Sigma(3), Sigma(3),
1749 FilterContents::BlurStyle::kNormal,
1750 Entity::TileMode::kClamp));
1751 canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1752 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1753 .blend_mode = BlendMode::kSourceOver});
1754 canvas.Restore();
1755
1756 Picture picture = canvas.EndRecordingAsPicture();
1757
1758 std::optional<Color> actual_color;
1759 bool found_subpass = false;
1760 picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool {
1761 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1762 actual_color = subpass->get()->GetClearColor();
1763 found_subpass = true;
1764 }
1765 // Fail if the first element isn't a subpass.
1766 return true;
1767 });
1768
1769 EXPECT_TRUE(found_subpass);
1770 EXPECT_FALSE(actual_color.has_value());
1771 }
1772
1773 TEST_P(AiksTest, CollapsedDrawPaintInSubpass) {
1774 Canvas canvas;
1775 canvas.DrawPaint(
1776 {.color = Color::Yellow(), .blend_mode = BlendMode::kSource});
1777 canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
1778 canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1779 .blend_mode = BlendMode::kSourceOver});
1780
1781 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1782 }
1783
1784 TEST_P(AiksTest, CollapsedDrawPaintInSubpassBackdropFilter) {
1785 // Bug: https://github.com/flutter/flutter/issues/131576
1786 Canvas canvas;
1787 canvas.DrawPaint(
1788 {.color = Color::Yellow(), .blend_mode = BlendMode::kSource});
1789 canvas.SaveLayer({}, {},
1790 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
1791 FilterContents::BlurStyle::kNormal,
1792 Entity::TileMode::kDecal));
1793 canvas.DrawPaint(
1794 {.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSourceOver});
1795
1796 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1797 }
1798
1799 TEST_P(AiksTest, ColorMatrixFilterSubpassCollapseOptimization) {
1800 Canvas canvas;
1801
1802 canvas.SaveLayer({
1803 .color_filter =
1804 ColorFilter::MakeMatrix({.array =
1805 {
1806 -1.0, 0, 0, 1.0, 0, //
1807 0, -1.0, 0, 1.0, 0, //
1808 0, 0, -1.0, 1.0, 0, //
1809 1.0, 1.0, 1.0, 1.0, 0 //
1810 }}),
1811 });
1812
1813 canvas.Translate({500, 300, 0});
1814 canvas.Rotate(Radians(2 * kPi / 3));
1815 canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
1816
1817 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1818 }
1819
1820 TEST_P(AiksTest, LinearToSrgbFilterSubpassCollapseOptimization) {
1821 Canvas canvas;
1822
1823 canvas.SaveLayer({
1824 .color_filter = ColorFilter::MakeLinearToSrgb(),
1825 });
1826
1827 canvas.Translate({500, 300, 0});
1828 canvas.Rotate(Radians(2 * kPi / 3));
1829 canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
1830
1831 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1832 }
1833
1834 TEST_P(AiksTest, SrgbToLinearFilterSubpassCollapseOptimization) {
1835 Canvas canvas;
1836
1837 canvas.SaveLayer({
1838 .color_filter = ColorFilter::MakeSrgbToLinear(),
1839 });
1840
1841 canvas.Translate({500, 300, 0});
1842 canvas.Rotate(Radians(2 * kPi / 3));
1843 canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
1844
1845 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1846 }
1847
1848 TEST_P(AiksTest, TranslucentSaveLayerDrawsCorrectly) {
1849 Canvas canvas;
1850
1851 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1852
1853 canvas.SaveLayer({.color = Color::Black().WithAlpha(0.5)});
1854 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1855 canvas.Restore();
1856
1857 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1858 }
1859
1860 TEST_P(AiksTest, TranslucentSaveLayerWithBlendColorFilterDrawsCorrectly) {
1861 Canvas canvas;
1862
1863 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1864
1865 canvas.SaveLayer({
1866 .color = Color::Black().WithAlpha(0.5),
1867 .color_filter =
1868 ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red()),
1869 });
1870 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1871 canvas.Restore();
1872
1873 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1874 }
1875
1876 TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterDrawsCorrectly) {
1877 Canvas canvas;
1878
1879 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1880
1881 canvas.SaveLayer({
1882 .color = Color::Black().WithAlpha(0.5),
1883 .image_filter = ImageFilter::MakeFromColorFilter(
1884 *ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red())),
1885 });
1886
1887 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1888 canvas.Restore();
1889
1890 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1891 }
1892
1893 TEST_P(AiksTest, TranslucentSaveLayerWithColorAndImageFilterDrawsCorrectly) {
1894 Canvas canvas;
1895
1896 canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
1897
1898 canvas.SaveLayer({
1899 .color = Color::Black().WithAlpha(0.5),
1900 .color_filter =
1901 ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red()),
1902 });
1903
1904 canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
1905 canvas.Restore();
1906
1907 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1908 }
1909
1910 TEST_P(AiksTest, ImageFilteredSaveLayerWithUnboundedContents) {
1911 Canvas canvas;
1912 canvas.Scale(GetContentScale());
1913
1914 auto test = [&canvas](const std::shared_ptr<ImageFilter>& filter) {
1915 auto DrawLine = [&canvas](const Point& p0, const Point& p1,
1916 const Paint& p) {
1917 auto path = PathBuilder{}
1918 .AddLine(p0, p1)
1919 .SetConvexity(Convexity::kConvex)
1920 .TakePath();
1921 Paint paint = p;
1922 paint.style = Paint::Style::kStroke;
1923 canvas.DrawPath(path, paint);
1924 };
1925 // Registration marks for the edge of the SaveLayer
1926 DrawLine(Point(75, 100), Point(225, 100), {.color = Color::White()});
1927 DrawLine(Point(75, 200), Point(225, 200), {.color = Color::White()});
1928 DrawLine(Point(100, 75), Point(100, 225), {.color = Color::White()});
1929 DrawLine(Point(200, 75), Point(200, 225), {.color = Color::White()});
1930
1931 canvas.SaveLayer({.image_filter = filter},
1932 Rect::MakeLTRB(100, 100, 200, 200));
1933 {
1934 // DrawPaint to verify correct behavior when the contents are unbounded.
1935 canvas.DrawPaint({.color = Color::Yellow()});
1936
1937 // Contrasting rectangle to see interior blurring
1938 canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
1939 {.color = Color::Blue()});
1940 }
1941 canvas.Restore();
1942 };
1943
1944 test(ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0},
1945 FilterContents::BlurStyle::kNormal,
1946 Entity::TileMode::kDecal));
1947
1948 canvas.Translate({200.0, 0.0});
1949
1950 test(ImageFilter::MakeDilate(Radius{10.0}, Radius{10.0}));
1951
1952 canvas.Translate({200.0, 0.0});
1953
1954 test(ImageFilter::MakeErode(Radius{10.0}, Radius{10.0}));
1955
1956 canvas.Translate({-400.0, 200.0});
1957
1958 auto rotate_filter =
1959 ImageFilter::MakeMatrix(Matrix::MakeTranslation({150, 150}) *
1960 Matrix::MakeRotationZ(Degrees{10.0}) *
1961 Matrix::MakeTranslation({-150, -150}),
1962 SamplerDescriptor{});
1963 test(rotate_filter);
1964
1965 canvas.Translate({200.0, 0.0});
1966
1967 auto rgb_swap_filter = ImageFilter::MakeFromColorFilter(
1968 *ColorFilter::MakeMatrix({.array = {
1969 0, 1, 0, 0, 0, //
1970 0, 0, 1, 0, 0, //
1971 1, 0, 0, 0, 0, //
1972 0, 0, 0, 1, 0 //
1973 }}));
1974 test(rgb_swap_filter);
1975
1976 canvas.Translate({200.0, 0.0});
1977
1978 test(ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter));
1979
1980 canvas.Translate({-400.0, 200.0});
1981
1982 test(ImageFilter::MakeLocalMatrix(Matrix::MakeTranslation({25.0, 25.0}),
1983 *rotate_filter));
1984
1985 canvas.Translate({200.0, 0.0});
1986
1987 test(ImageFilter::MakeLocalMatrix(Matrix::MakeTranslation({25.0, 25.0}),
1988 *rgb_swap_filter));
1989
1990 canvas.Translate({200.0, 0.0});
1991
1992 test(ImageFilter::MakeLocalMatrix(
1993 Matrix::MakeTranslation({25.0, 25.0}),
1994 *ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter)));
1995
1996 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1997 }
1998
1999 TEST_P(AiksTest, ImageFilteredUnboundedSaveLayerWithUnboundedContents) {
2000 Canvas canvas;
2001 canvas.Scale(GetContentScale());
2002
2003 auto blur_filter = ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0},
2004 FilterContents::BlurStyle::kNormal,
2005 Entity::TileMode::kDecal);
2006
2007 canvas.SaveLayer({.image_filter = blur_filter}, std::nullopt);
2008 {
2009 // DrawPaint to verify correct behavior when the contents are unbounded.
2010 canvas.DrawPaint({.color = Color::Yellow()});
2011
2012 // Contrasting rectangle to see interior blurring
2013 canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
2014 {.color = Color::Blue()});
2015 }
2016 canvas.Restore();
2017
2018 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2019 }
2020
2021 TEST_P(AiksTest, TranslucentSaveLayerImageDrawsCorrectly) {
2022 Canvas canvas;
2023
2024 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2025 canvas.DrawImage(image, {100, 100}, {});
2026
2027 canvas.SaveLayer({.color = Color::Black().WithAlpha(0.5)});
2028 canvas.DrawImage(image, {100, 500}, {});
2029 canvas.Restore();
2030
2031 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2032 }
2033
2034 TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixColorFilterDrawsCorrectly) {
2035 Canvas canvas;
2036
2037 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2038 canvas.DrawImage(image, {100, 100}, {});
2039
2040 canvas.SaveLayer({
2041 .color = Color::Black().WithAlpha(0.5),
2042 .color_filter = ColorFilter::MakeMatrix({.array =
2043 {
2044 1, 0, 0, 0, 0, //
2045 0, 1, 0, 0, 0, //
2046 0, 0, 1, 0, 0, //
2047 0, 0, 0, 2, 0 //
2048 }}),
2049 });
2050 canvas.DrawImage(image, {100, 500}, {});
2051 canvas.Restore();
2052
2053 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2054 }
2055
2056 TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly) {
2057 Canvas canvas;
2058
2059 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2060 canvas.DrawImage(image, {100, 100}, {});
2061
2062 canvas.SaveLayer({
2063 .color = Color::Black().WithAlpha(0.5),
2064 .image_filter = ImageFilter::MakeFromColorFilter(
2065 *ColorFilter::MakeMatrix({.array =
2066 {
2067 1, 0, 0, 0, 0, //
2068 0, 1, 0, 0, 0, //
2069 0, 0, 1, 0, 0, //
2070 0, 0, 0, 2, 0 //
2071 }})),
2072 });
2073 canvas.DrawImage(image, {100, 500}, {});
2074 canvas.Restore();
2075
2076 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2077 }
2078
2079 TEST_P(AiksTest,
2080 TranslucentSaveLayerWithColorFilterAndImageFilterDrawsCorrectly) {
2081 Canvas canvas;
2082
2083 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2084 canvas.DrawImage(image, {100, 100}, {});
2085
2086 canvas.SaveLayer({
2087 .color = Color::Black().WithAlpha(0.5),
2088 .image_filter = ImageFilter::MakeFromColorFilter(
2089 *ColorFilter::MakeMatrix({.array =
2090 {
2091 1, 0, 0, 0, 0, //
2092 0, 1, 0, 0, 0, //
2093 0, 0.2, 1, 0, 0, //
2094 0, 0, 0, 0.5, 0 //
2095 }})),
2096 .color_filter =
2097 ColorFilter::MakeBlend(BlendMode::kModulate, Color::Green()),
2098 });
2099 canvas.DrawImage(image, {100, 500}, {});
2100 canvas.Restore();
2101
2102 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2103 }
2104
2105 TEST_P(AiksTest, TranslucentSaveLayerWithAdvancedBlendModeDrawsCorrectly) {
2106 Canvas canvas;
2107 canvas.DrawRect(Rect::MakeXYWH(0, 0, 400, 400), {.color = Color::Red()});
2108 canvas.SaveLayer({
2109 .color = Color::Black().WithAlpha(0.5),
2110 .blend_mode = BlendMode::kLighten,
2111 });
2112 canvas.DrawCircle({200, 200}, 100, {.color = Color::Green()});
2113 canvas.Restore();
2114 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2115 }
2116
2117 /// This is a regression check for https://github.com/flutter/engine/pull/41129
2118 /// The entire screen is green if successful. If failing, no frames will render,
2119 /// or the entire screen will be transparent black.
2120 TEST_P(AiksTest, CanRenderTinyOverlappingSubpasses) {
2121 Canvas canvas;
2122 canvas.DrawPaint({.color = Color::Red()});
2123
2124 // Draw two overlapping subpixel circles.
2125 canvas.SaveLayer({});
2126 canvas.DrawCircle({100, 100}, 0.1, {.color = Color::Yellow()});
2127 canvas.Restore();
2128 canvas.SaveLayer({});
2129 canvas.DrawCircle({100, 100}, 0.1, {.color = Color::Yellow()});
2130 canvas.Restore();
2131
2132 canvas.DrawPaint({.color = Color::Green()});
2133
2134 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2135 }
2136
2137 TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) {
2138 Canvas canvas;
2139 canvas.Scale(Vector2(1.618, 1.618));
2140 canvas.DrawCircle(Point(), 10,
2141 {
2142 .color = Color::CornflowerBlue(),
2143 .blend_mode = BlendMode::kSourceOver,
2144 });
2145 Picture picture = canvas.EndRecordingAsPicture();
2146
2147 // Extract the SolidColorSource.
2148 // Entity entity;
2149 std::vector<Entity> entity;
2150 std::shared_ptr<SolidColorContents> contents;
2151 picture.pass->IterateAllEntities([e = &entity, &contents](Entity& entity) {
2152 if (ScalarNearlyEqual(entity.GetTransform().GetScale().x, 1.618f)) {
2153 contents =
2154 std::static_pointer_cast<SolidColorContents>(entity.GetContents());
2155 e->emplace_back(entity.Clone());
2156 return false;
2157 }
2158 return true;
2159 });
2160
2161 ASSERT_TRUE(entity.size() >= 1);
2162 ASSERT_TRUE(contents->IsOpaque());
2163 ASSERT_EQ(entity[0].GetBlendMode(), BlendMode::kSource);
2164 }
2165
2166 TEST_P(AiksTest, CanRenderDestructiveSaveLayer) {
2167 Canvas canvas;
2168
2169 canvas.DrawPaint({.color = Color::Red()});
2170 // Draw an empty savelayer with a destructive blend mode, which will replace
2171 // the entire red screen with fully transparent black, except for the green
2172 // circle drawn within the layer.
2173 canvas.SaveLayer({.blend_mode = BlendMode::kSource});
2174 canvas.DrawCircle({300, 300}, 100, {.color = Color::Green()});
2175 canvas.Restore();
2176
2177 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2178 }
2179
2180 // Regression test for https://github.com/flutter/flutter/issues/126701 .
2181 TEST_P(AiksTest, CanRenderClippedRuntimeEffects) {
2182 auto runtime_stages =
2183 OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
2184
2185 auto runtime_stage =
2186 runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
2187 ASSERT_TRUE(runtime_stage);
2188 ASSERT_TRUE(runtime_stage->IsDirty());
2189
2190 struct FragUniforms {
2191 Vector2 iResolution;
2192 Scalar iTime;
2193 } frag_uniforms = {.iResolution = Vector2(400, 400), .iTime = 100.0};
2194 auto uniform_data = std::make_shared<std::vector<uint8_t>>();
2195 uniform_data->resize(sizeof(FragUniforms));
2196 memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
2197
2198 std::vector<RuntimeEffectContents::TextureInput> texture_inputs;
2199
2200 Paint paint;
2201 paint.color_source = ColorSource::MakeRuntimeEffect(
2202 runtime_stage, uniform_data, texture_inputs);
2203
2204 Canvas canvas;
2205 canvas.Save();
2206 canvas.ClipRRect(Rect::MakeXYWH(0, 0, 400, 400), {10.0, 10.0},
2207 Entity::ClipOperation::kIntersect);
2208 canvas.DrawRect(Rect::MakeXYWH(0, 0, 400, 400), paint);
2209 canvas.Restore();
2210
2211 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2212 }
2213
2214 TEST_P(AiksTest, DrawPaintTransformsBounds) {
2215 auto runtime_stages = OpenAssetAsRuntimeStage("gradient.frag.iplr");
2216 auto runtime_stage =
2217 runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
2218 ASSERT_TRUE(runtime_stage);
2219 ASSERT_TRUE(runtime_stage->IsDirty());
2220
2221 struct FragUniforms {
2222 Size size;
2223 } frag_uniforms = {.size = Size::MakeWH(400, 400)};
2224 auto uniform_data = std::make_shared<std::vector<uint8_t>>();
2225 uniform_data->resize(sizeof(FragUniforms));
2226 memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
2227
2228 std::vector<RuntimeEffectContents::TextureInput> texture_inputs;
2229
2230 Paint paint;
2231 paint.color_source = ColorSource::MakeRuntimeEffect(
2232 runtime_stage, uniform_data, texture_inputs);
2233
2234 Canvas canvas;
2235 canvas.Save();
2236 canvas.Scale(GetContentScale());
2237 canvas.DrawPaint(paint);
2238 canvas.Restore();
2239
2240 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2241 }
2242
2243 TEST_P(AiksTest, CanDrawPoints) {
2244 std::vector<Point> points = {
2245 {0, 0}, //
2246 {100, 100}, //
2247 {100, 0}, //
2248 {0, 100}, //
2249 {0, 0}, //
2250 {48, 48}, //
2251 {52, 52}, //
2252 };
2253 std::vector<PointStyle> caps = {
2254 PointStyle::kRound,
2255 PointStyle::kSquare,
2256 };
2257 Paint paint;
2258 paint.color = Color::Yellow().WithAlpha(0.5);
2259
2260 Paint background;
2261 background.color = Color::Black();
2262
2263 Canvas canvas;
2264 canvas.DrawPaint(background);
2265 canvas.Translate({200, 200});
2266 canvas.DrawPoints(points, 10, paint, PointStyle::kRound);
2267 canvas.Translate({150, 0});
2268 canvas.DrawPoints(points, 10, paint, PointStyle::kSquare);
2269
2270 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2271 }
2272
2273 TEST_P(AiksTest, CanDrawPointsWithTextureMap) {
2274 auto texture = CreateTextureForFixture("table_mountain_nx.png",
2275 /*enable_mipmapping=*/true);
2276
2277 std::vector<Point> points = {
2278 {0, 0}, //
2279 {100, 100}, //
2280 {100, 0}, //
2281 {0, 100}, //
2282 {0, 0}, //
2283 {48, 48}, //
2284 {52, 52}, //
2285 };
2286 std::vector<PointStyle> caps = {
2287 PointStyle::kRound,
2288 PointStyle::kSquare,
2289 };
2290 Paint paint;
2291 paint.color_source = ColorSource::MakeImage(texture, Entity::TileMode::kClamp,
2292 Entity::TileMode::kClamp, {}, {});
2293
2294 Canvas canvas;
2295 canvas.Translate({200, 200});
2296 canvas.DrawPoints(points, 100, paint, PointStyle::kRound);
2297 canvas.Translate({150, 0});
2298 canvas.DrawPoints(points, 100, paint, PointStyle::kSquare);
2299
2300 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2301 }
2302
2303 // This currently renders solid blue, as the support for text color sources was
2304 // moved into DLDispatching. Path data requires the SkTextBlobs which are not
2305 // used in impeller::TextFrames.
2306 TEST_P(AiksTest, TextForegroundShaderWithTransform) {
2307 auto mapping = flutter::testing::OpenFixtureAsSkData("Roboto-Regular.ttf");
2308 ASSERT_NE(mapping, nullptr);
2309
2310 Scalar font_size = 100;
2311 sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
2312 SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
2313
2314 Paint text_paint;
2315 text_paint.color = Color::Blue();
2316
2317 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
2318 Color{0.1294, 0.5882, 0.9529, 1.0}};
2319 std::vector<Scalar> stops = {
2320 0.0,
2321 1.0,
2322 };
2323 text_paint.color_source = ColorSource::MakeLinearGradient(
2324 {0, 0}, {100, 100}, std::move(colors), std::move(stops),
2325 Entity::TileMode::kRepeat, {});
2326
2327 Canvas canvas;
2328 canvas.Translate({100, 100});
2329 canvas.Rotate(Radians(kPi / 4));
2330
2331 auto blob = SkTextBlob::MakeFromString("Hello", sk_font);
2332 ASSERT_NE(blob, nullptr);
2333 auto frame = MakeTextFrameFromTextBlobSkia(blob);
2334 canvas.DrawTextFrame(frame, Point(), text_paint);
2335
2336 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2337 }
2338
2339 TEST_P(AiksTest, MatrixSaveLayerFilter) {
2340 Canvas canvas;
2341 canvas.DrawPaint({.color = Color::Black()});
2342 canvas.SaveLayer({}, std::nullopt);
2343 {
2344 canvas.DrawCircle(Point(200, 200), 100,
2345 {.color = Color::Green().WithAlpha(0.5),
2346 .blend_mode = BlendMode::kPlus});
2347 // Should render a second circle, centered on the bottom-right-most edge of
2348 // the circle.
2349 canvas.SaveLayer({.image_filter = ImageFilter::MakeMatrix(
2350 Matrix::MakeTranslation(Vector2(1, 1) *
2351 (200 + 100 * k1OverSqrt2)) *
2352 Matrix::MakeScale(Vector2(1, 1) * 0.5) *
2353 Matrix::MakeTranslation(Vector2(-200, -200)),
2354 SamplerDescriptor{})},
2355 std::nullopt);
2356 canvas.DrawCircle(Point(200, 200), 100,
2357 {.color = Color::Green().WithAlpha(0.5),
2358 .blend_mode = BlendMode::kPlus});
2359 canvas.Restore();
2360 }
2361 canvas.Restore();
2362
2363 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2364 }
2365
2366 TEST_P(AiksTest, MatrixBackdropFilter) {
2367 Canvas canvas;
2368 canvas.DrawPaint({.color = Color::Black()});
2369 canvas.SaveLayer({}, std::nullopt);
2370 {
2371 canvas.DrawCircle(Point(200, 200), 100,
2372 {.color = Color::Green().WithAlpha(0.5),
2373 .blend_mode = BlendMode::kPlus});
2374 // Should render a second circle, centered on the bottom-right-most edge of
2375 // the circle.
2376 canvas.SaveLayer(
2377 {}, std::nullopt,
2378 ImageFilter::MakeMatrix(
2379 Matrix::MakeTranslation(Vector2(1, 1) * (100 + 100 * k1OverSqrt2)) *
2380 Matrix::MakeScale(Vector2(1, 1) * 0.5) *
2381 Matrix::MakeTranslation(Vector2(-100, -100)),
2382 SamplerDescriptor{}));
2383 canvas.Restore();
2384 }
2385 canvas.Restore();
2386
2387 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2388 }
2389
2390 TEST_P(AiksTest, SolidColorApplyColorFilter) {
2391 auto contents = SolidColorContents();
2392 contents.SetColor(Color::CornflowerBlue().WithAlpha(0.75));
2393 auto result = contents.ApplyColorFilter([](const Color& color) {
2394 return color.Blend(Color::LimeGreen().WithAlpha(0.75), BlendMode::kScreen);
2395 });
2396 ASSERT_TRUE(result);
2397 ASSERT_COLOR_NEAR(contents.GetColor(),
2398 Color(0.424452, 0.828743, 0.79105, 0.9375));
2399 }
2400
2401 TEST_P(AiksTest, DrawScaledTextWithPerspectiveNoSaveLayer) {
2402 Canvas canvas;
2403 canvas.Transform(Matrix(1.0, 0.0, 0.0, 0.0, //
2404 0.0, 1.0, 0.0, 0.0, //
2405 0.0, 0.0, 1.0, 0.01, //
2406 0.0, 0.0, 0.0, 1.0) * //
2407 Matrix::MakeRotationY({Degrees{10}}));
2408
2409 ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
2410 "Roboto-Regular.ttf"));
2411
2412 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2413 }
2414
2415 TEST_P(AiksTest, DrawScaledTextWithPerspectiveSaveLayer) {
2416 Canvas canvas;
2417 Paint save_paint;
2418 canvas.SaveLayer(save_paint);
2419 canvas.Transform(Matrix(1.0, 0.0, 0.0, 0.0, //
2420 0.0, 1.0, 0.0, 0.0, //
2421 0.0, 0.0, 1.0, 0.01, //
2422 0.0, 0.0, 0.0, 1.0) * //
2423 Matrix::MakeRotationY({Degrees{10}}));
2424
2425 ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
2426 "Roboto-Regular.ttf"));
2427 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2428 }
2429
2430 TEST_P(AiksTest, PipelineBlendSingleParameter) {
2431 Canvas canvas;
2432
2433 // Should render a green square in the middle of a blue circle.
2434 canvas.SaveLayer({});
2435 {
2436 canvas.Translate(Point(100, 100));
2437 canvas.DrawCircle(Point(200, 200), 200, {.color = Color::Blue()});
2438 canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
2439 canvas.DrawCircle(Point(200, 200), 200,
2440 {
2441 .color = Color::Green(),
2442 .blend_mode = BlendMode::kSourceOver,
2443 .image_filter = ImageFilter::MakeFromColorFilter(
2444 *ColorFilter::MakeBlend(BlendMode::kDestination,
2445 Color::White())),
2446 });
2447 canvas.Restore();
2448 }
2449
2450 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2451 }
2452
2453 // Regression test for https://github.com/flutter/flutter/issues/134678.
2454 TEST_P(AiksTest, ReleasesTextureOnTeardown) {
2455 auto context = MakeContext();
2456 std::weak_ptr<Texture> weak_texture;
2457
2458 {
2459 auto texture = CreateTextureForFixture("table_mountain_nx.png");
2460
2461 Canvas canvas;
2462 canvas.Scale(GetContentScale());
2463 canvas.Translate({100.0f, 100.0f, 0});
2464
2465 Paint paint;
2466 paint.color_source = ColorSource::MakeImage(
2467 texture, Entity::TileMode::kClamp, Entity::TileMode::kClamp, {}, {});
2468 canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
2469
2470 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2471 }
2472
2473 // See https://github.com/flutter/flutter/issues/134751.
2474 //
2475 // If the fence waiter was working this may not be released by the end of the
2476 // scope above. Adding a manual shutdown so that future changes to the fence
2477 // waiter will not flake this test.
2478 context->Shutdown();
2479
2480 // The texture should be released by now.
2481 ASSERT_TRUE(weak_texture.expired()) << "When the texture is no longer in use "
2482 "by the backend, it should be "
2483 "released.";
2484 }
2485
2486 TEST_P(AiksTest, MatrixImageFilterMagnify) {
2487 Scalar scale = 2.0;
2488 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
2489 if (AiksTest::ImGuiBegin("Controls", nullptr,
2490 ImGuiWindowFlags_AlwaysAutoResize)) {
2491 ImGui::SliderFloat("Scale", &scale, 1, 2);
2492 ImGui::End();
2493 }
2494 Canvas canvas;
2495 canvas.Scale(GetContentScale());
2496 auto image =
2497 std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2498 canvas.Translate({600, -200});
2499 canvas.SaveLayer({
2500 .image_filter = std::make_shared<MatrixImageFilter>(
2501 Matrix::MakeScale({scale, scale, 1}), SamplerDescriptor{}),
2502 });
2503 canvas.DrawImage(image, {0, 0},
2504 Paint{.color = Color::White().WithAlpha(0.5)});
2505 canvas.Restore();
2506 return canvas.EndRecordingAsPicture();
2507 };
2508
2509 ASSERT_TRUE(OpenPlaygroundHere(callback));
2510 }
2511
2512 // Render a white circle at the top left corner of the screen.
2513 TEST_P(AiksTest, MatrixImageFilterDoesntCullWhenTranslatedFromOffscreen) {
2514 Canvas canvas;
2515 canvas.Scale(GetContentScale());
2516 canvas.Translate({100, 100});
2517 // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
2518 // +300 translation applied by a SaveLayer image filter.
2519 canvas.SaveLayer({
2520 .image_filter = std::make_shared<MatrixImageFilter>(
2521 Matrix::MakeTranslation({300, 0}), SamplerDescriptor{}),
2522 });
2523 canvas.DrawCircle({-300, 0}, 100, {.color = Color::Green()});
2524 canvas.Restore();
2525
2526 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2527 }
2528
2529 // Render a white circle at the top left corner of the screen.
2530 TEST_P(AiksTest,
2531 MatrixImageFilterDoesntCullWhenScaledAndTranslatedFromOffscreen) {
2532 Canvas canvas;
2533 canvas.Scale(GetContentScale());
2534 canvas.Translate({100, 100});
2535 // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
2536 // +300 translation applied by a SaveLayer image filter.
2537 canvas.SaveLayer({
2538 .image_filter = std::make_shared<MatrixImageFilter>(
2539 Matrix::MakeTranslation({300, 0}) * Matrix::MakeScale({2, 2, 2}),
2540 SamplerDescriptor{}),
2541 });
2542 canvas.DrawCircle({-150, 0}, 50, {.color = Color::Green()});
2543 canvas.Restore();
2544
2545 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2546 }
2547
2548 // This should be solid red, if you see a little red box this is broken.
2549 TEST_P(AiksTest, ClearColorOptimizationWhenSubpassIsBiggerThanParentPass) {
2550 SetWindowSize({400, 400});
2551 Canvas canvas;
2552 canvas.Scale(GetContentScale());
2553 canvas.DrawRect(Rect::MakeLTRB(200, 200, 300, 300), {.color = Color::Red()});
2554 canvas.SaveLayer({
2555 .image_filter = std::make_shared<MatrixImageFilter>(
2556 Matrix::MakeScale({2, 2, 1}), SamplerDescriptor{}),
2557 });
2558 // Draw a rectangle that would fully cover the parent pass size, but not
2559 // the subpass that it is rendered in.
2560 canvas.DrawRect(Rect::MakeLTRB(0, 0, 400, 400), {.color = Color::Green()});
2561 // Draw a bigger rectangle to force the subpass to be bigger.
2562 canvas.DrawRect(Rect::MakeLTRB(0, 0, 800, 800), {.color = Color::Red()});
2563 canvas.Restore();
2564
2565 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2566 }
2567
2568 TEST_P(AiksTest, EmptySaveLayerIgnoresPaint) {
2569 Canvas canvas;
2570 canvas.Scale(GetContentScale());
2571 canvas.DrawPaint(Paint{.color = Color::Red()});
2572 canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
2573 canvas.SaveLayer(Paint{.color = Color::Blue()});
2574 canvas.Restore();
2575 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2576 }
2577
2578 TEST_P(AiksTest, EmptySaveLayerRendersWithClear) {
2579 Canvas canvas;
2580 canvas.Scale(GetContentScale());
2581 auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2582 canvas.DrawImage(image, {10, 10}, {});
2583 canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
2584 canvas.SaveLayer(Paint{.blend_mode = BlendMode::kClear});
2585 canvas.Restore();
2586 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2587 }
2588
2589 TEST_P(AiksTest, SubpassWithClearColorOptimization) {
2590 Canvas canvas;
2591
2592 // Use a non-srcOver blend mode to ensure that we don't detect this as an
2593 // opacity peephole optimization.
2594 canvas.SaveLayer(
2595 {.color = Color::Blue().WithAlpha(0.5), .blend_mode = BlendMode::kSource},
2596 Rect::MakeLTRB(0, 0, 200, 200));
2597 canvas.DrawPaint(
2598 {.color = Color::BlackTransparent(), .blend_mode = BlendMode::kSource});
2599 canvas.Restore();
2600
2601 canvas.SaveLayer(
2602 {.color = Color::Blue(), .blend_mode = BlendMode::kDestinationOver});
2603 canvas.Restore();
2604
2605 // This playground should appear blank on CI since we are only drawing
2606 // transparent black. If the clear color optimization is broken, the texture
2607 // will be filled with NaNs and may produce a magenta texture on macOS or iOS.
2608 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2609 }
2610
2611 TEST_P(AiksTest, ImageColorSourceEffectTransform) {
2612 // Compare with https://fiddle.skia.org/c/6cdc5aefb291fda3833b806ca347a885
2613
2614 Canvas canvas;
2615 auto texture = CreateTextureForFixture("monkey.png");
2616
2617 canvas.DrawPaint({.color = Color::White()});
2618
2619 // Translation
2620 {
2621 Paint paint;
2622 paint.color_source = ColorSource::MakeImage(
2623 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
2624 Matrix::MakeTranslation({50, 50}));
2625 canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint);
2626 }
2627
2628 // Rotation/skew
2629 {
2630 canvas.Save();
2631 canvas.Rotate(Degrees(45));
2632 Paint paint;
2633 paint.color_source = ColorSource::MakeImage(
2634 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
2635 Matrix(1, -1, 0, 0, //
2636 1, 1, 0, 0, //
2637 0, 0, 1, 0, //
2638 0, 0, 0, 1) //
2639 );
2640 canvas.DrawRect(Rect::MakeLTRB(100, 0, 200, 100), paint);
2641 canvas.Restore();
2642 }
2643
2644 // Scale
2645 {
2646 canvas.Translate(Vector2(100, 0));
2647 canvas.Scale(Vector2(100, 100));
2648 Paint paint;
2649 paint.color_source = ColorSource::MakeImage(
2650 texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
2651 Matrix::MakeScale(Vector2(0.005, 0.005)));
2652 canvas.DrawRect(Rect::MakeLTRB(0, 0, 1, 1), paint);
2653 }
2654
2655 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2656 }
2657
2658 TEST_P(AiksTest, CorrectClipDepthAssignedToEntities) {
2659 Canvas canvas; // Depth 1 (base pass)
2660 canvas.DrawRRect(Rect::MakeLTRB(0, 0, 100, 100), {10, 10}, {}); // Depth 2
2661 canvas.Save();
2662 {
2663 canvas.ClipRRect(Rect::MakeLTRB(0, 0, 50, 50), {10, 10}, {}); // Depth 4
2664 canvas.SaveLayer({}); // Depth 4
2665 {
2666 canvas.DrawRRect(Rect::MakeLTRB(0, 0, 50, 50), {10, 10}, {}); // Depth 3
2667 }
2668 canvas.Restore(); // Restore the savelayer.
2669 }
2670 canvas.Restore(); // Depth 5 -- this will no longer append a restore entity
2671 // once we switch to the clip depth approach.
2672
2673 auto picture = canvas.EndRecordingAsPicture();
2674
2675 std::vector<uint32_t> expected = {
2676 2, // DrawRRect
2677 4, // ClipRRect -- Has a depth value equal to the max depth of all the
2678 // content it affect. In this case, the SaveLayer and all
2679 // its contents are affected.
2680 4, // SaveLayer -- The SaveLayer is drawn to the parent pass after its
2681 // contents are rendered, so it should have a depth value
2682 // greater than all its contents.
2683 3, // DrawRRect
2684 5, // Restore (no longer necessary when clipping on the depth buffer)
2685 };
2686
2687 std::vector<uint32_t> actual;
2688
2689 picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool {
2690 if (auto* subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
2691 actual.push_back(subpass->get()->GetClipDepth());
2692 }
2693 if (Entity* entity = std::get_if<Entity>(&element)) {
2694 actual.push_back(entity->GetClipDepth());
2695 }
2696 return true;
2697 });
2698
2699 ASSERT_EQ(actual.size(), expected.size());
2700 for (size_t i = 0; i < expected.size(); i++) {
2701 EXPECT_EQ(expected[i], actual[i]) << "Index: " << i;
2702 }
2703 }
2704
2705 TEST_P(AiksTest, CanDrawPerspectiveTransformWithClips) {
2706 // Avoiding `GetSecondsElapsed()` to reduce risk of golden flakiness.
2707 int time = 0;
2708 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
2709 Canvas canvas;
2710
2711 canvas.Save();
2712 {
2713 canvas.Translate({300, 300});
2714
2715 // 1. Draw/restore a clip before drawing the image, which will get drawn
2716 // to the depth buffer behind the image.
2717 canvas.Save();
2718 {
2719 canvas.DrawPaint({.color = Color::Green()});
2720 canvas.ClipRect(Rect::MakeLTRB(-180, -180, 180, 180),
2721 Entity::ClipOperation::kDifference);
2722 canvas.DrawPaint({.color = Color::Black()});
2723 }
2724 canvas.Restore(); // Restore rectangle difference clip.
2725
2726 canvas.Save();
2727 {
2728 // 2. Draw an oval clip that applies to the image, which will get drawn
2729 // in front of the image on the depth buffer.
2730 canvas.ClipOval(Rect::MakeLTRB(-200, -200, 200, 200));
2731
2732 // 3. Draw the rotating image with a perspective transform.
2733 canvas.Transform(
2734 Matrix(1.0, 0.0, 0.0, 0.0, //
2735 0.0, 1.0, 0.0, 0.0, //
2736 0.0, 0.0, 1.0, 0.003, //
2737 0.0, 0.0, 0.0, 1.0) * //
2738 Matrix::MakeRotationY({Radians{-1.0f + (time++ / 60.0f)}}));
2739 auto image =
2740 std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2741 canvas.DrawImage(image, -Point(image->GetSize()) / 2, {});
2742 }
2743 canvas.Restore(); // Restore oval intersect clip.
2744
2745 // 4. Draw a semi-translucent blue circle atop all previous draws.
2746 canvas.DrawCircle({}, 230, {.color = Color::Blue().WithAlpha(0.4)});
2747 }
2748 canvas.Restore(); // Restore translation.
2749
2750 return canvas.EndRecordingAsPicture();
2751 };
2752 ASSERT_TRUE(OpenPlaygroundHere(callback));
2753 }
2754
2755 TEST_P(AiksTest, CanRenderClippedBackdropFilter) {
2756 Canvas canvas;
2757 Paint paint;
2758
2759 canvas.Scale(GetContentScale());
2760
2761 // Draw something interesting in the background.
2762 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
2763 Color{0.1294, 0.5882, 0.9529, 1.0}};
2764 std::vector<Scalar> stops = {
2765 0.0,
2766 1.0,
2767 };
2768 paint.color_source = ColorSource::MakeLinearGradient(
2769 {0, 0}, {100, 100}, std::move(colors), std::move(stops),
2770 Entity::TileMode::kRepeat, {});
2771 canvas.DrawPaint(paint);
2772
2773 Rect clip_rect = Rect::MakeLTRB(50, 50, 400, 300);
2774
2775 // Draw a clipped SaveLayer, where the clip coverage and SaveLayer size are
2776 // the same.
2777 canvas.ClipRRect(clip_rect, Size(100, 100),
2778 Entity::ClipOperation::kIntersect);
2779 canvas.SaveLayer({}, clip_rect,
2780 ImageFilter::MakeFromColorFilter(*ColorFilter::MakeBlend(
2781 BlendMode::kExclusion, Color::Red())));
2782 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2783 }
2784
2785 TEST_P(AiksTest, MipmapGenerationWorksCorrectly) {
2786 TextureDescriptor texture_descriptor;
2787 texture_descriptor.size = ISize{1024, 1024};
2788 texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
2789 texture_descriptor.storage_mode = StorageMode::kHostVisible;
2790 texture_descriptor.mip_count = texture_descriptor.size.MipCount();
2791
2792 std::vector<uint8_t> bytes(4194304);
2793 bool alternate = false;
2794 for (auto i = 0u; i < 4194304; i += 4) {
2795 if (alternate) {
2796 bytes[i] = 255;
2797 bytes[i + 1] = 0;
2798 bytes[i + 2] = 0;
2799 bytes[i + 3] = 255;
2800 } else {
2801 bytes[i] = 0;
2802 bytes[i + 1] = 255;
2803 bytes[i + 2] = 0;
2804 bytes[i + 3] = 255;
2805 }
2806 alternate = !alternate;
2807 }
2808
2809 ASSERT_EQ(texture_descriptor.GetByteSizeOfBaseMipLevel(), bytes.size());
2810 auto mapping = std::make_shared<fml::NonOwnedMapping>(
2811 bytes.data(), // data
2812 texture_descriptor.GetByteSizeOfBaseMipLevel() // size
2813 );
2814 auto texture =
2815 GetContext()->GetResourceAllocator()->CreateTexture(texture_descriptor);
2816
2817 auto device_buffer =
2818 GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
2819 auto command_buffer = GetContext()->CreateCommandBuffer();
2820 auto blit_pass = command_buffer->CreateBlitPass();
2821
2822 blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(device_buffer)),
2823 texture);
2824 blit_pass->GenerateMipmap(texture);
2825 EXPECT_TRUE(blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()));
2826 EXPECT_TRUE(GetContext()->GetCommandQueue()->Submit({command_buffer}).ok());
2827
2828 auto image = std::make_shared<Image>(texture);
2829
2830 Canvas canvas;
2831 canvas.DrawImageRect(image, Rect::MakeSize(texture->GetSize()),
2832 Rect::MakeLTRB(0, 0, 100, 100), {});
2833
2834 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2835 }
2836
2837 // https://github.com/flutter/flutter/issues/146648
2838 TEST_P(AiksTest, StrokedPathWithMoveToThenCloseDrawnCorrectly) {
2839 Path path = PathBuilder{}
2840 .MoveTo({0, 400})
2841 .LineTo({0, 0})
2842 .LineTo({400, 0})
2843 // MoveTo implicitly adds a contour, ensure that close doesn't
2844 // add another nearly-empty contour.
2845 .MoveTo({0, 400})
2846 .Close()
2847 .TakePath();
2848
2849 Canvas canvas;
2850 canvas.Translate({50, 50, 0});
2851 canvas.DrawPath(path, {
2852 .color = Color::Blue(),
2853 .stroke_width = 10,
2854 .stroke_cap = Cap::kRound,
2855 .style = Paint::Style::kStroke,
2856 });
2857 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2858 }
2859
2860 TEST_P(AiksTest, CanRenderTextWithLargePerspectiveTransform) {
2861 // Verifies that text scales are clamped to work around
2862 // https://github.com/flutter/flutter/issues/136112 .
2863
2864 Canvas canvas;
2865 Paint save_paint;
2866 canvas.SaveLayer(save_paint);
2867 canvas.Transform(Matrix(2000, 0, 0, 0, //
2868 0, 2000, 0, 0, //
2869 0, 0, -1, 9000, //
2870 0, 0, -1, 7000 //
2871 ));
2872
2873 ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
2874 "Roboto-Regular.ttf"));
2875 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2876 }
2877
2878 TEST_P(AiksTest, SetContentsWithRegion) {
2879 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
2880
2881 // Replace part of the texture with a red rectangle.
2882 std::vector<uint8_t> bytes(100 * 100 * 4);
2883 for (auto i = 0u; i < bytes.size(); i += 4) {
2884 bytes[i] = 255;
2885 bytes[i + 1] = 0;
2886 bytes[i + 2] = 0;
2887 bytes[i + 3] = 255;
2888 }
2889 auto mapping =
2890 std::make_shared<fml::NonOwnedMapping>(bytes.data(), bytes.size());
2891 auto device_buffer =
2892 GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
2893 auto cmd_buffer = GetContext()->CreateCommandBuffer();
2894 auto blit_pass = cmd_buffer->CreateBlitPass();
2895 blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), bridge,
2896 IRect::MakeLTRB(50, 50, 150, 150));
2897
2898 auto did_submit =
2899 blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()) &&
2900 GetContext()->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok();
2901 ASSERT_TRUE(did_submit);
2902
2903 auto image = std::make_shared<Image>(bridge);
2904
2905 Canvas canvas;
2906 canvas.DrawImage(image, {0, 0}, {});
2907
2908 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2909 }
2910
2911 } // namespace testing
2912 } // namespace impeller
2913
2914 // █████████████████████████████████████████████████████████████████████████████
2915 // █ NOTICE: Before adding new tests to this file consider adding it to one of
2916 // █ the subdivisions of AiksTest to avoid having one massive file.
2917 // █
2918 // █ Subdivisions:
2919 // █ - aiks_blend_unittests.cc
2920 // █ - aiks_blur_unittests.cc
2921 // █ - aiks_gradient_unittests.cc
2922 // █ - aiks_path_unittests.cc
2923 // █████████████████████████████████████████████████████████████████████████████
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:740
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