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