| 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"
17 #include "flutter/impeller/display_list/aiks_unittests.h"
18
19 #include "gmock/gmock.h"
20 #include "impeller/display_list/dl_dispatcher.h"
21 #include "impeller/display_list/dl_image_impeller.h"
22 #include "impeller/playground/widgets.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
322 TEST_P(AiksTest,
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(),
477 PixelFormat::kB10G10R10A10XR);
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
496 struct MaskBlurTestConfig {
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 #define MASK_BLUR_VARIANT_TEST(config) \
630 TEST_P(AiksTest, MaskBlurVariantTest##config) { \
631 ASSERT_TRUE(OpenPlaygroundHere( \
632 MaskBlurVariantTest(*this, kPaintVariations.at(#config)))); \
633 }
634
635 MASK_BLUR_VARIANT_TEST(NormalTranslucentZeroSigma)
636 MASK_BLUR_VARIANT_TEST(NormalTranslucent)
637 MASK_BLUR_VARIANT_TEST(SolidTranslucent)
638 MASK_BLUR_VARIANT_TEST(SolidOpaque)
639 MASK_BLUR_VARIANT_TEST(SolidTranslucentWithFilters)
640 MASK_BLUR_VARIANT_TEST(SolidTranslucentExclusionBlend)
641 MASK_BLUR_VARIANT_TEST(InnerTranslucent)
642 MASK_BLUR_VARIANT_TEST(InnerTranslucentWithBlurImageFilter)
643 MASK_BLUR_VARIANT_TEST(OuterTranslucent)
644 MASK_BLUR_VARIANT_TEST(OuterOpaqueWithBlurImageFilter)
645
646 #undef MASK_BLUR_VARIANT_TEST
647
648 TEST_P(AiksTest, GaussianBlurStyleInner) {
649 DisplayListBuilder builder;
650 builder.Scale(GetContentScale().x, GetContentScale().y);
651
652 DlPaint paint;
653 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1));
654 builder.DrawPaint(paint);
655
656 paint.setColor(DlColor::kGreen());
657 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kInner, 30));
658
659 DlPathBuilder path_builder;
660 path_builder.MoveTo(DlPoint(200, 200));
661 path_builder.LineTo(DlPoint(300, 400));
662 path_builder.LineTo(DlPoint(100, 400));
663 path_builder.Close();
664
665 builder.DrawPath(path_builder.TakePath(), paint);
666
667 // Draw another thing to make sure the clip area is reset.
668 DlPaint red;
669 red.setColor(DlColor::kRed());
670 builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), red);
671
672 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
673 }
674
675 TEST_P(AiksTest, GaussianBlurStyleOuter) {
676 DisplayListBuilder builder;
677 builder.Scale(GetContentScale().x, GetContentScale().y);
678
679 DlPaint paint;
680 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
681 builder.DrawPaint(paint);
682
683 paint.setColor(DlColor::kGreen());
684 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kOuter, 30));
685
686 DlPathBuilder path_builder;
687 path_builder.MoveTo(DlPoint(200, 200));
688 path_builder.LineTo(DlPoint(300, 400));
689 path_builder.LineTo(DlPoint(100, 400));
690 path_builder.Close();
691
692 builder.DrawPath(path_builder.TakePath(), paint);
693
694 // Draw another thing to make sure the clip area is reset.
695 DlPaint red;
696 red.setColor(DlColor::kRed());
697 builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), red);
698
699 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
700 }
701
702 TEST_P(AiksTest, GaussianBlurStyleSolid) {
703 DisplayListBuilder builder;
704 builder.Scale(GetContentScale().x, GetContentScale().y);
705
706 DlPaint paint;
707 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
708 builder.DrawPaint(paint);
709
710 paint.setColor(DlColor::kGreen());
711 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kSolid, 30));
712
713 DlPathBuilder path_builder;
714 path_builder.MoveTo(DlPoint(200, 200));
715 path_builder.LineTo(DlPoint(300, 400));
716 path_builder.LineTo(DlPoint(100, 400));
717 path_builder.Close();
718
719 builder.DrawPath(path_builder.TakePath(), paint);
720
721 // Draw another thing to make sure the clip area is reset.
722 DlPaint red;
723 red.setColor(DlColor::kRed());
724 builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), red);
725
726 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
727 }
728
729 TEST_P(AiksTest, MaskBlurTexture) {
730 Scalar sigma = 30;
731 auto callback = [&]() -> sk_sp<DisplayList> {
732 if (AiksTest::ImGuiBegin("Controls", nullptr,
733 ImGuiWindowFlags_AlwaysAutoResize)) {
734 ImGui::SliderFloat("Sigma", &sigma, 0, 500);
735 ImGui::End();
736 }
737
738 DisplayListBuilder builder;
739 builder.Scale(GetContentScale().x, GetContentScale().y);
740
741 DlPaint paint;
742 paint.setColor(DlColor::kGreen());
743 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
744
745 builder.DrawImage(
746 DlImageImpeller::Make(CreateTextureForFixture("boston.jpg")),
747 DlPoint(200, 200), DlImageSampling::kNearestNeighbor, &paint);
748
749 DlPaint red;
750 red.setColor(DlColor::kRed());
751 builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), red);
752
753 return builder.Build();
754 };
755 ASSERT_TRUE(OpenPlaygroundHere(callback));
756 }
757
758 TEST_P(AiksTest, MaskBlurDoesntStretchContents) {
759 Scalar sigma = 70;
760 auto callback = [&]() -> sk_sp<DisplayList> {
761 if (AiksTest::ImGuiBegin("Controls", nullptr,
762 ImGuiWindowFlags_AlwaysAutoResize)) {
763 ImGui::SliderFloat("Sigma", &sigma, 0, 500);
764 ImGui::End();
765 }
766
767 DisplayListBuilder builder;
768 builder.Scale(GetContentScale().x, GetContentScale().y);
769
770 DlPaint paint;
771 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
772 builder.DrawPaint(paint);
773
774 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
775
776 builder.Transform(Matrix::MakeTranslation({100, 100}) *
777 Matrix::MakeScale({0.5, 0.5, 1.0f}));
778
779 paint.setColorSource(DlColorSource::MakeImage(
780 DlImageImpeller::Make(boston), DlTileMode::kRepeat, DlTileMode::kRepeat,
781 DlImageSampling::kMipmapLinear));
782 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
783
784 builder.DrawRect(DlRect::MakeXYWH(0, 0, boston->GetSize().width,
785 boston->GetSize().height),
786 paint);
787
788 return builder.Build();
789 };
790 ASSERT_TRUE(OpenPlaygroundHere(callback));
791 }
792
793 TEST_P(AiksTest, GaussianBlurAtPeripheryVertical) {
794 DisplayListBuilder builder;
795
796 DlPaint paint;
797 builder.Scale(GetContentScale().x, GetContentScale().y);
798
799 paint.setColor(DlColor::kLimeGreen());
800 DlRoundRect rrect = DlRoundRect::MakeRectXY(
801 DlRect::MakeLTRB(0, 0, GetWindowSize().width, 100), 10, 10);
802 builder.DrawRoundRect(rrect, paint);
803
804 paint.setColor(DlColor::kMagenta());
805 rrect = DlRoundRect::MakeRectXY(
806 DlRect::MakeLTRB(0, 110, GetWindowSize().width, 210), 10, 10);
807 builder.DrawRoundRect(rrect, paint);
808 builder.ClipRect(DlRect::MakeLTRB(100, 0, 200, GetWindowSize().height));
809
810 DlPaint save_paint;
811 save_paint.setBlendMode(DlBlendMode::kSrc);
812
813 auto backdrop_filter = DlImageFilter::MakeBlur(20, 20, DlTileMode::kClamp);
814
815 builder.SaveLayer(std::nullopt, &save_paint, backdrop_filter.get());
816 builder.Restore();
817
818 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
819 }
820
821 TEST_P(AiksTest, GaussianBlurAtPeripheryHorizontal) {
822 DisplayListBuilder builder;
823
824 builder.Scale(GetContentScale().x, GetContentScale().y);
825 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
826 builder.DrawImageRect(
827 DlImageImpeller::Make(boston),
828 DlRect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height),
829 DlRect::MakeLTRB(0, 0, GetWindowSize().width, 100),
830 DlImageSampling::kNearestNeighbor);
831
832 DlPaint paint;
833 paint.setColor(DlColor::kMagenta());
834
835 DlRoundRect rrect = DlRoundRect::MakeRectXY(
836 DlRect::MakeLTRB(0, 110, GetWindowSize().width, 210), 10, 10);
837 builder.DrawRoundRect(rrect, paint);
838 builder.ClipRect(DlRect::MakeLTRB(0, 50, GetWindowSize().width, 150));
839
840 DlPaint save_paint;
841 save_paint.setBlendMode(DlBlendMode::kSrc);
842
843 auto backdrop_filter = DlImageFilter::MakeBlur(20, 20, DlTileMode::kClamp);
844 builder.SaveLayer(std::nullopt, &save_paint, backdrop_filter.get());
845
846 builder.Restore();
847 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
848 }
849
850 TEST_P(AiksTest, GaussianBlurAnimatedBackdrop) {
851 // This test is for checking out how stable rendering is when content is
852 // translated underneath a blur. Animating under a blur can cause
853 // *shimmering* to happen as a result of pixel alignment.
854 // See also: https://github.com/flutter/flutter/issues/140193
855 auto boston =
856 CreateTextureForFixture("boston.jpg", /*enable_mipmapping=*/true);
857 ASSERT_TRUE(boston);
858 int64_t count = 0;
859 Scalar sigma = 20.0;
860 Scalar freq = 0.1;
861 Scalar amp = 50.0;
862 auto callback = [&]() -> sk_sp<DisplayList> {
863 if (AiksTest::ImGuiBegin("Controls", nullptr,
864 ImGuiWindowFlags_AlwaysAutoResize)) {
865 ImGui::SliderFloat("Sigma", &sigma, 0, 200);
866 ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0);
867 ImGui::SliderFloat("Amplitude", &amp, 1, 100);
868 ImGui::End();
869 }
870
871 DisplayListBuilder builder;
872 builder.Scale(GetContentScale().x, GetContentScale().y);
873 Scalar y = amp * sin(freq * 2.0 * M_PI * count / 60);
874 builder.DrawImage(DlImageImpeller::Make(boston),
875 DlPoint(1024 / 2 - boston->GetSize().width / 2,
876 (768 / 2 - boston->GetSize().height / 2) + y),
877 DlImageSampling::kMipmapLinear);
878 static PlaygroundPoint point_a(Point(100, 100), 20, Color::Red());
879 static PlaygroundPoint point_b(Point(900, 700), 20, Color::Red());
880 auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
881
882 builder.ClipRect(
883 DlRect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
884 builder.ClipRect(DlRect::MakeLTRB(100, 100, 900, 700));
885
886 DlPaint paint;
887 paint.setBlendMode(DlBlendMode::kSrc);
888
889 auto backdrop_filter =
890 DlImageFilter::MakeBlur(sigma, sigma, DlTileMode::kClamp);
891 builder.SaveLayer(std::nullopt, &paint, backdrop_filter.get());
892 count += 1;
893 return builder.Build();
894 };
895 ASSERT_TRUE(OpenPlaygroundHere(callback));
896 }
897
898 TEST_P(AiksTest, GaussianBlurStyleInnerGradient) {
899 DisplayListBuilder builder;
900
901 builder.Scale(GetContentScale().x, GetContentScale().y);
902
903 DlPaint paint;
904 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
905 builder.DrawPaint(paint);
906
907 std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
908 DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
909 std::vector<Scalar> stops = {0.0, 1.0};
910
911 paint = DlPaint{};
912 paint.setColorSource(DlColorSource::MakeLinear(
913 /*start_point=*/{0, 0},
914 /*end_point=*/{200, 200},
915 /*stop_count=*/colors.size(),
916 /*colors=*/colors.data(),
917 /*stops=*/stops.data(),
918 /*tile_mode=*/DlTileMode::kMirror));
919 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kInner, 30));
920
921 DlPathBuilder path_builder;
922 path_builder.MoveTo(DlPoint(200, 200));
923 path_builder.LineTo(DlPoint(300, 400));
924 path_builder.LineTo(DlPoint(100, 400));
925 path_builder.Close();
926 builder.DrawPath(path_builder.TakePath(), paint);
927
928 // Draw another thing to make sure the clip area is reset.
929 DlPaint red;
930 red.setColor(DlColor::kRed());
931 builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), red);
932
933 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
934 }
935
936 TEST_P(AiksTest, GaussianBlurStyleSolidGradient) {
937 DisplayListBuilder builder;
938 builder.Scale(GetContentScale().x, GetContentScale().y);
939
940 DlPaint paint;
941 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
942 builder.DrawPaint(paint);
943
944 std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
945 DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
946 std::vector<Scalar> stops = {0.0, 1.0};
947
948 paint = DlPaint{};
949 paint.setColorSource(DlColorSource::MakeLinear(
950 /*start_point=*/{0, 0},
951 /*end_point=*/{200, 200},
952 /*stop_count=*/colors.size(),
953 /*colors=*/colors.data(),
954 /*stops=*/stops.data(),
955 /*tile_mode=*/DlTileMode::kMirror));
956 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kSolid, 30));
957
958 DlPathBuilder path_builder;
959 path_builder.MoveTo(DlPoint(200, 200));
960 path_builder.LineTo(DlPoint(300, 400));
961 path_builder.LineTo(DlPoint(100, 400));
962 path_builder.Close();
963 builder.DrawPath(path_builder.TakePath(), paint);
964
965 // Draw another thing to make sure the clip area is reset.
966 DlPaint red;
967 red.setColor(DlColor::kRed());
968 builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), red);
969 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
970 }
971
972 TEST_P(AiksTest, GaussianBlurStyleOuterGradient) {
973 DisplayListBuilder builder;
974 builder.Scale(GetContentScale().x, GetContentScale().y);
975
976 DlPaint paint;
977 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
978 builder.DrawPaint(paint);
979
980 std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
981 DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
982 std::vector<Scalar> stops = {0.0, 1.0};
983
984 paint = DlPaint{};
985 paint.setColorSource(DlColorSource::MakeLinear(
986 /*start_point=*/{0, 0},
987 /*end_point=*/{200, 200},
988 /*stop_count=*/colors.size(),
989 /*colors=*/colors.data(),
990 /*stops=*/stops.data(),
991 /*tile_mode=*/DlTileMode::kMirror));
992 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kOuter, 30));
993
994 DlPathBuilder path_builder;
995 path_builder.MoveTo(DlPoint(200, 200));
996 path_builder.LineTo(DlPoint(300, 400));
997 path_builder.LineTo(DlPoint(100, 400));
998 path_builder.Close();
999 builder.DrawPath(path_builder.TakePath(), paint);
1000
1001 // Draw another thing to make sure the clip area is reset.
1002 DlPaint red;
1003 red.setColor(DlColor::kRed());
1004 builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), red);
1005 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1006 }
1007
1008 TEST_P(AiksTest, GaussianBlurScaledAndClipped) {
1009 DisplayListBuilder builder;
1010 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1011 Rect bounds =
1012 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
1013 Vector2 image_center = Vector2(bounds.GetSize() / 2);
1014
1015 DlPaint paint;
1016 paint.setImageFilter(DlImageFilter::MakeBlur(20, 20, DlTileMode::kDecal));
1017
1018 Vector2 clip_size = {150, 75};
1019 Vector2 center = Vector2(1024, 768) / 2;
1020 builder.Scale(GetContentScale().x, GetContentScale().y);
1021
1022 auto rect =
1023 Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size);
1024 builder.ClipRect(DlRect::MakeLTRB(rect.GetLeft(), rect.GetTop(),
1025 rect.GetRight(), rect.GetBottom()));
1026 builder.Translate(center.x, center.y);
1027 builder.Scale(0.6, 0.6);
1028
1029 DlRect sk_bounds = DlRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
1030 bounds.GetRight(), bounds.GetBottom());
1031 Rect dest = bounds.Shift(-image_center);
1032 DlRect sk_dst = DlRect::MakeLTRB(dest.GetLeft(), dest.GetTop(),
1033 dest.GetRight(), dest.GetBottom());
1034 builder.DrawImageRect(DlImageImpeller::Make(boston), /*src=*/sk_bounds,
1035 /*dst=*/sk_dst, DlImageSampling::kNearestNeighbor,
1036 &paint);
1037
1038 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1039 }
1040
1041 TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) {
1042 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1043
1044 auto callback = [&]() -> sk_sp<DisplayList> {
1045 const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
1046 const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
1047 DlTileMode::kMirror, DlTileMode::kDecal};
1048
1049 static float rotation = 0;
1050 static float scale = 0.6;
1051 static int selected_tile_mode = 3;
1052
1053 if (AiksTest::ImGuiBegin("Controls", nullptr,
1054 ImGuiWindowFlags_AlwaysAutoResize)) {
1055 ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
1056 ImGui::SliderFloat("Scale", &scale, 0, 2.0);
1057 ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
1058 sizeof(tile_mode_names) / sizeof(char*));
1059 ImGui::End();
1060 }
1061
1062 DisplayListBuilder builder;
1063 Rect bounds =
1064 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
1065 Vector2 image_center = Vector2(bounds.GetSize() / 2);
1066 DlPaint paint;
1067 paint.setImageFilter(
1068 DlImageFilter::MakeBlur(20, 20, tile_modes[selected_tile_mode]));
1069
1070 static PlaygroundPoint point_a(Point(362, 309), 20, Color::Red());
1071 static PlaygroundPoint point_b(Point(662, 459), 20, Color::Red());
1072 auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
1073 Vector2 center = Vector2(1024, 768) / 2;
1074
1075 builder.Scale(GetContentScale().x, GetContentScale().y);
1076 builder.ClipRect(
1077 DlRect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
1078 builder.Translate(center.x, center.y);
1079 builder.Scale(scale, scale);
1080 builder.Rotate(rotation);
1081
1082 DlRect sk_bounds = DlRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
1083 bounds.GetRight(), bounds.GetBottom());
1084 Rect dest = bounds.Shift(-image_center);
1085 DlRect sk_dst = DlRect::MakeLTRB(dest.GetLeft(), dest.GetTop(),
1086 dest.GetRight(), dest.GetBottom());
1087 builder.DrawImageRect(DlImageImpeller::Make(boston), /*src=*/sk_bounds,
1088 /*dst=*/sk_dst, DlImageSampling::kNearestNeighbor,
1089 &paint);
1090 return builder.Build();
1091 };
1092
1093 ASSERT_TRUE(OpenPlaygroundHere(callback));
1094 }
1095
1096 TEST_P(AiksTest, GaussianBlurOneDimension) {
1097 DisplayListBuilder builder;
1098
1099 builder.Scale(GetContentScale().x, GetContentScale().y);
1100 builder.Scale(0.5, 0.5);
1101
1102 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1103 builder.DrawImage(DlImageImpeller::Make(boston), DlPoint(100, 100), {});
1104
1105 DlPaint paint;
1106 paint.setBlendMode(DlBlendMode::kSrc);
1107
1108 auto backdrop_filter = DlImageFilter::MakeBlur(50, 0, DlTileMode::kClamp);
1109 builder.SaveLayer(std::nullopt, &paint, backdrop_filter.get());
1110 builder.Restore();
1111 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1112 }
1113
1114 // Smoketest to catch issues with the coverage hint.
1115 // Draws a rotated blurred image within a rectangle clip. The center of the clip
1116 // rectangle is the center of the rotated image. The entire area of the clip
1117 // rectangle should be filled with opaque colors output by the blur.
1118 TEST_P(AiksTest, GaussianBlurRotatedAndClipped) {
1119 DisplayListBuilder builder;
1120
1121 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1122 Rect bounds =
1123 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
1124
1125 DlPaint paint;
1126 paint.setImageFilter(DlImageFilter::MakeBlur(20, 20, DlTileMode::kDecal));
1127
1128 Vector2 image_center = Vector2(bounds.GetSize() / 2);
1129 Vector2 clip_size = {150, 75};
1130 Vector2 center = Vector2(1024, 768) / 2;
1131 builder.Scale(GetContentScale().x, GetContentScale().y);
1132
1133 auto clip_bounds =
1134 Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size);
1135 builder.ClipRect(DlRect::MakeLTRB(clip_bounds.GetLeft(), clip_bounds.GetTop(),
1136 clip_bounds.GetRight(),
1137 clip_bounds.GetBottom()));
1138 builder.Translate(center.x, center.y);
1139 builder.Scale(0.6, 0.6);
1140 builder.Rotate(25);
1141
1142 auto dst_rect = bounds.Shift(-image_center);
1143 builder.DrawImageRect(
1144 DlImageImpeller::Make(boston), /*src=*/
1145 DlRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(), bounds.GetRight(),
1146 bounds.GetBottom()),
1147 /*dst=*/
1148 DlRect::MakeLTRB(dst_rect.GetLeft(), dst_rect.GetTop(),
1149 dst_rect.GetRight(), dst_rect.GetBottom()),
1150 DlImageSampling::kMipmapLinear, &paint);
1151
1152 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1153 }
1154
1155 TEST_P(AiksTest, GaussianBlurRotatedNonUniform) {
1156 auto callback = [&]() -> sk_sp<DisplayList> {
1157 const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
1158 const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
1159 DlTileMode::kMirror, DlTileMode::kDecal};
1160
1161 static float rotation = 45;
1162 static float scale = 0.6;
1163 static int selected_tile_mode = 3;
1164
1165 if (AiksTest::ImGuiBegin("Controls", nullptr,
1166 ImGuiWindowFlags_AlwaysAutoResize)) {
1167 ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
1168 ImGui::SliderFloat("Scale", &scale, 0, 2.0);
1169 ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
1170 sizeof(tile_mode_names) / sizeof(char*));
1171 ImGui::End();
1172 }
1173
1174 DisplayListBuilder builder;
1175
1176 DlPaint paint;
1177 paint.setColor(DlColor::kGreen());
1178 paint.setImageFilter(
1179 DlImageFilter::MakeBlur(50, 0, tile_modes[selected_tile_mode]));
1180
1181 Vector2 center = Vector2(1024, 768) / 2;
1182 builder.Scale(GetContentScale().x, GetContentScale().y);
1183 builder.Translate(center.x, center.y);
1184 builder.Scale(scale, scale);
1185 builder.Rotate(rotation);
1186
1187 DlRoundRect rrect =
1188 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(-100, -100, 200, 200), 10, 10);
1189 builder.DrawRoundRect(rrect, paint);
1190 return builder.Build();
1191 };
1192
1193 ASSERT_TRUE(OpenPlaygroundHere(callback));
1194 }
1195
1196 TEST_P(AiksTest, BlurredRectangleWithShader) {
1197 DisplayListBuilder builder;
1198 builder.Scale(GetContentScale().x, GetContentScale().y);
1199
1200 auto paint_lines = [&builder](Scalar dx, Scalar dy, DlPaint paint) {
1201 auto draw_line = [&builder, &paint](DlPoint a, DlPoint b) {
1202 DlPath line = DlPath::MakeLine(a, b);
1203 builder.DrawPath(line, paint);
1204 };
1205 paint.setStrokeWidth(5);
1206 paint.setDrawStyle(DlDrawStyle::kStroke);
1207 draw_line(DlPoint(dx + 100, dy + 100), DlPoint(dx + 200, dy + 200));
1208 draw_line(DlPoint(dx + 100, dy + 200), DlPoint(dx + 200, dy + 100));
1209 draw_line(DlPoint(dx + 150, dy + 100), DlPoint(dx + 200, dy + 150));
1210 draw_line(DlPoint(dx + 100, dy + 150), DlPoint(dx + 150, dy + 200));
1211 };
1212
1213 AiksContext renderer(GetContext(), nullptr);
1214 DisplayListBuilder recorder_builder;
1215 for (int x = 0; x < 5; ++x) {
1216 for (int y = 0; y < 5; ++y) {
1217 DlRect rect = DlRect::MakeXYWH(x * 20, y * 20, 20, 20);
1218 DlPaint paint;
1219 paint.setColor(((x + y) & 1) == 0 ? DlColor::kYellow()
1220 : DlColor::kBlue());
1221
1222 recorder_builder.DrawRect(rect, paint);
1223 }
1224 }
1225 auto texture =
1226 DisplayListToTexture(recorder_builder.Build(), {100, 100}, renderer);
1227
1228 auto image_source = DlColorSource::MakeImage(
1229 DlImageImpeller::Make(texture), DlTileMode::kRepeat, DlTileMode::kRepeat);
1230 auto blur_filter = DlImageFilter::MakeBlur(5, 5, DlTileMode::kDecal);
1231
1232 DlPaint paint;
1233 paint.setColor(DlColor::kDarkGreen());
1234 builder.DrawRect(DlRect::MakeLTRB(0, 0, 300, 600), paint);
1235
1236 paint.setColorSource(image_source);
1237 builder.DrawRect(DlRect::MakeLTRB(100, 100, 200, 200), paint);
1238
1239 paint.setColorSource(nullptr);
1240 paint.setColor(DlColor::kRed());
1241 builder.DrawRect(DlRect::MakeLTRB(300, 0, 600, 600), paint);
1242
1243 paint.setColorSource(image_source);
1244 paint.setImageFilter(blur_filter);
1245 builder.DrawRect(DlRect::MakeLTRB(400, 100, 500, 200), paint);
1246
1247 paint.setImageFilter(nullptr);
1248 paint_lines(0, 300, paint);
1249
1250 paint.setImageFilter(blur_filter);
1251 paint_lines(300, 300, paint);
1252
1253 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1254 }
1255
1256 // This addresses a bug where tiny blurs could result in mip maps that beyond
1257 // the limits for the textures used for blurring.
1258 // See also: b/323402168
1259 TEST_P(AiksTest, GaussianBlurSolidColorTinyMipMap) {
1260 AiksContext renderer(GetContext(), nullptr);
1261
1262 for (int32_t i = 1; i < 5; ++i) {
1263 DisplayListBuilder builder;
1264 Scalar fi = i;
1265 DlPathBuilder path_builder;
1266 path_builder.MoveTo(DlPoint(100, 100));
1267 path_builder.LineTo(DlPoint(100 + fi, 100 + fi));
1268
1269 DlPaint paint;
1270 paint.setColor(DlColor::kChartreuse());
1271 auto blur_filter = DlImageFilter::MakeBlur(0.1, 0.1, DlTileMode::kClamp);
1272 paint.setImageFilter(blur_filter);
1273
1274 builder.DrawPath(path_builder.TakePath(), paint);
1275
1276 auto image = DisplayListToTexture(builder.Build(), {1024, 768}, renderer);
1277 EXPECT_TRUE(image) << " length " << i;
1278 }
1279 }
1280
1281 // This addresses a bug where tiny blurs could result in mip maps that beyond
1282 // the limits for the textures used for blurring.
1283 // See also: b/323402168
1284 TEST_P(AiksTest, GaussianBlurBackdropTinyMipMap) {
1285 AiksContext renderer(GetContext(), nullptr);
1286 for (int32_t i = 1; i < 5; ++i) {
1287 DisplayListBuilder builder;
1288
1289 ISize clip_size = ISize(i, i);
1290 builder.Save();
1291 builder.ClipRect(
1292 DlRect::MakeXYWH(400, 400, clip_size.width, clip_size.height));
1293
1294 DlPaint paint;
1295 paint.setColor(DlColor::kGreen());
1296 auto blur_filter = DlImageFilter::MakeBlur(0.1, 0.1, DlTileMode::kDecal);
1297 paint.setImageFilter(blur_filter);
1298
1299 builder.DrawCircle(DlPoint(400, 400), 200, paint);
1300 builder.Restore();
1301
1302 auto image = DisplayListToTexture(builder.Build(), {1024, 768}, renderer);
1303 EXPECT_TRUE(image) << " length " << i;
1304 }
1305 }
1306
1307 TEST_P(AiksTest,
1308 CanRenderMultipleBackdropBlurWithSingleBackdropIdDifferentLayers) {
1309 auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
1310
1311 DisplayListBuilder builder;
1312
1313 DlPaint paint;
1314 builder.DrawImage(image, DlPoint(50.0, 50.0),
1315 DlImageSampling::kNearestNeighbor, &paint);
1316
1317 for (int i = 0; i < 6; i++) {
1318 if (i != 0) {
1319 DlPaint paint;
1320 paint.setColor(DlColor::kWhite().withAlphaF(0.95));
1321 builder.SaveLayer(std::nullopt, &paint);
1322 }
1323 DlRoundRect rrect = DlRoundRect::MakeRectXY(
1324 DlRect::MakeXYWH(50 + (i * 100), 250, 100, 100), 20, 20);
1325 builder.Save();
1326 builder.ClipRoundRect(rrect);
1327
1328 DlPaint save_paint;
1329 save_paint.setBlendMode(DlBlendMode::kSrc);
1330 auto backdrop_filter = DlImageFilter::MakeBlur(30, 30, DlTileMode::kClamp);
1331 builder.SaveLayer(std::nullopt, &save_paint, backdrop_filter.get(),
1332 /*backdrop_id=*/1);
1333 builder.Restore();
1334 builder.Restore();
1335 if (i != 0) {
1336 builder.Restore();
1337 }
1338 }
1339
1340 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1341 }
1342
1343 TEST_P(AiksTest, BlurGradientWithOpacity) {
1344 DisplayListBuilder builder;
1345 builder.Scale(GetContentScale().x, GetContentScale().y);
1346
1347 std::vector<DlColor> colors = {DlColor(0xFFFF0000), DlColor(0xFF00FF00)};
1348 std::vector<Scalar> stops = {0.0, 1.0};
1349
1350 auto gradient = DlColorSource::MakeLinear(
1351 {0, 0}, {400, 400}, 2, colors.data(), stops.data(), DlTileMode::kClamp);
1352
1353 DlPaint save_paint;
1354 save_paint.setOpacity(0.5);
1355 builder.SaveLayer(std::nullopt, &save_paint);
1356
1357 DlPaint paint;
1358 paint.setColorSource(gradient);
1359 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 1));
1360 builder.DrawRect(DlRect::MakeXYWH(100, 100, 200, 200), paint);
1361
1362 builder.Restore();
1363
1364 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1365 }
1366
1367 // The artifacts this test attempts to reproduce don't reproduce on playgrounds.
1368 // The test is animated as an attempt to catch any ghosting that may happen
1369 // when DontCare is used instead of Clear.
1370 // https://github.com/flutter/flutter/issues/171772
1371 TEST_P(AiksTest, CanRenderNestedBackdropBlur) {
1372 int64_t count = 0;
1373 auto callback = [&]() -> sk_sp<DisplayList> {
1374 DisplayListBuilder builder;
1375
1376 Scalar freq = 1.0;
1377 Scalar amp = 50.0;
1378 Scalar offset = amp * sin(freq * 2.0 * M_PI * count / 60.0);
1379
1380 // Draw some background content to be blurred.
1381 DlPaint paint;
1382 paint.setColor(DlColor::kCornflowerBlue());
1383 builder.DrawCircle(DlPoint(100 + offset, 100), 50, paint);
1384 paint.setColor(DlColor::kGreenYellow());
1385 builder.DrawCircle(DlPoint(300, 200 + offset), 100, paint);
1386 paint.setColor(DlColor::kDarkMagenta());
1387 builder.DrawCircle(DlPoint(140, 170), 75, paint);
1388 paint.setColor(DlColor::kOrangeRed());
1389 builder.DrawCircle(DlPoint(180 + offset, 120 + offset), 100, paint);
1390
1391 // This is the first backdrop blur, simulating the navigation transition.
1392 auto backdrop_filter1 = DlImageFilter::MakeBlur(15, 15, DlTileMode::kClamp);
1393 builder.SaveLayer(std::nullopt, nullptr, backdrop_filter1.get());
1394
1395 // Draw the semi-transparent container from the second screen.
1396 DlPaint transparent_paint;
1397 transparent_paint.setColor(DlColor::kWhite().withAlpha(0.1 * 255));
1398 builder.DrawPaint(transparent_paint);
1399
1400 {
1401 // This is the second, nested backdrop blur.
1402 auto backdrop_filter2 =
1403 DlImageFilter::MakeBlur(10, 10, DlTileMode::kClamp);
1404 builder.Save();
1405 builder.ClipRect(DlRect::MakeXYWH(150, 150, 300, 300));
1406 builder.SaveLayer(std::nullopt, nullptr, backdrop_filter2.get());
1407 builder.Restore(); // Restore from SaveLayer
1408 builder.Restore(); // Restore from ClipRect
1409 }
1410
1411 builder.Restore(); // Restore from the first SaveLayer
1412
1413 count++;
1414 return builder.Build();
1415 };
1416 ASSERT_TRUE(OpenPlaygroundHere(callback));
1417 }
1418
1419 } // namespace testing
1420 } // namespace impeller
MASK_BLUR_VARIANT_TEST
#define MASK_BLUR_VARIANT_TEST(config)
Definition: aiks_dl_blur_unittests.cc:629
aiks_unittests.h
impeller::AiksContext
Definition: aiks_context.h:20
impeller::AiksPlayground
Definition: aiks_playground.h:16
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:30
impeller::DlImageImpeller::Make
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Definition: dl_image_impeller.cc:23
impeller::Playground::GetContentScale
Point GetContentScale() const
Definition: playground.cc:189
dl_dispatcher.h
x
int32_t x
Definition: dl_golden_unittests.cc:458
dl_image_impeller.h
flutter
Definition: dl_golden_blur_unittests.cc:16
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::testing::DoGradientOvalStrokeMaskBlur
sk_sp< flutter::DisplayList > DoGradientOvalStrokeMaskBlur(Vector2 content_Scale, Scalar sigma, DlBlurStyle style)
Definition: aiks_dl_blur_unittests.cc:62
impeller::testing::MaskBlurVariantTest
static sk_sp< DisplayList > MaskBlurVariantTest(const AiksTest &test_context, const MaskBlurTestConfig &config)
Definition: aiks_dl_blur_unittests.cc:505
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:59
impeller::testing::kPaintVariations
static const std::map< std::string, MaskBlurTestConfig > kPaintVariations
Definition: aiks_dl_blur_unittests.cc:586
impeller
Definition: allocation.cc:12
impeller::DisplayListToTexture
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.
Definition: dl_dispatcher.cc:1214
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::DlRect
flutter::DlRect DlRect
Definition: dl_dispatcher.h:25
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::DrawPlaygroundPoint
Point DrawPlaygroundPoint(PlaygroundPoint &point)
Definition: widgets.cc:11
impeller::DrawPlaygroundLine
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:51
impeller::DlRoundRect
flutter::DlRoundRect DlRoundRect
Definition: dl_dispatcher.h:27
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::PixelFormat::kB10G10R10A10XR
@ kB10G10R10A10XR
impeller::DlPoint
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
impeller::DlPath
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
impeller::ISize
ISize64 ISize
Definition: size.h:162
impeller::Color::White
static constexpr Color White()
Definition: color.h:264
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:272
impeller::Color::AntiqueWhite
static constexpr Color AntiqueWhite()
Definition: color.h:286
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:274
impeller::Degrees
Definition: scalar.h:76
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::PlaygroundPoint
Definition: widgets.h:21
impeller::Radius
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:48
impeller::Sigma
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
impeller::Sigma::sigma
Scalar sigma
Definition: sigma.h:33
impeller::TPoint< Scalar >
impeller::TPoint::Rotate
constexpr TPoint Rotate(const Radians &angle) const
Definition: point.h:226
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::TRect< Scalar >
impeller::TRect::Expand
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
impeller::TRect::GetBottom
constexpr auto GetBottom() const
Definition: rect.h:361
impeller::TRect::GetTop
constexpr auto GetTop() const
Definition: rect.h:357
impeller::TRect::GetLeft
constexpr auto GetLeft() const
Definition: rect.h:355
impeller::TRect::GetSize
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
impeller::TRect::GetRight
constexpr auto GetRight() const
Definition: rect.h:359
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::TRect::Shift
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:606
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::TSize
Definition: size.h:25
impeller::TSize::height
Type height
Definition: size.h:29
impeller::TSize::width
Type width
Definition: size.h:28
impeller::testing::MaskBlurTestConfig
Definition: aiks_dl_blur_unittests.cc:496
impeller::testing::MaskBlurTestConfig::blend_mode
DlBlendMode blend_mode
Definition: aiks_dl_blur_unittests.cc:502
impeller::testing::MaskBlurTestConfig::invert_colors
bool invert_colors
Definition: aiks_dl_blur_unittests.cc:501
impeller::testing::MaskBlurTestConfig::alpha
Scalar alpha
Definition: aiks_dl_blur_unittests.cc:499
impeller::testing::MaskBlurTestConfig::style
DlBlurStyle style
Definition: aiks_dl_blur_unittests.cc:497
impeller::testing::MaskBlurTestConfig::image_filter
std::shared_ptr< DlImageFilter > image_filter
Definition: aiks_dl_blur_unittests.cc:500
impeller::testing::MaskBlurTestConfig::sigma
Scalar sigma
Definition: aiks_dl_blur_unittests.cc:498
widgets.h