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 "flutter/display_list/display_list.h"
6 #include "flutter/display_list/dl_blend_mode.h"
7 #include "flutter/display_list/dl_builder.h"
8 #include "flutter/display_list/dl_color.h"
9 #include "flutter/display_list/dl_paint.h"
10 #include "flutter/display_list/dl_sampling_options.h"
11 #include "flutter/display_list/dl_tile_mode.h"
12 #include "flutter/display_list/effects/dl_color_filter.h"
13 #include "flutter/display_list/effects/dl_color_source.h"
14 #include "flutter/display_list/effects/dl_image_filter.h"
15 #include "flutter/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(SkPoint{100, 0}, SkPoint{100, 60}, line_paint);
92  builder.DrawLine(SkPoint{0, 30}, SkPoint{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(SkPoint{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  DlColorFilter::MakeBlend(DlColor::kGreen(), DlBlendMode::kSrc));
179  builder.DrawCircle(SkPoint{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  DlColorFilter::MakeBlend(DlColor::kGreen(), DlBlendMode::kColor));
199  builder.DrawCircle(SkPoint{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(SkPoint{100, 100}, 50, paint);
215 
216  paint.setColor(DlColor::kGreenYellow());
217  builder.DrawCircle(SkPoint{300, 200}, 100, paint);
218 
219  paint.setColor(DlColor::kDarkMagenta());
220  builder.DrawCircle(SkPoint{140, 170}, 75, paint);
221 
222  paint.setColor(DlColor::kOrangeRed());
223  builder.DrawCircle(SkPoint{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 = DlImageFilter::MakeBlur(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(SkPoint{100, 100}, 50, paint);
248 
249  paint.setColor(DlColor::kGreenYellow());
250  builder.DrawCircle(SkPoint{300, 200}, 100, paint);
251 
252  paint.setColor(DlColor::kDarkMagenta());
253  builder.DrawCircle(SkPoint{140, 170}, 75, paint);
254 
255  paint.setColor(DlColor::kOrangeRed());
256  builder.DrawCircle(SkPoint{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 = DlImageFilter::MakeBlur(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, CanRenderBackdropBlurWithSingleBackdropId) {
272  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
273 
274  DisplayListBuilder builder;
275 
276  DlPaint paint;
277  builder.DrawImage(image, SkPoint::Make(50.0, 50.0),
278  DlImageSampling::kNearestNeighbor, &paint);
279 
280  SkRRect rrect =
281  SkRRect::MakeRectXY(SkRect::MakeXYWH(50, 250, 100, 100), 20, 20);
282  builder.Save();
283  builder.ClipRRect(rrect);
284 
285  DlPaint save_paint;
286  save_paint.setBlendMode(DlBlendMode::kSrc);
287  auto backdrop_filter = DlImageFilter::MakeBlur(30, 30, DlTileMode::kClamp);
288  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get(),
289  /*backdrop_id=*/1);
290  builder.Restore();
291  builder.Restore();
292 
293  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
294 }
295 
296 TEST_P(AiksTest, CanRenderMultipleBackdropBlurWithSingleBackdropId) {
297  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
298 
299  DisplayListBuilder builder;
300 
301  DlPaint paint;
302  builder.DrawImage(image, SkPoint::Make(50.0, 50.0),
303  DlImageSampling::kNearestNeighbor, &paint);
304 
305  for (int i = 0; i < 6; i++) {
306  SkRRect rrect = SkRRect::MakeRectXY(
307  SkRect::MakeXYWH(50 + (i * 100), 250, 100, 100), 20, 20);
308  builder.Save();
309  builder.ClipRRect(rrect);
310 
311  DlPaint save_paint;
312  save_paint.setBlendMode(DlBlendMode::kSrc);
313  auto backdrop_filter = DlImageFilter::MakeBlur(30, 30, DlTileMode::kClamp);
314  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get(),
315  /*backdrop_id=*/1);
316  builder.Restore();
317  builder.Restore();
318  }
319 
320  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
321 }
322 
324  CanRenderMultipleBackdropBlurWithSingleBackdropIdAndDistinctFilters) {
325  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
326 
327  DisplayListBuilder builder;
328 
329  DlPaint paint;
330  builder.DrawImage(image, SkPoint::Make(50.0, 50.0),
331  DlImageSampling::kNearestNeighbor, &paint);
332 
333  for (int i = 0; i < 6; i++) {
334  SkRRect rrect = SkRRect::MakeRectXY(
335  SkRect::MakeXYWH(50 + (i * 100), 250, 100, 100), 20, 20);
336  builder.Save();
337  builder.ClipRRect(rrect);
338 
339  DlPaint save_paint;
340  save_paint.setBlendMode(DlBlendMode::kSrc);
341  auto backdrop_filter =
342  DlImageFilter::MakeBlur(30 + i, 30, DlTileMode::kClamp);
343  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get(),
344  /*backdrop_id=*/1);
345  builder.Restore();
346  builder.Restore();
347  }
348 
349  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
350 }
351 
352 TEST_P(AiksTest, CanRenderBackdropBlurHugeSigma) {
353  DisplayListBuilder builder;
354 
355  DlPaint paint;
356  paint.setColor(DlColor::kGreen());
357  builder.DrawCircle(SkPoint{400, 400}, 300, paint);
358 
359  DlPaint save_paint;
360  save_paint.setBlendMode(DlBlendMode::kSrc);
361 
362  auto backdrop_filter =
363  DlImageFilter::MakeBlur(999999, 999999, DlTileMode::kClamp);
364  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
365  builder.Restore();
366 
367  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
368 }
369 
370 TEST_P(AiksTest, CanRenderClippedBlur) {
371  DisplayListBuilder builder;
372  builder.ClipRect(SkRect::MakeXYWH(100, 150, 400, 400));
373 
374  DlPaint paint;
375  paint.setColor(DlColor::kGreen());
376  paint.setImageFilter(DlImageFilter::MakeBlur(20, 20, DlTileMode::kDecal));
377  builder.DrawCircle(SkPoint{400, 400}, 200, paint);
378  builder.Restore();
379 
380  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
381 }
382 
383 TEST_P(AiksTest, ClippedBlurFilterRendersCorrectlyInteractive) {
384  auto callback = [&]() -> sk_sp<DisplayList> {
385  static PlaygroundPoint playground_point(Point(400, 400), 20,
386  Color::Green());
387  auto point = DrawPlaygroundPoint(playground_point);
388 
389  DisplayListBuilder builder;
390  auto location = point - Point(400, 400);
391  builder.Translate(location.x, location.y);
392 
393  DlPaint paint;
394  Sigma sigma = Radius{120 * 3};
395  paint.setMaskFilter(
396  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma));
397  paint.setColor(DlColor::kRed());
398 
399  SkPath path = SkPath::Rect(SkRect::MakeLTRB(0, 0, 800, 800));
400  path.addCircle(0, 0, 0.5);
401  builder.DrawPath(path, paint);
402  return builder.Build();
403  };
404  ASSERT_TRUE(OpenPlaygroundHere(callback));
405 }
406 
407 TEST_P(AiksTest, ClippedBlurFilterRendersCorrectly) {
408  DisplayListBuilder builder;
409  builder.Translate(0, -400);
410  DlPaint paint;
411 
412  Sigma sigma = Radius{120 * 3};
413  paint.setMaskFilter(
414  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma));
415  paint.setColor(DlColor::kRed());
416 
417  SkPath path = SkPath::Rect(SkRect::MakeLTRB(0, 0, 800, 800));
418  path.addCircle(0, 0, 0.5);
419  builder.DrawPath(path, paint);
420 
421  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
422 }
423 
424 TEST_P(AiksTest, ClearBlendWithBlur) {
425  DisplayListBuilder builder;
426  DlPaint paint;
427  paint.setColor(DlColor::kBlue());
428  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600.0, 600.0), paint);
429 
430  DlPaint clear;
431  clear.setBlendMode(DlBlendMode::kClear);
432  clear.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20));
433 
434  builder.DrawCircle(SkPoint{300.0, 300.0}, 200.0, clear);
435 
436  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
437 }
438 
439 TEST_P(AiksTest, BlurHasNoEdge) {
440  Scalar sigma = 47.6;
441  auto callback = [&]() -> sk_sp<DisplayList> {
442  if (AiksTest::ImGuiBegin("Controls", nullptr,
443  ImGuiWindowFlags_AlwaysAutoResize)) {
444  ImGui::SliderFloat("Sigma", &sigma, 0, 50);
445  ImGui::End();
446  }
447  DisplayListBuilder builder;
448  builder.Scale(GetContentScale().x, GetContentScale().y);
449  builder.DrawPaint({});
450 
451  DlPaint paint;
452  paint.setColor(DlColor::kGreen());
453  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
454 
455  builder.DrawRect(SkRect::MakeXYWH(300, 300, 200, 200), paint);
456  return builder.Build();
457  };
458 
459  ASSERT_TRUE(OpenPlaygroundHere(callback));
460 }
461 
462 TEST_P(AiksTest, MaskBlurWithZeroSigmaIsSkipped) {
463  DisplayListBuilder builder;
464 
465  DlPaint paint;
466  paint.setColor(DlColor::kBlue());
467  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 0));
468 
469  builder.DrawCircle(SkPoint{300, 300}, 200, paint);
470  builder.DrawRect(SkRect::MakeLTRB(100, 300, 500, 600), paint);
471 
472  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
473 }
474 
475 TEST_P(AiksTest, MaskBlurOnZeroDimensionIsSkippedWideGamut) {
476  // Making sure this test is run on a wide gamut enabled backend
477  EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
479 
480  DisplayListBuilder builder;
481  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
482 
483  DlPaint paint;
484  paint.setColor(DlColor::kBlue());
485  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 10));
486 
487  // Zero height above
488  builder.DrawRect(DlRect::MakeLTRB(100, 250, 500, 250), paint);
489  // Regular rect
490  builder.DrawRect(DlRect::MakeLTRB(100, 300, 500, 600), paint);
491  // Zero width to the right
492  builder.DrawRect(DlRect::MakeLTRB(550, 300, 550, 600), paint);
493 
494  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
495 }
496 
498  DlBlurStyle style = DlBlurStyle::kNormal;
499  Scalar sigma = 1.0f;
500  Scalar alpha = 1.0f;
501  std::shared_ptr<DlImageFilter> image_filter;
502  bool invert_colors = false;
503  DlBlendMode blend_mode = DlBlendMode::kSrcOver;
504 };
505 
506 static sk_sp<DisplayList> MaskBlurVariantTest(
507  const AiksTest& test_context,
508  const MaskBlurTestConfig& config) {
509  DisplayListBuilder builder;
510  builder.Scale(test_context.GetContentScale().x,
511  test_context.GetContentScale().y);
512  builder.Scale(0.8f, 0.8f);
513  builder.Translate(50.f, 50.f);
514 
515  DlPaint draw_paint;
516  draw_paint.setColor(
517  DlColor::RGBA(Color::AntiqueWhite().red, Color::AntiqueWhite().green,
518  Color::AntiqueWhite().blue, Color::AntiqueWhite().alpha));
519  builder.DrawPaint(draw_paint);
520 
521  DlPaint paint;
522  paint.setMaskFilter(DlBlurMaskFilter::Make(config.style, config.sigma));
523  paint.setInvertColors(config.invert_colors);
524  paint.setImageFilter(config.image_filter);
525  paint.setBlendMode(config.blend_mode);
526 
527  const Scalar x = 50;
528  const Scalar radius = 20.0f;
529  const Scalar y_spacing = 100.0f;
530  Scalar alpha = config.alpha * 255;
531 
532  Scalar y = 50;
533  paint.setColor(DlColor::kCrimson().withAlpha(alpha));
534  builder.DrawRect(SkRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
535  radius, 60.0f - radius),
536  paint);
537 
538  y += y_spacing;
539  paint.setColor(DlColor::kBlue().withAlpha(alpha));
540  builder.DrawCircle(SkPoint{x + 25, y + 25}, radius, paint);
541 
542  y += y_spacing;
543  paint.setColor(DlColor::kGreen().withAlpha(alpha));
544  builder.DrawOval(SkRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
545  radius, 60.0f - radius),
546  paint);
547 
548  y += y_spacing;
549  paint.setColor(DlColor::kPurple().withAlpha(alpha));
550  SkRRect rrect =
551  SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, 60.0f, 60.0f), radius, radius);
552  builder.DrawRRect(rrect, paint);
553 
554  y += y_spacing;
555  paint.setColor(DlColor::kOrange().withAlpha(alpha));
556 
557  rrect =
558  SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, 60.0f, 60.0f), radius, 5.0);
559  builder.DrawRRect(rrect, paint);
560 
561  y += y_spacing;
562  paint.setColor(DlColor::kMaroon().withAlpha(alpha));
563 
564  {
565  SkPath path;
566  path.moveTo(x + 0, y + 60);
567  path.lineTo(x + 30, y + 0);
568  path.lineTo(x + 60, y + 60);
569  path.close();
570 
571  builder.DrawPath(path, paint);
572  }
573 
574  y += y_spacing;
575  paint.setColor(DlColor::kMaroon().withAlpha(alpha));
576  {
577  SkPath path;
578  path.addArc(SkRect::MakeXYWH(x + 5, y, 50, 50), 90, 180);
579  path.addArc(SkRect::MakeXYWH(x + 25, y, 50, 50), 90, 180);
580  path.close();
581  builder.DrawPath(path, paint);
582  }
583 
584  return builder.Build();
585 }
586 
587 static const std::map<std::string, MaskBlurTestConfig> kPaintVariations = {
588  // 1. Normal style, translucent, zero sigma.
589  {"NormalTranslucentZeroSigma",
590  {.style = DlBlurStyle::kNormal, .sigma = 0.0f, .alpha = 0.5f}},
591  // 2. Normal style, translucent.
592  {"NormalTranslucent",
593  {.style = DlBlurStyle::kNormal, .sigma = 8.0f, .alpha = 0.5f}},
594  // 3. Solid style, translucent.
595  {"SolidTranslucent",
596  {.style = DlBlurStyle::kSolid, .sigma = 8.0f, .alpha = 0.5f}},
597  // 4. Solid style, opaque.
598  {"SolidOpaque", {.style = DlBlurStyle::kSolid, .sigma = 8.0f}},
599  // 5. Solid style, translucent, color & image filtered.
600  {"SolidTranslucentWithFilters",
601  {.style = DlBlurStyle::kSolid,
602  .sigma = 8.0f,
603  .alpha = 0.5f,
604  .image_filter = DlImageFilter::MakeBlur(3, 3, DlTileMode::kClamp),
605  .invert_colors = true}},
606  // 6. Solid style, translucent, exclusion blended.
607  {"SolidTranslucentExclusionBlend",
608  {.style = DlBlurStyle::kSolid,
609  .sigma = 8.0f,
610  .alpha = 0.5f,
611  .blend_mode = DlBlendMode::kExclusion}},
612  // 7. Inner style, translucent.
613  {"InnerTranslucent",
614  {.style = DlBlurStyle::kInner, .sigma = 8.0f, .alpha = 0.5f}},
615  // 8. Inner style, translucent, blurred.
616  {"InnerTranslucentWithBlurImageFilter",
617  {.style = DlBlurStyle::kInner,
618  .sigma = 8.0f,
619  .alpha = 0.5f,
620  .image_filter = DlImageFilter::MakeBlur(3, 3, DlTileMode::kClamp)}},
621  // 9. Outer style, translucent.
622  {"OuterTranslucent",
623  {.style = DlBlurStyle::kOuter, .sigma = 8.0f, .alpha = 0.5f}},
624  // 10. Outer style, opaque, image filtered.
625  {"OuterOpaqueWithBlurImageFilter",
626  {.style = DlBlurStyle::kOuter,
627  .sigma = 8.0f,
628  .image_filter = DlImageFilter::MakeBlur(3, 3, DlTileMode::kClamp)}},
629 };
630 
631 #define MASK_BLUR_VARIANT_TEST(config) \
632  TEST_P(AiksTest, MaskBlurVariantTest##config) { \
633  ASSERT_TRUE(OpenPlaygroundHere( \
634  MaskBlurVariantTest(*this, kPaintVariations.at(#config)))); \
635  }
636 
637 MASK_BLUR_VARIANT_TEST(NormalTranslucentZeroSigma)
638 MASK_BLUR_VARIANT_TEST(NormalTranslucent)
639 MASK_BLUR_VARIANT_TEST(SolidTranslucent)
640 MASK_BLUR_VARIANT_TEST(SolidOpaque)
641 MASK_BLUR_VARIANT_TEST(SolidTranslucentWithFilters)
642 MASK_BLUR_VARIANT_TEST(SolidTranslucentExclusionBlend)
643 MASK_BLUR_VARIANT_TEST(InnerTranslucent)
644 MASK_BLUR_VARIANT_TEST(InnerTranslucentWithBlurImageFilter)
645 MASK_BLUR_VARIANT_TEST(OuterTranslucent)
646 MASK_BLUR_VARIANT_TEST(OuterOpaqueWithBlurImageFilter)
647 
648 #undef MASK_BLUR_VARIANT_TEST
649 
650 TEST_P(AiksTest, GaussianBlurStyleInner) {
651  DisplayListBuilder builder;
652  builder.Scale(GetContentScale().x, GetContentScale().y);
653 
654  DlPaint paint;
655  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1));
656  builder.DrawPaint(paint);
657 
658  paint.setColor(DlColor::kGreen());
659  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kInner, 30));
660 
661  SkPath path;
662  path.moveTo(200, 200);
663  path.lineTo(300, 400);
664  path.lineTo(100, 400);
665  path.close();
666 
667  builder.DrawPath(path, paint);
668 
669  // Draw another thing to make sure the clip area is reset.
670  DlPaint red;
671  red.setColor(DlColor::kRed());
672  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
673 
674  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
675 }
676 
677 TEST_P(AiksTest, GaussianBlurStyleOuter) {
678  DisplayListBuilder builder;
679  builder.Scale(GetContentScale().x, GetContentScale().y);
680 
681  DlPaint paint;
682  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
683  builder.DrawPaint(paint);
684 
685  paint.setColor(DlColor::kGreen());
686  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kOuter, 30));
687 
688  SkPath path;
689  path.moveTo(200, 200);
690  path.lineTo(300, 400);
691  path.lineTo(100, 400);
692  path.close();
693 
694  builder.DrawPath(path, paint);
695 
696  // Draw another thing to make sure the clip area is reset.
697  DlPaint red;
698  red.setColor(DlColor::kRed());
699  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
700 
701  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
702 }
703 
704 TEST_P(AiksTest, GaussianBlurStyleSolid) {
705  DisplayListBuilder builder;
706  builder.Scale(GetContentScale().x, GetContentScale().y);
707 
708  DlPaint paint;
709  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
710  builder.DrawPaint(paint);
711 
712  paint.setColor(DlColor::kGreen());
713  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kSolid, 30));
714 
715  SkPath path;
716  path.moveTo(200, 200);
717  path.lineTo(300, 400);
718  path.lineTo(100, 400);
719  path.close();
720 
721  builder.DrawPath(path, paint);
722 
723  // Draw another thing to make sure the clip area is reset.
724  DlPaint red;
725  red.setColor(DlColor::kRed());
726  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
727 
728  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
729 }
730 
731 TEST_P(AiksTest, MaskBlurTexture) {
732  Scalar sigma = 30;
733  auto callback = [&]() -> sk_sp<DisplayList> {
734  if (AiksTest::ImGuiBegin("Controls", nullptr,
735  ImGuiWindowFlags_AlwaysAutoResize)) {
736  ImGui::SliderFloat("Sigma", &sigma, 0, 500);
737  ImGui::End();
738  }
739 
740  DisplayListBuilder builder;
741  builder.Scale(GetContentScale().x, GetContentScale().y);
742 
743  DlPaint paint;
744  paint.setColor(DlColor::kGreen());
745  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
746 
747  builder.DrawImage(
748  DlImageImpeller::Make(CreateTextureForFixture("boston.jpg")),
749  SkPoint{200, 200}, DlImageSampling::kNearestNeighbor, &paint);
750 
751  DlPaint red;
752  red.setColor(DlColor::kRed());
753  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
754 
755  return builder.Build();
756  };
757  ASSERT_TRUE(OpenPlaygroundHere(callback));
758 }
759 
760 TEST_P(AiksTest, MaskBlurDoesntStretchContents) {
761  Scalar sigma = 70;
762  auto callback = [&]() -> sk_sp<DisplayList> {
763  if (AiksTest::ImGuiBegin("Controls", nullptr,
764  ImGuiWindowFlags_AlwaysAutoResize)) {
765  ImGui::SliderFloat("Sigma", &sigma, 0, 500);
766  ImGui::End();
767  }
768 
769  DisplayListBuilder builder;
770  builder.Scale(GetContentScale().x, GetContentScale().y);
771 
772  DlPaint paint;
773  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
774  builder.DrawPaint(paint);
775 
776  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
777 
778  builder.Transform(SkMatrix::Translate(100, 100) *
779  SkMatrix::Scale(0.5, 0.5));
780 
781  paint.setColorSource(DlColorSource::MakeImage(
782  DlImageImpeller::Make(boston), DlTileMode::kRepeat, DlTileMode::kRepeat,
783  DlImageSampling::kMipmapLinear));
784  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
785 
786  builder.DrawRect(SkRect::MakeXYWH(0, 0, boston->GetSize().width,
787  boston->GetSize().height),
788  paint);
789 
790  return builder.Build();
791  };
792  ASSERT_TRUE(OpenPlaygroundHere(callback));
793 }
794 
795 TEST_P(AiksTest, GaussianBlurAtPeripheryVertical) {
796  DisplayListBuilder builder;
797 
798  DlPaint paint;
799  builder.Scale(GetContentScale().x, GetContentScale().y);
800 
801  paint.setColor(DlColor::kLimeGreen());
802  SkRRect rrect = SkRRect::MakeRectXY(
803  SkRect::MakeLTRB(0, 0, GetWindowSize().width, 100), 10, 10);
804  builder.DrawRRect(rrect, paint);
805 
806  paint.setColor(DlColor::kMagenta());
807  rrect = SkRRect::MakeRectXY(
808  SkRect::MakeLTRB(0, 110, GetWindowSize().width, 210), 10, 10);
809  builder.DrawRRect(rrect, paint);
810  builder.ClipRect(SkRect::MakeLTRB(100, 0, 200, GetWindowSize().height));
811 
812  DlPaint save_paint;
813  save_paint.setBlendMode(DlBlendMode::kSrc);
814 
815  auto backdrop_filter = DlImageFilter::MakeBlur(20, 20, DlTileMode::kClamp);
816 
817  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
818  builder.Restore();
819 
820  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
821 }
822 
823 TEST_P(AiksTest, GaussianBlurAtPeripheryHorizontal) {
824  DisplayListBuilder builder;
825 
826  builder.Scale(GetContentScale().x, GetContentScale().y);
827  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
828  builder.DrawImageRect(
829  DlImageImpeller::Make(boston),
830  SkRect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height),
831  SkRect::MakeLTRB(0, 0, GetWindowSize().width, 100),
832  DlImageSampling::kNearestNeighbor);
833 
834  DlPaint paint;
835  paint.setColor(DlColor::kMagenta());
836 
837  SkRRect rrect = SkRRect::MakeRectXY(
838  SkRect::MakeLTRB(0, 110, GetWindowSize().width, 210), 10, 10);
839  builder.DrawRRect(rrect, paint);
840  builder.ClipRect(SkRect::MakeLTRB(0, 50, GetWindowSize().width, 150));
841 
842  DlPaint save_paint;
843  save_paint.setBlendMode(DlBlendMode::kSrc);
844 
845  auto backdrop_filter = DlImageFilter::MakeBlur(20, 20, DlTileMode::kClamp);
846  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
847 
848  builder.Restore();
849  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
850 }
851 
852 TEST_P(AiksTest, GaussianBlurAnimatedBackdrop) {
853  // This test is for checking out how stable rendering is when content is
854  // translated underneath a blur. Animating under a blur can cause
855  // *shimmering* to happen as a result of pixel alignment.
856  // See also: https://github.com/flutter/flutter/issues/140193
857  auto boston =
858  CreateTextureForFixture("boston.jpg", /*enable_mipmapping=*/true);
859  ASSERT_TRUE(boston);
860  int64_t count = 0;
861  Scalar sigma = 20.0;
862  Scalar freq = 0.1;
863  Scalar amp = 50.0;
864  auto callback = [&]() -> sk_sp<DisplayList> {
865  if (AiksTest::ImGuiBegin("Controls", nullptr,
866  ImGuiWindowFlags_AlwaysAutoResize)) {
867  ImGui::SliderFloat("Sigma", &sigma, 0, 200);
868  ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0);
869  ImGui::SliderFloat("Amplitude", &amp, 1, 100);
870  ImGui::End();
871  }
872 
873  DisplayListBuilder builder;
874  builder.Scale(GetContentScale().x, GetContentScale().y);
875  Scalar y = amp * sin(freq * 2.0 * M_PI * count / 60);
876  builder.DrawImage(
877  DlImageImpeller::Make(boston),
878  SkPoint::Make(1024 / 2 - boston->GetSize().width / 2,
879  (768 / 2 - boston->GetSize().height / 2) + y),
880  DlImageSampling::kMipmapLinear);
881  static PlaygroundPoint point_a(Point(100, 100), 20, Color::Red());
882  static PlaygroundPoint point_b(Point(900, 700), 20, Color::Red());
883  auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
884 
885  builder.ClipRect(
886  SkRect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
887  builder.ClipRect(SkRect::MakeLTRB(100, 100, 900, 700));
888 
889  DlPaint paint;
890  paint.setBlendMode(DlBlendMode::kSrc);
891 
892  auto backdrop_filter =
893  DlImageFilter::MakeBlur(sigma, sigma, DlTileMode::kClamp);
894  builder.SaveLayer(nullptr, &paint, backdrop_filter.get());
895  count += 1;
896  return builder.Build();
897  };
898  ASSERT_TRUE(OpenPlaygroundHere(callback));
899 }
900 
901 TEST_P(AiksTest, GaussianBlurStyleInnerGradient) {
902  DisplayListBuilder builder;
903 
904  builder.Scale(GetContentScale().x, GetContentScale().y);
905 
906  DlPaint paint;
907  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
908  builder.DrawPaint(paint);
909 
910  std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
911  DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
912  std::vector<Scalar> stops = {0.0, 1.0};
913 
914  paint = DlPaint{};
915  paint.setColorSource(DlColorSource::MakeLinear(
916  /*start_point=*/{0, 0},
917  /*end_point=*/{200, 200},
918  /*stop_count=*/colors.size(),
919  /*colors=*/colors.data(),
920  /*stops=*/stops.data(),
921  /*tile_mode=*/DlTileMode::kMirror));
922  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kInner, 30));
923 
924  SkPath path;
925  path.moveTo(200, 200);
926  path.lineTo(300, 400);
927  path.lineTo(100, 400);
928  path.close();
929  builder.DrawPath(path, paint);
930 
931  // Draw another thing to make sure the clip area is reset.
932  DlPaint red;
933  red.setColor(DlColor::kRed());
934  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
935 
936  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
937 }
938 
939 TEST_P(AiksTest, GaussianBlurStyleSolidGradient) {
940  DisplayListBuilder builder;
941  builder.Scale(GetContentScale().x, GetContentScale().y);
942 
943  DlPaint paint;
944  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
945  builder.DrawPaint(paint);
946 
947  std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
948  DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
949  std::vector<Scalar> stops = {0.0, 1.0};
950 
951  paint = DlPaint{};
952  paint.setColorSource(DlColorSource::MakeLinear(
953  /*start_point=*/{0, 0},
954  /*end_point=*/{200, 200},
955  /*stop_count=*/colors.size(),
956  /*colors=*/colors.data(),
957  /*stops=*/stops.data(),
958  /*tile_mode=*/DlTileMode::kMirror));
959  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kSolid, 30));
960 
961  SkPath path;
962  path.moveTo(200, 200);
963  path.lineTo(300, 400);
964  path.lineTo(100, 400);
965  path.close();
966  builder.DrawPath(path, paint);
967 
968  // Draw another thing to make sure the clip area is reset.
969  DlPaint red;
970  red.setColor(DlColor::kRed());
971  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
972  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
973 }
974 
975 TEST_P(AiksTest, GaussianBlurStyleOuterGradient) {
976  DisplayListBuilder builder;
977  builder.Scale(GetContentScale().x, GetContentScale().y);
978 
979  DlPaint paint;
980  paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
981  builder.DrawPaint(paint);
982 
983  std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
984  DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
985  std::vector<Scalar> stops = {0.0, 1.0};
986 
987  paint = DlPaint{};
988  paint.setColorSource(DlColorSource::MakeLinear(
989  /*start_point=*/{0, 0},
990  /*end_point=*/{200, 200},
991  /*stop_count=*/colors.size(),
992  /*colors=*/colors.data(),
993  /*stops=*/stops.data(),
994  /*tile_mode=*/DlTileMode::kMirror));
995  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kOuter, 30));
996 
997  SkPath path;
998  path.moveTo(200, 200);
999  path.lineTo(300, 400);
1000  path.lineTo(100, 400);
1001  path.close();
1002  builder.DrawPath(path, paint);
1003 
1004  // Draw another thing to make sure the clip area is reset.
1005  DlPaint red;
1006  red.setColor(DlColor::kRed());
1007  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
1008  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1009 }
1010 
1011 TEST_P(AiksTest, GaussianBlurScaledAndClipped) {
1012  DisplayListBuilder builder;
1013  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1014  Rect bounds =
1015  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
1016  Vector2 image_center = Vector2(bounds.GetSize() / 2);
1017 
1018  DlPaint paint;
1019  paint.setImageFilter(DlImageFilter::MakeBlur(20, 20, DlTileMode::kDecal));
1020 
1021  Vector2 clip_size = {150, 75};
1022  Vector2 center = Vector2(1024, 768) / 2;
1023  builder.Scale(GetContentScale().x, GetContentScale().y);
1024 
1025  auto rect =
1026  Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size);
1027  builder.ClipRect(SkRect::MakeLTRB(rect.GetLeft(), rect.GetTop(),
1028  rect.GetRight(), rect.GetBottom()));
1029  builder.Translate(center.x, center.y);
1030  builder.Scale(0.6, 0.6);
1031 
1032  SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
1033  bounds.GetRight(), bounds.GetBottom());
1034  Rect dest = bounds.Shift(-image_center);
1035  SkRect sk_dst = SkRect::MakeLTRB(dest.GetLeft(), dest.GetTop(),
1036  dest.GetRight(), dest.GetBottom());
1037  builder.DrawImageRect(DlImageImpeller::Make(boston), /*src=*/sk_bounds,
1038  /*dst=*/sk_dst, DlImageSampling::kNearestNeighbor,
1039  &paint);
1040 
1041  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1042 }
1043 
1044 TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) {
1045  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1046 
1047  auto callback = [&]() -> sk_sp<DisplayList> {
1048  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
1049  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
1050  DlTileMode::kMirror, DlTileMode::kDecal};
1051 
1052  static float rotation = 0;
1053  static float scale = 0.6;
1054  static int selected_tile_mode = 3;
1055 
1056  if (AiksTest::ImGuiBegin("Controls", nullptr,
1057  ImGuiWindowFlags_AlwaysAutoResize)) {
1058  ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
1059  ImGui::SliderFloat("Scale", &scale, 0, 2.0);
1060  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
1061  sizeof(tile_mode_names) / sizeof(char*));
1062  ImGui::End();
1063  }
1064 
1065  DisplayListBuilder builder;
1066  Rect bounds =
1067  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
1068  Vector2 image_center = Vector2(bounds.GetSize() / 2);
1069  DlPaint paint;
1070  paint.setImageFilter(
1071  DlImageFilter::MakeBlur(20, 20, tile_modes[selected_tile_mode]));
1072 
1073  static PlaygroundPoint point_a(Point(362, 309), 20, Color::Red());
1074  static PlaygroundPoint point_b(Point(662, 459), 20, Color::Red());
1075  auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
1076  Vector2 center = Vector2(1024, 768) / 2;
1077 
1078  builder.Scale(GetContentScale().x, GetContentScale().y);
1079  builder.ClipRect(
1080  SkRect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
1081  builder.Translate(center.x, center.y);
1082  builder.Scale(scale, scale);
1083  builder.Rotate(rotation);
1084 
1085  SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
1086  bounds.GetRight(), bounds.GetBottom());
1087  Rect dest = bounds.Shift(-image_center);
1088  SkRect sk_dst = SkRect::MakeLTRB(dest.GetLeft(), dest.GetTop(),
1089  dest.GetRight(), dest.GetBottom());
1090  builder.DrawImageRect(DlImageImpeller::Make(boston), /*src=*/sk_bounds,
1091  /*dst=*/sk_dst, DlImageSampling::kNearestNeighbor,
1092  &paint);
1093  return builder.Build();
1094  };
1095 
1096  ASSERT_TRUE(OpenPlaygroundHere(callback));
1097 }
1098 
1099 TEST_P(AiksTest, GaussianBlurOneDimension) {
1100  DisplayListBuilder builder;
1101 
1102  builder.Scale(GetContentScale().x, GetContentScale().y);
1103  builder.Scale(0.5, 0.5);
1104 
1105  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1106  builder.DrawImage(DlImageImpeller::Make(boston), SkPoint{100, 100}, {});
1107 
1108  DlPaint paint;
1109  paint.setBlendMode(DlBlendMode::kSrc);
1110 
1111  auto backdrop_filter = DlImageFilter::MakeBlur(50, 0, DlTileMode::kClamp);
1112  builder.SaveLayer(nullptr, &paint, backdrop_filter.get());
1113  builder.Restore();
1114  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1115 }
1116 
1117 // Smoketest to catch issues with the coverage hint.
1118 // Draws a rotated blurred image within a rectangle clip. The center of the clip
1119 // rectangle is the center of the rotated image. The entire area of the clip
1120 // rectangle should be filled with opaque colors output by the blur.
1121 TEST_P(AiksTest, GaussianBlurRotatedAndClipped) {
1122  DisplayListBuilder builder;
1123 
1124  std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1125  Rect bounds =
1126  Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
1127 
1128  DlPaint paint;
1129  paint.setImageFilter(DlImageFilter::MakeBlur(20, 20, DlTileMode::kDecal));
1130 
1131  Vector2 image_center = Vector2(bounds.GetSize() / 2);
1132  Vector2 clip_size = {150, 75};
1133  Vector2 center = Vector2(1024, 768) / 2;
1134  builder.Scale(GetContentScale().x, GetContentScale().y);
1135 
1136  auto clip_bounds =
1137  Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size);
1138  builder.ClipRect(SkRect::MakeLTRB(clip_bounds.GetLeft(), clip_bounds.GetTop(),
1139  clip_bounds.GetRight(),
1140  clip_bounds.GetBottom()));
1141  builder.Translate(center.x, center.y);
1142  builder.Scale(0.6, 0.6);
1143  builder.Rotate(25);
1144 
1145  auto dst_rect = bounds.Shift(-image_center);
1146  builder.DrawImageRect(
1147  DlImageImpeller::Make(boston), /*src=*/
1148  SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(), bounds.GetRight(),
1149  bounds.GetBottom()),
1150  /*dst=*/
1151  SkRect::MakeLTRB(dst_rect.GetLeft(), dst_rect.GetTop(),
1152  dst_rect.GetRight(), dst_rect.GetBottom()),
1153  DlImageSampling::kMipmapLinear, &paint);
1154 
1155  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1156 }
1157 
1158 TEST_P(AiksTest, GaussianBlurRotatedNonUniform) {
1159  auto callback = [&]() -> sk_sp<DisplayList> {
1160  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
1161  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
1162  DlTileMode::kMirror, DlTileMode::kDecal};
1163 
1164  static float rotation = 45;
1165  static float scale = 0.6;
1166  static int selected_tile_mode = 3;
1167 
1168  if (AiksTest::ImGuiBegin("Controls", nullptr,
1169  ImGuiWindowFlags_AlwaysAutoResize)) {
1170  ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
1171  ImGui::SliderFloat("Scale", &scale, 0, 2.0);
1172  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
1173  sizeof(tile_mode_names) / sizeof(char*));
1174  ImGui::End();
1175  }
1176 
1177  DisplayListBuilder builder;
1178 
1179  DlPaint paint;
1180  paint.setColor(DlColor::kGreen());
1181  paint.setImageFilter(
1182  DlImageFilter::MakeBlur(50, 0, tile_modes[selected_tile_mode]));
1183 
1184  Vector2 center = Vector2(1024, 768) / 2;
1185  builder.Scale(GetContentScale().x, GetContentScale().y);
1186  builder.Translate(center.x, center.y);
1187  builder.Scale(scale, scale);
1188  builder.Rotate(rotation);
1189 
1190  SkRRect rrect =
1191  SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -100, 200, 200), 10, 10);
1192  builder.DrawRRect(rrect, paint);
1193  return builder.Build();
1194  };
1195 
1196  ASSERT_TRUE(OpenPlaygroundHere(callback));
1197 }
1198 
1199 TEST_P(AiksTest, BlurredRectangleWithShader) {
1200  DisplayListBuilder builder;
1201  builder.Scale(GetContentScale().x, GetContentScale().y);
1202 
1203  auto paint_lines = [&builder](Scalar dx, Scalar dy, DlPaint paint) {
1204  auto draw_line = [&builder, &paint](SkPoint a, SkPoint b) {
1205  SkPath line = SkPath::Line(a, b);
1206  builder.DrawPath(line, paint);
1207  };
1208  paint.setStrokeWidth(5);
1209  paint.setDrawStyle(DlDrawStyle::kStroke);
1210  draw_line(SkPoint::Make(dx + 100, dy + 100),
1211  SkPoint::Make(dx + 200, dy + 200));
1212  draw_line(SkPoint::Make(dx + 100, dy + 200),
1213  SkPoint::Make(dx + 200, dy + 100));
1214  draw_line(SkPoint::Make(dx + 150, dy + 100),
1215  SkPoint::Make(dx + 200, dy + 150));
1216  draw_line(SkPoint::Make(dx + 100, dy + 150),
1217  SkPoint::Make(dx + 150, dy + 200));
1218  };
1219 
1220  AiksContext renderer(GetContext(), nullptr);
1221  DisplayListBuilder recorder_builder;
1222  for (int x = 0; x < 5; ++x) {
1223  for (int y = 0; y < 5; ++y) {
1224  SkRect rect = SkRect::MakeXYWH(x * 20, y * 20, 20, 20);
1225  DlPaint paint;
1226  paint.setColor(((x + y) & 1) == 0 ? DlColor::kYellow()
1227  : DlColor::kBlue());
1228 
1229  recorder_builder.DrawRect(rect, paint);
1230  }
1231  }
1232  auto texture =
1233  DisplayListToTexture(recorder_builder.Build(), {100, 100}, renderer);
1234 
1235  auto image_source = DlColorSource::MakeImage(
1236  DlImageImpeller::Make(texture), DlTileMode::kRepeat, DlTileMode::kRepeat);
1237  auto blur_filter = DlImageFilter::MakeBlur(5, 5, DlTileMode::kDecal);
1238 
1239  DlPaint paint;
1240  paint.setColor(DlColor::kDarkGreen());
1241  builder.DrawRect(SkRect::MakeLTRB(0, 0, 300, 600), paint);
1242 
1243  paint.setColorSource(image_source);
1244  builder.DrawRect(SkRect::MakeLTRB(100, 100, 200, 200), paint);
1245 
1246  paint.setColorSource(nullptr);
1247  paint.setColor(DlColor::kRed());
1248  builder.DrawRect(SkRect::MakeLTRB(300, 0, 600, 600), paint);
1249 
1250  paint.setColorSource(image_source);
1251  paint.setImageFilter(blur_filter);
1252  builder.DrawRect(SkRect::MakeLTRB(400, 100, 500, 200), paint);
1253 
1254  paint.setImageFilter(nullptr);
1255  paint_lines(0, 300, paint);
1256 
1257  paint.setImageFilter(blur_filter);
1258  paint_lines(300, 300, paint);
1259 
1260  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1261 }
1262 
1263 TEST_P(AiksTest, GaussianBlurWithoutDecalSupport) {
1264  if (GetParam() != PlaygroundBackend::kMetal) {
1265  GTEST_SKIP()
1266  << "This backend doesn't yet support setting device capabilities.";
1267  }
1268  if (!WillRenderSomething()) {
1269  // Sometimes these tests are run without playgrounds enabled which is
1270  // pointless for this test since we are asserting that
1271  // `SupportsDecalSamplerAddressMode` is called.
1272  GTEST_SKIP() << "This test requires playgrounds.";
1273  }
1274 
1275  std::shared_ptr<const Capabilities> old_capabilities =
1276  GetContext()->GetCapabilities();
1277  auto mock_capabilities = std::make_shared<MockCapabilities>();
1278  EXPECT_CALL(*mock_capabilities, SupportsDecalSamplerAddressMode())
1279  .Times(::testing::AtLeast(1))
1280  .WillRepeatedly(::testing::Return(false));
1281  FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultColorFormat);
1282  FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultStencilFormat);
1283  FLT_FORWARD(mock_capabilities, old_capabilities,
1284  GetDefaultDepthStencilFormat);
1285  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsOffscreenMSAA);
1286  FLT_FORWARD(mock_capabilities, old_capabilities,
1287  SupportsImplicitResolvingMSAA);
1288  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsReadFromResolve);
1289  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsFramebufferFetch);
1290  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsSSBO);
1291  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsCompute);
1292  FLT_FORWARD(mock_capabilities, old_capabilities,
1293  SupportsTextureToTextureBlits);
1294  FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultGlyphAtlasFormat);
1295  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsTriangleFan);
1296  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsPrimitiveRestart);
1297  ASSERT_TRUE(SetCapabilities(mock_capabilities).ok());
1298 
1299  auto texture = DlImageImpeller::Make(CreateTextureForFixture("boston.jpg"));
1300 
1301  DisplayListBuilder builder;
1302  builder.Scale(GetContentScale().x * 0.5, GetContentScale().y * 0.5);
1303 
1304  DlPaint paint;
1305  paint.setColor(DlColor::kBlack());
1306  builder.DrawPaint(paint);
1307 
1308  auto blur_filter = DlImageFilter::MakeBlur(20, 20, DlTileMode::kDecal);
1309  paint.setImageFilter(blur_filter);
1310  builder.DrawImage(texture, SkPoint::Make(200, 200), {}, &paint);
1311  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1312 }
1313 
1314 // This addresses a bug where tiny blurs could result in mip maps that beyond
1315 // the limits for the textures used for blurring.
1316 // See also: b/323402168
1317 TEST_P(AiksTest, GaussianBlurSolidColorTinyMipMap) {
1318  AiksContext renderer(GetContext(), nullptr);
1319 
1320  for (int32_t i = 1; i < 5; ++i) {
1321  DisplayListBuilder builder;
1322  Scalar fi = i;
1323  SkPath path;
1324  path.moveTo(100, 100);
1325  path.lineTo(100 + fi, 100 + fi);
1326 
1327  DlPaint paint;
1328  paint.setColor(DlColor::kChartreuse());
1329  auto blur_filter = DlImageFilter::MakeBlur(0.1, 0.1, DlTileMode::kClamp);
1330  paint.setImageFilter(blur_filter);
1331 
1332  builder.DrawPath(path, paint);
1333 
1334  auto image = DisplayListToTexture(builder.Build(), {1024, 768}, renderer);
1335  EXPECT_TRUE(image) << " length " << i;
1336  }
1337 }
1338 
1339 // This addresses a bug where tiny blurs could result in mip maps that beyond
1340 // the limits for the textures used for blurring.
1341 // See also: b/323402168
1342 TEST_P(AiksTest, GaussianBlurBackdropTinyMipMap) {
1343  AiksContext renderer(GetContext(), nullptr);
1344  for (int32_t i = 1; i < 5; ++i) {
1345  DisplayListBuilder builder;
1346 
1347  ISize clip_size = ISize(i, i);
1348  builder.Save();
1349  builder.ClipRect(
1350  SkRect::MakeXYWH(400, 400, clip_size.width, clip_size.height));
1351 
1352  DlPaint paint;
1353  paint.setColor(DlColor::kGreen());
1354  auto blur_filter = DlImageFilter::MakeBlur(0.1, 0.1, DlTileMode::kDecal);
1355  paint.setImageFilter(blur_filter);
1356 
1357  builder.DrawCircle(SkPoint{400, 400}, 200, paint);
1358  builder.Restore();
1359 
1360  auto image = DisplayListToTexture(builder.Build(), {1024, 768}, renderer);
1361  EXPECT_TRUE(image) << " length " << i;
1362  }
1363 }
1364 
1366  CanRenderMultipleBackdropBlurWithSingleBackdropIdDifferentLayers) {
1367  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
1368 
1369  DisplayListBuilder builder;
1370 
1371  DlPaint paint;
1372  builder.DrawImage(image, SkPoint::Make(50.0, 50.0),
1373  DlImageSampling::kNearestNeighbor, &paint);
1374 
1375  for (int i = 0; i < 6; i++) {
1376  if (i != 0) {
1377  DlPaint paint;
1378  paint.setColor(DlColor::kWhite().withAlphaF(0.95));
1379  builder.SaveLayer(nullptr, &paint);
1380  }
1381  SkRRect rrect = SkRRect::MakeRectXY(
1382  SkRect::MakeXYWH(50 + (i * 100), 250, 100, 100), 20, 20);
1383  builder.Save();
1384  builder.ClipRRect(rrect);
1385 
1386  DlPaint save_paint;
1387  save_paint.setBlendMode(DlBlendMode::kSrc);
1388  auto backdrop_filter = DlImageFilter::MakeBlur(30, 30, DlTileMode::kClamp);
1389  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get(),
1390  /*backdrop_id=*/1);
1391  builder.Restore();
1392  builder.Restore();
1393  if (i != 0) {
1394  builder.Restore();
1395  }
1396  }
1397 
1398  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1399 }
1400 
1401 } // namespace testing
1402 } // namespace impeller
#define MASK_BLUR_VARIANT_TEST(config)
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Point GetContentScale() const
Definition: playground.cc:189
int32_t x
sk_sp< flutter::DisplayList > DoGradientOvalStrokeMaskBlur(Vector2 content_Scale, Scalar sigma, DlBlurStyle style)
static sk_sp< DisplayList > MaskBlurVariantTest(const AiksTest &test_context, const MaskBlurTestConfig &config)
TEST_P(AiksTest, DrawAtlasNoColor)
static const std::map< std::string, MaskBlurTestConfig > kPaintVariations
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.
Point Vector2
Definition: point.h:331
float Scalar
Definition: scalar.h:18
Point DrawPlaygroundPoint(PlaygroundPoint &point)
Definition: widgets.cc:9
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
TRect< Scalar > Rect
Definition: rect.h:792
TPoint< Scalar > Point
Definition: point.h:327
ISize64 ISize
Definition: size.h:174
const Scalar scale
static constexpr Color White()
Definition: color.h:263
static constexpr Color Red()
Definition: color.h:271
static constexpr Color AntiqueWhite()
Definition: color.h:285
static constexpr Color Green()
Definition: color.h:273
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:48
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
Scalar sigma
Definition: sigma.h:33
constexpr TPoint Rotate(const Radians &angle) const
Definition: point.h:226
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:622
constexpr auto GetBottom() const
Definition: rect.h:361
constexpr auto GetTop() const
Definition: rect.h:357
constexpr auto GetLeft() const
Definition: rect.h:355
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:331
constexpr auto GetRight() const
Definition: rect.h:359
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:606
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
Type height
Definition: size.h:29
Type width
Definition: size.h:28
std::shared_ptr< DlImageFilter > image_filter