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 <algorithm>
8 #include <array>
9 #include <cmath>
10 #include <cstdlib>
11 #include <memory>
12 #include <tuple>
13 #include <utility>
14 #include <vector>
15 
16 #include "flutter/testing/testing.h"
17 #include "impeller/aiks/canvas.h"
19 #include "impeller/aiks/image.h"
22 #include "impeller/aiks/testing/context_spy.h"
23 #include "impeller/core/capture.h"
30 #include "impeller/geometry/path.h"
32 #include "impeller/geometry/rect.h"
33 #include "impeller/geometry/size.h"
42 #include "third_party/imgui/imgui.h"
43 #include "third_party/skia/include/core/SkFontMgr.h"
44 #include "txt/platform.h"
45 
46 namespace impeller {
47 namespace testing {
48 
50 
51 TEST_P(AiksTest, CanvasCTMCanBeUpdated) {
52  Canvas canvas;
53  Matrix identity;
54  ASSERT_MATRIX_NEAR(canvas.GetCurrentTransform(), identity);
55  canvas.Translate(Size{100, 100});
57  Matrix::MakeTranslation({100.0, 100.0, 0.0}));
58 }
59 
60 TEST_P(AiksTest, CanvasCanPushPopCTM) {
61  Canvas canvas;
62  ASSERT_EQ(canvas.GetSaveCount(), 1u);
63  ASSERT_EQ(canvas.Restore(), false);
64 
65  canvas.Translate(Size{100, 100});
66  canvas.Save();
67  ASSERT_EQ(canvas.GetSaveCount(), 2u);
69  Matrix::MakeTranslation({100.0, 100.0, 0.0}));
70  ASSERT_TRUE(canvas.Restore());
71  ASSERT_EQ(canvas.GetSaveCount(), 1u);
73  Matrix::MakeTranslation({100.0, 100.0, 0.0}));
74 }
75 
76 TEST_P(AiksTest, CanRenderColoredRect) {
77  Canvas canvas;
78  Paint paint;
79  paint.color = Color::Blue();
80  canvas.DrawPath(PathBuilder{}
81  .AddRect(Rect::MakeXYWH(100.0, 100.0, 100.0, 100.0))
82  .TakePath(),
83  paint);
84  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
85 }
86 
87 TEST_P(AiksTest, CanRenderImage) {
88  Canvas canvas;
89  Paint paint;
90  auto image = std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
91  paint.color = Color::Red();
92  canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint);
93  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
94 }
95 
96 TEST_P(AiksTest, CanRenderInvertedImageWithColorFilter) {
97  Canvas canvas;
98  Paint paint;
99  auto image = std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
100  paint.color = Color::Red();
101  paint.color_filter =
103  paint.invert_colors = true;
104 
105  canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint);
106  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
107 }
108 
109 TEST_P(AiksTest, CanRenderColorFilterWithInvertColors) {
110  Canvas canvas;
111  Paint paint;
112  paint.color = Color::Red();
113  paint.color_filter =
115  paint.invert_colors = true;
116 
117  canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint);
118  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
119 }
120 
121 TEST_P(AiksTest, CanRenderColorFilterWithInvertColorsDrawPaint) {
122  Canvas canvas;
123  Paint paint;
124  paint.color = Color::Red();
125  paint.color_filter =
127  paint.invert_colors = true;
128 
129  canvas.DrawPaint(paint);
130  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
131 }
132 
133 TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer) {
134  Canvas canvas;
135 
136  Rect layer_rect = Rect::MakeXYWH(0, 0, 500, 500);
137  canvas.ClipRect(layer_rect);
138 
139  canvas.SaveLayer(
140  {
142  Color(0, 1, 0, 0.5)),
143  },
144  layer_rect);
145 
146  Paint paint;
147  canvas.DrawPaint({.color = Color::Black()});
148  canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300),
149  {.color = Color::White()});
150  canvas.Restore();
151 
152  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
153 }
154 
155 namespace {
156 bool GenerateMipmap(const std::shared_ptr<Context>& context,
157  std::shared_ptr<Texture> texture,
158  std::string label) {
159  auto buffer = context->CreateCommandBuffer();
160  if (!buffer) {
161  return false;
162  }
163  auto pass = buffer->CreateBlitPass();
164  if (!pass) {
165  return false;
166  }
167  pass->GenerateMipmap(std::move(texture), std::move(label));
168 
169  pass->EncodeCommands(context->GetResourceAllocator());
170  return context->GetCommandQueue()->Submit({buffer}).ok();
171 }
172 
173 void CanRenderTiledTexture(AiksTest* aiks_test,
174  Entity::TileMode tile_mode,
175  Matrix local_matrix = {}) {
176  auto context = aiks_test->GetContext();
177  ASSERT_TRUE(context);
178  auto texture = aiks_test->CreateTextureForFixture("table_mountain_nx.png",
179  /*enable_mipmapping=*/true);
180  GenerateMipmap(context, texture, "table_mountain_nx");
181  Canvas canvas;
182  canvas.Scale(aiks_test->GetContentScale());
183  canvas.Translate({100.0f, 100.0f, 0});
184  Paint paint;
185  paint.color_source =
186  ColorSource::MakeImage(texture, tile_mode, tile_mode, {}, local_matrix);
187  paint.color = Color(1, 1, 1, 1);
188  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
189 
190  // Should not change the image.
191  constexpr auto stroke_width = 64;
192  paint.style = Paint::Style::kStroke;
193  paint.stroke_width = stroke_width;
194  if (tile_mode == Entity::TileMode::kDecal) {
195  canvas.DrawRect(Rect::MakeXYWH(stroke_width, stroke_width, 600, 600),
196  paint);
197  } else {
198  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
199  }
200 
201  {
202  // Should not change the image.
203  PathBuilder path_builder;
204  path_builder.AddCircle({150, 150}, 150);
205  path_builder.AddRoundedRect(Rect::MakeLTRB(300, 300, 600, 600), 10);
206  paint.style = Paint::Style::kFill;
207  canvas.DrawPath(path_builder.TakePath(), paint);
208  }
209 
210  {
211  // Should not change the image. Tests the Convex short-cut code.
212  PathBuilder path_builder;
213  path_builder.AddCircle({150, 450}, 150);
214  path_builder.SetConvexity(Convexity::kConvex);
215  paint.style = Paint::Style::kFill;
216  canvas.DrawPath(path_builder.TakePath(), paint);
217  }
218 
219  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
220 }
221 } // namespace
222 
223 TEST_P(AiksTest, CanRenderTiledTextureClamp) {
224  CanRenderTiledTexture(this, Entity::TileMode::kClamp);
225 }
226 
227 TEST_P(AiksTest, CanRenderTiledTextureRepeat) {
228  CanRenderTiledTexture(this, Entity::TileMode::kRepeat);
229 }
230 
231 TEST_P(AiksTest, CanRenderTiledTextureMirror) {
232  CanRenderTiledTexture(this, Entity::TileMode::kMirror);
233 }
234 
235 TEST_P(AiksTest, CanRenderTiledTextureDecal) {
236  CanRenderTiledTexture(this, Entity::TileMode::kDecal);
237 }
238 
239 TEST_P(AiksTest, CanRenderTiledTextureClampWithTranslate) {
240  CanRenderTiledTexture(this, Entity::TileMode::kClamp,
241  Matrix::MakeTranslation({172.f, 172.f, 0.f}));
242 }
243 
244 TEST_P(AiksTest, CanRenderImageRect) {
245  Canvas canvas;
246  Paint paint;
247  auto image = std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
248  Size image_half_size = Size(image->GetSize()) * 0.5;
249 
250  // Render the bottom right quarter of the source image in a stretched rect.
251  auto source_rect = Rect::MakeSize(image_half_size);
252  source_rect = source_rect.Shift(Point(image_half_size));
253 
254  canvas.DrawImageRect(image, source_rect, Rect::MakeXYWH(100, 100, 600, 600),
255  paint);
256  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
257 }
258 
259 TEST_P(AiksTest, CanRenderSimpleClips) {
260  Canvas canvas;
261  canvas.Scale(GetContentScale());
262  Paint paint;
263 
264  paint.color = Color::White();
265  canvas.DrawPaint(paint);
266 
267  auto draw = [&canvas](const Paint& paint, Scalar x, Scalar y) {
268  canvas.Save();
269  canvas.Translate({x, y});
270  {
271  canvas.Save();
272  canvas.ClipRect(Rect::MakeLTRB(50, 50, 150, 150));
273  canvas.DrawPaint(paint);
274  canvas.Restore();
275  }
276  {
277  canvas.Save();
278  canvas.ClipOval(Rect::MakeLTRB(200, 50, 300, 150));
279  canvas.DrawPaint(paint);
280  canvas.Restore();
281  }
282  {
283  canvas.Save();
284  canvas.ClipRRect(Rect::MakeLTRB(50, 200, 150, 300), {20, 20});
285  canvas.DrawPaint(paint);
286  canvas.Restore();
287  }
288  {
289  canvas.Save();
290  canvas.ClipRRect(Rect::MakeLTRB(200, 230, 300, 270), {20, 20});
291  canvas.DrawPaint(paint);
292  canvas.Restore();
293  }
294  {
295  canvas.Save();
296  canvas.ClipRRect(Rect::MakeLTRB(230, 200, 270, 300), {20, 20});
297  canvas.DrawPaint(paint);
298  canvas.Restore();
299  }
300  canvas.Restore();
301  };
302 
303  paint.color = Color::Blue();
304  draw(paint, 0, 0);
305 
306  std::vector<Color> gradient_colors = {
307  Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
308  Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
309  Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
310  Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
311  Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
312  Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
313  Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
314  std::vector<Scalar> stops = {
315  0.0,
316  (1.0 / 6.0) * 1,
317  (1.0 / 6.0) * 2,
318  (1.0 / 6.0) * 3,
319  (1.0 / 6.0) * 4,
320  (1.0 / 6.0) * 5,
321  1.0,
322  };
323  auto texture = CreateTextureForFixture("airplane.jpg",
324  /*enable_mipmapping=*/true);
325 
327  {500, 600}, 75, std::move(gradient_colors), std::move(stops),
329  draw(paint, 0, 300);
330 
333  Matrix::MakeTranslation({0, 0}));
334  draw(paint, 300, 0);
335 
336  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
337 }
338 
339 TEST_P(AiksTest, CanRenderNestedClips) {
340  Canvas canvas;
341  Paint paint;
342  paint.color = Color::Fuchsia();
343  canvas.Save();
344  canvas.ClipPath(PathBuilder{}.AddCircle({200, 400}, 300).TakePath());
345  canvas.Restore();
346  canvas.ClipPath(PathBuilder{}.AddCircle({600, 400}, 300).TakePath());
347  canvas.ClipPath(PathBuilder{}.AddCircle({400, 600}, 300).TakePath());
348  canvas.DrawRect(Rect::MakeXYWH(200, 200, 400, 400), paint);
349  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
350 }
351 
352 TEST_P(AiksTest, CanRenderDifferenceClips) {
353  Paint paint;
354  Canvas canvas;
355  canvas.Translate({400, 400});
356 
357  // Limit drawing to face circle with a clip.
358  canvas.ClipPath(PathBuilder{}.AddCircle(Point(), 200).TakePath());
359  canvas.Save();
360 
361  // Cut away eyes/mouth using difference clips.
362  canvas.ClipPath(PathBuilder{}.AddCircle({-100, -50}, 30).TakePath(),
364  canvas.ClipPath(PathBuilder{}.AddCircle({100, -50}, 30).TakePath(),
366  canvas.ClipPath(PathBuilder{}
367  .AddQuadraticCurve({-100, 50}, {0, 150}, {100, 50})
368  .TakePath(),
370 
371  // Draw a huge yellow rectangle to prove the clipping works.
372  paint.color = Color::Yellow();
373  canvas.DrawRect(Rect::MakeXYWH(-1000, -1000, 2000, 2000), paint);
374 
375  // Remove the difference clips and draw hair that partially covers the eyes.
376  canvas.Restore();
377  paint.color = Color::Maroon();
378  canvas.DrawPath(PathBuilder{}
379  .MoveTo({200, -200})
380  .HorizontalLineTo(-200)
381  .VerticalLineTo(-40)
382  .CubicCurveTo({0, -40}, {0, -80}, {200, -80})
383  .TakePath(),
384  paint);
385 
386  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
387 }
388 
389 TEST_P(AiksTest, CanRenderWithContiguousClipRestores) {
390  Canvas canvas;
391 
392  // Cover the whole canvas with red.
393  canvas.DrawPaint({.color = Color::Red()});
394 
395  canvas.Save();
396 
397  // Append two clips, the second resulting in empty coverage.
398  canvas.ClipPath(
399  PathBuilder{}.AddRect(Rect::MakeXYWH(100, 100, 100, 100)).TakePath());
400  canvas.ClipPath(
401  PathBuilder{}.AddRect(Rect::MakeXYWH(300, 300, 100, 100)).TakePath());
402 
403  // Restore to no clips.
404  canvas.Restore();
405 
406  // Replace the whole canvas with green.
407  canvas.DrawPaint({.color = Color::Green()});
408 
409  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
410 }
411 
412 TEST_P(AiksTest, ClipsUseCurrentTransform) {
413  std::array<Color, 5> colors = {Color::White(), Color::Black(),
415  Color::Yellow()};
416  Canvas canvas;
417  Paint paint;
418 
419  canvas.Translate(Vector3(300, 300));
420  for (int i = 0; i < 15; i++) {
421  canvas.Scale(Vector3(0.8, 0.8));
422 
423  paint.color = colors[i % colors.size()];
424  canvas.ClipPath(PathBuilder{}.AddCircle({0, 0}, 300).TakePath());
425  canvas.DrawRect(Rect::MakeXYWH(-300, -300, 600, 600), paint);
426  }
427  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
428 }
429 
430 TEST_P(AiksTest, CanSaveLayerStandalone) {
431  Canvas canvas;
432 
433  Paint red;
434  red.color = Color::Red();
435 
436  Paint alpha;
437  alpha.color = Color::Red().WithAlpha(0.5);
438 
439  canvas.SaveLayer(alpha);
440 
441  canvas.DrawCircle({125, 125}, 125, red);
442 
443  canvas.Restore();
444 
445  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
446 }
447 
448 TEST_P(AiksTest, CanRenderDifferentShapesWithSameColorSource) {
449  Canvas canvas;
450  Paint paint;
451 
452  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
453  Color{0.1294, 0.5882, 0.9529, 1.0}};
454  std::vector<Scalar> stops = {
455  0.0,
456  1.0,
457  };
458 
460  {0, 0}, {100, 100}, std::move(colors), std::move(stops),
462 
463  canvas.Save();
464  canvas.Translate({100, 100, 0});
465  canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), paint);
466  canvas.Restore();
467 
468  canvas.Save();
469  canvas.Translate({100, 400, 0});
470  canvas.DrawCircle({100, 100}, 100, paint);
471  canvas.Restore();
472  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
473 }
474 
475 TEST_P(AiksTest, CanPictureConvertToImage) {
476  Canvas recorder_canvas;
477  Paint paint;
478  paint.color = Color{0.9568, 0.2627, 0.2118, 1.0};
479  recorder_canvas.DrawRect(Rect::MakeXYWH(100.0, 100.0, 600, 600), paint);
480  paint.color = Color{0.1294, 0.5882, 0.9529, 1.0};
481  recorder_canvas.DrawRect(Rect::MakeXYWH(200.0, 200.0, 600, 600), paint);
482 
483  Canvas canvas;
484  AiksContext renderer(GetContext(), nullptr);
485  paint.color = Color::BlackTransparent();
486  canvas.DrawPaint(paint);
487  Picture picture = recorder_canvas.EndRecordingAsPicture();
488  auto image = picture.ToImage(renderer, ISize{1000, 1000});
489  if (image) {
490  canvas.DrawImage(image, Point(), Paint());
491  paint.color = Color{0.1, 0.1, 0.1, 0.2};
492  canvas.DrawRect(Rect::MakeSize(ISize{1000, 1000}), paint);
493  }
494 
495  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
496 }
497 
498 // Regression test for https://github.com/flutter/flutter/issues/142358 .
499 // Without a change to force render pass construction the image is left in an
500 // undefined layout and triggers a validation error.
501 TEST_P(AiksTest, CanEmptyPictureConvertToImage) {
502  Canvas recorder_canvas;
503 
504  Canvas canvas;
505  AiksContext renderer(GetContext(), nullptr);
506  Paint paint;
507  paint.color = Color::BlackTransparent();
508  canvas.DrawPaint(paint);
509  Picture picture = recorder_canvas.EndRecordingAsPicture();
510  auto image = picture.ToImage(renderer, ISize{1000, 1000});
511  if (image) {
512  canvas.DrawImage(image, Point(), Paint());
513  paint.color = Color{0.1, 0.1, 0.1, 0.2};
514  canvas.DrawRect(Rect::MakeSize(ISize{1000, 1000}), paint);
515  }
516 
517  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
518 }
519 
520 TEST_P(AiksTest, BlendModeShouldCoverWholeScreen) {
521  Canvas canvas;
522  Paint paint;
523 
524  paint.color = Color::Red();
525  canvas.DrawPaint(paint);
526 
528  canvas.SaveLayer(paint);
529 
530  paint.color = Color::White();
531  canvas.DrawRect(Rect::MakeXYWH(100, 100, 400, 400), paint);
532 
534  canvas.SaveLayer(paint);
535 
536  paint.color = Color::Blue();
537  canvas.DrawRect(Rect::MakeXYWH(200, 200, 200, 200), paint);
538 
539  canvas.Restore();
540  canvas.Restore();
541 
542  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
543 }
544 
545 TEST_P(AiksTest, CanRenderGroupOpacity) {
546  Canvas canvas;
547 
548  Paint red;
549  red.color = Color::Red();
550  Paint green;
551  green.color = Color::Green().WithAlpha(0.5);
552  Paint blue;
553  blue.color = Color::Blue();
554 
555  Paint alpha;
556  alpha.color = Color::Red().WithAlpha(0.5);
557 
558  canvas.SaveLayer(alpha);
559 
560  canvas.DrawRect(Rect::MakeXYWH(000, 000, 100, 100), red);
561  canvas.DrawRect(Rect::MakeXYWH(020, 020, 100, 100), green);
562  canvas.DrawRect(Rect::MakeXYWH(040, 040, 100, 100), blue);
563 
564  canvas.Restore();
565 
566  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
567 }
568 
569 TEST_P(AiksTest, CoordinateConversionsAreCorrect) {
570  Canvas canvas;
571 
572  // Render a texture directly.
573  {
574  Paint paint;
575  auto image =
576  std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
577  paint.color = Color::Red();
578 
579  canvas.Save();
580  canvas.Translate({100, 200, 0});
581  canvas.Scale(Vector2{0.5, 0.5});
582  canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint);
583  canvas.Restore();
584  }
585 
586  // Render an offscreen rendered texture.
587  {
588  Paint red;
589  red.color = Color::Red();
590  Paint green;
591  green.color = Color::Green();
592  Paint blue;
593  blue.color = Color::Blue();
594 
595  Paint alpha;
596  alpha.color = Color::Red().WithAlpha(0.5);
597 
598  canvas.SaveLayer(alpha);
599 
600  canvas.DrawRect(Rect::MakeXYWH(000, 000, 100, 100), red);
601  canvas.DrawRect(Rect::MakeXYWH(020, 020, 100, 100), green);
602  canvas.DrawRect(Rect::MakeXYWH(040, 040, 100, 100), blue);
603 
604  canvas.Restore();
605  }
606 
607  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
608 }
609 
610 TEST_P(AiksTest, CanPerformFullScreenMSAA) {
611  Canvas canvas;
612 
613  Paint red;
614  red.color = Color::Red();
615 
616  canvas.DrawCircle({250, 250}, 125, red);
617 
618  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
619 }
620 
621 TEST_P(AiksTest, CanPerformSkew) {
622  Canvas canvas;
623 
624  Paint red;
625  red.color = Color::Red();
626 
627  canvas.Skew(2, 5);
628  canvas.DrawRect(Rect::MakeXYWH(0, 0, 100, 100), red);
629 
630  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
631 }
632 
633 TEST_P(AiksTest, CanPerformSaveLayerWithBounds) {
634  Canvas canvas;
635 
636  Paint red;
637  red.color = Color::Red();
638 
639  Paint green;
640  green.color = Color::Green();
641 
642  Paint blue;
643  blue.color = Color::Blue();
644 
645  Paint save;
646  save.color = Color::Black();
647 
648  canvas.SaveLayer(save, Rect::MakeXYWH(0, 0, 50, 50));
649 
650  canvas.DrawRect(Rect::MakeXYWH(0, 0, 100, 100), red);
651  canvas.DrawRect(Rect::MakeXYWH(10, 10, 100, 100), green);
652  canvas.DrawRect(Rect::MakeXYWH(20, 20, 100, 100), blue);
653 
654  canvas.Restore();
655 
656  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
657 }
658 
660  CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) {
661  Canvas canvas;
662 
663  Paint red;
664  red.color = Color::Red();
665 
666  Paint green;
667  green.color = Color::Green();
668 
669  Paint blue;
670  blue.color = Color::Blue();
671 
672  Paint save;
673  save.color = Color::Black().WithAlpha(0.5);
674 
675  canvas.SaveLayer(save, Rect::MakeXYWH(0, 0, 100000, 100000));
676 
677  canvas.DrawRect(Rect::MakeXYWH(0, 0, 100, 100), red);
678  canvas.DrawRect(Rect::MakeXYWH(10, 10, 100, 100), green);
679  canvas.DrawRect(Rect::MakeXYWH(20, 20, 100, 100), blue);
680 
681  canvas.Restore();
682 
683  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
684 }
685 
686 TEST_P(AiksTest, CanRenderRoundedRectWithNonUniformRadii) {
687  Canvas canvas;
688 
689  Paint paint;
690  paint.color = Color::Red();
691 
693  radii.top_left = {50, 25};
694  radii.top_right = {25, 50};
695  radii.bottom_right = {50, 25};
696  radii.bottom_left = {25, 50};
697 
698  auto path = PathBuilder{}
699  .AddRoundedRect(Rect::MakeXYWH(100, 100, 500, 500), radii)
700  .TakePath();
701 
702  canvas.DrawPath(path, paint);
703 
704  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
705 }
706 
710  Point position = Vector2(100, 200);
711  std::optional<Paint::MaskBlurDescriptor> mask_blur_descriptor;
712 };
713 
714 bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
715  Canvas& canvas,
716  const std::string& text,
717  const std::string_view& font_fixture,
718  TextRenderOptions options = {}) {
719  // Draw the baseline.
720  canvas.DrawRect(
721  Rect::MakeXYWH(options.position.x - 50, options.position.y, 900, 10),
722  Paint{.color = Color::Aqua().WithAlpha(0.25)});
723 
724  // Mark the point at which the text is drawn.
725  canvas.DrawCircle(options.position, 5.0,
726  Paint{.color = Color::Red().WithAlpha(0.25)});
727 
728  // Construct the text blob.
729  auto c_font_fixture = std::string(font_fixture);
730  auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
731  if (!mapping) {
732  return false;
733  }
734  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
735  SkFont sk_font(font_mgr->makeFromData(mapping), options.font_size);
736  auto blob = SkTextBlob::MakeFromString(text.c_str(), sk_font);
737  if (!blob) {
738  return false;
739  }
740 
741  // Create the Impeller text frame and draw it at the designated baseline.
742  auto frame = MakeTextFrameFromTextBlobSkia(blob);
743 
744  Paint text_paint;
745  text_paint.color = options.color;
746  text_paint.mask_blur_descriptor = options.mask_blur_descriptor;
747  canvas.DrawTextFrame(frame, options.position, text_paint);
748  return true;
749 }
750 
751 bool RenderTextInCanvasSTB(const std::shared_ptr<Context>& context,
752  Canvas& canvas,
753  const std::string& text,
754  const std::string& font_fixture,
755  TextRenderOptions options = {}) {
756  // Draw the baseline.
757  canvas.DrawRect(
758  Rect::MakeXYWH(options.position.x - 50, options.position.y, 900, 10),
759  Paint{.color = Color::Aqua().WithAlpha(0.25)});
760 
761  // Mark the point at which the text is drawn.
762  canvas.DrawCircle(options.position, 5.0,
763  Paint{.color = Color::Red().WithAlpha(0.25)});
764 
765  // Construct the text blob.
766  auto mapping = flutter::testing::OpenFixtureAsMapping(font_fixture.c_str());
767  if (!mapping) {
768  return false;
769  }
770  auto typeface_stb = std::make_shared<TypefaceSTB>(std::move(mapping));
771 
772  auto frame = MakeTextFrameSTB(
773  typeface_stb, Font::Metrics{.point_size = options.font_size}, text);
774 
775  Paint text_paint;
776  text_paint.color = options.color;
777  canvas.DrawTextFrame(frame, options.position, text_paint);
778  return true;
779 }
780 
781 TEST_P(AiksTest, CanRenderTextFrame) {
782  Canvas canvas;
783  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
784  ASSERT_TRUE(RenderTextInCanvasSkia(
785  GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
786  "Roboto-Regular.ttf"));
787  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
788 }
789 
790 TEST_P(AiksTest, CanRenderTextFrameSTB) {
791  Canvas canvas;
792  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
793  ASSERT_TRUE(RenderTextInCanvasSTB(
794  GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
795  "Roboto-Regular.ttf"));
796 
797  SetTypographerContext(TypographerContextSTB::Make());
798  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
799 }
800 
801 TEST_P(AiksTest, TextFrameSubpixelAlignment) {
802  // "Random" numbers between 0 and 1. Hardcoded to avoid flakiness in goldens.
803  std::array<Scalar, 20> phase_offsets = {
804  7.82637e-06, 0.131538, 0.755605, 0.45865, 0.532767,
805  0.218959, 0.0470446, 0.678865, 0.679296, 0.934693,
806  0.383502, 0.519416, 0.830965, 0.0345721, 0.0534616,
807  0.5297, 0.671149, 0.00769819, 0.383416, 0.0668422};
808  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
809  static float font_size = 20;
810  static float phase_variation = 0.2;
811  static float speed = 0.5;
812  static float magnitude = 100;
813  if (AiksTest::ImGuiBegin("Controls", nullptr,
814  ImGuiWindowFlags_AlwaysAutoResize)) {
815  ImGui::SliderFloat("Font size", &font_size, 5, 50);
816  ImGui::SliderFloat("Phase variation", &phase_variation, 0, 1);
817  ImGui::SliderFloat("Oscillation speed", &speed, 0, 2);
818  ImGui::SliderFloat("Oscillation magnitude", &magnitude, 0, 300);
819  ImGui::End();
820  }
821 
822  Canvas canvas;
823  canvas.Scale(GetContentScale());
824 
825  for (size_t i = 0; i < phase_offsets.size(); i++) {
826  auto position =
827  Point(200 + magnitude *
828  std::sin((-phase_offsets[i] * k2Pi * phase_variation +
829  GetSecondsElapsed() * speed)), //
830  200 + i * font_size * 1.1 //
831  );
833  GetContext(), canvas,
834  "the quick brown fox jumped over "
835  "the lazy dog!.?",
836  "Roboto-Regular.ttf",
837  {.font_size = font_size, .position = position})) {
838  return std::nullopt;
839  }
840  }
841  return canvas.EndRecordingAsPicture();
842  };
843 
844  ASSERT_TRUE(OpenPlaygroundHere(callback));
845 }
846 
847 TEST_P(AiksTest, CanRenderItalicizedText) {
848  Canvas canvas;
849  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
850 
851  ASSERT_TRUE(RenderTextInCanvasSkia(
852  GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
853  "HomemadeApple.ttf"));
854  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
855 }
856 
857 static constexpr std::string_view kFontFixture =
858 #if FML_OS_MACOSX
859  "Apple Color Emoji.ttc";
860 #else
861  "NotoColorEmoji.ttf";
862 #endif
863 
864 TEST_P(AiksTest, CanRenderEmojiTextFrame) {
865  Canvas canvas;
866  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
867 
868  ASSERT_TRUE(RenderTextInCanvasSkia(
869  GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture));
870  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
871 }
872 
873 TEST_P(AiksTest, CanRenderEmojiTextFrameWithBlur) {
874  Canvas canvas;
875  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
876 
877  ASSERT_TRUE(RenderTextInCanvasSkia(
878  GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture,
880  .mask_blur_descriptor = Paint::MaskBlurDescriptor{
882  .sigma = Sigma(4)}}));
883  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
884 }
885 
886 TEST_P(AiksTest, CanRenderEmojiTextFrameWithAlpha) {
887  Canvas canvas;
888  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
889 
890  ASSERT_TRUE(RenderTextInCanvasSkia(
891  GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture,
892  {.color = Color::Black().WithAlpha(0.5)}));
893  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
894 }
895 
896 TEST_P(AiksTest, CanRenderTextInSaveLayer) {
897  Canvas canvas;
898  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
899 
900  canvas.Translate({100, 100});
901  canvas.Scale(Vector2{0.5, 0.5});
902 
903  // Blend the layer with the parent pass using kClear to expose the coverage.
904  canvas.SaveLayer({.blend_mode = BlendMode::kClear});
905  ASSERT_TRUE(RenderTextInCanvasSkia(
906  GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
907  "Roboto-Regular.ttf"));
908  canvas.Restore();
909 
910  // Render the text again over the cleared coverage rect.
911  ASSERT_TRUE(RenderTextInCanvasSkia(
912  GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
913  "Roboto-Regular.ttf"));
914 
915  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
916 }
917 
918 TEST_P(AiksTest, CanRenderTextOutsideBoundaries) {
919  Canvas canvas;
920  canvas.Translate({200, 150});
921 
922  // Construct the text blob.
923  auto mapping = flutter::testing::OpenFixtureAsSkData("wtf.otf");
924  ASSERT_NE(mapping, nullptr);
925 
926  Scalar font_size = 80;
927  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
928  SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
929 
930  Paint text_paint;
931  text_paint.color = Color::Blue().WithAlpha(0.8);
932 
933  struct {
934  Point position;
935  const char* text;
936  } text[] = {{Point(0, 0), "0F0F0F0"},
937  {Point(1, 2), "789"},
938  {Point(1, 3), "456"},
939  {Point(1, 4), "123"},
940  {Point(0, 6), "0F0F0F0"}};
941  for (auto& t : text) {
942  canvas.Save();
943  canvas.Translate(t.position * Point(font_size * 2, font_size * 1.1));
944  {
945  auto blob = SkTextBlob::MakeFromString(t.text, sk_font);
946  ASSERT_NE(blob, nullptr);
947  auto frame = MakeTextFrameFromTextBlobSkia(blob);
948  canvas.DrawTextFrame(frame, Point(), text_paint);
949  }
950  canvas.Restore();
951  }
952 
953  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
954 }
955 
956 TEST_P(AiksTest, TextRotated) {
957  Canvas canvas;
958  canvas.Scale(GetContentScale());
959  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
960 
961  canvas.Transform(Matrix(0.25, -0.3, 0, -0.002, //
962  0, 0.5, 0, 0, //
963  0, 0, 0.3, 0, //
964  100, 100, 0, 1.3));
965  ASSERT_TRUE(RenderTextInCanvasSkia(
966  GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
967  "Roboto-Regular.ttf"));
968 
969  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
970 }
971 
972 TEST_P(AiksTest, CanDrawPaint) {
973  Canvas canvas;
974  canvas.Scale(Vector2(0.2, 0.2));
975  canvas.DrawPaint({.color = Color::MediumTurquoise()});
976  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
977 }
978 
979 TEST_P(AiksTest, CanDrawPaintMultipleTimes) {
980  Canvas canvas;
981  canvas.Scale(Vector2(0.2, 0.2));
982  canvas.DrawPaint({.color = Color::MediumTurquoise()});
983  canvas.DrawPaint({.color = Color::Color::OrangeRed().WithAlpha(0.5)});
984  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
985 }
986 
987 TEST_P(AiksTest, CanDrawPaintWithAdvancedBlend) {
988  Canvas canvas;
989  canvas.Scale(Vector2(0.2, 0.2));
990  canvas.DrawPaint({.color = Color::MediumTurquoise()});
991  canvas.DrawPaint({.color = Color::Color::OrangeRed().WithAlpha(0.5),
992  .blend_mode = BlendMode::kHue});
993  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
994 }
995 
996 TEST_P(AiksTest, DrawPaintWithAdvancedBlendOverFilter) {
997  Paint filtered = {
998  .color = Color::Black(),
999  .mask_blur_descriptor =
1002  .sigma = Sigma(60),
1003  },
1004  };
1005 
1006  Canvas canvas;
1007  canvas.DrawPaint({.color = Color::White()});
1008  canvas.DrawCircle({300, 300}, 200, filtered);
1009  canvas.DrawPaint({.color = Color::Green(), .blend_mode = BlendMode::kScreen});
1010  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1011 }
1012 
1013 TEST_P(AiksTest, DrawAdvancedBlendPartlyOffscreen) {
1014  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
1015  Color{0.1294, 0.5882, 0.9529, 1.0}};
1016  std::vector<Scalar> stops = {0.0, 1.0};
1017 
1018  Paint paint = {
1020  {0, 0}, {100, 100}, std::move(colors), std::move(stops),
1022  .blend_mode = BlendMode::kLighten,
1023  };
1024 
1025  Canvas canvas;
1026  canvas.DrawPaint({.color = Color::Blue()});
1027  canvas.Scale(Vector2(2, 2));
1028  canvas.ClipRect(Rect::MakeLTRB(0, 0, 200, 200));
1029  canvas.DrawCircle({100, 100}, 100, paint);
1030  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1031 }
1032 
1033 #define BLEND_MODE_TUPLE(blend_mode) {#blend_mode, BlendMode::k##blend_mode},
1034 
1036  std::vector<const char*> blend_mode_names;
1037  std::vector<BlendMode> blend_mode_values;
1038 };
1039 
1041  std::vector<const char*> blend_mode_names;
1042  std::vector<BlendMode> blend_mode_values;
1043  {
1044  const std::vector<std::tuple<const char*, BlendMode>> blends = {
1046  assert(blends.size() ==
1047  static_cast<size_t>(Entity::kLastAdvancedBlendMode) + 1);
1048  for (const auto& [name, mode] : blends) {
1049  blend_mode_names.push_back(name);
1050  blend_mode_values.push_back(mode);
1051  }
1052  }
1053 
1054  return {blend_mode_names, blend_mode_values};
1055 }
1056 
1057 TEST_P(AiksTest, CanDrawPaintMultipleTimesInteractive) {
1058  auto modes = GetBlendModeSelection();
1059 
1060  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1061  static Color background = Color::MediumTurquoise();
1062  static Color foreground = Color::Color::OrangeRed().WithAlpha(0.5);
1063  static int current_blend_index = 3;
1064 
1065  if (AiksTest::ImGuiBegin("Controls", nullptr,
1066  ImGuiWindowFlags_AlwaysAutoResize)) {
1067  ImGui::ColorEdit4("Background", reinterpret_cast<float*>(&background));
1068  ImGui::ColorEdit4("Foreground", reinterpret_cast<float*>(&foreground));
1069  ImGui::ListBox("Blend mode", &current_blend_index,
1070  modes.blend_mode_names.data(),
1071  modes.blend_mode_names.size());
1072  ImGui::End();
1073  }
1074 
1075  Canvas canvas;
1076  canvas.Scale(Vector2(0.2, 0.2));
1077  canvas.DrawPaint({.color = background});
1078  canvas.DrawPaint(
1079  {.color = foreground,
1080  .blend_mode = static_cast<BlendMode>(current_blend_index)});
1081  return canvas.EndRecordingAsPicture();
1082  };
1083  ASSERT_TRUE(OpenPlaygroundHere(callback));
1084 }
1085 
1086 TEST_P(AiksTest, PaintBlendModeIsRespected) {
1087  Paint paint;
1088  Canvas canvas;
1089  // Default is kSourceOver.
1090  paint.color = Color(1, 0, 0, 0.5);
1091  canvas.DrawCircle(Point(150, 200), 100, paint);
1092  paint.color = Color(0, 1, 0, 0.5);
1093  canvas.DrawCircle(Point(250, 200), 100, paint);
1094 
1095  paint.blend_mode = BlendMode::kPlus;
1096  paint.color = Color::Red();
1097  canvas.DrawCircle(Point(450, 250), 100, paint);
1098  paint.color = Color::Green();
1099  canvas.DrawCircle(Point(550, 250), 100, paint);
1100  paint.color = Color::Blue();
1101  canvas.DrawCircle(Point(500, 150), 100, paint);
1102  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1103 }
1104 
1105 TEST_P(AiksTest, ColorWheel) {
1106  // Compare with https://fiddle.skia.org/c/@BlendModes
1107 
1108  BlendModeSelection blend_modes = GetBlendModeSelection();
1109 
1110  auto draw_color_wheel = [](Canvas& canvas) {
1111  /// color_wheel_sampler: r=0 -> fuchsia, r=2pi/3 -> yellow, r=4pi/3 ->
1112  /// cyan domain: r >= 0 (because modulo used is non euclidean)
1113  auto color_wheel_sampler = [](Radians r) {
1114  Scalar x = r.radians / k2Pi + 1;
1115 
1116  // https://www.desmos.com/calculator/6nhjelyoaj
1117  auto color_cycle = [](Scalar x) {
1118  Scalar cycle = std::fmod(x, 6.0f);
1119  return std::max(0.0f, std::min(1.0f, 2 - std::abs(2 - cycle)));
1120  };
1121  return Color(color_cycle(6 * x + 1), //
1122  color_cycle(6 * x - 1), //
1123  color_cycle(6 * x - 3), //
1124  1);
1125  };
1126 
1127  Paint paint;
1129 
1130  // Draw a fancy color wheel for the backdrop.
1131  // https://www.desmos.com/calculator/xw7kafthwd
1132  const int max_dist = 900;
1133  for (int i = 0; i <= 900; i++) {
1134  Radians r(kPhi / k2Pi * i);
1135  Scalar distance = r.radians / std::powf(4.12, 0.0026 * r.radians);
1136  Scalar normalized_distance = static_cast<Scalar>(i) / max_dist;
1137 
1138  paint.color =
1139  color_wheel_sampler(r).WithAlpha(1.0f - normalized_distance);
1140  Point position(distance * std::sin(r.radians),
1141  -distance * std::cos(r.radians));
1142 
1143  canvas.DrawCircle(position, 9 + normalized_distance * 3, paint);
1144  }
1145  };
1146 
1147  std::shared_ptr<Image> color_wheel_image;
1148  Matrix color_wheel_transform;
1149 
1150  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1151  // UI state.
1152  static bool cache_the_wheel = true;
1153  static int current_blend_index = 3;
1154  static float dst_alpha = 1;
1155  static float src_alpha = 1;
1156  static Color color0 = Color::Red();
1157  static Color color1 = Color::Green();
1158  static Color color2 = Color::Blue();
1159 
1160  if (AiksTest::ImGuiBegin("Controls", nullptr,
1161  ImGuiWindowFlags_AlwaysAutoResize)) {
1162  ImGui::Checkbox("Cache the wheel", &cache_the_wheel);
1163  ImGui::ListBox("Blending mode", &current_blend_index,
1164  blend_modes.blend_mode_names.data(),
1165  blend_modes.blend_mode_names.size());
1166  ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
1167  ImGui::ColorEdit4("Color A", reinterpret_cast<float*>(&color0));
1168  ImGui::ColorEdit4("Color B", reinterpret_cast<float*>(&color1));
1169  ImGui::ColorEdit4("Color C", reinterpret_cast<float*>(&color2));
1170  ImGui::SliderFloat("Destination alpha", &dst_alpha, 0, 1);
1171  ImGui::End();
1172  }
1173 
1174  static Point content_scale;
1175  Point new_content_scale = GetContentScale();
1176 
1177  if (!cache_the_wheel || new_content_scale != content_scale) {
1178  content_scale = new_content_scale;
1179 
1180  // Render the color wheel to an image.
1181 
1182  Canvas canvas;
1183  canvas.Scale(content_scale);
1184 
1185  canvas.Translate(Vector2(500, 400));
1186  canvas.Scale(Vector2(3, 3));
1187 
1188  draw_color_wheel(canvas);
1189  auto color_wheel_picture = canvas.EndRecordingAsPicture();
1190  auto snapshot = color_wheel_picture.Snapshot(renderer);
1191  if (!snapshot.has_value() || !snapshot->texture) {
1192  return std::nullopt;
1193  }
1194  color_wheel_image = std::make_shared<Image>(snapshot->texture);
1195  color_wheel_transform = snapshot->transform;
1196  }
1197 
1198  Canvas canvas;
1199 
1200  // Blit the color wheel backdrop to the screen with managed alpha.
1201  canvas.SaveLayer({.color = Color::White().WithAlpha(dst_alpha),
1202  .blend_mode = BlendMode::kSource});
1203  {
1204  canvas.DrawPaint({.color = Color::White()});
1205 
1206  canvas.Save();
1207  canvas.Transform(color_wheel_transform);
1208  canvas.DrawImage(color_wheel_image, Point(), Paint());
1209  canvas.Restore();
1210  }
1211  canvas.Restore();
1212 
1213  canvas.Scale(content_scale);
1214  canvas.Translate(Vector2(500, 400));
1215  canvas.Scale(Vector2(3, 3));
1216 
1217  // Draw 3 circles to a subpass and blend it in.
1218  canvas.SaveLayer(
1219  {.color = Color::White().WithAlpha(src_alpha),
1220  .blend_mode = blend_modes.blend_mode_values[current_blend_index]});
1221  {
1222  Paint paint;
1223  paint.blend_mode = BlendMode::kPlus;
1224  const Scalar x = std::sin(k2Pi / 3);
1225  const Scalar y = -std::cos(k2Pi / 3);
1226  paint.color = color0;
1227  canvas.DrawCircle(Point(-x, y) * 45, 65, paint);
1228  paint.color = color1;
1229  canvas.DrawCircle(Point(0, -1) * 45, 65, paint);
1230  paint.color = color2;
1231  canvas.DrawCircle(Point(x, y) * 45, 65, paint);
1232  }
1233  canvas.Restore();
1234 
1235  return canvas.EndRecordingAsPicture();
1236  };
1237 
1238  ASSERT_TRUE(OpenPlaygroundHere(callback));
1239 }
1240 
1241 TEST_P(AiksTest, TransformMultipliesCorrectly) {
1242  Canvas canvas;
1244 
1245  // clang-format off
1246  canvas.Translate(Vector3(100, 200));
1248  canvas.GetCurrentTransform(),
1249  Matrix( 1, 0, 0, 0,
1250  0, 1, 0, 0,
1251  0, 0, 1, 0,
1252  100, 200, 0, 1));
1253 
1254  canvas.Rotate(Radians(kPiOver2));
1256  canvas.GetCurrentTransform(),
1257  Matrix( 0, 1, 0, 0,
1258  -1, 0, 0, 0,
1259  0, 0, 1, 0,
1260  100, 200, 0, 1));
1261 
1262  canvas.Scale(Vector3(2, 3));
1264  canvas.GetCurrentTransform(),
1265  Matrix( 0, 2, 0, 0,
1266  -3, 0, 0, 0,
1267  0, 0, 0, 0,
1268  100, 200, 0, 1));
1269 
1270  canvas.Translate(Vector3(100, 200));
1272  canvas.GetCurrentTransform(),
1273  Matrix( 0, 2, 0, 0,
1274  -3, 0, 0, 0,
1275  0, 0, 0, 0,
1276  -500, 400, 0, 1));
1277  // clang-format on
1278 }
1279 
1280 TEST_P(AiksTest, FilledCirclesRenderCorrectly) {
1281  Canvas canvas;
1282  canvas.Scale(GetContentScale());
1283  Paint paint;
1284  const int color_count = 3;
1285  Color colors[color_count] = {
1286  Color::Blue(),
1287  Color::Green(),
1288  Color::Crimson(),
1289  };
1290 
1291  paint.color = Color::White();
1292  canvas.DrawPaint(paint);
1293 
1294  int c_index = 0;
1295  int radius = 600;
1296  while (radius > 0) {
1297  paint.color = colors[(c_index++) % color_count];
1298  canvas.DrawCircle({10, 10}, radius, paint);
1299  if (radius > 30) {
1300  radius -= 10;
1301  } else {
1302  radius -= 2;
1303  }
1304  }
1305 
1306  std::vector<Color> gradient_colors = {
1307  Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1308  Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1309  Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1310  Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1311  Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1312  Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1313  Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1314  std::vector<Scalar> stops = {
1315  0.0,
1316  (1.0 / 6.0) * 1,
1317  (1.0 / 6.0) * 2,
1318  (1.0 / 6.0) * 3,
1319  (1.0 / 6.0) * 4,
1320  (1.0 / 6.0) * 5,
1321  1.0,
1322  };
1323  auto texture = CreateTextureForFixture("airplane.jpg",
1324  /*enable_mipmapping=*/true);
1325 
1327  {500, 600}, 75, std::move(gradient_colors), std::move(stops),
1329  canvas.DrawCircle({500, 600}, 100, paint);
1330 
1333  Matrix::MakeTranslation({700, 200}));
1334  canvas.DrawCircle({800, 300}, 100, paint);
1335 
1336  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1337 }
1338 
1339 TEST_P(AiksTest, StrokedCirclesRenderCorrectly) {
1340  Canvas canvas;
1341  canvas.Scale(GetContentScale());
1342  Paint paint;
1343  const int color_count = 3;
1344  Color colors[color_count] = {
1345  Color::Blue(),
1346  Color::Green(),
1347  Color::Crimson(),
1348  };
1349 
1350  paint.color = Color::White();
1351  canvas.DrawPaint(paint);
1352 
1353  int c_index = 0;
1354 
1355  auto draw = [&paint, &colors, &c_index](Canvas& canvas, Point center,
1356  Scalar r, Scalar dr, int n) {
1357  for (int i = 0; i < n; i++) {
1358  paint.color = colors[(c_index++) % color_count];
1359  canvas.DrawCircle(center, r, paint);
1360  r += dr;
1361  }
1362  };
1363 
1364  paint.style = Paint::Style::kStroke;
1365  paint.stroke_width = 1;
1366  draw(canvas, {10, 10}, 2, 2, 14); // r = [2, 28], covers [1,29]
1367  paint.stroke_width = 5;
1368  draw(canvas, {10, 10}, 35, 10, 56); // r = [35, 585], covers [30,590]
1369 
1370  std::vector<Color> gradient_colors = {
1371  Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1372  Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1373  Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1374  Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1375  Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1376  Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1377  Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1378  std::vector<Scalar> stops = {
1379  0.0,
1380  (1.0 / 6.0) * 1,
1381  (1.0 / 6.0) * 2,
1382  (1.0 / 6.0) * 3,
1383  (1.0 / 6.0) * 4,
1384  (1.0 / 6.0) * 5,
1385  1.0,
1386  };
1387  auto texture = CreateTextureForFixture("airplane.jpg",
1388  /*enable_mipmapping=*/true);
1389 
1391  {500, 600}, 75, std::move(gradient_colors), std::move(stops),
1393  draw(canvas, {500, 600}, 5, 10, 10);
1394 
1397  Matrix::MakeTranslation({700, 200}));
1398  draw(canvas, {800, 300}, 5, 10, 10);
1399 
1400  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1401 }
1402 
1403 TEST_P(AiksTest, FilledEllipsesRenderCorrectly) {
1404  Canvas canvas;
1405  canvas.Scale(GetContentScale());
1406  Paint paint;
1407  const int color_count = 3;
1408  Color colors[color_count] = {
1409  Color::Blue(),
1410  Color::Green(),
1411  Color::Crimson(),
1412  };
1413 
1414  paint.color = Color::White();
1415  canvas.DrawPaint(paint);
1416 
1417  int c_index = 0;
1418  int long_radius = 600;
1419  int short_radius = 600;
1420  while (long_radius > 0 && short_radius > 0) {
1421  paint.color = colors[(c_index++) % color_count];
1422  canvas.DrawOval(Rect::MakeXYWH(10 - long_radius, 10 - short_radius,
1423  long_radius * 2, short_radius * 2),
1424  paint);
1425  canvas.DrawOval(Rect::MakeXYWH(1000 - short_radius, 750 - long_radius,
1426  short_radius * 2, long_radius * 2),
1427  paint);
1428  if (short_radius > 30) {
1429  short_radius -= 10;
1430  long_radius -= 5;
1431  } else {
1432  short_radius -= 2;
1433  long_radius -= 1;
1434  }
1435  }
1436 
1437  std::vector<Color> gradient_colors = {
1438  Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1439  Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1440  Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1441  Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1442  Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1443  Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1444  Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1445  std::vector<Scalar> stops = {
1446  0.0,
1447  (1.0 / 6.0) * 1,
1448  (1.0 / 6.0) * 2,
1449  (1.0 / 6.0) * 3,
1450  (1.0 / 6.0) * 4,
1451  (1.0 / 6.0) * 5,
1452  1.0,
1453  };
1454  auto texture = CreateTextureForFixture("airplane.jpg",
1455  /*enable_mipmapping=*/true);
1456 
1457  paint.color = Color::White().WithAlpha(0.5);
1458 
1460  {300, 650}, 75, std::move(gradient_colors), std::move(stops),
1462  canvas.DrawOval(Rect::MakeXYWH(200, 625, 200, 50), paint);
1463  canvas.DrawOval(Rect::MakeXYWH(275, 550, 50, 200), paint);
1464 
1467  Matrix::MakeTranslation({610, 15}));
1468  canvas.DrawOval(Rect::MakeXYWH(610, 90, 200, 50), paint);
1469  canvas.DrawOval(Rect::MakeXYWH(685, 15, 50, 200), paint);
1470 
1471  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1472 }
1473 
1474 TEST_P(AiksTest, FilledRoundRectsRenderCorrectly) {
1475  Canvas canvas;
1476  canvas.Scale(GetContentScale());
1477  Paint paint;
1478  const int color_count = 3;
1479  Color colors[color_count] = {
1480  Color::Blue(),
1481  Color::Green(),
1482  Color::Crimson(),
1483  };
1484 
1485  paint.color = Color::White();
1486  canvas.DrawPaint(paint);
1487 
1488  int c_index = 0;
1489  for (int i = 0; i < 4; i++) {
1490  for (int j = 0; j < 4; j++) {
1491  paint.color = colors[(c_index++) % color_count];
1492  canvas.DrawRRect(Rect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80),
1493  Size(i * 5 + 10, j * 5 + 10), paint);
1494  }
1495  }
1496  paint.color = colors[(c_index++) % color_count];
1497  canvas.DrawRRect(Rect::MakeXYWH(10, 420, 380, 80), Size(40, 40), paint);
1498  paint.color = colors[(c_index++) % color_count];
1499  canvas.DrawRRect(Rect::MakeXYWH(410, 20, 80, 380), Size(40, 40), paint);
1500 
1501  std::vector<Color> gradient_colors = {
1502  Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1503  Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1504  Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1505  Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1506  Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1507  Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1508  Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1509  std::vector<Scalar> stops = {
1510  0.0,
1511  (1.0 / 6.0) * 1,
1512  (1.0 / 6.0) * 2,
1513  (1.0 / 6.0) * 3,
1514  (1.0 / 6.0) * 4,
1515  (1.0 / 6.0) * 5,
1516  1.0,
1517  };
1518  auto texture = CreateTextureForFixture("airplane.jpg",
1519  /*enable_mipmapping=*/true);
1520 
1521  paint.color = Color::White().WithAlpha(0.1);
1523  {550, 550}, 75, gradient_colors, stops, Entity::TileMode::kMirror, {});
1524  for (int i = 1; i <= 10; i++) {
1525  int j = 11 - i;
1526  canvas.DrawRRect(Rect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1527  550 + i * 20, 550 + j * 20),
1528  Size(i * 10, j * 10), paint);
1529  }
1530  paint.color = Color::White().WithAlpha(0.5);
1532  {200, 650}, 75, std::move(gradient_colors), std::move(stops),
1534  canvas.DrawRRect(Rect::MakeLTRB(100, 610, 300, 690), Size(40, 40), paint);
1535  canvas.DrawRRect(Rect::MakeLTRB(160, 550, 240, 750), Size(40, 40), paint);
1536 
1537  paint.color = Color::White().WithAlpha(0.1);
1540  Matrix::MakeTranslation({520, 20}));
1541  for (int i = 1; i <= 10; i++) {
1542  int j = 11 - i;
1543  canvas.DrawRRect(Rect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1544  720 + i * 20, 220 + j * 20),
1545  Size(i * 10, j * 10), paint);
1546  }
1547  paint.color = Color::White().WithAlpha(0.5);
1550  Matrix::MakeTranslation({800, 300}));
1551  canvas.DrawRRect(Rect::MakeLTRB(800, 410, 1000, 490), Size(40, 40), paint);
1552  canvas.DrawRRect(Rect::MakeLTRB(860, 350, 940, 550), Size(40, 40), paint);
1553 
1554  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1555 }
1556 
1557 TEST_P(AiksTest, SolidColorCirclesOvalsRRectsMaskBlurCorrectly) {
1558  Canvas canvas;
1559  canvas.Scale(GetContentScale());
1560  Paint paint;
1563  .sigma = Sigma{1},
1564  };
1565 
1566  canvas.DrawPaint({.color = Color::White()});
1567 
1568  paint.color = Color::Crimson();
1569  Scalar y = 100.0f;
1570  for (int i = 0; i < 5; i++) {
1571  Scalar x = (i + 1) * 100;
1572  Scalar radius = x / 10.0f;
1573  canvas.DrawRect(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1574  radius, 60.0f - radius),
1575  paint);
1576  }
1577 
1578  paint.color = Color::Blue();
1579  y += 100.0f;
1580  for (int i = 0; i < 5; i++) {
1581  Scalar x = (i + 1) * 100;
1582  Scalar radius = x / 10.0f;
1583  canvas.DrawCircle({x + 25, y + 25}, radius, paint);
1584  }
1585 
1586  paint.color = Color::Green();
1587  y += 100.0f;
1588  for (int i = 0; i < 5; i++) {
1589  Scalar x = (i + 1) * 100;
1590  Scalar radius = x / 10.0f;
1591  canvas.DrawOval(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1592  radius, 60.0f - radius),
1593  paint);
1594  }
1595 
1596  paint.color = Color::Purple();
1597  y += 100.0f;
1598  for (int i = 0; i < 5; i++) {
1599  Scalar x = (i + 1) * 100;
1600  Scalar radius = x / 20.0f;
1601  canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
1602  {radius, radius}, //
1603  paint);
1604  }
1605 
1606  paint.color = Color::Orange();
1607  y += 100.0f;
1608  for (int i = 0; i < 5; i++) {
1609  Scalar x = (i + 1) * 100;
1610  Scalar radius = x / 20.0f;
1611  canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
1612  {radius, 5.0f}, paint);
1613  }
1614 
1615  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1616 }
1617 
1618 TEST_P(AiksTest, FilledRoundRectPathsRenderCorrectly) {
1619  Canvas canvas;
1620  canvas.Scale(GetContentScale());
1621  Paint paint;
1622  const int color_count = 3;
1623  Color colors[color_count] = {
1624  Color::Blue(),
1625  Color::Green(),
1626  Color::Crimson(),
1627  };
1628 
1629  paint.color = Color::White();
1630  canvas.DrawPaint(paint);
1631 
1632  auto draw_rrect_as_path = [&canvas](const Rect& rect, const Size& radii,
1633  const Paint& paint) {
1634  PathBuilder builder = PathBuilder();
1635  builder.AddRoundedRect(rect, radii);
1636  canvas.DrawPath(builder.TakePath(), paint);
1637  };
1638 
1639  int c_index = 0;
1640  for (int i = 0; i < 4; i++) {
1641  for (int j = 0; j < 4; j++) {
1642  paint.color = colors[(c_index++) % color_count];
1643  draw_rrect_as_path(Rect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80),
1644  Size(i * 5 + 10, j * 5 + 10), paint);
1645  }
1646  }
1647  paint.color = colors[(c_index++) % color_count];
1648  draw_rrect_as_path(Rect::MakeXYWH(10, 420, 380, 80), Size(40, 40), paint);
1649  paint.color = colors[(c_index++) % color_count];
1650  draw_rrect_as_path(Rect::MakeXYWH(410, 20, 80, 380), Size(40, 40), paint);
1651 
1652  std::vector<Color> gradient_colors = {
1653  Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
1654  Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
1655  Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
1656  Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
1657  Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
1658  Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
1659  Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
1660  std::vector<Scalar> stops = {
1661  0.0,
1662  (1.0 / 6.0) * 1,
1663  (1.0 / 6.0) * 2,
1664  (1.0 / 6.0) * 3,
1665  (1.0 / 6.0) * 4,
1666  (1.0 / 6.0) * 5,
1667  1.0,
1668  };
1669  auto texture = CreateTextureForFixture("airplane.jpg",
1670  /*enable_mipmapping=*/true);
1671 
1672  paint.color = Color::White().WithAlpha(0.1);
1674  {550, 550}, 75, gradient_colors, stops, Entity::TileMode::kMirror, {});
1675  for (int i = 1; i <= 10; i++) {
1676  int j = 11 - i;
1677  draw_rrect_as_path(Rect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1678  550 + i * 20, 550 + j * 20),
1679  Size(i * 10, j * 10), paint);
1680  }
1681  paint.color = Color::White().WithAlpha(0.5);
1683  {200, 650}, 75, std::move(gradient_colors), std::move(stops),
1685  draw_rrect_as_path(Rect::MakeLTRB(100, 610, 300, 690), Size(40, 40), paint);
1686  draw_rrect_as_path(Rect::MakeLTRB(160, 550, 240, 750), Size(40, 40), paint);
1687 
1688  paint.color = Color::White().WithAlpha(0.1);
1691  Matrix::MakeTranslation({520, 20}));
1692  for (int i = 1; i <= 10; i++) {
1693  int j = 11 - i;
1694  draw_rrect_as_path(Rect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1695  720 + i * 20, 220 + j * 20),
1696  Size(i * 10, j * 10), paint);
1697  }
1698  paint.color = Color::White().WithAlpha(0.5);
1701  Matrix::MakeTranslation({800, 300}));
1702  draw_rrect_as_path(Rect::MakeLTRB(800, 410, 1000, 490), Size(40, 40), paint);
1703  draw_rrect_as_path(Rect::MakeLTRB(860, 350, 940, 550), Size(40, 40), paint);
1704 
1705  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1706 }
1707 
1708 TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
1709  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1710  Canvas canvas;
1711  canvas.Scale(GetContentScale());
1712 
1713  Paint alpha;
1714  alpha.color = Color::Red().WithAlpha(0.5);
1715 
1716  auto current = Point{25, 25};
1717  const auto offset = Point{25, 25};
1718  const auto size = Size(100, 100);
1719 
1720  static PlaygroundPoint point_a(Point(40, 40), 10, Color::White());
1721  static PlaygroundPoint point_b(Point(160, 160), 10, Color::White());
1722  auto [b0, b1] = DrawPlaygroundLine(point_a, point_b);
1723  auto bounds = Rect::MakeLTRB(b0.x, b0.y, b1.x, b1.y);
1724 
1725  canvas.DrawRect(bounds, Paint{.color = Color::Yellow(),
1726  .stroke_width = 5.0f,
1727  .style = Paint::Style::kStroke});
1728 
1729  canvas.SaveLayer(alpha, bounds);
1730 
1731  canvas.DrawRect(Rect::MakeOriginSize(current, size),
1732  Paint{.color = Color::Red()});
1733  canvas.DrawRect(Rect::MakeOriginSize(current += offset, size),
1734  Paint{.color = Color::Green()});
1735  canvas.DrawRect(Rect::MakeOriginSize(current += offset, size),
1736  Paint{.color = Color::Blue()});
1737 
1738  canvas.Restore();
1739 
1740  return canvas.EndRecordingAsPicture();
1741  };
1742 
1743  ASSERT_TRUE(OpenPlaygroundHere(callback));
1744 }
1745 
1746 TEST_P(AiksTest, SaveLayerDrawsBehindSubsequentEntities) {
1747  // Compare with https://fiddle.skia.org/c/9e03de8567ffb49e7e83f53b64bcf636
1748  Canvas canvas;
1749  Paint paint;
1750 
1751  paint.color = Color::Black();
1752  Rect rect = Rect::MakeXYWH(25, 25, 25, 25);
1753  canvas.DrawRect(rect, paint);
1754 
1755  canvas.Translate({10, 10});
1756  canvas.SaveLayer({});
1757 
1758  paint.color = Color::Green();
1759  canvas.DrawRect(rect, paint);
1760 
1761  canvas.Restore();
1762 
1763  canvas.Translate({10, 10});
1764  paint.color = Color::Red();
1765  canvas.DrawRect(rect, paint);
1766 
1767  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1768 }
1769 
1770 TEST_P(AiksTest, SiblingSaveLayerBoundsAreRespected) {
1771  Canvas canvas;
1772  Paint paint;
1773  Rect rect = Rect::MakeXYWH(0, 0, 1000, 1000);
1774 
1775  // Black, green, and red squares offset by [10, 10].
1776  {
1777  canvas.SaveLayer({}, Rect::MakeXYWH(25, 25, 25, 25));
1778  paint.color = Color::Black();
1779  canvas.DrawRect(rect, paint);
1780  canvas.Restore();
1781  }
1782 
1783  {
1784  canvas.SaveLayer({}, Rect::MakeXYWH(35, 35, 25, 25));
1785  paint.color = Color::Green();
1786  canvas.DrawRect(rect, paint);
1787  canvas.Restore();
1788  }
1789 
1790  {
1791  canvas.SaveLayer({}, Rect::MakeXYWH(45, 45, 25, 25));
1792  paint.color = Color::Red();
1793  canvas.DrawRect(rect, paint);
1794  canvas.Restore();
1795  }
1796 
1797  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1798 }
1799 
1800 TEST_P(AiksTest, CanRenderClippedLayers) {
1801  Canvas canvas;
1802 
1803  canvas.DrawPaint({.color = Color::White()});
1804 
1805  // Draw a green circle on the screen.
1806  {
1807  // Increase the clip depth for the savelayer to contend with.
1808  canvas.ClipPath(PathBuilder{}.AddCircle({100, 100}, 50).TakePath());
1809 
1810  canvas.SaveLayer({}, Rect::MakeXYWH(50, 50, 100, 100));
1811 
1812  // Fill the layer with white.
1813  canvas.DrawRect(Rect::MakeSize(Size{400, 400}), {.color = Color::White()});
1814  // Fill the layer with green, but do so with a color blend that can't be
1815  // collapsed into the parent pass.
1816  // TODO(jonahwilliams): this blend mode was changed from color burn to
1817  // hardlight to work around https://github.com/flutter/flutter/issues/136554
1818  // .
1819  canvas.DrawRect(
1820  Rect::MakeSize(Size{400, 400}),
1821  {.color = Color::Green(), .blend_mode = BlendMode::kHardLight});
1822  }
1823 
1824  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1825 }
1826 
1827 TEST_P(AiksTest, SaveLayerFiltersScaleWithTransform) {
1828  Canvas canvas;
1829  canvas.Scale(GetContentScale());
1830  canvas.Translate(Vector2(100, 100));
1831 
1832  auto texture = std::make_shared<Image>(CreateTextureForFixture("boston.jpg"));
1833  auto draw_image_layer = [&canvas, &texture](const Paint& paint) {
1834  canvas.SaveLayer(paint);
1835  canvas.DrawImage(texture, {}, Paint{});
1836  canvas.Restore();
1837  };
1838 
1839  Paint effect_paint;
1842  .sigma = Sigma{6},
1843  };
1844  draw_image_layer(effect_paint);
1845 
1846  canvas.Translate(Vector2(300, 300));
1847  canvas.Scale(Vector2(3, 3));
1848  draw_image_layer(effect_paint);
1849 
1850  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1851 }
1852 
1853 #if IMPELLER_ENABLE_3D
1854 TEST_P(AiksTest, SceneColorSource) {
1855  // Load up the scene.
1856  auto mapping =
1857  flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb.ipscene");
1858  ASSERT_NE(mapping, nullptr);
1859 
1860  std::shared_ptr<scene::Node> gltf_scene = scene::Node::MakeFromFlatbuffer(
1861  *mapping, *GetContext()->GetResourceAllocator());
1862  ASSERT_NE(gltf_scene, nullptr);
1863 
1864  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1865  Paint paint;
1866 
1867  static Scalar distance = 2;
1868  static Scalar y_pos = 0;
1869  static Scalar fov = 45;
1870  if (AiksTest::ImGuiBegin("Controls", nullptr,
1871  ImGuiWindowFlags_AlwaysAutoResize)) {
1872  ImGui::SliderFloat("Distance", &distance, 0, 4);
1873  ImGui::SliderFloat("Y", &y_pos, -3, 3);
1874  ImGui::SliderFloat("FOV", &fov, 1, 180);
1875  ImGui::End();
1876  }
1877 
1878  Scalar angle = GetSecondsElapsed();
1879  auto camera_position =
1880  Vector3(distance * std::sin(angle), y_pos, -distance * std::cos(angle));
1881 
1882  paint.color_source = ColorSource::MakeScene(
1883  gltf_scene,
1884  Matrix::MakePerspective(Degrees(fov), GetWindowSize(), 0.1, 1000) *
1885  Matrix::MakeLookAt(camera_position, {0, 0, 0}, {0, 1, 0}));
1886 
1887  Canvas canvas;
1888  canvas.DrawPaint(Paint{.color = Color::MakeRGBA8(0xf9, 0xf9, 0xf9, 0xff)});
1889  canvas.Scale(GetContentScale());
1890  canvas.DrawPaint(paint);
1891  return canvas.EndRecordingAsPicture();
1892  };
1893 
1894  ASSERT_TRUE(OpenPlaygroundHere(callback));
1895 }
1896 #endif // IMPELLER_ENABLE_3D
1897 
1898 TEST_P(AiksTest, PaintWithFilters) {
1899  // validate that a paint with a color filter "HasFilters", no other filters
1900  // impact this setting.
1901  Paint paint;
1902 
1903  ASSERT_FALSE(paint.HasColorFilter());
1904 
1905  paint.color_filter =
1907 
1908  ASSERT_TRUE(paint.HasColorFilter());
1909 
1910  paint.image_filter = ImageFilter::MakeBlur(Sigma(1.0), Sigma(1.0),
1913 
1914  ASSERT_TRUE(paint.HasColorFilter());
1915 
1916  paint.mask_blur_descriptor = {};
1917 
1918  ASSERT_TRUE(paint.HasColorFilter());
1919 
1920  paint.color_filter = nullptr;
1921 
1922  ASSERT_FALSE(paint.HasColorFilter());
1923 }
1924 
1925 TEST_P(AiksTest, OpacityPeepHoleApplicationTest) {
1926  auto entity_pass = std::make_shared<EntityPass>();
1927  auto rect = Rect::MakeLTRB(0, 0, 100, 100);
1928  Paint paint;
1929  paint.color = Color::White().WithAlpha(0.5);
1930  paint.color_filter =
1932 
1933  // Paint has color filter, can't elide.
1934  auto delegate = std::make_shared<OpacityPeepholePassDelegate>(paint);
1935  ASSERT_FALSE(delegate->CanCollapseIntoParentPass(entity_pass.get()));
1936 
1937  paint.color_filter = nullptr;
1938  paint.image_filter = ImageFilter::MakeBlur(Sigma(1.0), Sigma(1.0),
1941 
1942  // Paint has image filter, can't elide.
1943  delegate = std::make_shared<OpacityPeepholePassDelegate>(paint);
1944  ASSERT_FALSE(delegate->CanCollapseIntoParentPass(entity_pass.get()));
1945 
1946  paint.image_filter = nullptr;
1947  paint.color = Color::Red();
1948 
1949  // Paint has no alpha, can't elide;
1950  delegate = std::make_shared<OpacityPeepholePassDelegate>(paint);
1951  ASSERT_FALSE(delegate->CanCollapseIntoParentPass(entity_pass.get()));
1952 
1953  // Positive test.
1954  Entity entity;
1956  PathBuilder{}.AddRect(rect).TakePath(), Color::Red()));
1957  entity_pass->AddEntity(std::move(entity));
1958  paint.color = Color::Red().WithAlpha(0.5);
1959 
1960  delegate = std::make_shared<OpacityPeepholePassDelegate>(paint);
1961  ASSERT_TRUE(delegate->CanCollapseIntoParentPass(entity_pass.get()));
1962 }
1963 
1964 TEST_P(AiksTest, DrawPaintAbsorbsClears) {
1965  Canvas canvas;
1966  canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1967  canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1968  .blend_mode = BlendMode::kSourceOver});
1969 
1970  Picture picture = canvas.EndRecordingAsPicture();
1971  auto expected = Color::Red().Blend(Color::CornflowerBlue().WithAlpha(0.75),
1973  ASSERT_EQ(picture.pass->GetClearColor(), expected);
1974 
1975  std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
1976  std::shared_ptr<Context> real_context = GetContext();
1977  std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
1978  AiksContext renderer(mock_context, nullptr);
1979  std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
1980 
1981  ASSERT_EQ(spy->render_passes_.size(), 1llu);
1982  std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
1983  ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
1984 }
1985 
1986 // This is important to enforce with texture reuse, since cached textures need
1987 // to be cleared before reuse.
1989  ParentSaveLayerCreatesRenderPassWhenChildBackdropFilterIsPresent) {
1990  Canvas canvas;
1991  canvas.SaveLayer({}, std::nullopt, ImageFilter::MakeMatrix(Matrix(), {}));
1992  canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
1993  canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
1994  .blend_mode = BlendMode::kSourceOver});
1995  canvas.Restore();
1996 
1997  Picture picture = canvas.EndRecordingAsPicture();
1998 
1999  std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
2000  std::shared_ptr<Context> real_context = GetContext();
2001  std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
2002  AiksContext renderer(mock_context, nullptr);
2003  std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
2004 
2005  ASSERT_EQ(spy->render_passes_.size(),
2006  GetBackend() == PlaygroundBackend::kOpenGLES ? 4llu : 3llu);
2007  std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
2008  ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
2009 }
2010 
2011 TEST_P(AiksTest, DrawRectAbsorbsClears) {
2012  Canvas canvas;
2013  canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
2014  {.color = Color::Red(), .blend_mode = BlendMode::kSource});
2015  canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
2016  {.color = Color::CornflowerBlue().WithAlpha(0.75),
2017  .blend_mode = BlendMode::kSourceOver});
2018 
2019  std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
2020  Picture picture = canvas.EndRecordingAsPicture();
2021  std::shared_ptr<Context> real_context = GetContext();
2022  std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
2023  AiksContext renderer(mock_context, nullptr);
2024  std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
2025 
2026  ASSERT_EQ(spy->render_passes_.size(), 1llu);
2027  std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
2028  ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
2029 }
2030 
2031 TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRRect) {
2032  Canvas canvas;
2033  canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), {5.0, 5.0},
2034  {.color = Color::Red(), .blend_mode = BlendMode::kSource});
2035  canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), {5.0, 5.0},
2036  {.color = Color::CornflowerBlue().WithAlpha(0.75),
2037  .blend_mode = BlendMode::kSourceOver});
2038 
2039  std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
2040  Picture picture = canvas.EndRecordingAsPicture();
2041  std::shared_ptr<Context> real_context = GetContext();
2042  std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
2043  AiksContext renderer(mock_context, nullptr);
2044  std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
2045 
2046  ASSERT_EQ(spy->render_passes_.size(), 1llu);
2047  std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
2048  ASSERT_EQ(render_pass->GetCommands().size(), 2llu);
2049 }
2050 
2051 TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRotation) {
2052  Canvas canvas;
2053  canvas.Translate(Vector3(150.0, 150.0, 0.0));
2054  canvas.Rotate(Degrees(45.0));
2055  canvas.Translate(Vector3(-150.0, -150.0, 0.0));
2056  canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
2057  {.color = Color::Red(), .blend_mode = BlendMode::kSource});
2058 
2059  std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
2060  Picture picture = canvas.EndRecordingAsPicture();
2061  std::shared_ptr<Context> real_context = GetContext();
2062  std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
2063  AiksContext renderer(mock_context, nullptr);
2064  std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
2065 
2066  ASSERT_EQ(spy->render_passes_.size(), 1llu);
2067  std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
2068  ASSERT_EQ(render_pass->GetCommands().size(), 1llu);
2069 }
2070 
2071 TEST_P(AiksTest, DrawRectAbsorbsClearsNegative) {
2072  Canvas canvas;
2073  canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
2074  {.color = Color::Red(), .blend_mode = BlendMode::kSource});
2075  canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300),
2076  {.color = Color::CornflowerBlue().WithAlpha(0.75),
2077  .blend_mode = BlendMode::kSourceOver});
2078 
2079  std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
2080  Picture picture = canvas.EndRecordingAsPicture();
2081  std::shared_ptr<Context> real_context = GetContext();
2082  std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
2083  AiksContext renderer(mock_context, nullptr);
2084  std::shared_ptr<Image> image = picture.ToImage(renderer, {301, 301});
2085 
2086  ASSERT_EQ(spy->render_passes_.size(), 1llu);
2087  std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
2088  ASSERT_EQ(render_pass->GetCommands().size(), 2llu);
2089 }
2090 
2091 TEST_P(AiksTest, ClipRectElidesNoOpClips) {
2092  Canvas canvas(Rect::MakeXYWH(0, 0, 100, 100));
2093  canvas.ClipRect(Rect::MakeXYWH(0, 0, 100, 100));
2094  canvas.ClipRect(Rect::MakeXYWH(-100, -100, 300, 300));
2095  canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
2096  canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
2097  .blend_mode = BlendMode::kSourceOver});
2098 
2099  Picture picture = canvas.EndRecordingAsPicture();
2100  auto expected = Color::Red().Blend(Color::CornflowerBlue().WithAlpha(0.75),
2102  ASSERT_EQ(picture.pass->GetClearColor(), expected);
2103 
2104  std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
2105  std::shared_ptr<Context> real_context = GetContext();
2106  std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
2107  AiksContext renderer(mock_context, nullptr);
2108  std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
2109 
2110  ASSERT_EQ(spy->render_passes_.size(), 1llu);
2111  std::shared_ptr<RenderPass> render_pass = spy->render_passes_[0];
2112  ASSERT_EQ(render_pass->GetCommands().size(), 0llu);
2113 }
2114 
2115 TEST_P(AiksTest, ClearColorOptimizationDoesNotApplyForBackdropFilters) {
2116  Canvas canvas;
2117  canvas.SaveLayer({}, std::nullopt,
2121  canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
2122  canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
2123  .blend_mode = BlendMode::kSourceOver});
2124  canvas.Restore();
2125 
2126  Picture picture = canvas.EndRecordingAsPicture();
2127 
2128  std::optional<Color> actual_color;
2129  bool found_subpass = false;
2130  picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool {
2131  if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
2132  actual_color = subpass->get()->GetClearColor();
2133  found_subpass = true;
2134  }
2135  // Fail if the first element isn't a subpass.
2136  return true;
2137  });
2138 
2139  EXPECT_TRUE(found_subpass);
2140  EXPECT_FALSE(actual_color.has_value());
2141 }
2142 
2143 TEST_P(AiksTest, CollapsedDrawPaintInSubpass) {
2144  Canvas canvas;
2145  canvas.DrawPaint(
2146  {.color = Color::Yellow(), .blend_mode = BlendMode::kSource});
2147  canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
2148  canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
2149  .blend_mode = BlendMode::kSourceOver});
2150 
2151  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2152 }
2153 
2154 TEST_P(AiksTest, CollapsedDrawPaintInSubpassBackdropFilter) {
2155  // Bug: https://github.com/flutter/flutter/issues/131576
2156  Canvas canvas;
2157  canvas.DrawPaint(
2158  {.color = Color::Yellow(), .blend_mode = BlendMode::kSource});
2159  canvas.SaveLayer({}, {},
2160  ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
2163  canvas.DrawPaint(
2164  {.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSourceOver});
2165 
2166  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2167 }
2168 
2169 TEST_P(AiksTest, ForegroundBlendSubpassCollapseOptimization) {
2170  Canvas canvas;
2171 
2172  canvas.SaveLayer({
2173  .color_filter =
2175  });
2176 
2177  canvas.Translate({500, 300, 0});
2178  canvas.Rotate(Radians(2 * kPi / 3));
2179  canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
2180 
2181  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2182 }
2183 
2184 TEST_P(AiksTest, ColorMatrixFilterSubpassCollapseOptimization) {
2185  Canvas canvas;
2186 
2187  canvas.SaveLayer({
2188  .color_filter =
2189  ColorFilter::MakeMatrix({.array =
2190  {
2191  -1.0, 0, 0, 1.0, 0, //
2192  0, -1.0, 0, 1.0, 0, //
2193  0, 0, -1.0, 1.0, 0, //
2194  1.0, 1.0, 1.0, 1.0, 0 //
2195  }}),
2196  });
2197 
2198  canvas.Translate({500, 300, 0});
2199  canvas.Rotate(Radians(2 * kPi / 3));
2200  canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
2201 
2202  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2203 }
2204 
2205 TEST_P(AiksTest, LinearToSrgbFilterSubpassCollapseOptimization) {
2206  Canvas canvas;
2207 
2208  canvas.SaveLayer({
2209  .color_filter = ColorFilter::MakeLinearToSrgb(),
2210  });
2211 
2212  canvas.Translate({500, 300, 0});
2213  canvas.Rotate(Radians(2 * kPi / 3));
2214  canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
2215 
2216  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2217 }
2218 
2219 TEST_P(AiksTest, SrgbToLinearFilterSubpassCollapseOptimization) {
2220  Canvas canvas;
2221 
2222  canvas.SaveLayer({
2223  .color_filter = ColorFilter::MakeSrgbToLinear(),
2224  });
2225 
2226  canvas.Translate({500, 300, 0});
2227  canvas.Rotate(Radians(2 * kPi / 3));
2228  canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
2229 
2230  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2231 }
2232 
2233 static Picture BlendModeTest(Vector2 content_scale,
2234  BlendMode blend_mode,
2235  const std::shared_ptr<Image>& src_image,
2236  const std::shared_ptr<Image>& dst_image) {
2237  Color destination_color = Color::CornflowerBlue().WithAlpha(0.75);
2238  auto source_colors = std::vector<Color>({Color::White().WithAlpha(0.75),
2239  Color::LimeGreen().WithAlpha(0.75),
2240  Color::Black().WithAlpha(0.75)});
2241 
2242  Canvas canvas;
2243  canvas.DrawPaint({.color = Color::Black()});
2244  // TODO(bdero): Why does this cause the left image to double scale on high DPI
2245  // displays.
2246  // canvas.Scale(content_scale);
2247 
2248  //----------------------------------------------------------------------------
2249  /// 1. Save layer blending (top squares).
2250  ///
2251 
2252  canvas.Save();
2253  for (const auto& color : source_colors) {
2254  canvas.Save();
2255  {
2256  canvas.ClipRect(Rect::MakeXYWH(25, 25, 100, 100));
2257  // Perform the blend in a SaveLayer so that the initial backdrop color is
2258  // fully transparent black. SourceOver blend the result onto the parent
2259  // pass.
2260  canvas.SaveLayer({});
2261  {
2262  canvas.DrawPaint({.color = destination_color});
2263  // Draw the source color in an offscreen pass and blend it to the parent
2264  // pass.
2265  canvas.SaveLayer({.blend_mode = blend_mode});
2266  { //
2267  canvas.DrawRect(Rect::MakeXYWH(25, 25, 100, 100), {.color = color});
2268  }
2269  canvas.Restore();
2270  }
2271  canvas.Restore();
2272  }
2273  canvas.Restore();
2274  canvas.Translate(Vector2(100, 0));
2275  }
2276  canvas.RestoreToCount(0);
2277 
2278  //----------------------------------------------------------------------------
2279  /// 2. CPU blend modes (bottom squares).
2280  ///
2281 
2282  canvas.Save();
2283  canvas.Translate({0, 100});
2284  // Perform the blend in a SaveLayer so that the initial backdrop color is
2285  // fully transparent black. SourceOver blend the result onto the parent pass.
2286  canvas.SaveLayer({});
2287  for (const auto& color : source_colors) {
2288  // Simply write the CPU blended color to the pass.
2289  canvas.DrawRect(Rect::MakeXYWH(25, 25, 100, 100),
2290  {.color = destination_color.Blend(color, blend_mode),
2291  .blend_mode = BlendMode::kSourceOver});
2292  canvas.Translate(Vector2(100, 0));
2293  }
2294  canvas.Restore();
2295  canvas.Restore();
2296 
2297  //----------------------------------------------------------------------------
2298  /// 3. Image blending (bottom images).
2299  ///
2300  /// Compare these results with the images in the Flutter blend mode
2301  /// documentation: https://api.flutter.dev/flutter/dart-ui/BlendMode.html
2302  ///
2303 
2304  canvas.Translate({0, 250});
2305 
2306  // Draw grid behind the images.
2307  canvas.DrawRect(Rect::MakeLTRB(0, 0, 800, 400),
2308  {.color = Color::MakeRGBA8(41, 41, 41, 255)});
2309  Paint square_paint = {.color = Color::MakeRGBA8(15, 15, 15, 255)};
2310  for (int y = 0; y < 400 / 8; y++) {
2311  for (int x = 0; x < 800 / 16; x++) {
2312  canvas.DrawRect(Rect::MakeXYWH(x * 16 + (y % 2) * 8, y * 8, 8, 8),
2313  square_paint);
2314  }
2315  }
2316 
2317  // Uploaded image source (left image).
2318  canvas.Save();
2319  canvas.SaveLayer({.blend_mode = BlendMode::kSourceOver});
2320  {
2321  canvas.DrawImage(dst_image, {0, 0}, {.blend_mode = BlendMode::kSourceOver});
2322  canvas.DrawImage(src_image, {0, 0}, {.blend_mode = blend_mode});
2323  }
2324  canvas.Restore();
2325  canvas.Restore();
2326 
2327  // Rendered image source (right image).
2328  canvas.Save();
2329  canvas.SaveLayer({.blend_mode = BlendMode::kSourceOver});
2330  {
2331  canvas.DrawImage(dst_image, {400, 0},
2332  {.blend_mode = BlendMode::kSourceOver});
2333  canvas.SaveLayer({.blend_mode = blend_mode});
2334  {
2335  canvas.DrawImage(src_image, {400, 0},
2336  {.blend_mode = BlendMode::kSourceOver});
2337  }
2338  canvas.Restore();
2339  }
2340  canvas.Restore();
2341  canvas.Restore();
2342 
2343  return canvas.EndRecordingAsPicture();
2344 }
2345 
2346 #define BLEND_MODE_TEST(blend_mode) \
2347  TEST_P(AiksTest, BlendMode##blend_mode) { \
2348  auto src_image = std::make_shared<Image>( \
2349  CreateTextureForFixture("blend_mode_src.png")); \
2350  auto dst_image = std::make_shared<Image>( \
2351  CreateTextureForFixture("blend_mode_dst.png")); \
2352  OpenPlaygroundHere(BlendModeTest( \
2353  GetContentScale(), BlendMode::k##blend_mode, src_image, dst_image)); \
2354  }
2356 
2357 TEST_P(AiksTest, TranslucentSaveLayerDrawsCorrectly) {
2358  Canvas canvas;
2359 
2360  canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
2361 
2362  canvas.SaveLayer({.color = Color::Black().WithAlpha(0.5)});
2363  canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
2364  canvas.Restore();
2365 
2366  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2367 }
2368 
2369 TEST_P(AiksTest, TranslucentSaveLayerWithBlendColorFilterDrawsCorrectly) {
2370  Canvas canvas;
2371 
2372  canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
2373 
2374  canvas.SaveLayer({
2375  .color = Color::Black().WithAlpha(0.5),
2376  .color_filter =
2378  });
2379  canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
2380  canvas.Restore();
2381 
2382  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2383 }
2384 
2385 TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterDrawsCorrectly) {
2386  Canvas canvas;
2387 
2388  canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
2389 
2390  canvas.SaveLayer({
2391  .color = Color::Black().WithAlpha(0.5),
2392  .image_filter = ImageFilter::MakeFromColorFilter(
2394  });
2395 
2396  canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
2397  canvas.Restore();
2398 
2399  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2400 }
2401 
2402 TEST_P(AiksTest, TranslucentSaveLayerWithColorAndImageFilterDrawsCorrectly) {
2403  Canvas canvas;
2404 
2405  canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
2406 
2407  canvas.SaveLayer({
2408  .color = Color::Black().WithAlpha(0.5),
2409  .color_filter =
2411  });
2412 
2413  canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
2414  canvas.Restore();
2415 
2416  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2417 }
2418 
2419 TEST_P(AiksTest, ImageFilteredSaveLayerWithUnboundedContents) {
2420  Canvas canvas;
2421  canvas.Scale(GetContentScale());
2422 
2423  auto test = [&canvas](const std::shared_ptr<ImageFilter>& filter) {
2424  auto DrawLine = [&canvas](const Point& p0, const Point& p1,
2425  const Paint& p) {
2426  auto path = PathBuilder{}
2427  .AddLine(p0, p1)
2429  .TakePath();
2430  Paint paint = p;
2431  paint.style = Paint::Style::kStroke;
2432  canvas.DrawPath(path, paint);
2433  };
2434  // Registration marks for the edge of the SaveLayer
2435  DrawLine(Point(75, 100), Point(225, 100), {.color = Color::White()});
2436  DrawLine(Point(75, 200), Point(225, 200), {.color = Color::White()});
2437  DrawLine(Point(100, 75), Point(100, 225), {.color = Color::White()});
2438  DrawLine(Point(200, 75), Point(200, 225), {.color = Color::White()});
2439 
2440  canvas.SaveLayer({.image_filter = filter},
2441  Rect::MakeLTRB(100, 100, 200, 200));
2442  {
2443  // DrawPaint to verify correct behavior when the contents are unbounded.
2444  canvas.DrawPaint({.color = Color::Yellow()});
2445 
2446  // Contrasting rectangle to see interior blurring
2447  canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
2448  {.color = Color::Blue()});
2449  }
2450  canvas.Restore();
2451  };
2452 
2453  test(ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0},
2456 
2457  canvas.Translate({200.0, 0.0});
2458 
2459  test(ImageFilter::MakeDilate(Radius{10.0}, Radius{10.0}));
2460 
2461  canvas.Translate({200.0, 0.0});
2462 
2463  test(ImageFilter::MakeErode(Radius{10.0}, Radius{10.0}));
2464 
2465  canvas.Translate({-400.0, 200.0});
2466 
2467  auto rotate_filter =
2470  Matrix::MakeTranslation({-150, -150}),
2471  SamplerDescriptor{});
2472  test(rotate_filter);
2473 
2474  canvas.Translate({200.0, 0.0});
2475 
2476  auto rgb_swap_filter = ImageFilter::MakeFromColorFilter(
2477  *ColorFilter::MakeMatrix({.array = {
2478  0, 1, 0, 0, 0, //
2479  0, 0, 1, 0, 0, //
2480  1, 0, 0, 0, 0, //
2481  0, 0, 0, 1, 0 //
2482  }}));
2483  test(rgb_swap_filter);
2484 
2485  canvas.Translate({200.0, 0.0});
2486 
2487  test(ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter));
2488 
2489  canvas.Translate({-400.0, 200.0});
2490 
2492  *rotate_filter));
2493 
2494  canvas.Translate({200.0, 0.0});
2495 
2497  *rgb_swap_filter));
2498 
2499  canvas.Translate({200.0, 0.0});
2500 
2502  Matrix::MakeTranslation({25.0, 25.0}),
2503  *ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter)));
2504 
2505  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2506 }
2507 
2508 TEST_P(AiksTest, ImageFilteredUnboundedSaveLayerWithUnboundedContents) {
2509  Canvas canvas;
2510  canvas.Scale(GetContentScale());
2511 
2512  auto blur_filter = ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0},
2515 
2516  canvas.SaveLayer({.image_filter = blur_filter}, std::nullopt);
2517  {
2518  // DrawPaint to verify correct behavior when the contents are unbounded.
2519  canvas.DrawPaint({.color = Color::Yellow()});
2520 
2521  // Contrasting rectangle to see interior blurring
2522  canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
2523  {.color = Color::Blue()});
2524  }
2525  canvas.Restore();
2526 
2527  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2528 }
2529 
2530 TEST_P(AiksTest, TranslucentSaveLayerImageDrawsCorrectly) {
2531  Canvas canvas;
2532 
2533  auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2534  canvas.DrawImage(image, {100, 100}, {});
2535 
2536  canvas.SaveLayer({.color = Color::Black().WithAlpha(0.5)});
2537  canvas.DrawImage(image, {100, 500}, {});
2538  canvas.Restore();
2539 
2540  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2541 }
2542 
2543 TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixColorFilterDrawsCorrectly) {
2544  Canvas canvas;
2545 
2546  auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2547  canvas.DrawImage(image, {100, 100}, {});
2548 
2549  canvas.SaveLayer({
2550  .color = Color::Black().WithAlpha(0.5),
2551  .color_filter = ColorFilter::MakeMatrix({.array =
2552  {
2553  1, 0, 0, 0, 0, //
2554  0, 1, 0, 0, 0, //
2555  0, 0, 1, 0, 0, //
2556  0, 0, 0, 2, 0 //
2557  }}),
2558  });
2559  canvas.DrawImage(image, {100, 500}, {});
2560  canvas.Restore();
2561 
2562  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2563 }
2564 
2565 TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly) {
2566  Canvas canvas;
2567 
2568  auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2569  canvas.DrawImage(image, {100, 100}, {});
2570 
2571  canvas.SaveLayer({
2572  .color = Color::Black().WithAlpha(0.5),
2573  .image_filter = ImageFilter::MakeFromColorFilter(
2574  *ColorFilter::MakeMatrix({.array =
2575  {
2576  1, 0, 0, 0, 0, //
2577  0, 1, 0, 0, 0, //
2578  0, 0, 1, 0, 0, //
2579  0, 0, 0, 2, 0 //
2580  }})),
2581  });
2582  canvas.DrawImage(image, {100, 500}, {});
2583  canvas.Restore();
2584 
2585  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2586 }
2587 
2589  TranslucentSaveLayerWithColorFilterAndImageFilterDrawsCorrectly) {
2590  Canvas canvas;
2591 
2592  auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
2593  canvas.DrawImage(image, {100, 100}, {});
2594 
2595  canvas.SaveLayer({
2596  .color = Color::Black().WithAlpha(0.5),
2597  .image_filter = ImageFilter::MakeFromColorFilter(
2598  *ColorFilter::MakeMatrix({.array =
2599  {
2600  1, 0, 0, 0, 0, //
2601  0, 1, 0, 0, 0, //
2602  0, 0.2, 1, 0, 0, //
2603  0, 0, 0, 0.5, 0 //
2604  }})),
2605  .color_filter =
2607  });
2608  canvas.DrawImage(image, {100, 500}, {});
2609  canvas.Restore();
2610 
2611  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2612 }
2613 
2614 TEST_P(AiksTest, TranslucentSaveLayerWithAdvancedBlendModeDrawsCorrectly) {
2615  Canvas canvas;
2616  canvas.DrawRect(Rect::MakeXYWH(0, 0, 400, 400), {.color = Color::Red()});
2617  canvas.SaveLayer({
2618  .color = Color::Black().WithAlpha(0.5),
2619  .blend_mode = BlendMode::kLighten,
2620  });
2621  canvas.DrawCircle({200, 200}, 100, {.color = Color::Green()});
2622  canvas.Restore();
2623  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2624 }
2625 
2626 /// This is a regression check for https://github.com/flutter/engine/pull/41129
2627 /// The entire screen is green if successful. If failing, no frames will render,
2628 /// or the entire screen will be transparent black.
2629 TEST_P(AiksTest, CanRenderTinyOverlappingSubpasses) {
2630  Canvas canvas;
2631  canvas.DrawPaint({.color = Color::Red()});
2632 
2633  // Draw two overlapping subpixel circles.
2634  canvas.SaveLayer({});
2635  canvas.DrawCircle({100, 100}, 0.1, {.color = Color::Yellow()});
2636  canvas.Restore();
2637  canvas.SaveLayer({});
2638  canvas.DrawCircle({100, 100}, 0.1, {.color = Color::Yellow()});
2639  canvas.Restore();
2640 
2641  canvas.DrawPaint({.color = Color::Green()});
2642 
2643  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2644 }
2645 
2646 /// Tests that the debug checkerboard displays for offscreen textures when
2647 /// enabled. Most of the complexity here is just to future proof by making pass
2648 /// collapsing hard.
2649 TEST_P(AiksTest, CanRenderOffscreenCheckerboard) {
2650  Canvas canvas;
2652 
2653  canvas.DrawPaint({.color = Color::AntiqueWhite()});
2654  canvas.DrawCircle({400, 300}, 200,
2655  {.color = Color::CornflowerBlue().WithAlpha(0.75)});
2656 
2657  canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
2658  {
2659  canvas.DrawCircle({500, 400}, 200,
2660  {.color = Color::DarkBlue().WithAlpha(0.75)});
2661  canvas.DrawCircle({550, 450}, 200,
2662  {.color = Color::LightCoral().WithAlpha(0.75),
2663  .blend_mode = BlendMode::kLuminosity});
2664  }
2665  canvas.Restore();
2666 
2667  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2668 }
2669 
2670 TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) {
2671  Canvas canvas;
2672  canvas.Scale(Vector2(1.618, 1.618));
2673  canvas.DrawCircle(Point(), 10,
2674  {
2675  .color = Color::CornflowerBlue(),
2676  .blend_mode = BlendMode::kSourceOver,
2677  });
2678  Picture picture = canvas.EndRecordingAsPicture();
2679 
2680  // Extract the SolidColorSource.
2681  // Entity entity;
2682  std::vector<Entity> entity;
2683  std::shared_ptr<SolidColorContents> contents;
2684  picture.pass->IterateAllEntities([e = &entity, &contents](Entity& entity) {
2685  if (ScalarNearlyEqual(entity.GetTransform().GetScale().x, 1.618f)) {
2686  contents =
2687  std::static_pointer_cast<SolidColorContents>(entity.GetContents());
2688  e->emplace_back(entity.Clone());
2689  return false;
2690  }
2691  return true;
2692  });
2693 
2694  ASSERT_TRUE(entity.size() >= 1);
2695  ASSERT_TRUE(contents->IsOpaque());
2696  ASSERT_EQ(entity[0].GetBlendMode(), BlendMode::kSource);
2697 }
2698 
2699 TEST_P(AiksTest, CanRenderDestructiveSaveLayer) {
2700  Canvas canvas;
2701 
2702  canvas.DrawPaint({.color = Color::Red()});
2703  // Draw an empty savelayer with a destructive blend mode, which will replace
2704  // the entire red screen with fully transparent black, except for the green
2705  // circle drawn within the layer.
2706  canvas.SaveLayer({.blend_mode = BlendMode::kSource});
2707  canvas.DrawCircle({300, 300}, 100, {.color = Color::Green()});
2708  canvas.Restore();
2709 
2710  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2711 }
2712 
2713 // Regression test for https://github.com/flutter/flutter/issues/126701 .
2714 TEST_P(AiksTest, CanRenderClippedRuntimeEffects) {
2715  auto runtime_stages =
2716  OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
2717 
2718  auto runtime_stage =
2719  runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
2720  ASSERT_TRUE(runtime_stage);
2721  ASSERT_TRUE(runtime_stage->IsDirty());
2722 
2723  struct FragUniforms {
2724  Vector2 iResolution;
2725  Scalar iTime;
2726  } frag_uniforms = {.iResolution = Vector2(400, 400), .iTime = 100.0};
2727  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
2728  uniform_data->resize(sizeof(FragUniforms));
2729  memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
2730 
2731  std::vector<RuntimeEffectContents::TextureInput> texture_inputs;
2732 
2733  Paint paint;
2735  runtime_stage, uniform_data, texture_inputs);
2736 
2737  Canvas canvas;
2738  canvas.Save();
2739  canvas.ClipRRect(Rect::MakeXYWH(0, 0, 400, 400), {10.0, 10.0},
2741  canvas.DrawRect(Rect::MakeXYWH(0, 0, 400, 400), paint);
2742  canvas.Restore();
2743 
2744  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2745 }
2746 
2747 TEST_P(AiksTest, DrawPaintTransformsBounds) {
2748  auto runtime_stages = OpenAssetAsRuntimeStage("gradient.frag.iplr");
2749  auto runtime_stage =
2750  runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
2751  ASSERT_TRUE(runtime_stage);
2752  ASSERT_TRUE(runtime_stage->IsDirty());
2753 
2754  struct FragUniforms {
2755  Size size;
2756  } frag_uniforms = {.size = Size::MakeWH(400, 400)};
2757  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
2758  uniform_data->resize(sizeof(FragUniforms));
2759  memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
2760 
2761  std::vector<RuntimeEffectContents::TextureInput> texture_inputs;
2762 
2763  Paint paint;
2765  runtime_stage, uniform_data, texture_inputs);
2766 
2767  Canvas canvas;
2768  canvas.Save();
2769  canvas.Scale(GetContentScale());
2770  canvas.DrawPaint(paint);
2771  canvas.Restore();
2772 
2773  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2774 }
2775 
2776 TEST_P(AiksTest, CanDrawPoints) {
2777  std::vector<Point> points = {
2778  {0, 0}, //
2779  {100, 100}, //
2780  {100, 0}, //
2781  {0, 100}, //
2782  {0, 0}, //
2783  {48, 48}, //
2784  {52, 52}, //
2785  };
2786  std::vector<PointStyle> caps = {
2789  };
2790  Paint paint;
2791  paint.color = Color::Yellow().WithAlpha(0.5);
2792 
2793  Paint background;
2794  background.color = Color::Black();
2795 
2796  Canvas canvas;
2797  canvas.DrawPaint(background);
2798  canvas.Translate({200, 200});
2799  canvas.DrawPoints(points, 10, paint, PointStyle::kRound);
2800  canvas.Translate({150, 0});
2801  canvas.DrawPoints(points, 10, paint, PointStyle::kSquare);
2802 
2803  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2804 }
2805 
2806 // Regression test for https://github.com/flutter/flutter/issues/127374.
2807 TEST_P(AiksTest, DrawAtlasWithColorAdvancedAndTransform) {
2808  // Draws the image as four squares stiched together.
2809  auto atlas = CreateTextureForFixture("bay_bridge.jpg");
2810  auto size = atlas->GetSize();
2811  auto image = std::make_shared<Image>(atlas);
2812  // Divide image into four quadrants.
2813  Scalar half_width = size.width / 2;
2814  Scalar half_height = size.height / 2;
2815  std::vector<Rect> texture_coordinates = {
2816  Rect::MakeLTRB(0, 0, half_width, half_height),
2817  Rect::MakeLTRB(half_width, 0, size.width, half_height),
2818  Rect::MakeLTRB(0, half_height, half_width, size.height),
2819  Rect::MakeLTRB(half_width, half_height, size.width, size.height)};
2820  // Position quadrants adjacent to eachother.
2821  std::vector<Matrix> transforms = {
2822  Matrix::MakeTranslation({0, 0, 0}),
2823  Matrix::MakeTranslation({half_width, 0, 0}),
2824  Matrix::MakeTranslation({0, half_height, 0}),
2825  Matrix::MakeTranslation({half_width, half_height, 0})};
2826  std::vector<Color> colors = {Color::Red(), Color::Green(), Color::Blue(),
2827  Color::Yellow()};
2828 
2829  Paint paint;
2830 
2831  Canvas canvas;
2832  canvas.Scale({0.25, 0.25, 1.0});
2833  canvas.DrawAtlas(image, transforms, texture_coordinates, colors,
2834  BlendMode::kModulate, {}, std::nullopt, paint);
2835 
2836  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2837 }
2838 
2839 // Regression test for https://github.com/flutter/flutter/issues/127374.
2840 TEST_P(AiksTest, DrawAtlasAdvancedAndTransform) {
2841  // Draws the image as four squares stiched together.
2842  auto atlas = CreateTextureForFixture("bay_bridge.jpg");
2843  auto size = atlas->GetSize();
2844  auto image = std::make_shared<Image>(atlas);
2845  // Divide image into four quadrants.
2846  Scalar half_width = size.width / 2;
2847  Scalar half_height = size.height / 2;
2848  std::vector<Rect> texture_coordinates = {
2849  Rect::MakeLTRB(0, 0, half_width, half_height),
2850  Rect::MakeLTRB(half_width, 0, size.width, half_height),
2851  Rect::MakeLTRB(0, half_height, half_width, size.height),
2852  Rect::MakeLTRB(half_width, half_height, size.width, size.height)};
2853  // Position quadrants adjacent to eachother.
2854  std::vector<Matrix> transforms = {
2855  Matrix::MakeTranslation({0, 0, 0}),
2856  Matrix::MakeTranslation({half_width, 0, 0}),
2857  Matrix::MakeTranslation({0, half_height, 0}),
2858  Matrix::MakeTranslation({half_width, half_height, 0})};
2859 
2860  Paint paint;
2861 
2862  Canvas canvas;
2863  canvas.Scale({0.25, 0.25, 1.0});
2864  canvas.DrawAtlas(image, transforms, texture_coordinates, {},
2865  BlendMode::kModulate, {}, std::nullopt, paint);
2866 
2867  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2868 }
2869 
2870 TEST_P(AiksTest, CanDrawPointsWithTextureMap) {
2871  auto texture = CreateTextureForFixture("table_mountain_nx.png",
2872  /*enable_mipmapping=*/true);
2873 
2874  std::vector<Point> points = {
2875  {0, 0}, //
2876  {100, 100}, //
2877  {100, 0}, //
2878  {0, 100}, //
2879  {0, 0}, //
2880  {48, 48}, //
2881  {52, 52}, //
2882  };
2883  std::vector<PointStyle> caps = {
2886  };
2887  Paint paint;
2889  Entity::TileMode::kClamp, {}, {});
2890 
2891  Canvas canvas;
2892  canvas.Translate({200, 200});
2893  canvas.DrawPoints(points, 100, paint, PointStyle::kRound);
2894  canvas.Translate({150, 0});
2895  canvas.DrawPoints(points, 100, paint, PointStyle::kSquare);
2896 
2897  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2898 }
2899 
2900 // This currently renders solid blue, as the support for text color sources was
2901 // moved into DLDispatching. Path data requires the SkTextBlobs which are not
2902 // used in impeller::TextFrames.
2903 TEST_P(AiksTest, TextForegroundShaderWithTransform) {
2904  auto mapping = flutter::testing::OpenFixtureAsSkData("Roboto-Regular.ttf");
2905  ASSERT_NE(mapping, nullptr);
2906 
2907  Scalar font_size = 100;
2908  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
2909  SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
2910 
2911  Paint text_paint;
2912  text_paint.color = Color::Blue();
2913 
2914  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
2915  Color{0.1294, 0.5882, 0.9529, 1.0}};
2916  std::vector<Scalar> stops = {
2917  0.0,
2918  1.0,
2919  };
2921  {0, 0}, {100, 100}, std::move(colors), std::move(stops),
2923 
2924  Canvas canvas;
2925  canvas.Translate({100, 100});
2926  canvas.Rotate(Radians(kPi / 4));
2927 
2928  auto blob = SkTextBlob::MakeFromString("Hello", sk_font);
2929  ASSERT_NE(blob, nullptr);
2930  auto frame = MakeTextFrameFromTextBlobSkia(blob);
2931  canvas.DrawTextFrame(frame, Point(), text_paint);
2932 
2933  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2934 }
2935 
2936 TEST_P(AiksTest, MatrixSaveLayerFilter) {
2937  Canvas canvas;
2938  canvas.DrawPaint({.color = Color::Black()});
2939  canvas.SaveLayer({}, std::nullopt);
2940  {
2941  canvas.DrawCircle(Point(200, 200), 100,
2942  {.color = Color::Green().WithAlpha(0.5),
2943  .blend_mode = BlendMode::kPlus});
2944  // Should render a second circle, centered on the bottom-right-most edge of
2945  // the circle.
2946  canvas.SaveLayer({.image_filter = ImageFilter::MakeMatrix(
2948  (200 + 100 * k1OverSqrt2)) *
2949  Matrix::MakeScale(Vector2(1, 1) * 0.5) *
2950  Matrix::MakeTranslation(Vector2(-200, -200)),
2951  SamplerDescriptor{})},
2952  std::nullopt);
2953  canvas.DrawCircle(Point(200, 200), 100,
2954  {.color = Color::Green().WithAlpha(0.5),
2955  .blend_mode = BlendMode::kPlus});
2956  canvas.Restore();
2957  }
2958  canvas.Restore();
2959 
2960  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2961 }
2962 
2963 TEST_P(AiksTest, MatrixBackdropFilter) {
2964  Canvas canvas;
2965  canvas.DrawPaint({.color = Color::Black()});
2966  canvas.SaveLayer({}, std::nullopt);
2967  {
2968  canvas.DrawCircle(Point(200, 200), 100,
2969  {.color = Color::Green().WithAlpha(0.5),
2970  .blend_mode = BlendMode::kPlus});
2971  // Should render a second circle, centered on the bottom-right-most edge of
2972  // the circle.
2973  canvas.SaveLayer(
2974  {}, std::nullopt,
2976  Matrix::MakeTranslation(Vector2(1, 1) * (100 + 100 * k1OverSqrt2)) *
2977  Matrix::MakeScale(Vector2(1, 1) * 0.5) *
2978  Matrix::MakeTranslation(Vector2(-100, -100)),
2979  SamplerDescriptor{}));
2980  canvas.Restore();
2981  }
2982  canvas.Restore();
2983 
2984  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2985 }
2986 
2987 TEST_P(AiksTest, SolidColorApplyColorFilter) {
2988  auto contents = SolidColorContents();
2989  contents.SetColor(Color::CornflowerBlue().WithAlpha(0.75));
2990  auto result = contents.ApplyColorFilter([](const Color& color) {
2991  return color.Blend(Color::LimeGreen().WithAlpha(0.75), BlendMode::kScreen);
2992  });
2993  ASSERT_TRUE(result);
2994  ASSERT_COLOR_NEAR(contents.GetColor(),
2995  Color(0.424452, 0.828743, 0.79105, 0.9375));
2996 }
2997 
2998 TEST_P(AiksTest, DrawScaledTextWithPerspectiveNoSaveLayer) {
2999  Canvas canvas;
3000  canvas.Transform(Matrix(1.0, 0.0, 0.0, 0.0, //
3001  0.0, 1.0, 0.0, 0.0, //
3002  0.0, 0.0, 1.0, 0.01, //
3003  0.0, 0.0, 0.0, 1.0) * //
3005 
3006  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
3007  "Roboto-Regular.ttf"));
3008 
3009  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3010 }
3011 
3012 TEST_P(AiksTest, DrawScaledTextWithPerspectiveSaveLayer) {
3013  Canvas canvas;
3014  Paint save_paint;
3015  canvas.SaveLayer(save_paint);
3016  canvas.Transform(Matrix(1.0, 0.0, 0.0, 0.0, //
3017  0.0, 1.0, 0.0, 0.0, //
3018  0.0, 0.0, 1.0, 0.01, //
3019  0.0, 0.0, 0.0, 1.0) * //
3021 
3022  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), canvas, "Hello world",
3023  "Roboto-Regular.ttf"));
3024  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3025 }
3026 
3027 TEST_P(AiksTest, PipelineBlendSingleParameter) {
3028  Canvas canvas;
3029 
3030  // Should render a green square in the middle of a blue circle.
3031  canvas.SaveLayer({});
3032  {
3033  canvas.Translate(Point(100, 100));
3034  canvas.DrawCircle(Point(200, 200), 200, {.color = Color::Blue()});
3035  canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
3036  canvas.DrawCircle(Point(200, 200), 200,
3037  {
3038  .color = Color::Green(),
3039  .blend_mode = BlendMode::kSourceOver,
3040  .image_filter = ImageFilter::MakeFromColorFilter(
3042  Color::White())),
3043  });
3044  canvas.Restore();
3045  }
3046 
3047  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3048 }
3049 
3051  auto capture_context = CaptureContext::MakeAllowlist({"TestDocument"});
3052 
3053  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
3054  Canvas canvas;
3055 
3056  capture_context.Rewind();
3057  auto document = capture_context.GetDocument("TestDocument");
3058 
3059  auto color = document.AddColor("Background color", Color::CornflowerBlue());
3060  canvas.DrawPaint({.color = color});
3061 
3062  if (AiksTest::ImGuiBegin("TestDocument", nullptr,
3063  ImGuiWindowFlags_AlwaysAutoResize)) {
3064  document.GetElement()->properties.Iterate([](CaptureProperty& property) {
3065  property.Invoke({.color = [](CaptureColorProperty& p) {
3066  ImGui::ColorEdit4(p.label.c_str(),
3067  reinterpret_cast<float*>(&p.value));
3068  }});
3069  });
3070  ImGui::End();
3071  }
3072 
3073  return canvas.EndRecordingAsPicture();
3074  };
3075  OpenPlaygroundHere(callback);
3076 }
3077 
3078 TEST_P(AiksTest, CaptureInactivatedByDefault) {
3079  ASSERT_FALSE(GetContext()->capture.IsActive());
3080 }
3081 
3082 // Regression test for https://github.com/flutter/flutter/issues/134678.
3083 TEST_P(AiksTest, ReleasesTextureOnTeardown) {
3084  auto context = MakeContext();
3085  std::weak_ptr<Texture> weak_texture;
3086 
3087  {
3088  auto texture = CreateTextureForFixture("table_mountain_nx.png");
3089 
3090  Canvas canvas;
3091  canvas.Scale(GetContentScale());
3092  canvas.Translate({100.0f, 100.0f, 0});
3093 
3094  Paint paint;
3097  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
3098 
3099  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3100  }
3101 
3102  // See https://github.com/flutter/flutter/issues/134751.
3103  //
3104  // If the fence waiter was working this may not be released by the end of the
3105  // scope above. Adding a manual shutdown so that future changes to the fence
3106  // waiter will not flake this test.
3107  context->Shutdown();
3108 
3109  // The texture should be released by now.
3110  ASSERT_TRUE(weak_texture.expired()) << "When the texture is no longer in use "
3111  "by the backend, it should be "
3112  "released.";
3113 }
3114 
3115 // Regression test for https://github.com/flutter/flutter/issues/135441 .
3116 TEST_P(AiksTest, VerticesGeometryUVPositionData) {
3117  Canvas canvas;
3118  Paint paint;
3119  auto texture = CreateTextureForFixture("table_mountain_nx.png");
3120 
3122  Entity::TileMode::kClamp, {}, {});
3123 
3124  auto vertices = {Point(0, 0), Point(texture->GetSize().width, 0),
3125  Point(0, texture->GetSize().height)};
3126  std::vector<uint16_t> indices = {0u, 1u, 2u};
3127  std::vector<Point> texture_coordinates = {};
3128  std::vector<Color> vertex_colors = {};
3129  auto geometry = std::make_shared<VerticesGeometry>(
3130  vertices, indices, texture_coordinates, vertex_colors,
3132 
3133  canvas.DrawVertices(geometry, BlendMode::kSourceOver, paint);
3134  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3135 }
3136 
3137 // Regression test for https://github.com/flutter/flutter/issues/135441 .
3138 TEST_P(AiksTest, VerticesGeometryUVPositionDataWithTranslate) {
3139  Canvas canvas;
3140  Paint paint;
3141  auto texture = CreateTextureForFixture("table_mountain_nx.png");
3142 
3145  Matrix::MakeTranslation({100.0, 100.0}));
3146 
3147  auto vertices = {Point(0, 0), Point(texture->GetSize().width, 0),
3148  Point(0, texture->GetSize().height)};
3149  std::vector<uint16_t> indices = {0u, 1u, 2u};
3150  std::vector<Point> texture_coordinates = {};
3151  std::vector<Color> vertex_colors = {};
3152  auto geometry = std::make_shared<VerticesGeometry>(
3153  vertices, indices, texture_coordinates, vertex_colors,
3155 
3156  canvas.DrawVertices(geometry, BlendMode::kSourceOver, paint);
3157  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3158 }
3159 
3160 TEST_P(AiksTest, ClearBlend) {
3161  Canvas canvas;
3162  Paint white;
3163  white.color = Color::Blue();
3164  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600.0, 600.0), white);
3165 
3166  Paint clear;
3167  clear.blend_mode = BlendMode::kClear;
3168 
3169  canvas.DrawCircle(Point::MakeXY(300.0, 300.0), 200.0, clear);
3170 }
3171 
3172 TEST_P(AiksTest, MatrixImageFilterMagnify) {
3173  Canvas canvas;
3174  canvas.Scale(GetContentScale());
3175  auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
3176  canvas.Translate({600, -200});
3177  canvas.SaveLayer({
3178  .image_filter = std::make_shared<MatrixImageFilter>(
3179  Matrix::MakeScale({2, 2, 2}), SamplerDescriptor{}),
3180  });
3181  canvas.DrawImage(image, {0, 0},
3182  Paint{.color = Color::White().WithAlpha(0.5)});
3183  canvas.Restore();
3184 
3185  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3186 }
3187 
3188 // Render a white circle at the top left corner of the screen.
3189 TEST_P(AiksTest, MatrixImageFilterDoesntCullWhenTranslatedFromOffscreen) {
3190  Canvas canvas;
3191  canvas.Scale(GetContentScale());
3192  canvas.Translate({100, 100});
3193  // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
3194  // +300 translation applied by a SaveLayer image filter.
3195  canvas.SaveLayer({
3196  .image_filter = std::make_shared<MatrixImageFilter>(
3198  });
3199  canvas.DrawCircle({-300, 0}, 100, {.color = Color::Green()});
3200  canvas.Restore();
3201 
3202  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3203 }
3204 
3205 // Render a white circle at the top left corner of the screen.
3207  MatrixImageFilterDoesntCullWhenScaledAndTranslatedFromOffscreen) {
3208  Canvas canvas;
3209  canvas.Scale(GetContentScale());
3210  canvas.Translate({100, 100});
3211  // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
3212  // +300 translation applied by a SaveLayer image filter.
3213  canvas.SaveLayer({
3214  .image_filter = std::make_shared<MatrixImageFilter>(
3215  Matrix::MakeTranslation({300, 0}) * Matrix::MakeScale({2, 2, 2}),
3216  SamplerDescriptor{}),
3217  });
3218  canvas.DrawCircle({-150, 0}, 50, {.color = Color::Green()});
3219  canvas.Restore();
3220 
3221  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3222 }
3223 
3224 // This should be solid red, if you see a little red box this is broken.
3225 TEST_P(AiksTest, ClearColorOptimizationWhenSubpassIsBiggerThanParentPass) {
3226  SetWindowSize({400, 400});
3227  Canvas canvas;
3228  canvas.Scale(GetContentScale());
3229  canvas.DrawRect(Rect::MakeLTRB(200, 200, 300, 300), {.color = Color::Red()});
3230  canvas.SaveLayer({
3231  .image_filter = std::make_shared<MatrixImageFilter>(
3232  Matrix::MakeScale({2, 2, 1}), SamplerDescriptor{}),
3233  });
3234  // Draw a rectangle that would fully cover the parent pass size, but not
3235  // the subpass that it is rendered in.
3236  canvas.DrawRect(Rect::MakeLTRB(0, 0, 400, 400), {.color = Color::Green()});
3237  // Draw a bigger rectangle to force the subpass to be bigger.
3238  canvas.DrawRect(Rect::MakeLTRB(0, 0, 800, 800), {.color = Color::Red()});
3239  canvas.Restore();
3240 
3241  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3242 }
3243 
3244 TEST_P(AiksTest, EmptySaveLayerIgnoresPaint) {
3245  Canvas canvas;
3246  canvas.Scale(GetContentScale());
3247  canvas.DrawPaint(Paint{.color = Color::Red()});
3248  canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
3249  canvas.SaveLayer(Paint{.color = Color::Blue()});
3250  canvas.Restore();
3251  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3252 }
3253 
3254 TEST_P(AiksTest, EmptySaveLayerRendersWithClear) {
3255  Canvas canvas;
3256  canvas.Scale(GetContentScale());
3257  auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
3258  canvas.DrawImage(image, {10, 10}, {});
3259  canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
3261  canvas.Restore();
3262  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3263 }
3264 
3265 TEST_P(AiksTest, SubpassWithClearColorOptimization) {
3266  Canvas canvas;
3267 
3268  // Use a non-srcOver blend mode to ensure that we don't detect this as an
3269  // opacity peephole optimization.
3270  canvas.SaveLayer(
3271  {.color = Color::Blue().WithAlpha(0.5), .blend_mode = BlendMode::kSource},
3272  Rect::MakeLTRB(0, 0, 200, 200));
3273  canvas.DrawPaint(
3274  {.color = Color::BlackTransparent(), .blend_mode = BlendMode::kSource});
3275  canvas.Restore();
3276 
3277  canvas.SaveLayer(
3278  {.color = Color::Blue(), .blend_mode = BlendMode::kDestinationOver});
3279  canvas.Restore();
3280 
3281  // This playground should appear blank on CI since we are only drawing
3282  // transparent black. If the clear color optimization is broken, the texture
3283  // will be filled with NaNs and may produce a magenta texture on macOS or iOS.
3284  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3285 }
3286 
3287 TEST_P(AiksTest, ImageColorSourceEffectTransform) {
3288  // Compare with https://fiddle.skia.org/c/6cdc5aefb291fda3833b806ca347a885
3289 
3290  Canvas canvas;
3291  auto texture = CreateTextureForFixture("monkey.png");
3292 
3293  canvas.DrawPaint({.color = Color::White()});
3294 
3295  // Translation
3296  {
3297  Paint paint;
3300  Matrix::MakeTranslation({50, 50}));
3301  canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint);
3302  }
3303 
3304  // Rotation/skew
3305  {
3306  canvas.Save();
3307  canvas.Rotate(Degrees(45));
3308  Paint paint;
3311  Matrix(1, -1, 0, 0, //
3312  1, 1, 0, 0, //
3313  0, 0, 1, 0, //
3314  0, 0, 0, 1) //
3315  );
3316  canvas.DrawRect(Rect::MakeLTRB(100, 0, 200, 100), paint);
3317  canvas.Restore();
3318  }
3319 
3320  // Scale
3321  {
3322  canvas.Translate(Vector2(100, 0));
3323  canvas.Scale(Vector2(100, 100));
3324  Paint paint;
3327  Matrix::MakeScale(Vector2(0.005, 0.005)));
3328  canvas.DrawRect(Rect::MakeLTRB(0, 0, 1, 1), paint);
3329  }
3330 
3331  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3332 }
3333 
3334 TEST_P(AiksTest, CorrectClipDepthAssignedToEntities) {
3335  Canvas canvas; // Depth 1 (base pass)
3336  canvas.DrawRRect(Rect::MakeLTRB(0, 0, 100, 100), {10, 10}, {}); // Depth 2
3337  canvas.Save();
3338  {
3339  canvas.ClipRRect(Rect::MakeLTRB(0, 0, 50, 50), {10, 10}, {}); // Depth 4
3340  canvas.SaveLayer({}); // Depth 4
3341  {
3342  canvas.DrawRRect(Rect::MakeLTRB(0, 0, 50, 50), {10, 10}, {}); // Depth 3
3343  }
3344  canvas.Restore(); // Restore the savelayer.
3345  }
3346  canvas.Restore(); // Depth 5 -- this will no longer append a restore entity
3347  // once we switch to the clip depth approach.
3348 
3349  auto picture = canvas.EndRecordingAsPicture();
3350 
3351  std::vector<uint32_t> expected = {
3352  2, // DrawRRect
3353  4, // ClipRRect -- Has a depth value equal to the max depth of all the
3354  // content it affect. In this case, the SaveLayer and all
3355  // its contents are affected.
3356  4, // SaveLayer -- The SaveLayer is drawn to the parent pass after its
3357  // contents are rendered, so it should have a depth value
3358  // greater than all its contents.
3359  3, // DrawRRect
3360  5, // Restore (no longer necessary when clipping on the depth buffer)
3361  };
3362 
3363  std::vector<uint32_t> actual;
3364 
3365  picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool {
3366  if (auto* subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
3367  actual.push_back(subpass->get()->GetNewClipDepth());
3368  }
3369  if (Entity* entity = std::get_if<Entity>(&element)) {
3370  actual.push_back(entity->GetNewClipDepth());
3371  }
3372  return true;
3373  });
3374 
3375  ASSERT_EQ(actual.size(), expected.size());
3376  for (size_t i = 0; i < expected.size(); i++) {
3377  EXPECT_EQ(expected[i], actual[i]) << "Index: " << i;
3378  }
3379 }
3380 
3381 TEST_P(AiksTest, CanDrawPerspectiveTransformWithClips) {
3382  // Avoiding `GetSecondsElapsed()` to reduce risk of golden flakiness.
3383  int time = 0;
3384  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
3385  Canvas canvas;
3386 
3387  canvas.Save();
3388  {
3389  canvas.Translate({300, 300});
3390 
3391  // 1. Draw/restore a clip before drawing the image, which will get drawn
3392  // to the depth buffer behind the image.
3393  canvas.Save();
3394  {
3395  canvas.DrawPaint({.color = Color::Green()});
3396  canvas.ClipRect(Rect::MakeLTRB(-180, -180, 180, 180),
3398  canvas.DrawPaint({.color = Color::Black()});
3399  }
3400  canvas.Restore(); // Restore rectangle difference clip.
3401 
3402  canvas.Save();
3403  {
3404  // 2. Draw an oval clip that applies to the image, which will get drawn
3405  // in front of the image on the depth buffer.
3406  canvas.ClipOval(Rect::MakeLTRB(-200, -200, 200, 200));
3407 
3408  // 3. Draw the rotating image with a perspective transform.
3409  canvas.Transform(
3410  Matrix(1.0, 0.0, 0.0, 0.0, //
3411  0.0, 1.0, 0.0, 0.0, //
3412  0.0, 0.0, 1.0, 0.003, //
3413  0.0, 0.0, 0.0, 1.0) * //
3414  Matrix::MakeRotationY({Radians{-1.0f + (time++ / 60.0f)}}));
3415  auto image =
3416  std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
3417  canvas.DrawImage(image, -Point(image->GetSize()) / 2, {});
3418  }
3419  canvas.Restore(); // Restore oval intersect clip.
3420 
3421  // 4. Draw a semi-translucent blue circle atop all previous draws.
3422  canvas.DrawCircle({}, 230, {.color = Color::Blue().WithAlpha(0.4)});
3423  }
3424  canvas.Restore(); // Restore translation.
3425 
3426  return canvas.EndRecordingAsPicture();
3427  };
3428  ASSERT_TRUE(OpenPlaygroundHere(callback));
3429 }
3430 
3431 TEST_P(AiksTest, CanRenderClippedBackdropFilter) {
3432  Canvas canvas;
3433  Paint paint;
3434 
3435  canvas.Scale(GetContentScale());
3436 
3437  // Draw something interesting in the background.
3438  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
3439  Color{0.1294, 0.5882, 0.9529, 1.0}};
3440  std::vector<Scalar> stops = {
3441  0.0,
3442  1.0,
3443  };
3445  {0, 0}, {100, 100}, std::move(colors), std::move(stops),
3447  canvas.DrawPaint(paint);
3448 
3449  Rect clip_rect = Rect::MakeLTRB(50, 50, 400, 300);
3450 
3451  // Draw a clipped SaveLayer, where the clip coverage and SaveLayer size are
3452  // the same.
3453  canvas.ClipRRect(clip_rect, Size(100, 100),
3455  canvas.SaveLayer({}, clip_rect,
3458  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3459 }
3460 
3461 } // namespace testing
3462 } // namespace impeller
3463 
3464 // █████████████████████████████████████████████████████████████████████████████
3465 // █ NOTICE: Before adding new tests to this file consider adding it to one of
3466 // █ the subdivisions of AiksTest to avoid having one massive file.
3467 // █
3468 // █ Subdivisions:
3469 // █ - aiks_blur_unittests.cc
3470 // █ - aiks_gradient_unittests.cc
3471 // █ - aiks_path_unittests.cc
3472 // █████████████████████████████████████████████████████████████████████████████
BLEND_MODE_TEST
#define BLEND_MODE_TEST(blend_mode)
Definition: aiks_unittests.cc:2346
impeller::SolidColorContents::Make
static std::unique_ptr< SolidColorContents > Make(const Path &path, Color color)
Definition: solid_color_contents.cc:69
impeller::Canvas::DrawPoints
void DrawPoints(std::vector< Point > points, Scalar radius, const Paint &paint, PointStyle point_style)
Definition: canvas.cc:683
impeller::Color::Blue
static constexpr Color Blue()
Definition: color.h:268
typeface_stb.h
impeller::Entity::ClipOperation::kIntersect
@ kIntersect
path.h
impeller::AiksPlayground
Definition: aiks_playground.h:18
ASSERT_COLOR_NEAR
#define ASSERT_COLOR_NEAR(a, b)
Definition: geometry_asserts.h:159
impeller::Entity::TileMode::kClamp
@ kClamp
impeller::k1OverSqrt2
constexpr float k1OverSqrt2
Definition: constants.h:50
impeller::Canvas::EndRecordingAsPicture
Picture EndRecordingAsPicture()
Definition: canvas.cc:756
impeller::Picture::pass
std::unique_ptr< EntityPass > pass
Definition: picture.h:21
impeller::Canvas::ClipOval
void ClipOval(const Rect &bounds, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:570
capture.h
impeller::PathBuilder::AddQuadraticCurve
PathBuilder & AddQuadraticCurve(Point p1, Point cp, Point p2)
Move to point p1, then insert a quadradic curve from p1 to p2 with the control point cp.
Definition: path_builder.cc:97
impeller::Canvas::DrawRRect
void DrawRRect(const Rect &rect, const Size &corner_radii, const Paint &paint)
Definition: canvas.cc:488
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::Scalar
float Scalar
Definition: scalar.h:18
image_filter.h
impeller::AiksContext
Definition: aiks_context.h:20
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:717
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:264
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:49
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:45
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:272
impeller::BlendMode
BlendMode
Definition: color.h:59
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:709
impeller::BlendMode::kLuminosity
@ kLuminosity
impeller::Paint::color
Color color
Definition: paint.h:55
paint_pass_delegate.h
impeller::Entity::TileMode::kDecal
@ kDecal
impeller::BlendMode::kSource
@ kSource
impeller::BlendMode::kColorDodge
@ kColorDodge
impeller::Canvas::DrawTextFrame
void DrawTextFrame(const std::shared_ptr< TextFrame > &text_frame, Point position, const Paint &paint)
Definition: canvas.cc:821
impeller::BlendMode::kDestination
@ kDestination
impeller::Color::Purple
static constexpr Color Purple()
Definition: color.h:734
impeller::Canvas
Definition: canvas.h:58
impeller::PathBuilder
Definition: path_builder.h:14
impeller::Paint::MaskBlurDescriptor::style
FilterContents::BlurStyle style
Definition: paint.h:40
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:711
impeller::Vector2
Point Vector2
Definition: point.h:320
impeller::Canvas::DrawVertices
void DrawVertices(const std::shared_ptr< VerticesGeometry > &vertices, BlendMode blend_mode, const Paint &paint)
Definition: canvas.cc:865
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:195
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
BLEND_MODE_TUPLE
#define BLEND_MODE_TUPLE(blend_mode)
Definition: aiks_unittests.cc:1033
impeller::Paint::MaskBlurDescriptor
Definition: paint.h:39
impeller::Color::CornflowerBlue
static constexpr Color CornflowerBlue()
Definition: color.h:334
impeller::testing::TextRenderOptions
Definition: aiks_unittests.cc:707
impeller::Canvas::DebugOptions::offscreen_texture_checkerboard
bool offscreen_texture_checkerboard
Definition: canvas.h:65
impeller::PathBuilder::AddRoundedRect
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
Definition: path_builder.cc:145
impeller::Color::Yellow
static constexpr Color Yellow()
Definition: color.h:834
impeller::Radians::radians
Scalar radians
Definition: scalar.h:39
impeller::Color::Fuchsia
static constexpr Color Fuchsia()
Definition: color.h:458
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:163
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
impeller::Canvas::debug_options
struct impeller::Canvas::DebugOptions debug_options
impeller::FilterContents::BlurStyle::kNormal
@ kNormal
Blurred inside and outside.
impeller::PathBuilder::SetConvexity
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:80
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:56
impeller::PlaygroundBackendToRuntimeStageBackend
constexpr RuntimeStageBackend PlaygroundBackendToRuntimeStageBackend(PlaygroundBackend backend)
Definition: playground.h:35
impeller::Vector3::x
Scalar x
Definition: vector.h:23
stroke_width
const Scalar stroke_width
Definition: stroke_path_geometry.cc:293
impeller::Canvas::DrawRect
void DrawRect(const Rect &rect, const Paint &paint)
Definition: canvas.cc:441
impeller::Canvas::GetCurrentTransform
const Matrix & GetCurrentTransform() const
Definition: canvas.cc:247
impeller::BlendMode::kModulate
@ kModulate
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:112
impeller::Color::Maroon
static constexpr Color Maroon()
Definition: color.h:606
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:708
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:751
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:32
impeller::Canvas::SaveLayer
void SaveLayer(const Paint &paint, std::optional< Rect > bounds=std::nullopt, const std::shared_ptr< ImageFilter > &backdrop_filter=nullptr, ContentBoundsPromise bounds_promise=ContentBoundsPromise::kUnknown)
Definition: canvas.cc:786
impeller::Matrix::MakeLookAt
static constexpr Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:497
typographer_context_skia.h
impeller::Canvas::DrawImage
void DrawImage(const std::shared_ptr< Image > &image, Point offset, const Paint &paint, SamplerDescriptor sampler={})
Definition: canvas.cc:703
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:471
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
impeller::Entity::SetContents
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:93
impeller::BlendMode::kDifference
@ kDifference
path_builder.h
impeller::kPhi
constexpr float kPhi
Definition: constants.h:53
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:21
impeller::saturated::distance
SI distance
Definition: saturated_math.h:57
impeller::BlendMode::kLighten
@ kLighten
text_frame_skia.h
impeller::Paint::color_filter
std::shared_ptr< ColorFilter > color_filter
Definition: paint.h:68
impeller::Picture
Definition: picture.h:20
impeller::TSize< Scalar >
impeller::PointStyle::kSquare
@ kSquare
Points are drawn as circles.
impeller::Point
TPoint< Scalar > Point
Definition: point.h:316
impeller::k2Pi
constexpr float k2Pi
Definition: constants.h:29
impeller::CaptureContext::MakeAllowlist
static CaptureContext MakeAllowlist(std::initializer_list< std::string > allowlist)
Definition: capture.cc:159
impeller::CaptureContext
Definition: capture.h:269
impeller::Canvas::Scale
void Scale(const Vector2 &scale)
Definition: canvas.cc:264
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:306
impeller::ColorFilter::MakeBlend
static std::shared_ptr< ColorFilter > MakeBlend(BlendMode blend_mode, Color color)
Definition: color_filter.cc:23
widgets.h
impeller::Canvas::Save
void Save()
Definition: canvas.cc:136
impeller::testing::GetBlendModeSelection
static BlendModeSelection GetBlendModeSelection()
Definition: aiks_unittests.cc:1040
impeller::Picture::Snapshot
std::optional< Snapshot > Snapshot(AiksContext &context)
Definition: picture.cc:17
impeller::BlendMode::kClear
@ kClear
text_frame_stb.h
impeller::Paint::style
Style style
Definition: paint.h:63
impeller::Color::DarkBlue
static constexpr Color DarkBlue()
Definition: color.h:350
impeller::EntityPass::Element
std::variant< Entity, std::unique_ptr< EntityPass > > Element
Definition: entity_pass.h:54
impeller::VerticesGeometry::VertexMode::kTriangleStrip
@ kTriangleStrip
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:270
impeller::Entity::kLastAdvancedBlendMode
static constexpr BlendMode kLastAdvancedBlendMode
Definition: entity.h:24
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:21
impeller::Color::SkyBlue
static constexpr Color SkyBlue()
Definition: color.h:774
impeller::TRect< Scalar >::MakeOriginSize
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:140
impeller::Canvas::DrawCircle
void DrawCircle(const Point &center, Scalar radius, const Paint &paint)
Definition: canvas.cc:515
impeller::Color::LightCoral
static constexpr Color LightCoral()
Definition: color.h:534
impeller::TypographerContextSTB::Make
static std::unique_ptr< TypographerContext > Make()
Definition: typographer_context_stb.cc:29
impeller::Entity::GetContents
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:97
impeller::Color::White
static constexpr Color White()
Definition: color.h:256
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
bool Restore()
Definition: canvas.cc:208
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::Canvas::DrawAtlas
void DrawAtlas(const std::shared_ptr< Image > &atlas, std::vector< Matrix > transforms, std::vector< Rect > texture_coordinates, std::vector< Color > colors, BlendMode blend_mode, SamplerDescriptor sampler, std::optional< Rect > cull_rect, const Paint &paint)
Definition: canvas.cc:925
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:419
canvas.h
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:266
impeller::Canvas::DrawPath
void DrawPath(const Path &path, const Paint &paint)
Definition: canvas.cc:292
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:302
impeller::Canvas::GetSaveCount
size_t GetSaveCount() const
Definition: canvas.cc:280
ASSERT_MATRIX_NEAR
#define ASSERT_MATRIX_NEAR(a, b)
Definition: geometry_asserts.h:156
impeller::testing::TextRenderOptions::position
Point position
Definition: aiks_unittests.cc:710
impeller::testing::BlendModeSelection
Definition: aiks_unittests.cc:1035
impeller::Entity::TileMode
TileMode
Definition: entity.h:42
impeller::testing::BlendModeTest
static Picture BlendModeTest(Vector2 content_scale, BlendMode blend_mode, const std::shared_ptr< Image > &src_image, const std::shared_ptr< Image > &dst_image)
Definition: aiks_unittests.cc:2233
IMPELLER_FOR_EACH_BLEND_MODE
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:19
impeller::MakeTextFrameFromTextBlobSkia
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
Definition: text_frame_skia.cc:41
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:108
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:590
impeller::Color::AntiqueWhite
static constexpr Color AntiqueWhite()
Definition: color.h:278
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:638
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:193
impeller::Canvas::DrawOval
void DrawOval(const Rect &rect, const Paint &paint)
Definition: canvas.cc:461
impeller::BlendMode::kExclusion
@ kExclusion
impeller::CaptureProperty
A capturable property type.
Definition: capture.h:68
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:714
impeller::PlaygroundBackend::kOpenGLES
@ kOpenGLES
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:209
impeller::Canvas::Rotate
void Rotate(Radians radians)
Definition: canvas.cc:276
image.h
impeller::ColorFilter::MakeLinearToSrgb
static std::shared_ptr< ColorFilter > MakeLinearToSrgb()
Definition: color_filter.cc:36
impeller::Color::Orange
static constexpr Color Orange()
Definition: color.h:682
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
snapshot.h
impeller::testing::TEST_P
TEST_P(AiksTest, CanRenderMaskBlurHugeSigma)
Definition: aiks_blur_unittests.cc:23
rect.h
impeller::Entity::Clone
Entity Clone() const
Definition: entity.cc:210
impeller::TPoint< Scalar >
impeller::Canvas::Transform
void Transform(const Matrix &transform)
Definition: canvas.cc:243
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:262
impeller::testing::kFontFixture
static constexpr std::string_view kFontFixture
Definition: aiks_unittests.cc:857
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:258
impeller::Paint::invert_colors
bool invert_colors
Definition: paint.h:65
impeller::PathBuilder::RoundingRadii::bottom_left
Point bottom_left
Definition: path_builder.h:107
impeller::testing::BlendModeSelection::blend_mode_values
std::vector< BlendMode > blend_mode_values
Definition: aiks_unittests.cc:1037
impeller::BlendMode::kScreen
@ kScreen
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:59
impeller::Degrees
Definition: scalar.h:46
color.h
impeller::Paint::HasColorFilter
bool HasColorFilter() const
Whether this paint has a color filter that can apply opacity.
Definition: paint.cc:227
impeller::Color::LimeGreen
static constexpr Color LimeGreen()
Definition: color.h:594
color_filter.h
impeller::PathBuilder::AddCircle
PathBuilder & AddCircle(const Point &center, Scalar radius)
Definition: path_builder.cc:130
typographer_context_stb.h
impeller::BlendMode::kHue
@ kHue
impeller::Canvas::ClipRect
void ClipRect(const Rect &rect, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:549
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
offset
Point offset
Definition: stroke_path_geometry.cc:300
impeller::ColorFilter::MakeSrgbToLinear
static std::shared_ptr< ColorFilter > MakeSrgbToLinear()
Definition: color_filter.cc:32
impeller::testing::BlendModeSelection::blend_mode_names
std::vector< const char * > blend_mode_names
Definition: aiks_unittests.cc:1036
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
render_target_cache.h
impeller::TSize< Scalar >::MakeWH
static constexpr TSize MakeWH(Type width, Type height)
Definition: size.h:34
impeller
Definition: aiks_blur_unittests.cc:20
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:69
impeller::BlendMode::kMultiply
@ kMultiply
impeller::Paint::stroke_width
Scalar stroke_width
Definition: paint.h:59
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:64
impeller::Color::Crimson
static constexpr Color Crimson()
Definition: color.h:342
impeller::Paint::image_filter
std::shared_ptr< ImageFilter > image_filter
Definition: paint.h:67
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:260
impeller::Canvas::ClipPath
void ClipPath(const Path &path, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:539
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