Flutter Impeller
aiks_blur_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 "impeller/aiks/canvas.h"
12 #include "impeller/renderer/testing/mocks.h"
13 #include "third_party/imgui/imgui.h"
14 
15 ////////////////////////////////////////////////////////////////////////////////
16 // This is for tests of Canvas that are interested the results of rendering
17 // blurs.
18 ////////////////////////////////////////////////////////////////////////////////
19 
20 namespace impeller {
21 namespace testing {
22 
23 TEST_P(AiksTest, CanRenderMaskBlurHugeSigma) {
24  Canvas canvas;
25  canvas.DrawCircle({400, 400}, 300,
26  {.color = Color::Green(),
27  .mask_blur_descriptor = Paint::MaskBlurDescriptor{
29  .sigma = Sigma(99999),
30  }});
31  canvas.Restore();
32 
33  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
34 }
35 
36 TEST_P(AiksTest, CanRenderForegroundBlendWithMaskBlur) {
37  // This case triggers the ForegroundPorterDuffBlend path. The color filter
38  // should apply to the color only, and respect the alpha mask.
39  Canvas canvas;
40  canvas.ClipRect(Rect::MakeXYWH(100, 150, 400, 400));
41  canvas.DrawCircle({400, 400}, 200,
42  {
43  .color = Color::White(),
44  .color_filter = ColorFilter::MakeBlend(
46  .mask_blur_descriptor =
49  .sigma = Radius(20),
50  },
51  });
52  canvas.Restore();
53 
54  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
55 }
56 
57 TEST_P(AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur) {
58  // This case triggers the ForegroundAdvancedBlend path. The color filter
59  // should apply to the color only, and respect the alpha mask.
60  Canvas canvas;
61  canvas.ClipRect(Rect::MakeXYWH(100, 150, 400, 400));
62  canvas.DrawCircle({400, 400}, 200,
63  {
64  .color = Color::Grey(),
65  .color_filter = ColorFilter::MakeBlend(
67  .mask_blur_descriptor =
70  .sigma = Radius(20),
71  },
72  });
73  canvas.Restore();
74 
75  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
76 }
77 
78 TEST_P(AiksTest, CanRenderBackdropBlurInteractive) {
79  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
80  static PlaygroundPoint point_a(Point(50, 50), 30, Color::White());
81  static PlaygroundPoint point_b(Point(300, 200), 30, Color::White());
82  auto [a, b] = DrawPlaygroundLine(point_a, point_b);
83 
84  Canvas canvas;
85  canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
86  canvas.DrawCircle({300, 200}, 100, {.color = Color::GreenYellow()});
87  canvas.DrawCircle({140, 170}, 75, {.color = Color::DarkMagenta()});
88  canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()});
89  canvas.ClipRRect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), {20, 20});
90  canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
91  ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
94  canvas.Restore();
95 
96  return canvas.EndRecordingAsPicture();
97  };
98 
99  ASSERT_TRUE(OpenPlaygroundHere(callback));
100 }
101 
102 TEST_P(AiksTest, CanRenderBackdropBlur) {
103  Canvas canvas;
104  canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
105  canvas.DrawCircle({300, 200}, 100, {.color = Color::GreenYellow()});
106  canvas.DrawCircle({140, 170}, 75, {.color = Color::DarkMagenta()});
107  canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()});
108  canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), {20, 20});
109  canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
110  ImageFilter::MakeBlur(Sigma(30.0), Sigma(30.0),
113  canvas.Restore();
114 
115  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
116 }
117 
118 TEST_P(AiksTest, CanRenderBackdropBlurHugeSigma) {
119  Canvas canvas;
120  canvas.DrawCircle({400, 400}, 300, {.color = Color::Green()});
121  canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
122  ImageFilter::MakeBlur(Sigma(999999), Sigma(999999),
125  canvas.Restore();
126 
127  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
128 }
129 
130 TEST_P(AiksTest, CanRenderClippedBlur) {
131  Canvas canvas;
132  canvas.ClipRect(Rect::MakeXYWH(100, 150, 400, 400));
133  canvas.DrawCircle(
134  {400, 400}, 200,
135  {
136  .color = Color::Green(),
137  .image_filter = ImageFilter::MakeBlur(
140  });
141  canvas.Restore();
142 
143  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
144 }
145 
146 TEST_P(AiksTest, ClippedBlurFilterRendersCorrectlyInteractive) {
147  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
148  static PlaygroundPoint playground_point(Point(400, 400), 20,
149  Color::Green());
150  auto point = DrawPlaygroundPoint(playground_point);
151 
152  Canvas canvas;
153  canvas.Translate(point - Point(400, 400));
154  Paint paint;
157  .sigma = Radius{120 * 3},
158  };
159  paint.color = Color::Red();
160  PathBuilder builder{};
161  builder.AddRect(Rect::MakeLTRB(0, 0, 800, 800));
162  canvas.DrawPath(builder.TakePath(), paint);
163  return canvas.EndRecordingAsPicture();
164  };
165  ASSERT_TRUE(OpenPlaygroundHere(callback));
166 }
167 
168 TEST_P(AiksTest, ClippedBlurFilterRendersCorrectly) {
169  Canvas canvas;
170  canvas.Translate(Point(0, -400));
171  Paint paint;
174  .sigma = Radius{120 * 3},
175  };
176  paint.color = Color::Red();
177  PathBuilder builder{};
178  builder.AddRect(Rect::MakeLTRB(0, 0, 800, 800));
179  canvas.DrawPath(builder.TakePath(), paint);
180  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
181 }
182 
183 TEST_P(AiksTest, ClearBlendWithBlur) {
184  Canvas canvas;
185  Paint white;
186  white.color = Color::Blue();
187  canvas.DrawRect(Rect::MakeXYWH(0, 0, 600.0, 600.0), white);
188 
189  Paint clear;
193  .sigma = Sigma(20),
194  };
195 
196  canvas.DrawCircle(Point::MakeXY(300.0, 300.0), 200.0, clear);
197 
198  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
199 }
200 
201 TEST_P(AiksTest, BlurHasNoEdge) {
202  Canvas canvas;
203  canvas.Scale(GetContentScale());
204  canvas.DrawPaint({});
205  Paint blur = {
206  .color = Color::Green(),
207  .mask_blur_descriptor =
210  .sigma = Sigma(47.6),
211  },
212  };
213  canvas.DrawRect(Rect::MakeXYWH(300, 300, 200, 200), blur);
214  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
215 }
216 
217 TEST_P(AiksTest, BlurredRectangleWithShader) {
218  Canvas canvas;
219  canvas.Scale(GetContentScale());
220 
221  auto paint_lines = [&canvas](Scalar dx, Scalar dy, Paint paint) {
222  auto draw_line = [&canvas, &paint](Point a, Point b) {
223  canvas.DrawPath(PathBuilder{}.AddLine(a, b).TakePath(), paint);
224  };
225  paint.stroke_width = 5;
226  paint.style = Paint::Style::kStroke;
227  draw_line(Point(dx + 100, dy + 100), Point(dx + 200, dy + 200));
228  draw_line(Point(dx + 100, dy + 200), Point(dx + 200, dy + 100));
229  draw_line(Point(dx + 150, dy + 100), Point(dx + 200, dy + 150));
230  draw_line(Point(dx + 100, dy + 150), Point(dx + 150, dy + 200));
231  };
232 
233  AiksContext renderer(GetContext(), nullptr);
234  Canvas recorder_canvas;
235  for (int x = 0; x < 5; ++x) {
236  for (int y = 0; y < 5; ++y) {
237  Rect rect = Rect::MakeXYWH(x * 20, y * 20, 20, 20);
238  Paint paint{.color =
239  ((x + y) & 1) == 0 ? Color::Yellow() : Color::Blue()};
240  recorder_canvas.DrawRect(rect, paint);
241  }
242  }
243  Picture picture = recorder_canvas.EndRecordingAsPicture();
244  std::shared_ptr<Texture> texture =
245  picture.ToImage(renderer, ISize{100, 100})->GetTexture();
246 
247  ColorSource image_source = ColorSource::MakeImage(
249  std::shared_ptr<ImageFilter> blur_filter = ImageFilter::MakeBlur(
252  canvas.DrawRect(Rect::MakeLTRB(0, 0, 300, 600),
254  canvas.DrawRect(Rect::MakeLTRB(100, 100, 200, 200),
255  Paint{.color_source = image_source});
256  canvas.DrawRect(Rect::MakeLTRB(300, 0, 600, 600),
257  Paint{.color = Color::Red()});
258  canvas.DrawRect(
259  Rect::MakeLTRB(400, 100, 500, 200),
260  Paint{.color_source = image_source, .image_filter = blur_filter});
261  paint_lines(0, 300, Paint{.color_source = image_source});
262  paint_lines(300, 300,
263  Paint{.color_source = image_source, .image_filter = blur_filter});
264  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
265 }
266 
267 TEST_P(AiksTest, MaskBlurWithZeroSigmaIsSkipped) {
268  Canvas canvas;
269 
270  Paint paint = {
271  .color = Color::Blue(),
272  .mask_blur_descriptor =
275  .sigma = Sigma(0),
276  },
277  };
278 
279  canvas.DrawCircle({300, 300}, 200, paint);
280  canvas.DrawRect(Rect::MakeLTRB(100, 300, 500, 600), paint);
281 
282  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
283 }
284 
287  Scalar sigma = 1.0f;
288  Scalar alpha = 1.0f;
289  std::shared_ptr<ImageFilter> image_filter;
290  bool invert_colors = false;
292 };
293 
294 static Picture MaskBlurVariantTest(const AiksTest& test_context,
295  const MaskBlurTestConfig& config) {
296  Canvas canvas;
297  canvas.Scale(test_context.GetContentScale());
298  canvas.Scale(Vector2{0.8f, 0.8f});
299  Paint paint;
302  .sigma = Sigma{1},
303  };
304 
305  canvas.DrawPaint({.color = Color::AntiqueWhite()});
306 
307  paint.mask_blur_descriptor->style = config.style;
308  paint.mask_blur_descriptor->sigma = Sigma{config.sigma};
309  paint.image_filter = config.image_filter;
310  paint.invert_colors = config.invert_colors;
311  paint.blend_mode = config.blend_mode;
312 
313  const Scalar x = 50;
314  const Scalar radius = 20.0f;
315  const Scalar y_spacing = 100.0f;
316 
317  Scalar y = 50;
318  paint.color = Color::Crimson().WithAlpha(config.alpha);
319  canvas.DrawRect(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
320  radius, 60.0f - radius),
321  paint);
322 
323  y += y_spacing;
324  paint.color = Color::Blue().WithAlpha(config.alpha);
325  canvas.DrawCircle({x + 25, y + 25}, radius, paint);
326 
327  y += y_spacing;
328  paint.color = Color::Green().WithAlpha(config.alpha);
329  canvas.DrawOval(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
330  radius, 60.0f - radius),
331  paint);
332 
333  y += y_spacing;
334  paint.color = Color::Purple().WithAlpha(config.alpha);
335  canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
336  {radius, radius}, //
337  paint);
338 
339  y += y_spacing;
340  paint.color = Color::Orange().WithAlpha(config.alpha);
341  canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
342  {radius, 5.0f}, paint);
343 
344  y += y_spacing;
345  paint.color = Color::Maroon().WithAlpha(config.alpha);
346  canvas.DrawPath(PathBuilder{}
347  .MoveTo({x + 0, y + 60})
348  .LineTo({x + 30, y + 0})
349  .LineTo({x + 60, y + 60})
350  .Close()
351  .TakePath(),
352  paint);
353 
354  y += y_spacing;
355  paint.color = Color::Maroon().WithAlpha(config.alpha);
356  canvas.DrawPath(PathBuilder{}
357  .AddArc(Rect::MakeXYWH(x + 5, y, 50, 50),
358  Radians{kPi / 2}, Radians{kPi})
359  .AddArc(Rect::MakeXYWH(x + 25, y, 50, 50),
360  Radians{kPi / 2}, Radians{kPi})
361  .Close()
362  .TakePath(),
363  paint);
364 
365  return canvas.EndRecordingAsPicture();
366 }
367 
368 static const std::map<std::string, MaskBlurTestConfig> kPaintVariations = {
369  // 1. Normal style, translucent, zero sigma.
370  {"NormalTranslucentZeroSigma",
372  .sigma = 0.0f,
373  .alpha = 0.5f}},
374  // 2. Normal style, translucent.
375  {"NormalTranslucent",
377  .sigma = 8.0f,
378  .alpha = 0.5f}},
379  // 3. Solid style, translucent.
380  {"SolidTranslucent",
382  .sigma = 8.0f,
383  .alpha = 0.5f}},
384  // 4. Solid style, opaque.
385  {"SolidOpaque",
386  {.style = FilterContents::BlurStyle::kSolid, .sigma = 8.0f}},
387  // 5. Solid style, translucent, color & image filtered.
388  {"SolidTranslucentWithFilters",
390  .sigma = 8.0f,
391  .alpha = 0.5f,
392  .image_filter = ImageFilter::MakeBlur(Sigma{3},
393  Sigma{3},
396  .invert_colors = true}},
397  // 6. Solid style, translucent, exclusion blended.
398  {"SolidTranslucentExclusionBlend",
400  .sigma = 8.0f,
401  .alpha = 0.5f,
402  .blend_mode = BlendMode::kExclusion}},
403  // 7. Inner style, translucent.
404  {"InnerTranslucent",
406  .sigma = 8.0f,
407  .alpha = 0.5f}},
408  // 8. Inner style, translucent, blurred.
409  {"InnerTranslucentWithBlurImageFilter",
411  .sigma = 8.0f,
412  .alpha = 0.5f,
413  .image_filter = ImageFilter::MakeBlur(Sigma{3},
414  Sigma{3},
417  // 9. Outer style, translucent.
418  {"OuterTranslucent",
420  .sigma = 8.0f,
421  .alpha = 0.5f}},
422  // 10. Outer style, opaque, image filtered.
423  {"OuterOpaqueWithBlurImageFilter",
425  .sigma = 8.0f,
426  .image_filter = ImageFilter::MakeBlur(Sigma{3},
427  Sigma{3},
430 };
431 
432 #define MASK_BLUR_VARIANT_TEST(config) \
433  TEST_P(AiksTest, MaskBlurVariantTest##config) { \
434  ASSERT_TRUE(OpenPlaygroundHere( \
435  MaskBlurVariantTest(*this, kPaintVariations.at(#config)))); \
436  }
437 
438 MASK_BLUR_VARIANT_TEST(NormalTranslucentZeroSigma)
439 MASK_BLUR_VARIANT_TEST(NormalTranslucent)
440 MASK_BLUR_VARIANT_TEST(SolidTranslucent)
441 MASK_BLUR_VARIANT_TEST(SolidOpaque)
442 MASK_BLUR_VARIANT_TEST(SolidTranslucentWithFilters)
443 MASK_BLUR_VARIANT_TEST(SolidTranslucentExclusionBlend)
444 MASK_BLUR_VARIANT_TEST(InnerTranslucent)
445 MASK_BLUR_VARIANT_TEST(InnerTranslucentWithBlurImageFilter)
446 MASK_BLUR_VARIANT_TEST(OuterTranslucent)
447 MASK_BLUR_VARIANT_TEST(OuterOpaqueWithBlurImageFilter)
448 
449 #undef MASK_BLUR_VARIANT_TEST
450 
451 TEST_P(AiksTest, GaussianBlurAtPeripheryVertical) {
452  Canvas canvas;
453 
454  canvas.Scale(GetContentScale());
455  canvas.DrawRRect(Rect::MakeLTRB(0, 0, GetWindowSize().width, 100),
456  Size(10, 10), Paint{.color = Color::LimeGreen()});
457  canvas.DrawRRect(Rect::MakeLTRB(0, 110, GetWindowSize().width, 210),
458  Size(10, 10), Paint{.color = Color::Magenta()});
459  canvas.ClipRect(Rect::MakeLTRB(100, 0, 200, GetWindowSize().height));
460  canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
461  ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
464  canvas.Restore();
465 
466  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
467 }
468 
469 TEST_P(AiksTest, GaussianBlurAtPeripheryHorizontal) {
470  Canvas canvas;
471 
472  canvas.Scale(GetContentScale());
473  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
474  canvas.DrawImageRect(
475  std::make_shared<Image>(boston),
476  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height),
477  Rect::MakeLTRB(0, 0, GetWindowSize().width, 100), Paint{});
478  canvas.DrawRRect(Rect::MakeLTRB(0, 110, GetWindowSize().width, 210),
479  Size(10, 10), Paint{.color = Color::Magenta()});
480  canvas.ClipRect(Rect::MakeLTRB(0, 50, GetWindowSize().width, 150));
481  canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
482  ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
485  canvas.Restore();
486  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
487 }
488 
489 #define FLT_FORWARD(mock, real, method) \
490  EXPECT_CALL(*mock, method()) \
491  .WillRepeatedly(::testing::Return(real->method()));
492 
493 TEST_P(AiksTest, GaussianBlurWithoutDecalSupport) {
494  if (GetParam() != PlaygroundBackend::kMetal) {
495  GTEST_SKIP_(
496  "This backend doesn't yet support setting device capabilities.");
497  }
498  if (!WillRenderSomething()) {
499  // Sometimes these tests are run without playgrounds enabled which is
500  // pointless for this test since we are asserting that
501  // `SupportsDecalSamplerAddressMode` is called.
502  GTEST_SKIP_("This test requires playgrounds.");
503  }
504 
505  std::shared_ptr<const Capabilities> old_capabilities =
506  GetContext()->GetCapabilities();
507  auto mock_capabilities = std::make_shared<MockCapabilities>();
508  EXPECT_CALL(*mock_capabilities, SupportsDecalSamplerAddressMode())
509  .Times(::testing::AtLeast(1))
510  .WillRepeatedly(::testing::Return(false));
511  FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultColorFormat);
512  FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultStencilFormat);
513  FLT_FORWARD(mock_capabilities, old_capabilities,
514  GetDefaultDepthStencilFormat);
515  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsOffscreenMSAA);
516  FLT_FORWARD(mock_capabilities, old_capabilities,
517  SupportsImplicitResolvingMSAA);
518  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsReadFromResolve);
519  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsFramebufferFetch);
520  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsSSBO);
521  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsCompute);
522  FLT_FORWARD(mock_capabilities, old_capabilities,
523  SupportsTextureToTextureBlits);
524  FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultGlyphAtlasFormat);
525  ASSERT_TRUE(SetCapabilities(mock_capabilities).ok());
526 
527  auto texture = std::make_shared<Image>(CreateTextureForFixture("boston.jpg"));
528  Canvas canvas;
529  canvas.Scale(GetContentScale() * 0.5);
530  canvas.DrawPaint({.color = Color::Black()});
531  canvas.DrawImage(
532  texture, Point(200, 200),
533  {
534  .image_filter = ImageFilter::MakeBlur(
537  });
538  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
539 }
540 
541 TEST_P(AiksTest, GaussianBlurOneDimension) {
542  Canvas canvas;
543 
544  canvas.Scale(GetContentScale());
545  canvas.Scale({0.5, 0.5, 1.0});
546  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
547  canvas.DrawImage(std::make_shared<Image>(boston), Point(100, 100), Paint{});
548  canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
549  ImageFilter::MakeBlur(Sigma(50.0), Sigma(0.0),
552  canvas.Restore();
553  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
554 }
555 
556 // Smoketest to catch issues with the coverage hint.
557 // Draws a rotated blurred image within a rectangle clip. The center of the clip
558 // rectangle is the center of the rotated image. The entire area of the clip
559 // rectangle should be filled with opaque colors output by the blur.
560 TEST_P(AiksTest, GaussianBlurRotatedAndClipped) {
561  Canvas canvas;
562  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
563  Rect bounds =
564  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
565  Vector2 image_center = Vector2(bounds.GetSize() / 2);
566  Paint paint = {.image_filter =
567  ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
570  Vector2 clip_size = {150, 75};
571  Vector2 center = Vector2(1024, 768) / 2;
572  canvas.Scale(GetContentScale());
573  canvas.ClipRect(
574  Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size));
575  canvas.Translate({center.x, center.y, 0});
576  canvas.Scale({0.6, 0.6, 1});
577  canvas.Rotate(Degrees(25));
578 
579  canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,
580  /*dest=*/bounds.Shift(-image_center), paint);
581 
582  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
583 }
584 
585 TEST_P(AiksTest, GaussianBlurScaledAndClipped) {
586  Canvas canvas;
587  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
588  Rect bounds =
589  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
590  Vector2 image_center = Vector2(bounds.GetSize() / 2);
591  Paint paint = {.image_filter =
592  ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
595  Vector2 clip_size = {150, 75};
596  Vector2 center = Vector2(1024, 768) / 2;
597  canvas.Scale(GetContentScale());
598  canvas.ClipRect(
599  Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size));
600  canvas.Translate({center.x, center.y, 0});
601  canvas.Scale({0.6, 0.6, 1});
602 
603  canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,
604  /*dest=*/bounds.Shift(-image_center), paint);
605 
606  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
607 }
608 
609 TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) {
610  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
611 
612  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
613  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
614  const Entity::TileMode tile_modes[] = {
617 
618  static float rotation = 0;
619  static float scale = 0.6;
620  static int selected_tile_mode = 3;
621 
622  if (AiksTest::ImGuiBegin("Controls", nullptr,
623  ImGuiWindowFlags_AlwaysAutoResize)) {
624  ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
625  ImGui::SliderFloat("Scale", &scale, 0, 2.0);
626  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
627  sizeof(tile_mode_names) / sizeof(char*));
628  ImGui::End();
629  }
630 
631  Canvas canvas;
632  Rect bounds =
633  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
634  Vector2 image_center = Vector2(bounds.GetSize() / 2);
635  Paint paint = {.image_filter =
636  ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
638  tile_modes[selected_tile_mode])};
639  static PlaygroundPoint point_a(Point(362, 309), 20, Color::Red());
640  static PlaygroundPoint point_b(Point(662, 459), 20, Color::Red());
641  auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
642  Vector2 center = Vector2(1024, 768) / 2;
643  canvas.Scale(GetContentScale());
644  canvas.ClipRect(
645  Rect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
646  canvas.Translate({center.x, center.y, 0});
647  canvas.Scale({scale, scale, 1});
648  canvas.Rotate(Degrees(rotation));
649 
650  canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,
651  /*dest=*/bounds.Shift(-image_center), paint);
652  return canvas.EndRecordingAsPicture();
653  };
654 
655  ASSERT_TRUE(OpenPlaygroundHere(callback));
656 }
657 
658 // This addresses a bug where tiny blurs could result in mip maps that beyond
659 // the limits for the textures used for blurring.
660 // See also: b/323402168
661 TEST_P(AiksTest, GaussianBlurSolidColorTinyMipMap) {
662  for (int32_t i = 1; i < 5; ++i) {
663  Canvas canvas;
664  Scalar fi = i;
665  canvas.DrawPath(
666  PathBuilder{}
667  .MoveTo({100, 100})
668  .LineTo({100.f + fi, 100.f + fi})
669  .TakePath(),
670  {.color = Color::Chartreuse(),
671  .image_filter = ImageFilter::MakeBlur(
674 
675  Picture picture = canvas.EndRecordingAsPicture();
676  std::shared_ptr<RenderTargetCache> cache =
677  std::make_shared<RenderTargetCache>(
678  GetContext()->GetResourceAllocator());
679  AiksContext aiks_context(GetContext(), nullptr, cache);
680  std::shared_ptr<Image> image = picture.ToImage(aiks_context, {1024, 768});
681  EXPECT_TRUE(image) << " length " << i;
682  }
683 }
684 
685 // This addresses a bug where tiny blurs could result in mip maps that beyond
686 // the limits for the textures used for blurring.
687 // See also: b/323402168
688 TEST_P(AiksTest, GaussianBlurBackdropTinyMipMap) {
689  for (int32_t i = 0; i < 5; ++i) {
690  Canvas canvas;
691  ISize clip_size = ISize(i, i);
692  canvas.ClipRect(
693  Rect::MakeXYWH(400, 400, clip_size.width, clip_size.height));
694  canvas.DrawCircle(
695  {400, 400}, 200,
696  {
697  .color = Color::Green(),
698  .image_filter = ImageFilter::MakeBlur(
701  });
702  canvas.Restore();
703 
704  Picture picture = canvas.EndRecordingAsPicture();
705  std::shared_ptr<RenderTargetCache> cache =
706  std::make_shared<RenderTargetCache>(
707  GetContext()->GetResourceAllocator());
708  AiksContext aiks_context(GetContext(), nullptr, cache);
709  std::shared_ptr<Image> image = picture.ToImage(aiks_context, {1024, 768});
710  EXPECT_TRUE(image) << " clip rect " << i;
711  }
712 }
713 
714 TEST_P(AiksTest, GaussianBlurAnimatedBackdrop) {
715  // This test is for checking out how stable rendering is when content is
716  // translated underneath a blur. Animating under a blur can cause
717  // *shimmering* to happen as a result of pixel alignment.
718  // See also: https://github.com/flutter/flutter/issues/140193
719  auto boston = std::make_shared<Image>(
720  CreateTextureForFixture("boston.jpg", /*enable_mipmapping=*/true));
721  ASSERT_TRUE(boston);
722  int64_t count = 0;
723  Scalar sigma = 20.0;
724  Scalar freq = 0.1;
725  Scalar amp = 50.0;
726  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
727  if (AiksTest::ImGuiBegin("Controls", nullptr,
728  ImGuiWindowFlags_AlwaysAutoResize)) {
729  ImGui::SliderFloat("Sigma", &sigma, 0, 200);
730  ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0);
731  ImGui::SliderFloat("Amplitude", &amp, 1, 100);
732  ImGui::End();
733  }
734 
735  Canvas canvas;
736  canvas.Scale(GetContentScale());
737  Scalar y = amp * sin(freq * 2.0 * M_PI * count / 60);
738  canvas.DrawImage(boston,
739  Point(1024 / 2 - boston->GetSize().width / 2,
740  (768 / 2 - boston->GetSize().height / 2) + y),
741  {});
742  static PlaygroundPoint point_a(Point(100, 100), 20, Color::Red());
743  static PlaygroundPoint point_b(Point(900, 700), 20, Color::Red());
744  auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
745  canvas.ClipRect(
746  Rect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
747  canvas.ClipRect(Rect::MakeLTRB(100, 100, 900, 700));
748  canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
749  ImageFilter::MakeBlur(Sigma(sigma), Sigma(sigma),
752  count += 1;
753  return canvas.EndRecordingAsPicture();
754  };
755  ASSERT_TRUE(OpenPlaygroundHere(callback));
756 }
757 
758 TEST_P(AiksTest, GaussianBlurStyleInnerGradient) {
759  Canvas canvas;
760  canvas.Scale(GetContentScale());
761 
762  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
763 
764  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
765  Color{0.7568, 0.2627, 0.2118, 1.0}};
766  std::vector<Scalar> stops = {0.0, 1.0};
767 
768  Paint paint;
770  {0, 0}, {200, 200}, std::move(colors), std::move(stops),
774  .sigma = Sigma(30),
775  };
776  canvas.DrawPath(PathBuilder()
777  .MoveTo({200, 200})
778  .LineTo({300, 400})
779  .LineTo({100, 400})
780  .Close()
781  .TakePath(),
782  paint);
783 
784  // Draw another thing to make sure the clip area is reset.
785  Paint red;
786  red.color = Color::Red();
787  canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
788  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
789 }
790 
791 TEST_P(AiksTest, GaussianBlurStyleSolidGradient) {
792  Canvas canvas;
793  canvas.Scale(GetContentScale());
794 
795  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
796 
797  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
798  Color{0.7568, 0.2627, 0.2118, 1.0}};
799  std::vector<Scalar> stops = {0.0, 1.0};
800 
801  Paint paint;
803  {0, 0}, {200, 200}, std::move(colors), std::move(stops),
807  .sigma = Sigma(30),
808  };
809  canvas.DrawPath(PathBuilder()
810  .MoveTo({200, 200})
811  .LineTo({300, 400})
812  .LineTo({100, 400})
813  .Close()
814  .TakePath(),
815  paint);
816 
817  // Draw another thing to make sure the clip area is reset.
818  Paint red;
819  red.color = Color::Red();
820  canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
821  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
822 }
823 
824 TEST_P(AiksTest, GaussianBlurStyleOuterGradient) {
825  Canvas canvas;
826  canvas.Scale(GetContentScale());
827 
828  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
829 
830  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
831  Color{0.7568, 0.2627, 0.2118, 1.0}};
832  std::vector<Scalar> stops = {0.0, 1.0};
833 
834  Paint paint;
836  {0, 0}, {200, 200}, std::move(colors), std::move(stops),
840  .sigma = Sigma(30),
841  };
842  canvas.DrawPath(PathBuilder()
843  .MoveTo({200, 200})
844  .LineTo({300, 400})
845  .LineTo({100, 400})
846  .Close()
847  .TakePath(),
848  paint);
849 
850  // Draw another thing to make sure the clip area is reset.
851  Paint red;
852  red.color = Color::Red();
853  canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
854  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
855 }
856 
857 TEST_P(AiksTest, GaussianBlurStyleInner) {
858  Canvas canvas;
859  canvas.Scale(GetContentScale());
860 
861  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
862 
863  Paint paint;
864  paint.color = Color::Green();
867  .sigma = Sigma(30),
868  };
869  canvas.DrawPath(PathBuilder()
870  .MoveTo({200, 200})
871  .LineTo({300, 400})
872  .LineTo({100, 400})
873  .Close()
874  .TakePath(),
875  paint);
876 
877  // Draw another thing to make sure the clip area is reset.
878  Paint red;
879  red.color = Color::Red();
880  canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
881 
882  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
883 }
884 
885 TEST_P(AiksTest, GaussianBlurStyleOuter) {
886  Canvas canvas;
887  canvas.Scale(GetContentScale());
888 
889  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
890 
891  Paint paint;
892  paint.color = Color::Green();
895  .sigma = Sigma(30),
896  };
897  canvas.DrawPath(PathBuilder()
898  .MoveTo({200, 200})
899  .LineTo({300, 400})
900  .LineTo({100, 400})
901  .Close()
902  .TakePath(),
903  paint);
904 
905  // Draw another thing to make sure the clip area is reset.
906  Paint red;
907  red.color = Color::Red();
908  canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
909 
910  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
911 }
912 
913 TEST_P(AiksTest, GaussianBlurStyleSolid) {
914  Canvas canvas;
915  canvas.Scale(GetContentScale());
916 
917  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
918 
919  Paint paint;
920  paint.color = Color::Green();
923  .sigma = Sigma(30),
924  };
925  canvas.DrawPath(PathBuilder()
926  .MoveTo({200, 200})
927  .LineTo({300, 400})
928  .LineTo({100, 400})
929  .Close()
930  .TakePath(),
931  paint);
932 
933  // Draw another thing to make sure the clip area is reset.
934  Paint red;
935  red.color = Color::Red();
936  canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
937 
938  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
939 }
940 
941 TEST_P(AiksTest, MaskBlurTexture) {
942  Scalar sigma = 30;
943  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
944  if (AiksTest::ImGuiBegin("Controls", nullptr,
945  ImGuiWindowFlags_AlwaysAutoResize)) {
946  ImGui::SliderFloat("Sigma", &sigma, 0, 500);
947  ImGui::End();
948  }
949  Canvas canvas;
950  canvas.Scale(GetContentScale());
951  Paint paint;
952  paint.color = Color::Green();
955  .sigma = Sigma(sigma),
956  };
957  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
958  canvas.DrawImage(std::make_shared<Image>(boston), {200, 200}, paint);
959  Paint red;
960  red.color = Color::Red();
961  canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
962  return canvas.EndRecordingAsPicture();
963  };
964  ASSERT_TRUE(OpenPlaygroundHere(callback));
965 }
966 
967 TEST_P(AiksTest, GuassianBlurUpdatesMipmapContents) {
968  // This makes sure if mip maps are recycled across invocations of blurs the
969  // contents get updated each frame correctly. If they aren't updated the color
970  // inside the blur and outside the blur will be different.
971  //
972  // If there is some change to render target caching this could display a false
973  // positive in the future. Also, if the LOD that is rendered is 1 it could
974  // present a false positive.
975  int32_t count = 0;
976  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
977  Canvas canvas;
978  if (count++ == 0) {
979  canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
980  } else {
981  canvas.DrawCircle({100, 100}, 50, {.color = Color::Chartreuse()});
982  }
983  canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), {20, 20});
984  canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
985  ImageFilter::MakeBlur(Sigma(30.0), Sigma(30.0),
988  canvas.Restore();
989  return canvas.EndRecordingAsPicture();
990  };
991 
992  ASSERT_TRUE(OpenPlaygroundHere(callback));
993 }
994 
995 TEST_P(AiksTest, GaussianBlurSetsMipCountOnPass) {
996  Canvas canvas;
997  canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
998  canvas.SaveLayer({}, std::nullopt,
1002  canvas.Restore();
1003 
1004  Picture picture = canvas.EndRecordingAsPicture();
1005  EXPECT_EQ(4, picture.pass->GetRequiredMipCount());
1006 }
1007 
1008 TEST_P(AiksTest, GaussianBlurAllocatesCorrectMipCountRenderTarget) {
1009  size_t blur_required_mip_count =
1010  GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1011 
1012  Canvas canvas;
1013  canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
1014  canvas.SaveLayer({}, std::nullopt,
1018  canvas.Restore();
1019 
1020  Picture picture = canvas.EndRecordingAsPicture();
1021  std::shared_ptr<RenderTargetCache> cache =
1022  std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1023  AiksContext aiks_context(GetContext(), nullptr, cache);
1024  picture.ToImage(aiks_context, {100, 100});
1025 
1026  size_t max_mip_count = 0;
1027  for (auto it = cache->GetRenderTargetDataBegin();
1028  it != cache->GetRenderTargetDataEnd(); ++it) {
1029  max_mip_count = std::max(it->config.mip_count, max_mip_count);
1030  }
1031  EXPECT_EQ(max_mip_count, blur_required_mip_count);
1032 }
1033 
1034 TEST_P(AiksTest, GaussianBlurMipMapNestedLayer) {
1035  fml::testing::LogCapture log_capture;
1036  size_t blur_required_mip_count =
1037  GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1038 
1039  Canvas canvas;
1040  canvas.DrawPaint({.color = Color::Wheat()});
1041  canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
1042  canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
1043  canvas.SaveLayer({}, std::nullopt,
1047  canvas.DrawCircle({200, 200}, 50, {.color = Color::Chartreuse()});
1048 
1049  Picture picture = canvas.EndRecordingAsPicture();
1050  std::shared_ptr<RenderTargetCache> cache =
1051  std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1052  AiksContext aiks_context(GetContext(), nullptr, cache);
1053  picture.ToImage(aiks_context, {100, 100});
1054 
1055  size_t max_mip_count = 0;
1056  for (auto it = cache->GetRenderTargetDataBegin();
1057  it != cache->GetRenderTargetDataEnd(); ++it) {
1058  max_mip_count = std::max(it->config.mip_count, max_mip_count);
1059  }
1060  EXPECT_EQ(max_mip_count, blur_required_mip_count);
1061  // The log is FML_DLOG, so only check in debug builds.
1062 #ifndef NDEBUG
1063  if (GetParam() != PlaygroundBackend::kOpenGLES) {
1064  EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1065  std::string::npos);
1066  } else {
1067  EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1068  std::string::npos);
1069  }
1070 #endif
1071 }
1072 
1073 TEST_P(AiksTest, GaussianBlurMipMapImageFilter) {
1074  size_t blur_required_mip_count =
1075  GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1076  fml::testing::LogCapture log_capture;
1077  Canvas canvas;
1078  canvas.SaveLayer(
1079  {.image_filter = ImageFilter::MakeBlur(Sigma(30), Sigma(30),
1082  canvas.DrawCircle({200, 200}, 50, {.color = Color::Chartreuse()});
1083 
1084  Picture picture = canvas.EndRecordingAsPicture();
1085  std::shared_ptr<RenderTargetCache> cache =
1086  std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1087  AiksContext aiks_context(GetContext(), nullptr, cache);
1088  picture.ToImage(aiks_context, {1024, 768});
1089 
1090  size_t max_mip_count = 0;
1091  for (auto it = cache->GetRenderTargetDataBegin();
1092  it != cache->GetRenderTargetDataEnd(); ++it) {
1093  max_mip_count = std::max(it->config.mip_count, max_mip_count);
1094  }
1095  EXPECT_EQ(max_mip_count, blur_required_mip_count);
1096  // The log is FML_DLOG, so only check in debug builds.
1097 #ifndef NDEBUG
1098  if (GetParam() != PlaygroundBackend::kOpenGLES) {
1099  EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1100  std::string::npos);
1101  } else {
1102  EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1103  std::string::npos);
1104  }
1105 #endif
1106 }
1107 
1108 TEST_P(AiksTest, GaussianBlurMipMapSolidColor) {
1109  size_t blur_required_mip_count =
1110  GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1111  fml::testing::LogCapture log_capture;
1112  Canvas canvas;
1113  canvas.DrawPath(PathBuilder{}
1114  .MoveTo({100, 100})
1115  .LineTo({200, 100})
1116  .LineTo({150, 200})
1117  .LineTo({50, 200})
1118  .Close()
1119  .TakePath(),
1120  {.color = Color::Chartreuse(),
1121  .image_filter = ImageFilter::MakeBlur(
1124 
1125  Picture picture = canvas.EndRecordingAsPicture();
1126  std::shared_ptr<RenderTargetCache> cache =
1127  std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1128  AiksContext aiks_context(GetContext(), nullptr, cache);
1129  picture.ToImage(aiks_context, {1024, 768});
1130 
1131  size_t max_mip_count = 0;
1132  for (auto it = cache->GetRenderTargetDataBegin();
1133  it != cache->GetRenderTargetDataEnd(); ++it) {
1134  max_mip_count = std::max(it->config.mip_count, max_mip_count);
1135  }
1136  EXPECT_EQ(max_mip_count, blur_required_mip_count);
1137  // The log is FML_DLOG, so only check in debug builds.
1138 #ifndef NDEBUG
1139  if (GetParam() != PlaygroundBackend::kOpenGLES) {
1140  EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1141  std::string::npos);
1142  } else {
1143  EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1144  std::string::npos);
1145  }
1146 #endif
1147 }
1148 
1149 TEST_P(AiksTest, MaskBlurDoesntStretchContents) {
1150  Scalar sigma = 70;
1151  auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1152  if (AiksTest::ImGuiBegin("Controls", nullptr,
1153  ImGuiWindowFlags_AlwaysAutoResize)) {
1154  ImGui::SliderFloat("Sigma", &sigma, 0, 500);
1155  ImGui::End();
1156  }
1157  Canvas canvas;
1158  canvas.Scale(GetContentScale());
1159  canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
1160 
1161  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1162  ColorSource image_source = ColorSource::MakeImage(
1164 
1165  canvas.Transform(Matrix::MakeTranslation({100, 100, 0}) *
1166  Matrix::MakeScale({0.5, 0.5, 1.0}));
1167  Paint paint = {
1168  .color_source = image_source,
1169  .mask_blur_descriptor =
1172  .sigma = Sigma(sigma),
1173  },
1174  };
1175  canvas.DrawRect(
1176  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height),
1177  paint);
1178 
1179  return canvas.EndRecordingAsPicture();
1180  };
1181  ASSERT_TRUE(OpenPlaygroundHere(callback));
1182 }
1183 
1184 } // namespace testing
1185 } // namespace impeller
impeller::Color::DarkMagenta
static constexpr Color DarkMagenta()
Definition: color.h:378
impeller::Color::Blue
static constexpr Color Blue()
Definition: color.h:268
impeller::testing::MaskBlurTestConfig::alpha
Scalar alpha
Definition: aiks_blur_unittests.cc:288
impeller::AiksPlayground
Definition: aiks_playground.h:18
impeller::Entity::TileMode::kClamp
@ kClamp
impeller::Canvas::EndRecordingAsPicture
Picture EndRecordingAsPicture()
Definition: canvas.cc:756
impeller::Picture::pass
std::unique_ptr< EntityPass > pass
Definition: picture.h:21
impeller::Canvas::DrawRRect
void DrawRRect(const Rect &rect, const Size &corner_radii, const Paint &paint)
Definition: canvas.cc:488
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::Scalar
float Scalar
Definition: scalar.h:18
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::testing::MaskBlurTestConfig::sigma
Scalar sigma
Definition: aiks_blur_unittests.cc:287
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:264
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::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::BlendMode
BlendMode
Definition: color.h:59
impeller::FilterContents::BlurStyle
BlurStyle
Definition: filter_contents.h:26
impeller::Color
Definition: color.h:124
impeller::Paint::color
Color color
Definition: paint.h:55
impeller::Entity::TileMode::kDecal
@ kDecal
impeller::BlendMode::kSource
@ kSource
impeller::PlaygroundBackend::kMetal
@ kMetal
impeller::Color::Purple
static constexpr Color Purple()
Definition: color.h:734
impeller::Canvas
Definition: canvas.h:58
impeller::testing::MaskBlurTestConfig::blend_mode
BlendMode blend_mode
Definition: aiks_blur_unittests.cc:291
impeller::PathBuilder
Definition: path_builder.h:14
impeller::Paint::MaskBlurDescriptor::style
FilterContents::BlurStyle style
Definition: paint.h:40
impeller::Vector2
Point Vector2
Definition: point.h:320
impeller::BlendMode::kColor
@ kColor
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::Paint::MaskBlurDescriptor
Definition: paint.h:39
impeller::testing::MaskBlurVariantTest
static Picture MaskBlurVariantTest(const AiksTest &test_context, const MaskBlurTestConfig &config)
Definition: aiks_blur_unittests.cc:294
impeller::Color::CornflowerBlue
static constexpr Color CornflowerBlue()
Definition: color.h:334
impeller::testing::MaskBlurTestConfig
Definition: aiks_blur_unittests.cc:285
impeller::Color::Yellow
static constexpr Color Yellow()
Definition: color.h:834
gaussian_blur_filter_contents.h
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::FilterContents::BlurStyle::kNormal
@ kNormal
Blurred inside and outside.
impeller::Entity::TileMode::kRepeat
@ kRepeat
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Color::Wheat
static constexpr Color Wheat()
Definition: color.h:826
impeller::Paint::color_source
ColorSource color_source
Definition: paint.h:56
impeller::Canvas::DrawRect
void DrawRect(const Rect &rect, const Paint &paint)
Definition: canvas.cc:441
impeller::MoveTo
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:18
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:112
impeller::Color::Maroon
static constexpr Color Maroon()
Definition: color.h:606
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::TRect::Shift
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:531
impeller::Canvas::DrawImage
void DrawImage(const std::shared_ptr< Image > &image, Point offset, const Paint &paint, SamplerDescriptor sampler={})
Definition: canvas.cc:703
impeller::Entity::TileMode::kMirror
@ kMirror
path_builder.h
impeller::Color::Magenta
static constexpr Color Magenta()
Definition: color.h:602
impeller::Picture
Definition: picture.h:20
impeller::TSize< int64_t >
impeller::Point
TPoint< Scalar > Point
Definition: point.h:316
impeller::testing::kPaintVariations
static const std::map< std::string, MaskBlurTestConfig > kPaintVariations
Definition: aiks_blur_unittests.cc:368
impeller::ColorSource
Definition: color_source.h:28
MASK_BLUR_VARIANT_TEST
#define MASK_BLUR_VARIANT_TEST(config)
Definition: aiks_blur_unittests.cc:432
impeller::Canvas::Scale
void Scale(const Vector2 &scale)
Definition: canvas.cc:264
impeller::Color::OrangeRed
static constexpr Color OrangeRed()
Definition: color.h:686
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::FilterContents::BlurStyle::kSolid
@ kSolid
Solid inside, blurred outside.
impeller::testing::MaskBlurTestConfig::image_filter
std::shared_ptr< ImageFilter > image_filter
Definition: aiks_blur_unittests.cc:289
impeller::ColorFilter::MakeBlend
static std::shared_ptr< ColorFilter > MakeBlend(BlendMode blend_mode, Color color)
Definition: color_filter.cc:23
widgets.h
impeller::testing::MaskBlurTestConfig::style
FilterContents::BlurStyle style
Definition: aiks_blur_unittests.cc:286
impeller::BlendMode::kClear
@ kClear
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:270
impeller::Canvas::DrawCircle
void DrawCircle(const Point &center, Scalar radius, const Paint &paint)
Definition: canvas.cc:515
impeller::Color::Grey
static constexpr Color Grey()
Definition: color.h:486
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::Canvas::Restore
bool Restore()
Definition: canvas.cc:208
impeller::Radians
Definition: scalar.h:38
impeller::Color::Chartreuse
static constexpr Color Chartreuse()
Definition: color.h:322
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::FilterContents::BlurStyle::kInner
@ kInner
Blurred inside, nothing outside.
impeller::Entity::TileMode
TileMode
Definition: entity.h:42
impeller::TSize::width
Type width
Definition: size.h:22
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::Canvas::ClipRRect
void ClipRRect(const Rect &rect, const Size &corner_radii, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:590
impeller::Close
void Close(PathBuilder *builder)
Definition: tessellator.cc:36
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:138
impeller::Color::AntiqueWhite
static constexpr Color AntiqueWhite()
Definition: color.h:278
impeller::Picture::ToImage
std::shared_ptr< Image > ToImage(AiksContext &context, ISize size) const
Definition: picture.cc:31
impeller::Canvas::DrawOval
void DrawOval(const Rect &rect, const Paint &paint)
Definition: canvas.cc:461
impeller::BlendMode::kExclusion
@ kExclusion
impeller::Color::GreenYellow
static constexpr Color GreenYellow()
Definition: color.h:482
impeller::FilterContents::BlurStyle::kOuter
@ kOuter
Nothing inside, blurred outside.
impeller::TRect::GetSize
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition: rect.h:294
impeller::PlaygroundBackend::kOpenGLES
@ kOpenGLES
impeller::Canvas::Rotate
void Rotate(Radians radians)
Definition: canvas.cc:276
impeller::LineTo
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:22
impeller::Playground::GetContentScale
Point GetContentScale() const
Definition: playground.cc:186
impeller::DrawPlaygroundPoint
Point DrawPlaygroundPoint(PlaygroundPoint &point)
Definition: widgets.cc:9
impeller::Color::Orange
static constexpr Color Orange()
Definition: color.h:682
impeller::PlaygroundPoint
Definition: widgets.h:17
impeller::GaussianBlurFilterContents::kNoMipsError
static std::string_view kNoMipsError
Definition: gaussian_blur_filter_contents.h:36
impeller::testing::TEST_P
TEST_P(AiksTest, CanRenderMaskBlurHugeSigma)
Definition: aiks_blur_unittests.cc:23
FLT_FORWARD
#define FLT_FORWARD(mock, real, method)
Definition: aiks_blur_unittests.cc:489
impeller::TPoint< Scalar >
impeller::Canvas::Transform
void Transform(const Matrix &transform)
Definition: canvas.cc:243
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::PathBuilder::MoveTo
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:33
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:258
impeller::Paint::invert_colors
bool invert_colors
Definition: paint.h:65
scale
const Scalar scale
Definition: stroke_path_geometry.cc:297
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
impeller::Color::LimeGreen
static constexpr Color LimeGreen()
Definition: color.h:594
impeller::TSize::height
Type height
Definition: size.h:23
impeller::Color::DarkGreen
static constexpr Color DarkGreen()
Definition: color.h:366
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
render_target_cache.h
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::TRect
Definition: rect.h:122
impeller::PathBuilder::AddArc
PathBuilder & AddArc(const Rect &oval_bounds, Radians start, Radians sweep, bool use_center=false)
Definition: path_builder.cc:313
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::Paint::blend_mode
BlendMode blend_mode
Definition: paint.h:64
impeller::testing::MaskBlurTestConfig::invert_colors
bool invert_colors
Definition: aiks_blur_unittests.cc:290
impeller::Color::Crimson
static constexpr Color Crimson()
Definition: color.h:342
impeller::TRect::Expand
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition: rect.h:547
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