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