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