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