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