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