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