Flutter Impeller
aiks_dl_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 
5 #include "display_list/display_list.h"
6 #include "display_list/dl_blend_mode.h"
7 #include "display_list/dl_builder.h"
8 #include "display_list/dl_color.h"
9 #include "display_list/dl_paint.h"
10 #include "display_list/dl_sampling_options.h"
11 #include "display_list/dl_tile_mode.h"
12 #include "display_list/effects/dl_color_filter.h"
13 #include "display_list/effects/dl_color_source.h"
14 #include "display_list/effects/dl_image_filter.h"
15 #include "display_list/effects/dl_mask_filter.h"
17 
18 #include "gmock/gmock.h"
22 #include "impeller/renderer/testing/mocks.h"
23 #include "include/core/SkRRect.h"
24 #include "include/core/SkRect.h"
25 #include "third_party/imgui/imgui.h"
26 
27 ////////////////////////////////////////////////////////////////////////////////
28 // This is for tests of Canvas that are interested the results of rendering
29 // blurs.
30 ////////////////////////////////////////////////////////////////////////////////
31 
32 namespace impeller {
33 namespace testing {
34 
35 using namespace flutter;
36 
37 // The shapes of these ovals should appear equal. They are demonstrating the
38 // difference between the fast pass and not.
39 TEST_P(AiksTest, SolidColorOvalsMaskBlurTinySigma) {
40  DisplayListBuilder builder;
41  builder.Scale(GetContentScale().x, GetContentScale().y);
42 
43  std::vector<float> sigmas = {0.0, 0.01, 1.0};
44  std::vector<DlColor> colors = {DlColor::kGreen(), DlColor::kYellow(),
45  DlColor::kRed()};
46  for (uint32_t i = 0; i < sigmas.size(); ++i) {
47  DlPaint paint;
48  paint.setColor(colors[i]);
49  paint.setMaskFilter(
50  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigmas[i]));
51 
52  builder.Save();
53  builder.Translate(100 + (i * 100), 100);
54  SkRRect rrect =
55  SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 60.0f, 160.0f), 50, 100);
56  builder.DrawRRect(rrect, paint);
57  builder.Restore();
58  }
59 
60  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
61 }
62 
63 sk_sp<flutter::DisplayList> DoGradientOvalStrokeMaskBlur(Vector2 content_Scale,
64  Scalar sigma,
65  DlBlurStyle style) {
66  DisplayListBuilder builder;
67  builder.Scale(content_Scale.x, content_Scale.y);
68 
69  DlPaint background_paint;
70  background_paint.setColor(DlColor(1, 0.1, 0.1, 0.1, DlColorSpace::kSRGB));
71  builder.DrawPaint(background_paint);
72 
73  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue()};
74  std::vector<Scalar> stops = {0.0, 1.0};
75 
76  DlPaint paint;
77  paint.setMaskFilter(DlBlurMaskFilter::Make(style, sigma));
78  auto gradient = DlColorSource::MakeLinear(
79  {0, 0}, {200, 200}, 2, colors.data(), stops.data(), DlTileMode::kClamp);
80  paint.setColorSource(gradient);
81  paint.setColor(DlColor::kWhite());
82  paint.setDrawStyle(DlDrawStyle::kStroke);
83  paint.setStrokeWidth(20);
84 
85  builder.Save();
86  builder.Translate(100, 100);
87 
88  {
89  DlPaint line_paint;
90  line_paint.setColor(DlColor::kWhite());
91  builder.DrawLine({100, 0}, {100, 60}, line_paint);
92  builder.DrawLine({0, 30}, {200, 30}, line_paint);
93  }
94 
95  SkRRect rrect =
96  SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 200.0f, 60.0f), 50, 100);
97  builder.DrawRRect(rrect, paint);
98  builder.Restore();
99 
100  return builder.Build();
101 }
102 
103 // https://github.com/flutter/flutter/issues/155930
104 TEST_P(AiksTest, GradientOvalStrokeMaskBlur) {
105  ASSERT_TRUE(OpenPlaygroundHere(DoGradientOvalStrokeMaskBlur(
106  GetContentScale(), /*sigma=*/10, DlBlurStyle::kNormal)));
107 }
108 
109 TEST_P(AiksTest, GradientOvalStrokeMaskBlurSigmaZero) {
110  ASSERT_TRUE(OpenPlaygroundHere(DoGradientOvalStrokeMaskBlur(
111  GetContentScale(), /*sigma=*/0, DlBlurStyle::kNormal)));
112 }
113 
114 TEST_P(AiksTest, GradientOvalStrokeMaskBlurOuter) {
115  ASSERT_TRUE(OpenPlaygroundHere(DoGradientOvalStrokeMaskBlur(
116  GetContentScale(), /*sigma=*/10, DlBlurStyle::kOuter)));
117 }
118 
119 TEST_P(AiksTest, GradientOvalStrokeMaskBlurInner) {
120  ASSERT_TRUE(OpenPlaygroundHere(DoGradientOvalStrokeMaskBlur(
121  GetContentScale(), /*sigma=*/10, DlBlurStyle::kInner)));
122 }
123 
124 TEST_P(AiksTest, GradientOvalStrokeMaskBlurSolid) {
125  ASSERT_TRUE(OpenPlaygroundHere(DoGradientOvalStrokeMaskBlur(
126  GetContentScale(), /*sigma=*/10, DlBlurStyle::kSolid)));
127 }
128 
129 TEST_P(AiksTest, SolidColorCircleMaskBlurTinySigma) {
130  DisplayListBuilder builder;
131  builder.Scale(GetContentScale().x, GetContentScale().y);
132 
133  std::vector<float> sigmas = {0.0, 0.01, 1.0};
134  std::vector<DlColor> colors = {DlColor::kGreen(), DlColor::kYellow(),
135  DlColor::kRed()};
136  for (uint32_t i = 0; i < sigmas.size(); ++i) {
137  DlPaint paint;
138  paint.setColor(colors[i]);
139  paint.setMaskFilter(
140  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigmas[i]));
141 
142  builder.Save();
143  builder.Translate(100 + (i * 100), 100);
144  SkRRect rrect =
145  SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 100.0f, 100.0f), 100, 100);
146  builder.DrawRRect(rrect, paint);
147  builder.Restore();
148  }
149 
150  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
151 }
152 
153 TEST_P(AiksTest, CanRenderMaskBlurHugeSigma) {
154  DisplayListBuilder builder;
155 
156  DlPaint paint;
157  paint.setColor(DlColor::kGreen());
158  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 99999));
159  builder.DrawCircle({400, 400}, 300, paint);
160  builder.Restore();
161 
162  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
163 }
164 
165 TEST_P(AiksTest, CanRenderForegroundBlendWithMaskBlur) {
166  // This case triggers the ForegroundPorterDuffBlend path. The color filter
167  // should apply to the color only, and respect the alpha mask.
168  DisplayListBuilder builder;
169  builder.ClipRect(SkRect::MakeXYWH(100, 150, 400, 400));
170 
171  DlPaint paint;
172  paint.setColor(DlColor::kWhite());
173 
174  Sigma sigma = Radius(20);
175  paint.setMaskFilter(
176  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma));
177  paint.setColorFilter(
178  DlBlendColorFilter::Make(DlColor::kGreen(), DlBlendMode::kSrc));
179  builder.DrawCircle({400, 400}, 200, paint);
180 
181  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
182 }
183 
184 TEST_P(AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur) {
185  // This case triggers the ForegroundAdvancedBlend path. The color filter
186  // should apply to the color only, and respect the alpha mask.
187  DisplayListBuilder builder;
188  builder.ClipRect(SkRect::MakeXYWH(100, 150, 400, 400));
189 
190  DlPaint paint;
191  paint.setColor(
192  DlColor::RGBA(128.0f / 255.0f, 128.0f / 255.0f, 128.0f / 255.0f, 1.0f));
193 
194  Sigma sigma = Radius(20);
195  paint.setMaskFilter(
196  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma));
197  paint.setColorFilter(
198  DlBlendColorFilter::Make(DlColor::kGreen(), DlBlendMode::kColor));
199  builder.DrawCircle({400, 400}, 200, paint);
200  builder.Restore();
201 
202  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
203 }
204 
205 TEST_P(AiksTest, CanRenderBackdropBlurInteractive) {
206  auto callback = [&]() -> sk_sp<DisplayList> {
207  static PlaygroundPoint point_a(Point(50, 50), 30, Color::White());
208  static PlaygroundPoint point_b(Point(300, 200), 30, Color::White());
209  auto [a, b] = DrawPlaygroundLine(point_a, point_b);
210 
211  DisplayListBuilder builder;
212  DlPaint paint;
213  paint.setColor(DlColor::kCornflowerBlue());
214  builder.DrawCircle({100, 100}, 50, paint);
215 
216  paint.setColor(DlColor::kGreenYellow());
217  builder.DrawCircle({300, 200}, 100, paint);
218 
219  paint.setColor(DlColor::kDarkMagenta());
220  builder.DrawCircle({140, 170}, 75, paint);
221 
222  paint.setColor(DlColor::kOrangeRed());
223  builder.DrawCircle({180, 120}, 100, paint);
224 
225  SkRRect rrect =
226  SkRRect::MakeRectXY(SkRect::MakeLTRB(a.x, a.y, b.x, b.y), 20, 20);
227  builder.ClipRRect(rrect);
228 
229  DlPaint save_paint;
230  save_paint.setBlendMode(DlBlendMode::kSrc);
231 
232  auto backdrop_filter = DlBlurImageFilter::Make(20, 20, DlTileMode::kClamp);
233  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
234  builder.Restore();
235 
236  return builder.Build();
237  };
238 
239  ASSERT_TRUE(OpenPlaygroundHere(callback));
240 }
241 
242 TEST_P(AiksTest, CanRenderBackdropBlur) {
243  DisplayListBuilder builder;
244 
245  DlPaint paint;
246  paint.setColor(DlColor::kCornflowerBlue());
247  builder.DrawCircle({100, 100}, 50, paint);
248 
249  paint.setColor(DlColor::kGreenYellow());
250  builder.DrawCircle({300, 200}, 100, paint);
251 
252  paint.setColor(DlColor::kDarkMagenta());
253  builder.DrawCircle({140, 170}, 75, paint);
254 
255  paint.setColor(DlColor::kOrangeRed());
256  builder.DrawCircle({180, 120}, 100, paint);
257 
258  SkRRect rrect =
259  SkRRect::MakeRectXY(SkRect::MakeLTRB(75, 50, 375, 275), 20, 20);
260  builder.ClipRRect(rrect);
261 
262  DlPaint save_paint;
263  save_paint.setBlendMode(DlBlendMode::kSrc);
264  auto backdrop_filter = DlBlurImageFilter::Make(30, 30, DlTileMode::kClamp);
265  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
266  builder.Restore();
267 
268  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
269 }
270 
271 TEST_P(AiksTest, CanRenderBackdropBlurHugeSigma) {
272  DisplayListBuilder builder;
273 
274  DlPaint paint;
275  paint.setColor(DlColor::kGreen());
276  builder.DrawCircle({400, 400}, 300, paint);
277 
278  DlPaint save_paint;
279  save_paint.setBlendMode(DlBlendMode::kSrc);
280 
281  auto backdrop_filter =
282  DlBlurImageFilter::Make(999999, 999999, DlTileMode::kClamp);
283  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
284  builder.Restore();
285 
286  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
287 }
288 
289 TEST_P(AiksTest, CanRenderClippedBlur) {
290  DisplayListBuilder builder;
291  builder.ClipRect(SkRect::MakeXYWH(100, 150, 400, 400));
292 
293  DlPaint paint;
294  paint.setColor(DlColor::kGreen());
295  paint.setImageFilter(DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal));
296  builder.DrawCircle({400, 400}, 200, paint);
297  builder.Restore();
298 
299  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
300 }
301 
302 TEST_P(AiksTest, ClippedBlurFilterRendersCorrectlyInteractive) {
303  auto callback = [&]() -> sk_sp<DisplayList> {
304  static PlaygroundPoint playground_point(Point(400, 400), 20,
305  Color::Green());
306  auto point = DrawPlaygroundPoint(playground_point);
307 
308  DisplayListBuilder builder;
309  auto location = point - Point(400, 400);
310  builder.Translate(location.x, location.y);
311 
312  DlPaint paint;
313  Sigma sigma = Radius{120 * 3};
314  paint.setMaskFilter(
315  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma));
316  paint.setColor(DlColor::kRed());
317 
318  SkPath path = SkPath::Rect(SkRect::MakeLTRB(0, 0, 800, 800));
319  path.addCircle(0, 0, 0.5);
320  builder.DrawPath(path, paint);
321  return builder.Build();
322  };
323  ASSERT_TRUE(OpenPlaygroundHere(callback));
324 }
325 
326 TEST_P(AiksTest, ClippedBlurFilterRendersCorrectly) {
327  DisplayListBuilder builder;
328  builder.Translate(0, -400);
329  DlPaint paint;
330 
331  Sigma sigma = Radius{120 * 3};
332  paint.setMaskFilter(
333  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma));
334  paint.setColor(DlColor::kRed());
335 
336  SkPath path = SkPath::Rect(SkRect::MakeLTRB(0, 0, 800, 800));
337  path.addCircle(0, 0, 0.5);
338  builder.DrawPath(path, paint);
339 
340  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
341 }
342 
343 TEST_P(AiksTest, ClearBlendWithBlur) {
344  DisplayListBuilder builder;
345  DlPaint paint;
346  paint.setColor(DlColor::kBlue());
347  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600.0, 600.0), paint);
348 
349  DlPaint clear;
350  clear.setBlendMode(DlBlendMode::kClear);
351  clear.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20));
352 
353  builder.DrawCircle({300.0, 300.0}, 200.0, clear);
354 
355  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
356 }
357 
358 TEST_P(AiksTest, BlurHasNoEdge) {
359  Scalar sigma = 47.6;
360  auto callback = [&]() -> sk_sp<DisplayList> {
361  if (AiksTest::ImGuiBegin("Controls", nullptr,
362  ImGuiWindowFlags_AlwaysAutoResize)) {
363  ImGui::SliderFloat("Sigma", &sigma, 0, 50);
364  ImGui::End();
365  }
366  DisplayListBuilder builder;
367  builder.Scale(GetContentScale().x, GetContentScale().y);
368  builder.DrawPaint({});
369 
370  DlPaint paint;
371  paint.setColor(DlColor::kGreen());
372  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
373 
374  builder.DrawRect(SkRect::MakeXYWH(300, 300, 200, 200), paint);
375  return builder.Build();
376  };
377 
378  ASSERT_TRUE(OpenPlaygroundHere(callback));
379 }
380 
381 TEST_P(AiksTest, MaskBlurWithZeroSigmaIsSkipped) {
382  DisplayListBuilder builder;
383 
384  DlPaint paint;
385  paint.setColor(DlColor::kBlue());
386  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 0));
387 
388  builder.DrawCircle({300, 300}, 200, paint);
389  builder.DrawRect(SkRect::MakeLTRB(100, 300, 500, 600), paint);
390 
391  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
392 }
393 
395  DlBlurStyle style = DlBlurStyle::kNormal;
396  Scalar sigma = 1.0f;
397  Scalar alpha = 1.0f;
398  std::shared_ptr<DlImageFilter> image_filter;
399  bool invert_colors = false;
400  DlBlendMode blend_mode = DlBlendMode::kSrcOver;
401 };
402 
403 static sk_sp<DisplayList> MaskBlurVariantTest(
404  const AiksTest& test_context,
405  const MaskBlurTestConfig& config) {
406  DisplayListBuilder builder;
407  builder.Scale(test_context.GetContentScale().x,
408  test_context.GetContentScale().y);
409  builder.Scale(0.8f, 0.8f);
410  builder.Translate(50.f, 50.f);
411 
412  DlPaint draw_paint;
413  draw_paint.setColor(
414  DlColor::RGBA(Color::AntiqueWhite().red, Color::AntiqueWhite().green,
415  Color::AntiqueWhite().blue, Color::AntiqueWhite().alpha));
416  builder.DrawPaint(draw_paint);
417 
418  DlPaint paint;
419  paint.setMaskFilter(DlBlurMaskFilter::Make(config.style, config.sigma));
420  paint.setInvertColors(config.invert_colors);
421  paint.setImageFilter(config.image_filter);
422  paint.setBlendMode(config.blend_mode);
423 
424  const Scalar x = 50;
425  const Scalar radius = 20.0f;
426  const Scalar y_spacing = 100.0f;
427  Scalar alpha = config.alpha * 255;
428 
429  Scalar y = 50;
430  paint.setColor(DlColor::kCrimson().withAlpha(alpha));
431  builder.DrawRect(SkRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
432  radius, 60.0f - radius),
433  paint);
434 
435  y += y_spacing;
436  paint.setColor(DlColor::kBlue().withAlpha(alpha));
437  builder.DrawCircle({x + 25, y + 25}, radius, paint);
438 
439  y += y_spacing;
440  paint.setColor(DlColor::kGreen().withAlpha(alpha));
441  builder.DrawOval(SkRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
442  radius, 60.0f - radius),
443  paint);
444 
445  y += y_spacing;
446  paint.setColor(DlColor::kPurple().withAlpha(alpha));
447  SkRRect rrect =
448  SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, 60.0f, 60.0f), radius, radius);
449  builder.DrawRRect(rrect, paint);
450 
451  y += y_spacing;
452  paint.setColor(DlColor::kOrange().withAlpha(alpha));
453 
454  rrect =
455  SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, 60.0f, 60.0f), radius, 5.0);
456  builder.DrawRRect(rrect, paint);
457 
458  y += y_spacing;
459  paint.setColor(DlColor::kMaroon().withAlpha(alpha));
460 
461  {
462  SkPath path;
463  path.moveTo(x + 0, y + 60);
464  path.lineTo(x + 30, y + 0);
465  path.lineTo(x + 60, y + 60);
466  path.close();
467 
468  builder.DrawPath(path, paint);
469  }
470 
471  y += y_spacing;
472  paint.setColor(DlColor::kMaroon().withAlpha(alpha));
473  {
474  SkPath path;
475  path.addArc(SkRect::MakeXYWH(x + 5, y, 50, 50), 90, 180);
476  path.addArc(SkRect::MakeXYWH(x + 25, y, 50, 50), 90, 180);
477  path.close();
478  builder.DrawPath(path, paint);
479  }
480 
481  return builder.Build();
482 }
483 
484 static const std::map<std::string, MaskBlurTestConfig> kPaintVariations = {
485  // 1. Normal style, translucent, zero sigma.
486  {"NormalTranslucentZeroSigma",
487  {.style = DlBlurStyle::kNormal, .sigma = 0.0f, .alpha = 0.5f}},
488  // 2. Normal style, translucent.
489  {"NormalTranslucent",
490  {.style = DlBlurStyle::kNormal, .sigma = 8.0f, .alpha = 0.5f}},
491  // 3. Solid style, translucent.
492  {"SolidTranslucent",
493  {.style = DlBlurStyle::kSolid, .sigma = 8.0f, .alpha = 0.5f}},
494  // 4. Solid style, opaque.
495  {"SolidOpaque", {.style = DlBlurStyle::kSolid, .sigma = 8.0f}},
496  // 5. Solid style, translucent, color & image filtered.
497  {"SolidTranslucentWithFilters",
498  {.style = DlBlurStyle::kSolid,
499  .sigma = 8.0f,
500  .alpha = 0.5f,
501  .image_filter = DlBlurImageFilter::Make(3, 3, DlTileMode::kClamp),
502  .invert_colors = true}},
503  // 6. Solid style, translucent, exclusion blended.
504  {"SolidTranslucentExclusionBlend",
505  {.style = DlBlurStyle::kSolid,
506  .sigma = 8.0f,
507  .alpha = 0.5f,
508  .blend_mode = DlBlendMode::kExclusion}},
509  // 7. Inner style, translucent.
510  {"InnerTranslucent",
511  {.style = DlBlurStyle::kInner, .sigma = 8.0f, .alpha = 0.5f}},
512  // 8. Inner style, translucent, blurred.
513  {"InnerTranslucentWithBlurImageFilter",
514  {.style = DlBlurStyle::kInner,
515  .sigma = 8.0f,
516  .alpha = 0.5f,
517  .image_filter = DlBlurImageFilter::Make(3, 3, DlTileMode::kClamp)}},
518  // 9. Outer style, translucent.
519  {"OuterTranslucent",
520  {.style = DlBlurStyle::kOuter, .sigma = 8.0f, .alpha = 0.5f}},
521  // 10. Outer style, opaque, image filtered.
522  {"OuterOpaqueWithBlurImageFilter",
523  {.style = DlBlurStyle::kOuter,
524  .sigma = 8.0f,
525  .image_filter = DlBlurImageFilter::Make(3, 3, DlTileMode::kClamp)}},
526 };
527 
528 #define MASK_BLUR_VARIANT_TEST(config) \
529  TEST_P(AiksTest, MaskBlurVariantTest##config) { \
530  ASSERT_TRUE(OpenPlaygroundHere( \
531  MaskBlurVariantTest(*this, kPaintVariations.at(#config)))); \
532  }
533 
534 MASK_BLUR_VARIANT_TEST(NormalTranslucentZeroSigma)
535 MASK_BLUR_VARIANT_TEST(NormalTranslucent)
536 MASK_BLUR_VARIANT_TEST(SolidTranslucent)
537 MASK_BLUR_VARIANT_TEST(SolidOpaque)
538 MASK_BLUR_VARIANT_TEST(SolidTranslucentWithFilters)
539 MASK_BLUR_VARIANT_TEST(SolidTranslucentExclusionBlend)
540 MASK_BLUR_VARIANT_TEST(InnerTranslucent)
541 MASK_BLUR_VARIANT_TEST(InnerTranslucentWithBlurImageFilter)
542 MASK_BLUR_VARIANT_TEST(OuterTranslucent)
543 MASK_BLUR_VARIANT_TEST(OuterOpaqueWithBlurImageFilter)
544 
545 #undef MASK_BLUR_VARIANT_TEST
546 
547 TEST_P(AiksTest, GaussianBlurStyleInner) {
548  DisplayListBuilder builder;
549  builder.Scale(GetContentScale().x, GetContentScale().y);
550 
551  DlPaint paint;
552  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1));
553  builder.DrawPaint(paint);
554 
555  paint.setColor(DlColor::kGreen());
556  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kInner, 30));
557 
558  SkPath path;
559  path.moveTo(200, 200);
560  path.lineTo(300, 400);
561  path.lineTo(100, 400);
562  path.close();
563 
564  builder.DrawPath(path, paint);
565 
566  // Draw another thing to make sure the clip area is reset.
567  DlPaint red;
568  red.setColor(DlColor::kRed());
569  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
570 
571  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
572 }
573 
574 TEST_P(AiksTest, GaussianBlurStyleOuter) {
575  DisplayListBuilder builder;
576  builder.Scale(GetContentScale().x, GetContentScale().y);
577 
578  DlPaint paint;
579  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
580  builder.DrawPaint(paint);
581 
582  paint.setColor(DlColor::kGreen());
583  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kOuter, 30));
584 
585  SkPath path;
586  path.moveTo(200, 200);
587  path.lineTo(300, 400);
588  path.lineTo(100, 400);
589  path.close();
590 
591  builder.DrawPath(path, paint);
592 
593  // Draw another thing to make sure the clip area is reset.
594  DlPaint red;
595  red.setColor(DlColor::kRed());
596  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
597 
598  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
599 }
600 
601 TEST_P(AiksTest, GaussianBlurStyleSolid) {
602  DisplayListBuilder builder;
603  builder.Scale(GetContentScale().x, GetContentScale().y);
604 
605  DlPaint paint;
606  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
607  builder.DrawPaint(paint);
608 
609  paint.setColor(DlColor::kGreen());
610  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kSolid, 30));
611 
612  SkPath path;
613  path.moveTo(200, 200);
614  path.lineTo(300, 400);
615  path.lineTo(100, 400);
616  path.close();
617 
618  builder.DrawPath(path, paint);
619 
620  // Draw another thing to make sure the clip area is reset.
621  DlPaint red;
622  red.setColor(DlColor::kRed());
623  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
624 
625  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
626 }
627 
628 TEST_P(AiksTest, MaskBlurTexture) {
629  Scalar sigma = 30;
630  auto callback = [&]() -> sk_sp<DisplayList> {
631  if (AiksTest::ImGuiBegin("Controls", nullptr,
632  ImGuiWindowFlags_AlwaysAutoResize)) {
633  ImGui::SliderFloat("Sigma", &sigma, 0, 500);
634  ImGui::End();
635  }
636 
637  DisplayListBuilder builder;
638  builder.Scale(GetContentScale().x, GetContentScale().y);
639 
640  DlPaint paint;
641  paint.setColor(DlColor::kGreen());
642  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
643 
644  builder.DrawImage(
645  DlImageImpeller::Make(CreateTextureForFixture("boston.jpg")),
646  {200, 200}, DlImageSampling::kNearestNeighbor, &paint);
647 
648  DlPaint red;
649  red.setColor(DlColor::kRed());
650  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
651 
652  return builder.Build();
653  };
654  ASSERT_TRUE(OpenPlaygroundHere(callback));
655 }
656 
657 TEST_P(AiksTest, MaskBlurDoesntStretchContents) {
658  Scalar sigma = 70;
659  auto callback = [&]() -> sk_sp<DisplayList> {
660  if (AiksTest::ImGuiBegin("Controls", nullptr,
661  ImGuiWindowFlags_AlwaysAutoResize)) {
662  ImGui::SliderFloat("Sigma", &sigma, 0, 500);
663  ImGui::End();
664  }
665 
666  DisplayListBuilder builder;
667  builder.Scale(GetContentScale().x, GetContentScale().y);
668 
669  DlPaint paint;
670  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
671  builder.DrawPaint(paint);
672 
673  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
674 
675  builder.Transform(SkMatrix::Translate(100, 100) *
676  SkMatrix::Scale(0.5, 0.5));
677 
678  paint.setColorSource(std::make_shared<DlImageColorSource>(
679  DlImageImpeller::Make(boston), DlTileMode::kRepeat, DlTileMode::kRepeat,
680  DlImageSampling::kMipmapLinear));
681  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
682 
683  builder.DrawRect(SkRect::MakeXYWH(0, 0, boston->GetSize().width,
684  boston->GetSize().height),
685  paint);
686 
687  return builder.Build();
688  };
689  ASSERT_TRUE(OpenPlaygroundHere(callback));
690 }
691 
692 TEST_P(AiksTest, GaussianBlurAtPeripheryVertical) {
693  DisplayListBuilder builder;
694 
695  DlPaint paint;
696  builder.Scale(GetContentScale().x, GetContentScale().y);
697 
698  paint.setColor(DlColor::kLimeGreen());
699  SkRRect rrect = SkRRect::MakeRectXY(
700  SkRect::MakeLTRB(0, 0, GetWindowSize().width, 100), 10, 10);
701  builder.DrawRRect(rrect, paint);
702 
703  paint.setColor(DlColor::kMagenta());
704  rrect = SkRRect::MakeRectXY(
705  SkRect::MakeLTRB(0, 110, GetWindowSize().width, 210), 10, 10);
706  builder.DrawRRect(rrect, paint);
707  builder.ClipRect(SkRect::MakeLTRB(100, 0, 200, GetWindowSize().height));
708 
709  DlPaint save_paint;
710  save_paint.setBlendMode(DlBlendMode::kSrc);
711 
712  auto backdrop_filter = DlBlurImageFilter::Make(20, 20, DlTileMode::kClamp);
713 
714  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
715  builder.Restore();
716 
717  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
718 }
719 
720 TEST_P(AiksTest, GaussianBlurAtPeripheryHorizontal) {
721  DisplayListBuilder builder;
722 
723  builder.Scale(GetContentScale().x, GetContentScale().y);
724  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
725  builder.DrawImageRect(
726  DlImageImpeller::Make(boston),
727  SkRect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height),
728  SkRect::MakeLTRB(0, 0, GetWindowSize().width, 100),
729  DlImageSampling::kNearestNeighbor);
730 
731  DlPaint paint;
732  paint.setColor(DlColor::kMagenta());
733 
734  SkRRect rrect = SkRRect::MakeRectXY(
735  SkRect::MakeLTRB(0, 110, GetWindowSize().width, 210), 10, 10);
736  builder.DrawRRect(rrect, paint);
737  builder.ClipRect(SkRect::MakeLTRB(0, 50, GetWindowSize().width, 150));
738 
739  DlPaint save_paint;
740  save_paint.setBlendMode(DlBlendMode::kSrc);
741 
742  auto backdrop_filter = DlBlurImageFilter::Make(20, 20, DlTileMode::kClamp);
743  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
744 
745  builder.Restore();
746  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
747 }
748 
749 TEST_P(AiksTest, GaussianBlurAnimatedBackdrop) {
750  // This test is for checking out how stable rendering is when content is
751  // translated underneath a blur. Animating under a blur can cause
752  // *shimmering* to happen as a result of pixel alignment.
753  // See also: https://github.com/flutter/flutter/issues/140193
754  auto boston =
755  CreateTextureForFixture("boston.jpg", /*enable_mipmapping=*/true);
756  ASSERT_TRUE(boston);
757  int64_t count = 0;
758  Scalar sigma = 20.0;
759  Scalar freq = 0.1;
760  Scalar amp = 50.0;
761  auto callback = [&]() -> sk_sp<DisplayList> {
762  if (AiksTest::ImGuiBegin("Controls", nullptr,
763  ImGuiWindowFlags_AlwaysAutoResize)) {
764  ImGui::SliderFloat("Sigma", &sigma, 0, 200);
765  ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0);
766  ImGui::SliderFloat("Amplitude", &amp, 1, 100);
767  ImGui::End();
768  }
769 
770  DisplayListBuilder builder;
771  builder.Scale(GetContentScale().x, GetContentScale().y);
772  Scalar y = amp * sin(freq * 2.0 * M_PI * count / 60);
773  builder.DrawImage(
774  DlImageImpeller::Make(boston),
775  SkPoint::Make(1024 / 2 - boston->GetSize().width / 2,
776  (768 / 2 - boston->GetSize().height / 2) + y),
777  DlImageSampling::kMipmapLinear);
778  static PlaygroundPoint point_a(Point(100, 100), 20, Color::Red());
779  static PlaygroundPoint point_b(Point(900, 700), 20, Color::Red());
780  auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
781 
782  builder.ClipRect(
783  SkRect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
784  builder.ClipRect(SkRect::MakeLTRB(100, 100, 900, 700));
785 
786  DlPaint paint;
787  paint.setBlendMode(DlBlendMode::kSrc);
788 
789  auto backdrop_filter =
790  DlBlurImageFilter::Make(sigma, sigma, DlTileMode::kClamp);
791  builder.SaveLayer(nullptr, &paint, backdrop_filter.get());
792  count += 1;
793  return builder.Build();
794  };
795  ASSERT_TRUE(OpenPlaygroundHere(callback));
796 }
797 
798 TEST_P(AiksTest, GaussianBlurStyleInnerGradient) {
799  DisplayListBuilder builder;
800 
801  builder.Scale(GetContentScale().x, GetContentScale().y);
802 
803  DlPaint paint;
804  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
805  builder.DrawPaint(paint);
806 
807  std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
808  DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
809  std::vector<Scalar> stops = {0.0, 1.0};
810 
811  paint = DlPaint{};
812  paint.setColorSource(DlColorSource::MakeLinear(
813  /*start_point=*/{0, 0},
814  /*end_point=*/{200, 200},
815  /*stop_count=*/colors.size(),
816  /*colors=*/colors.data(),
817  /*stops=*/stops.data(),
818  /*tile_mode=*/DlTileMode::kMirror));
819  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kInner, 30));
820 
821  SkPath path;
822  path.moveTo(200, 200);
823  path.lineTo(300, 400);
824  path.lineTo(100, 400);
825  path.close();
826  builder.DrawPath(path, paint);
827 
828  // Draw another thing to make sure the clip area is reset.
829  DlPaint red;
830  red.setColor(DlColor::kRed());
831  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
832 
833  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
834 }
835 
836 TEST_P(AiksTest, GaussianBlurStyleSolidGradient) {
837  DisplayListBuilder builder;
838  builder.Scale(GetContentScale().x, GetContentScale().y);
839 
840  DlPaint paint;
841  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
842  builder.DrawPaint(paint);
843 
844  std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
845  DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
846  std::vector<Scalar> stops = {0.0, 1.0};
847 
848  paint = DlPaint{};
849  paint.setColorSource(DlColorSource::MakeLinear(
850  /*start_point=*/{0, 0},
851  /*end_point=*/{200, 200},
852  /*stop_count=*/colors.size(),
853  /*colors=*/colors.data(),
854  /*stops=*/stops.data(),
855  /*tile_mode=*/DlTileMode::kMirror));
856  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kSolid, 30));
857 
858  SkPath path;
859  path.moveTo(200, 200);
860  path.lineTo(300, 400);
861  path.lineTo(100, 400);
862  path.close();
863  builder.DrawPath(path, paint);
864 
865  // Draw another thing to make sure the clip area is reset.
866  DlPaint red;
867  red.setColor(DlColor::kRed());
868  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
869  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
870 }
871 
872 TEST_P(AiksTest, GaussianBlurStyleOuterGradient) {
873  DisplayListBuilder builder;
874  builder.Scale(GetContentScale().x, GetContentScale().y);
875 
876  DlPaint paint;
877  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
878  builder.DrawPaint(paint);
879 
880  std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
881  DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
882  std::vector<Scalar> stops = {0.0, 1.0};
883 
884  paint = DlPaint{};
885  paint.setColorSource(DlColorSource::MakeLinear(
886  /*start_point=*/{0, 0},
887  /*end_point=*/{200, 200},
888  /*stop_count=*/colors.size(),
889  /*colors=*/colors.data(),
890  /*stops=*/stops.data(),
891  /*tile_mode=*/DlTileMode::kMirror));
892  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kOuter, 30));
893 
894  SkPath path;
895  path.moveTo(200, 200);
896  path.lineTo(300, 400);
897  path.lineTo(100, 400);
898  path.close();
899  builder.DrawPath(path, paint);
900 
901  // Draw another thing to make sure the clip area is reset.
902  DlPaint red;
903  red.setColor(DlColor::kRed());
904  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
905  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
906 }
907 
908 TEST_P(AiksTest, GaussianBlurScaledAndClipped) {
909  DisplayListBuilder builder;
910  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
911  Rect bounds =
912  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
913  Vector2 image_center = Vector2(bounds.GetSize() / 2);
914 
915  DlPaint paint;
916  paint.setImageFilter(DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal));
917 
918  Vector2 clip_size = {150, 75};
919  Vector2 center = Vector2(1024, 768) / 2;
920  builder.Scale(GetContentScale().x, GetContentScale().y);
921 
922  auto rect =
923  Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size);
924  builder.ClipRect(SkRect::MakeLTRB(rect.GetLeft(), rect.GetTop(),
925  rect.GetRight(), rect.GetBottom()));
926  builder.Translate(center.x, center.y);
927  builder.Scale(0.6, 0.6);
928 
929  SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
930  bounds.GetRight(), bounds.GetBottom());
931  Rect dest = bounds.Shift(-image_center);
932  SkRect sk_dst = SkRect::MakeLTRB(dest.GetLeft(), dest.GetTop(),
933  dest.GetRight(), dest.GetBottom());
934  builder.DrawImageRect(DlImageImpeller::Make(boston), /*src=*/sk_bounds,
935  /*dst=*/sk_dst, DlImageSampling::kNearestNeighbor,
936  &paint);
937 
938  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
939 }
940 
941 TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) {
942  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
943 
944  auto callback = [&]() -> sk_sp<DisplayList> {
945  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
946  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
947  DlTileMode::kMirror, DlTileMode::kDecal};
948 
949  static float rotation = 0;
950  static float scale = 0.6;
951  static int selected_tile_mode = 3;
952 
953  if (AiksTest::ImGuiBegin("Controls", nullptr,
954  ImGuiWindowFlags_AlwaysAutoResize)) {
955  ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
956  ImGui::SliderFloat("Scale", &scale, 0, 2.0);
957  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
958  sizeof(tile_mode_names) / sizeof(char*));
959  ImGui::End();
960  }
961 
962  DisplayListBuilder builder;
963  Rect bounds =
964  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
965  Vector2 image_center = Vector2(bounds.GetSize() / 2);
966  DlPaint paint;
967  paint.setImageFilter(
968  DlBlurImageFilter::Make(20, 20, tile_modes[selected_tile_mode]));
969 
970  static PlaygroundPoint point_a(Point(362, 309), 20, Color::Red());
971  static PlaygroundPoint point_b(Point(662, 459), 20, Color::Red());
972  auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
973  Vector2 center = Vector2(1024, 768) / 2;
974 
975  builder.Scale(GetContentScale().x, GetContentScale().y);
976  builder.ClipRect(
977  SkRect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
978  builder.Translate(center.x, center.y);
979  builder.Scale(scale, scale);
980  builder.Rotate(rotation);
981 
982  SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
983  bounds.GetRight(), bounds.GetBottom());
984  Rect dest = bounds.Shift(-image_center);
985  SkRect sk_dst = SkRect::MakeLTRB(dest.GetLeft(), dest.GetTop(),
986  dest.GetRight(), dest.GetBottom());
987  builder.DrawImageRect(DlImageImpeller::Make(boston), /*src=*/sk_bounds,
988  /*dst=*/sk_dst, DlImageSampling::kNearestNeighbor,
989  &paint);
990  return builder.Build();
991  };
992 
993  ASSERT_TRUE(OpenPlaygroundHere(callback));
994 }
995 
996 TEST_P(AiksTest, GaussianBlurOneDimension) {
997  DisplayListBuilder builder;
998 
999  builder.Scale(GetContentScale().x, GetContentScale().y);
1000  builder.Scale(0.5, 0.5);
1001 
1002  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1003  builder.DrawImage(DlImageImpeller::Make(boston), {100, 100}, {});
1004 
1005  DlPaint paint;
1006  paint.setBlendMode(DlBlendMode::kSrc);
1007 
1008  auto backdrop_filter = DlBlurImageFilter::Make(50, 0, DlTileMode::kClamp);
1009  builder.SaveLayer(nullptr, &paint, backdrop_filter.get());
1010  builder.Restore();
1011  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1012 }
1013 
1014 // Smoketest to catch issues with the coverage hint.
1015 // Draws a rotated blurred image within a rectangle clip. The center of the clip
1016 // rectangle is the center of the rotated image. The entire area of the clip
1017 // rectangle should be filled with opaque colors output by the blur.
1018 TEST_P(AiksTest, GaussianBlurRotatedAndClipped) {
1019  DisplayListBuilder builder;
1020 
1021  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1022  Rect bounds =
1023  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
1024 
1025  DlPaint paint;
1026  paint.setImageFilter(DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal));
1027 
1028  Vector2 image_center = Vector2(bounds.GetSize() / 2);
1029  Vector2 clip_size = {150, 75};
1030  Vector2 center = Vector2(1024, 768) / 2;
1031  builder.Scale(GetContentScale().x, GetContentScale().y);
1032 
1033  auto clip_bounds =
1034  Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size);
1035  builder.ClipRect(SkRect::MakeLTRB(clip_bounds.GetLeft(), clip_bounds.GetTop(),
1036  clip_bounds.GetRight(),
1037  clip_bounds.GetBottom()));
1038  builder.Translate(center.x, center.y);
1039  builder.Scale(0.6, 0.6);
1040  builder.Rotate(25);
1041 
1042  auto dst_rect = bounds.Shift(-image_center);
1043  builder.DrawImageRect(
1044  DlImageImpeller::Make(boston), /*src=*/
1045  SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(), bounds.GetRight(),
1046  bounds.GetBottom()),
1047  /*dst=*/
1048  SkRect::MakeLTRB(dst_rect.GetLeft(), dst_rect.GetTop(),
1049  dst_rect.GetRight(), dst_rect.GetBottom()),
1050  DlImageSampling::kMipmapLinear, &paint);
1051 
1052  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1053 }
1054 
1055 TEST_P(AiksTest, GaussianBlurRotatedNonUniform) {
1056  auto callback = [&]() -> sk_sp<DisplayList> {
1057  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
1058  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
1059  DlTileMode::kMirror, DlTileMode::kDecal};
1060 
1061  static float rotation = 45;
1062  static float scale = 0.6;
1063  static int selected_tile_mode = 3;
1064 
1065  if (AiksTest::ImGuiBegin("Controls", nullptr,
1066  ImGuiWindowFlags_AlwaysAutoResize)) {
1067  ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
1068  ImGui::SliderFloat("Scale", &scale, 0, 2.0);
1069  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
1070  sizeof(tile_mode_names) / sizeof(char*));
1071  ImGui::End();
1072  }
1073 
1074  DisplayListBuilder builder;
1075 
1076  DlPaint paint;
1077  paint.setColor(DlColor::kGreen());
1078  paint.setImageFilter(
1079  DlBlurImageFilter::Make(50, 0, tile_modes[selected_tile_mode]));
1080 
1081  Vector2 center = Vector2(1024, 768) / 2;
1082  builder.Scale(GetContentScale().x, GetContentScale().y);
1083  builder.Translate(center.x, center.y);
1084  builder.Scale(scale, scale);
1085  builder.Rotate(rotation);
1086 
1087  SkRRect rrect =
1088  SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -100, 200, 200), 10, 10);
1089  builder.DrawRRect(rrect, paint);
1090  return builder.Build();
1091  };
1092 
1093  ASSERT_TRUE(OpenPlaygroundHere(callback));
1094 }
1095 
1096 TEST_P(AiksTest, BlurredRectangleWithShader) {
1097  DisplayListBuilder builder;
1098  builder.Scale(GetContentScale().x, GetContentScale().y);
1099 
1100  auto paint_lines = [&builder](Scalar dx, Scalar dy, DlPaint paint) {
1101  auto draw_line = [&builder, &paint](SkPoint a, SkPoint b) {
1102  SkPath line = SkPath::Line(a, b);
1103  builder.DrawPath(line, paint);
1104  };
1105  paint.setStrokeWidth(5);
1106  paint.setDrawStyle(DlDrawStyle::kStroke);
1107  draw_line(SkPoint::Make(dx + 100, dy + 100),
1108  SkPoint::Make(dx + 200, dy + 200));
1109  draw_line(SkPoint::Make(dx + 100, dy + 200),
1110  SkPoint::Make(dx + 200, dy + 100));
1111  draw_line(SkPoint::Make(dx + 150, dy + 100),
1112  SkPoint::Make(dx + 200, dy + 150));
1113  draw_line(SkPoint::Make(dx + 100, dy + 150),
1114  SkPoint::Make(dx + 150, dy + 200));
1115  };
1116 
1117  AiksContext renderer(GetContext(), nullptr);
1118  DisplayListBuilder recorder_builder;
1119  for (int x = 0; x < 5; ++x) {
1120  for (int y = 0; y < 5; ++y) {
1121  SkRect rect = SkRect::MakeXYWH(x * 20, y * 20, 20, 20);
1122  DlPaint paint;
1123  paint.setColor(((x + y) & 1) == 0 ? DlColor::kYellow()
1124  : DlColor::kBlue());
1125 
1126  recorder_builder.DrawRect(rect, paint);
1127  }
1128  }
1129  auto texture =
1130  DisplayListToTexture(recorder_builder.Build(), {100, 100}, renderer);
1131 
1132  auto image_source = std::make_shared<DlImageColorSource>(
1133  DlImageImpeller::Make(texture), DlTileMode::kRepeat, DlTileMode::kRepeat);
1134  auto blur_filter = DlBlurImageFilter::Make(5, 5, DlTileMode::kDecal);
1135 
1136  DlPaint paint;
1137  paint.setColor(DlColor::kDarkGreen());
1138  builder.DrawRect(SkRect::MakeLTRB(0, 0, 300, 600), paint);
1139 
1140  paint.setColorSource(image_source);
1141  builder.DrawRect(SkRect::MakeLTRB(100, 100, 200, 200), paint);
1142 
1143  paint.setColorSource(nullptr);
1144  paint.setColor(DlColor::kRed());
1145  builder.DrawRect(SkRect::MakeLTRB(300, 0, 600, 600), paint);
1146 
1147  paint.setColorSource(image_source);
1148  paint.setImageFilter(blur_filter);
1149  builder.DrawRect(SkRect::MakeLTRB(400, 100, 500, 200), paint);
1150 
1151  paint.setImageFilter(nullptr);
1152  paint_lines(0, 300, paint);
1153 
1154  paint.setImageFilter(blur_filter);
1155  paint_lines(300, 300, paint);
1156 
1157  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1158 }
1159 
1160 TEST_P(AiksTest, GaussianBlurWithoutDecalSupport) {
1161  if (GetParam() != PlaygroundBackend::kMetal) {
1162  GTEST_SKIP()
1163  << "This backend doesn't yet support setting device capabilities.";
1164  }
1165  if (!WillRenderSomething()) {
1166  // Sometimes these tests are run without playgrounds enabled which is
1167  // pointless for this test since we are asserting that
1168  // `SupportsDecalSamplerAddressMode` is called.
1169  GTEST_SKIP() << "This test requires playgrounds.";
1170  }
1171 
1172  std::shared_ptr<const Capabilities> old_capabilities =
1173  GetContext()->GetCapabilities();
1174  auto mock_capabilities = std::make_shared<MockCapabilities>();
1175  EXPECT_CALL(*mock_capabilities, SupportsDecalSamplerAddressMode())
1176  .Times(::testing::AtLeast(1))
1177  .WillRepeatedly(::testing::Return(false));
1178  FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultColorFormat);
1179  FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultStencilFormat);
1180  FLT_FORWARD(mock_capabilities, old_capabilities,
1181  GetDefaultDepthStencilFormat);
1182  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsOffscreenMSAA);
1183  FLT_FORWARD(mock_capabilities, old_capabilities,
1184  SupportsImplicitResolvingMSAA);
1185  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsReadFromResolve);
1186  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsFramebufferFetch);
1187  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsSSBO);
1188  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsCompute);
1189  FLT_FORWARD(mock_capabilities, old_capabilities,
1190  SupportsTextureToTextureBlits);
1191  FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultGlyphAtlasFormat);
1192  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsTriangleFan);
1193  ASSERT_TRUE(SetCapabilities(mock_capabilities).ok());
1194 
1195  auto texture = DlImageImpeller::Make(CreateTextureForFixture("boston.jpg"));
1196 
1197  DisplayListBuilder builder;
1198  builder.Scale(GetContentScale().x * 0.5, GetContentScale().y * 0.5);
1199 
1200  DlPaint paint;
1201  paint.setColor(DlColor::kBlack());
1202  builder.DrawPaint(paint);
1203 
1204  auto blur_filter = DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal);
1205  paint.setImageFilter(blur_filter);
1206  builder.DrawImage(texture, SkPoint::Make(200, 200), {}, &paint);
1207  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1208 }
1209 
1210 // This addresses a bug where tiny blurs could result in mip maps that beyond
1211 // the limits for the textures used for blurring.
1212 // See also: b/323402168
1213 TEST_P(AiksTest, GaussianBlurSolidColorTinyMipMap) {
1214  AiksContext renderer(GetContext(), nullptr);
1215 
1216  for (int32_t i = 1; i < 5; ++i) {
1217  DisplayListBuilder builder;
1218  Scalar fi = i;
1219  SkPath path;
1220  path.moveTo(100, 100);
1221  path.lineTo(100 + fi, 100 + fi);
1222 
1223  DlPaint paint;
1224  paint.setColor(DlColor::kChartreuse());
1225  auto blur_filter = DlBlurImageFilter::Make(0.1, 0.1, DlTileMode::kClamp);
1226  paint.setImageFilter(blur_filter);
1227 
1228  builder.DrawPath(path, paint);
1229 
1230  auto image = DisplayListToTexture(builder.Build(), {1024, 768}, renderer);
1231  EXPECT_TRUE(image) << " length " << i;
1232  }
1233 }
1234 
1235 // This addresses a bug where tiny blurs could result in mip maps that beyond
1236 // the limits for the textures used for blurring.
1237 // See also: b/323402168
1238 TEST_P(AiksTest, GaussianBlurBackdropTinyMipMap) {
1239  AiksContext renderer(GetContext(), nullptr);
1240  for (int32_t i = 1; i < 5; ++i) {
1241  DisplayListBuilder builder;
1242 
1243  ISize clip_size = ISize(i, i);
1244  builder.Save();
1245  builder.ClipRect(
1246  SkRect::MakeXYWH(400, 400, clip_size.width, clip_size.height));
1247 
1248  DlPaint paint;
1249  paint.setColor(DlColor::kGreen());
1250  auto blur_filter = DlBlurImageFilter::Make(0.1, 0.1, DlTileMode::kDecal);
1251  paint.setImageFilter(blur_filter);
1252 
1253  builder.DrawCircle({400, 400}, 200, paint);
1254  builder.Restore();
1255 
1256  auto image = DisplayListToTexture(builder.Build(), {1024, 768}, renderer);
1257  EXPECT_TRUE(image) << " length " << i;
1258  }
1259 }
1260 
1261 } // namespace testing
1262 } // namespace impeller
impeller::ISize
ISize64 ISize
Definition: size.h:140
impeller::Sigma::sigma
Scalar sigma
Definition: sigma.h:33
impeller::testing::MaskBlurTestConfig::alpha
Scalar alpha
Definition: aiks_dl_blur_unittests.cc:397
impeller::AiksPlayground
Definition: aiks_playground.h:16
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::AiksContext
Definition: aiks_context.h:19
impeller::testing::MaskBlurTestConfig::sigma
Scalar sigma
Definition: aiks_dl_blur_unittests.cc:396
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:271
aiks_unittests.h
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::PlaygroundBackend::kMetal
@ kMetal
dl_dispatcher.h
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::testing::MaskBlurTestConfig
Definition: aiks_dl_blur_unittests.cc:394
impeller::DlImageImpeller::Make
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Definition: dl_image_impeller.cc:23
impeller::TRect::Shift
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:596
impeller::testing::DoGradientOvalStrokeMaskBlur
sk_sp< flutter::DisplayList > DoGradientOvalStrokeMaskBlur(Vector2 content_Scale, Scalar sigma, DlBlurStyle style)
Definition: aiks_dl_blur_unittests.cc:63
impeller::TSize
Definition: size.h:19
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::testing::kPaintVariations
static const std::map< std::string, MaskBlurTestConfig > kPaintVariations
Definition: aiks_dl_blur_unittests.cc:484
impeller::TRect::GetLeft
constexpr auto GetLeft() const
Definition: rect.h:345
impeller::Radius
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:48
widgets.h
impeller::Color::White
static constexpr Color White()
Definition: color.h:263
impeller::Sigma
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
flutter
Definition: dl_golden_blur_unittests.cc:15
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:273
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:776
impeller::testing::MaskBlurTestConfig::blend_mode
DlBlendMode blend_mode
Definition: aiks_dl_blur_unittests.cc:400
impeller::TSize::width
Type width
Definition: size.h:22
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:78
impeller::Color::AntiqueWhite
static constexpr Color AntiqueWhite()
Definition: color.h:285
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:321
impeller::TRect::GetRight
constexpr auto GetRight() const
Definition: rect.h:349
impeller::Playground::GetContentScale
Point GetContentScale() const
Definition: playground.cc:185
impeller::DrawPlaygroundPoint
Point DrawPlaygroundPoint(PlaygroundPoint &point)
Definition: widgets.cc:9
impeller::PlaygroundPoint
Definition: widgets.h:17
MASK_BLUR_VARIANT_TEST
#define MASK_BLUR_VARIANT_TEST(config)
Definition: aiks_dl_blur_unittests.cc:528
impeller::TPoint< Scalar >
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::testing::MaskBlurTestConfig::image_filter
std::shared_ptr< DlImageFilter > image_filter
Definition: aiks_dl_blur_unittests.cc:398
scale
const Scalar scale
Definition: stroke_path_geometry.cc:301
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:30
impeller::TRect::GetBottom
constexpr auto GetBottom() const
Definition: rect.h:351
impeller::TPoint::Rotate
constexpr TPoint Rotate(const Radians &angle) const
Definition: point.h:226
impeller::testing::MaskBlurTestConfig::style
DlBlurStyle style
Definition: aiks_dl_blur_unittests.cc:395
impeller::DisplayListToTexture
std::shared_ptr< Texture > DisplayListToTexture(const sk_sp< flutter::DisplayList > &display_list, ISize size, AiksContext &context, bool reset_host_buffer, bool generate_mips)
Render the provided display list to a texture with the given size.
Definition: dl_dispatcher.cc:1195
impeller::TSize::height
Type height
Definition: size.h:23
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::testing::MaskBlurVariantTest
static sk_sp< DisplayList > MaskBlurVariantTest(const AiksTest &test_context, const MaskBlurTestConfig &config)
Definition: aiks_dl_blur_unittests.cc:403
impeller
Definition: allocation.cc:12
impeller::TRect::GetTop
constexpr auto GetTop() const
Definition: rect.h:347
impeller::TRect< Scalar >
impeller::testing::MaskBlurTestConfig::invert_colors
bool invert_colors
Definition: aiks_dl_blur_unittests.cc:399
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:612
impeller::DrawPlaygroundLine
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
dl_image_impeller.h