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