| 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 "display_list/display_list.h"
6 #include "display_list/dl_blend_mode.h"
7 #include "display_list/dl_builder.h"
8 #include "display_list/dl_color.h"
9 #include "display_list/dl_paint.h"
10 #include "display_list/dl_sampling_options.h"
11 #include "display_list/dl_tile_mode.h"
12 #include "display_list/effects/dl_color_filter.h"
13 #include "display_list/effects/dl_color_source.h"
14 #include "display_list/effects/dl_image_filter.h"
15 #include "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({100, 0}, {100, 60}, line_paint);
92 builder.DrawLine({0, 30}, {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({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 DlBlendColorFilter::Make(DlColor::kGreen(), DlBlendMode::kSrc));
179 builder.DrawCircle({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 DlBlendColorFilter::Make(DlColor::kGreen(), DlBlendMode::kColor));
199 builder.DrawCircle({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({100, 100}, 50, paint);
215
216 paint.setColor(DlColor::kGreenYellow());
217 builder.DrawCircle({300, 200}, 100, paint);
218
219 paint.setColor(DlColor::kDarkMagenta());
220 builder.DrawCircle({140, 170}, 75, paint);
221
222 paint.setColor(DlColor::kOrangeRed());
223 builder.DrawCircle({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 = DlBlurImageFilter::Make(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({100, 100}, 50, paint);
248
249 paint.setColor(DlColor::kGreenYellow());
250 builder.DrawCircle({300, 200}, 100, paint);
251
252 paint.setColor(DlColor::kDarkMagenta());
253 builder.DrawCircle({140, 170}, 75, paint);
254
255 paint.setColor(DlColor::kOrangeRed());
256 builder.DrawCircle({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 = DlBlurImageFilter::Make(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, CanRenderBackdropBlurHugeSigma) {
272 DisplayListBuilder builder;
273
274 DlPaint paint;
275 paint.setColor(DlColor::kGreen());
276 builder.DrawCircle({400, 400}, 300, paint);
277
278 DlPaint save_paint;
279 save_paint.setBlendMode(DlBlendMode::kSrc);
280
281 auto backdrop_filter =
282 DlBlurImageFilter::Make(999999, 999999, DlTileMode::kClamp);
283 builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
284 builder.Restore();
285
286 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
287 }
288
289 TEST_P(AiksTest, CanRenderClippedBlur) {
290 DisplayListBuilder builder;
291 builder.ClipRect(SkRect::MakeXYWH(100, 150, 400, 400));
292
293 DlPaint paint;
294 paint.setColor(DlColor::kGreen());
295 paint.setImageFilter(DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal));
296 builder.DrawCircle({400, 400}, 200, paint);
297 builder.Restore();
298
299 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
300 }
301
302 TEST_P(AiksTest, ClippedBlurFilterRendersCorrectlyInteractive) {
303 auto callback = [&]() -> sk_sp<DisplayList> {
304 static PlaygroundPoint playground_point(Point(400, 400), 20,
305 Color::Green());
306 auto point = DrawPlaygroundPoint(playground_point);
307
308 DisplayListBuilder builder;
309 auto location = point - Point(400, 400);
310 builder.Translate(location.x, location.y);
311
312 DlPaint paint;
313 Sigma sigma = Radius{120 * 3};
314 paint.setMaskFilter(
315 DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma));
316 paint.setColor(DlColor::kRed());
317
318 SkPath path = SkPath::Rect(SkRect::MakeLTRB(0, 0, 800, 800));
319 path.addCircle(0, 0, 0.5);
320 builder.DrawPath(path, paint);
321 return builder.Build();
322 };
323 ASSERT_TRUE(OpenPlaygroundHere(callback));
324 }
325
326 TEST_P(AiksTest, ClippedBlurFilterRendersCorrectly) {
327 DisplayListBuilder builder;
328 builder.Translate(0, -400);
329 DlPaint paint;
330
331 Sigma sigma = Radius{120 * 3};
332 paint.setMaskFilter(
333 DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma));
334 paint.setColor(DlColor::kRed());
335
336 SkPath path = SkPath::Rect(SkRect::MakeLTRB(0, 0, 800, 800));
337 path.addCircle(0, 0, 0.5);
338 builder.DrawPath(path, paint);
339
340 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
341 }
342
343 TEST_P(AiksTest, ClearBlendWithBlur) {
344 DisplayListBuilder builder;
345 DlPaint paint;
346 paint.setColor(DlColor::kBlue());
347 builder.DrawRect(SkRect::MakeXYWH(0, 0, 600.0, 600.0), paint);
348
349 DlPaint clear;
350 clear.setBlendMode(DlBlendMode::kClear);
351 clear.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20));
352
353 builder.DrawCircle({300.0, 300.0}, 200.0, clear);
354
355 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
356 }
357
358 TEST_P(AiksTest, BlurHasNoEdge) {
359 Scalar sigma = 47.6;
360 auto callback = [&]() -> sk_sp<DisplayList> {
361 if (AiksTest::ImGuiBegin("Controls", nullptr,
362 ImGuiWindowFlags_AlwaysAutoResize)) {
363 ImGui::SliderFloat("Sigma", &sigma, 0, 50);
364 ImGui::End();
365 }
366 DisplayListBuilder builder;
367 builder.Scale(GetContentScale().x, GetContentScale().y);
368 builder.DrawPaint({});
369
370 DlPaint paint;
371 paint.setColor(DlColor::kGreen());
372 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
373
374 builder.DrawRect(SkRect::MakeXYWH(300, 300, 200, 200), paint);
375 return builder.Build();
376 };
377
378 ASSERT_TRUE(OpenPlaygroundHere(callback));
379 }
380
381 TEST_P(AiksTest, MaskBlurWithZeroSigmaIsSkipped) {
382 DisplayListBuilder builder;
383
384 DlPaint paint;
385 paint.setColor(DlColor::kBlue());
386 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 0));
387
388 builder.DrawCircle({300, 300}, 200, paint);
389 builder.DrawRect(SkRect::MakeLTRB(100, 300, 500, 600), paint);
390
391 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
392 }
393
394 struct MaskBlurTestConfig {
395 DlBlurStyle style = DlBlurStyle::kNormal;
396 Scalar sigma = 1.0f;
397 Scalar alpha = 1.0f;
398 std::shared_ptr<DlImageFilter> image_filter;
399 bool invert_colors = false;
400 DlBlendMode blend_mode = DlBlendMode::kSrcOver;
401 };
402
403 static sk_sp<DisplayList> MaskBlurVariantTest(
404 const AiksTest& test_context,
405 const MaskBlurTestConfig& config) {
406 DisplayListBuilder builder;
407 builder.Scale(test_context.GetContentScale().x,
408 test_context.GetContentScale().y);
409 builder.Scale(0.8f, 0.8f);
410 builder.Translate(50.f, 50.f);
411
412 DlPaint draw_paint;
413 draw_paint.setColor(
414 DlColor::RGBA(Color::AntiqueWhite().red, Color::AntiqueWhite().green,
415 Color::AntiqueWhite().blue, Color::AntiqueWhite().alpha));
416 builder.DrawPaint(draw_paint);
417
418 DlPaint paint;
419 paint.setMaskFilter(DlBlurMaskFilter::Make(config.style, config.sigma));
420 paint.setInvertColors(config.invert_colors);
421 paint.setImageFilter(config.image_filter);
422 paint.setBlendMode(config.blend_mode);
423
424 const Scalar x = 50;
425 const Scalar radius = 20.0f;
426 const Scalar y_spacing = 100.0f;
427 Scalar alpha = config.alpha * 255;
428
429 Scalar y = 50;
430 paint.setColor(DlColor::kCrimson().withAlpha(alpha));
431 builder.DrawRect(SkRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
432 radius, 60.0f - radius),
433 paint);
434
435 y += y_spacing;
436 paint.setColor(DlColor::kBlue().withAlpha(alpha));
437 builder.DrawCircle({x + 25, y + 25}, radius, paint);
438
439 y += y_spacing;
440 paint.setColor(DlColor::kGreen().withAlpha(alpha));
441 builder.DrawOval(SkRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
442 radius, 60.0f - radius),
443 paint);
444
445 y += y_spacing;
446 paint.setColor(DlColor::kPurple().withAlpha(alpha));
447 SkRRect rrect =
448 SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, 60.0f, 60.0f), radius, radius);
449 builder.DrawRRect(rrect, paint);
450
451 y += y_spacing;
452 paint.setColor(DlColor::kOrange().withAlpha(alpha));
453
454 rrect =
455 SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, 60.0f, 60.0f), radius, 5.0);
456 builder.DrawRRect(rrect, paint);
457
458 y += y_spacing;
459 paint.setColor(DlColor::kMaroon().withAlpha(alpha));
460
461 {
462 SkPath path;
463 path.moveTo(x + 0, y + 60);
464 path.lineTo(x + 30, y + 0);
465 path.lineTo(x + 60, y + 60);
466 path.close();
467
468 builder.DrawPath(path, paint);
469 }
470
471 y += y_spacing;
472 paint.setColor(DlColor::kMaroon().withAlpha(alpha));
473 {
474 SkPath path;
475 path.addArc(SkRect::MakeXYWH(x + 5, y, 50, 50), 90, 180);
476 path.addArc(SkRect::MakeXYWH(x + 25, y, 50, 50), 90, 180);
477 path.close();
478 builder.DrawPath(path, paint);
479 }
480
481 return builder.Build();
482 }
483
484 static const std::map<std::string, MaskBlurTestConfig> kPaintVariations = {
485 // 1. Normal style, translucent, zero sigma.
486 {"NormalTranslucentZeroSigma",
487 {.style = DlBlurStyle::kNormal, .sigma = 0.0f, .alpha = 0.5f}},
488 // 2. Normal style, translucent.
489 {"NormalTranslucent",
490 {.style = DlBlurStyle::kNormal, .sigma = 8.0f, .alpha = 0.5f}},
491 // 3. Solid style, translucent.
492 {"SolidTranslucent",
493 {.style = DlBlurStyle::kSolid, .sigma = 8.0f, .alpha = 0.5f}},
494 // 4. Solid style, opaque.
495 {"SolidOpaque", {.style = DlBlurStyle::kSolid, .sigma = 8.0f}},
496 // 5. Solid style, translucent, color & image filtered.
497 {"SolidTranslucentWithFilters",
498 {.style = DlBlurStyle::kSolid,
499 .sigma = 8.0f,
500 .alpha = 0.5f,
501 .image_filter = DlBlurImageFilter::Make(3, 3, DlTileMode::kClamp),
502 .invert_colors = true}},
503 // 6. Solid style, translucent, exclusion blended.
504 {"SolidTranslucentExclusionBlend",
505 {.style = DlBlurStyle::kSolid,
506 .sigma = 8.0f,
507 .alpha = 0.5f,
508 .blend_mode = DlBlendMode::kExclusion}},
509 // 7. Inner style, translucent.
510 {"InnerTranslucent",
511 {.style = DlBlurStyle::kInner, .sigma = 8.0f, .alpha = 0.5f}},
512 // 8. Inner style, translucent, blurred.
513 {"InnerTranslucentWithBlurImageFilter",
514 {.style = DlBlurStyle::kInner,
515 .sigma = 8.0f,
516 .alpha = 0.5f,
517 .image_filter = DlBlurImageFilter::Make(3, 3, DlTileMode::kClamp)}},
518 // 9. Outer style, translucent.
519 {"OuterTranslucent",
520 {.style = DlBlurStyle::kOuter, .sigma = 8.0f, .alpha = 0.5f}},
521 // 10. Outer style, opaque, image filtered.
522 {"OuterOpaqueWithBlurImageFilter",
523 {.style = DlBlurStyle::kOuter,
524 .sigma = 8.0f,
525 .image_filter = DlBlurImageFilter::Make(3, 3, DlTileMode::kClamp)}},
526 };
527
528 #define MASK_BLUR_VARIANT_TEST(config) \
529 TEST_P(AiksTest, MaskBlurVariantTest##config) { \
530 ASSERT_TRUE(OpenPlaygroundHere( \
531 MaskBlurVariantTest(*this, kPaintVariations.at(#config)))); \
532 }
533
534 MASK_BLUR_VARIANT_TEST(NormalTranslucentZeroSigma)
535 MASK_BLUR_VARIANT_TEST(NormalTranslucent)
536 MASK_BLUR_VARIANT_TEST(SolidTranslucent)
537 MASK_BLUR_VARIANT_TEST(SolidOpaque)
538 MASK_BLUR_VARIANT_TEST(SolidTranslucentWithFilters)
539 MASK_BLUR_VARIANT_TEST(SolidTranslucentExclusionBlend)
540 MASK_BLUR_VARIANT_TEST(InnerTranslucent)
541 MASK_BLUR_VARIANT_TEST(InnerTranslucentWithBlurImageFilter)
542 MASK_BLUR_VARIANT_TEST(OuterTranslucent)
543 MASK_BLUR_VARIANT_TEST(OuterOpaqueWithBlurImageFilter)
544
545 #undef MASK_BLUR_VARIANT_TEST
546
547 TEST_P(AiksTest, GaussianBlurStyleInner) {
548 DisplayListBuilder builder;
549 builder.Scale(GetContentScale().x, GetContentScale().y);
550
551 DlPaint paint;
552 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1));
553 builder.DrawPaint(paint);
554
555 paint.setColor(DlColor::kGreen());
556 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kInner, 30));
557
558 SkPath path;
559 path.moveTo(200, 200);
560 path.lineTo(300, 400);
561 path.lineTo(100, 400);
562 path.close();
563
564 builder.DrawPath(path, paint);
565
566 // Draw another thing to make sure the clip area is reset.
567 DlPaint red;
568 red.setColor(DlColor::kRed());
569 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
570
571 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
572 }
573
574 TEST_P(AiksTest, GaussianBlurStyleOuter) {
575 DisplayListBuilder builder;
576 builder.Scale(GetContentScale().x, GetContentScale().y);
577
578 DlPaint paint;
579 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
580 builder.DrawPaint(paint);
581
582 paint.setColor(DlColor::kGreen());
583 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kOuter, 30));
584
585 SkPath path;
586 path.moveTo(200, 200);
587 path.lineTo(300, 400);
588 path.lineTo(100, 400);
589 path.close();
590
591 builder.DrawPath(path, paint);
592
593 // Draw another thing to make sure the clip area is reset.
594 DlPaint red;
595 red.setColor(DlColor::kRed());
596 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
597
598 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
599 }
600
601 TEST_P(AiksTest, GaussianBlurStyleSolid) {
602 DisplayListBuilder builder;
603 builder.Scale(GetContentScale().x, GetContentScale().y);
604
605 DlPaint paint;
606 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
607 builder.DrawPaint(paint);
608
609 paint.setColor(DlColor::kGreen());
610 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kSolid, 30));
611
612 SkPath path;
613 path.moveTo(200, 200);
614 path.lineTo(300, 400);
615 path.lineTo(100, 400);
616 path.close();
617
618 builder.DrawPath(path, paint);
619
620 // Draw another thing to make sure the clip area is reset.
621 DlPaint red;
622 red.setColor(DlColor::kRed());
623 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
624
625 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
626 }
627
628 TEST_P(AiksTest, MaskBlurTexture) {
629 Scalar sigma = 30;
630 auto callback = [&]() -> sk_sp<DisplayList> {
631 if (AiksTest::ImGuiBegin("Controls", nullptr,
632 ImGuiWindowFlags_AlwaysAutoResize)) {
633 ImGui::SliderFloat("Sigma", &sigma, 0, 500);
634 ImGui::End();
635 }
636
637 DisplayListBuilder builder;
638 builder.Scale(GetContentScale().x, GetContentScale().y);
639
640 DlPaint paint;
641 paint.setColor(DlColor::kGreen());
642 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
643
644 builder.DrawImage(
645 DlImageImpeller::Make(CreateTextureForFixture("boston.jpg")),
646 {200, 200}, DlImageSampling::kNearestNeighbor, &paint);
647
648 DlPaint red;
649 red.setColor(DlColor::kRed());
650 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
651
652 return builder.Build();
653 };
654 ASSERT_TRUE(OpenPlaygroundHere(callback));
655 }
656
657 TEST_P(AiksTest, MaskBlurDoesntStretchContents) {
658 Scalar sigma = 70;
659 auto callback = [&]() -> sk_sp<DisplayList> {
660 if (AiksTest::ImGuiBegin("Controls", nullptr,
661 ImGuiWindowFlags_AlwaysAutoResize)) {
662 ImGui::SliderFloat("Sigma", &sigma, 0, 500);
663 ImGui::End();
664 }
665
666 DisplayListBuilder builder;
667 builder.Scale(GetContentScale().x, GetContentScale().y);
668
669 DlPaint paint;
670 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
671 builder.DrawPaint(paint);
672
673 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
674
675 builder.Transform(SkMatrix::Translate(100, 100) *
676 SkMatrix::Scale(0.5, 0.5));
677
678 paint.setColorSource(std::make_shared<DlImageColorSource>(
679 DlImageImpeller::Make(boston), DlTileMode::kRepeat, DlTileMode::kRepeat,
680 DlImageSampling::kMipmapLinear));
681 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma));
682
683 builder.DrawRect(SkRect::MakeXYWH(0, 0, boston->GetSize().width,
684 boston->GetSize().height),
685 paint);
686
687 return builder.Build();
688 };
689 ASSERT_TRUE(OpenPlaygroundHere(callback));
690 }
691
692 TEST_P(AiksTest, GaussianBlurAtPeripheryVertical) {
693 DisplayListBuilder builder;
694
695 DlPaint paint;
696 builder.Scale(GetContentScale().x, GetContentScale().y);
697
698 paint.setColor(DlColor::kLimeGreen());
699 SkRRect rrect = SkRRect::MakeRectXY(
700 SkRect::MakeLTRB(0, 0, GetWindowSize().width, 100), 10, 10);
701 builder.DrawRRect(rrect, paint);
702
703 paint.setColor(DlColor::kMagenta());
704 rrect = SkRRect::MakeRectXY(
705 SkRect::MakeLTRB(0, 110, GetWindowSize().width, 210), 10, 10);
706 builder.DrawRRect(rrect, paint);
707 builder.ClipRect(SkRect::MakeLTRB(100, 0, 200, GetWindowSize().height));
708
709 DlPaint save_paint;
710 save_paint.setBlendMode(DlBlendMode::kSrc);
711
712 auto backdrop_filter = DlBlurImageFilter::Make(20, 20, DlTileMode::kClamp);
713
714 builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
715 builder.Restore();
716
717 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
718 }
719
720 TEST_P(AiksTest, GaussianBlurAtPeripheryHorizontal) {
721 DisplayListBuilder builder;
722
723 builder.Scale(GetContentScale().x, GetContentScale().y);
724 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
725 builder.DrawImageRect(
726 DlImageImpeller::Make(boston),
727 SkRect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height),
728 SkRect::MakeLTRB(0, 0, GetWindowSize().width, 100),
729 DlImageSampling::kNearestNeighbor);
730
731 DlPaint paint;
732 paint.setColor(DlColor::kMagenta());
733
734 SkRRect rrect = SkRRect::MakeRectXY(
735 SkRect::MakeLTRB(0, 110, GetWindowSize().width, 210), 10, 10);
736 builder.DrawRRect(rrect, paint);
737 builder.ClipRect(SkRect::MakeLTRB(0, 50, GetWindowSize().width, 150));
738
739 DlPaint save_paint;
740 save_paint.setBlendMode(DlBlendMode::kSrc);
741
742 auto backdrop_filter = DlBlurImageFilter::Make(20, 20, DlTileMode::kClamp);
743 builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get());
744
745 builder.Restore();
746 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
747 }
748
749 TEST_P(AiksTest, GaussianBlurAnimatedBackdrop) {
750 // This test is for checking out how stable rendering is when content is
751 // translated underneath a blur. Animating under a blur can cause
752 // *shimmering* to happen as a result of pixel alignment.
753 // See also: https://github.com/flutter/flutter/issues/140193
754 auto boston =
755 CreateTextureForFixture("boston.jpg", /*enable_mipmapping=*/true);
756 ASSERT_TRUE(boston);
757 int64_t count = 0;
758 Scalar sigma = 20.0;
759 Scalar freq = 0.1;
760 Scalar amp = 50.0;
761 auto callback = [&]() -> sk_sp<DisplayList> {
762 if (AiksTest::ImGuiBegin("Controls", nullptr,
763 ImGuiWindowFlags_AlwaysAutoResize)) {
764 ImGui::SliderFloat("Sigma", &sigma, 0, 200);
765 ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0);
766 ImGui::SliderFloat("Amplitude", &amp, 1, 100);
767 ImGui::End();
768 }
769
770 DisplayListBuilder builder;
771 builder.Scale(GetContentScale().x, GetContentScale().y);
772 Scalar y = amp * sin(freq * 2.0 * M_PI * count / 60);
773 builder.DrawImage(
774 DlImageImpeller::Make(boston),
775 SkPoint::Make(1024 / 2 - boston->GetSize().width / 2,
776 (768 / 2 - boston->GetSize().height / 2) + y),
777 DlImageSampling::kMipmapLinear);
778 static PlaygroundPoint point_a(Point(100, 100), 20, Color::Red());
779 static PlaygroundPoint point_b(Point(900, 700), 20, Color::Red());
780 auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
781
782 builder.ClipRect(
783 SkRect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
784 builder.ClipRect(SkRect::MakeLTRB(100, 100, 900, 700));
785
786 DlPaint paint;
787 paint.setBlendMode(DlBlendMode::kSrc);
788
789 auto backdrop_filter =
790 DlBlurImageFilter::Make(sigma, sigma, DlTileMode::kClamp);
791 builder.SaveLayer(nullptr, &paint, backdrop_filter.get());
792 count += 1;
793 return builder.Build();
794 };
795 ASSERT_TRUE(OpenPlaygroundHere(callback));
796 }
797
798 TEST_P(AiksTest, GaussianBlurStyleInnerGradient) {
799 DisplayListBuilder builder;
800
801 builder.Scale(GetContentScale().x, GetContentScale().y);
802
803 DlPaint paint;
804 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
805 builder.DrawPaint(paint);
806
807 std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
808 DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
809 std::vector<Scalar> stops = {0.0, 1.0};
810
811 paint = DlPaint{};
812 paint.setColorSource(DlColorSource::MakeLinear(
813 /*start_point=*/{0, 0},
814 /*end_point=*/{200, 200},
815 /*stop_count=*/colors.size(),
816 /*colors=*/colors.data(),
817 /*stops=*/stops.data(),
818 /*tile_mode=*/DlTileMode::kMirror));
819 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kInner, 30));
820
821 SkPath path;
822 path.moveTo(200, 200);
823 path.lineTo(300, 400);
824 path.lineTo(100, 400);
825 path.close();
826 builder.DrawPath(path, paint);
827
828 // Draw another thing to make sure the clip area is reset.
829 DlPaint red;
830 red.setColor(DlColor::kRed());
831 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
832
833 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
834 }
835
836 TEST_P(AiksTest, GaussianBlurStyleSolidGradient) {
837 DisplayListBuilder builder;
838 builder.Scale(GetContentScale().x, GetContentScale().y);
839
840 DlPaint paint;
841 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
842 builder.DrawPaint(paint);
843
844 std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
845 DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
846 std::vector<Scalar> stops = {0.0, 1.0};
847
848 paint = DlPaint{};
849 paint.setColorSource(DlColorSource::MakeLinear(
850 /*start_point=*/{0, 0},
851 /*end_point=*/{200, 200},
852 /*stop_count=*/colors.size(),
853 /*colors=*/colors.data(),
854 /*stops=*/stops.data(),
855 /*tile_mode=*/DlTileMode::kMirror));
856 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kSolid, 30));
857
858 SkPath path;
859 path.moveTo(200, 200);
860 path.lineTo(300, 400);
861 path.lineTo(100, 400);
862 path.close();
863 builder.DrawPath(path, paint);
864
865 // Draw another thing to make sure the clip area is reset.
866 DlPaint red;
867 red.setColor(DlColor::kRed());
868 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
869 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
870 }
871
872 TEST_P(AiksTest, GaussianBlurStyleOuterGradient) {
873 DisplayListBuilder builder;
874 builder.Scale(GetContentScale().x, GetContentScale().y);
875
876 DlPaint paint;
877 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0));
878 builder.DrawPaint(paint);
879
880 std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
881 DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)};
882 std::vector<Scalar> stops = {0.0, 1.0};
883
884 paint = DlPaint{};
885 paint.setColorSource(DlColorSource::MakeLinear(
886 /*start_point=*/{0, 0},
887 /*end_point=*/{200, 200},
888 /*stop_count=*/colors.size(),
889 /*colors=*/colors.data(),
890 /*stops=*/stops.data(),
891 /*tile_mode=*/DlTileMode::kMirror));
892 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kOuter, 30));
893
894 SkPath path;
895 path.moveTo(200, 200);
896 path.lineTo(300, 400);
897 path.lineTo(100, 400);
898 path.close();
899 builder.DrawPath(path, paint);
900
901 // Draw another thing to make sure the clip area is reset.
902 DlPaint red;
903 red.setColor(DlColor::kRed());
904 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red);
905 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
906 }
907
908 TEST_P(AiksTest, GaussianBlurScaledAndClipped) {
909 DisplayListBuilder builder;
910 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
911 Rect bounds =
912 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
913 Vector2 image_center = Vector2(bounds.GetSize() / 2);
914
915 DlPaint paint;
916 paint.setImageFilter(DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal));
917
918 Vector2 clip_size = {150, 75};
919 Vector2 center = Vector2(1024, 768) / 2;
920 builder.Scale(GetContentScale().x, GetContentScale().y);
921
922 auto rect =
923 Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size);
924 builder.ClipRect(SkRect::MakeLTRB(rect.GetLeft(), rect.GetTop(),
925 rect.GetRight(), rect.GetBottom()));
926 builder.Translate(center.x, center.y);
927 builder.Scale(0.6, 0.6);
928
929 SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
930 bounds.GetRight(), bounds.GetBottom());
931 Rect dest = bounds.Shift(-image_center);
932 SkRect sk_dst = SkRect::MakeLTRB(dest.GetLeft(), dest.GetTop(),
933 dest.GetRight(), dest.GetBottom());
934 builder.DrawImageRect(DlImageImpeller::Make(boston), /*src=*/sk_bounds,
935 /*dst=*/sk_dst, DlImageSampling::kNearestNeighbor,
936 &paint);
937
938 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
939 }
940
941 TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) {
942 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
943
944 auto callback = [&]() -> sk_sp<DisplayList> {
945 const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
946 const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
947 DlTileMode::kMirror, DlTileMode::kDecal};
948
949 static float rotation = 0;
950 static float scale = 0.6;
951 static int selected_tile_mode = 3;
952
953 if (AiksTest::ImGuiBegin("Controls", nullptr,
954 ImGuiWindowFlags_AlwaysAutoResize)) {
955 ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
956 ImGui::SliderFloat("Scale", &scale, 0, 2.0);
957 ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
958 sizeof(tile_mode_names) / sizeof(char*));
959 ImGui::End();
960 }
961
962 DisplayListBuilder builder;
963 Rect bounds =
964 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
965 Vector2 image_center = Vector2(bounds.GetSize() / 2);
966 DlPaint paint;
967 paint.setImageFilter(
968 DlBlurImageFilter::Make(20, 20, tile_modes[selected_tile_mode]));
969
970 static PlaygroundPoint point_a(Point(362, 309), 20, Color::Red());
971 static PlaygroundPoint point_b(Point(662, 459), 20, Color::Red());
972 auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
973 Vector2 center = Vector2(1024, 768) / 2;
974
975 builder.Scale(GetContentScale().x, GetContentScale().y);
976 builder.ClipRect(
977 SkRect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
978 builder.Translate(center.x, center.y);
979 builder.Scale(scale, scale);
980 builder.Rotate(rotation);
981
982 SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
983 bounds.GetRight(), bounds.GetBottom());
984 Rect dest = bounds.Shift(-image_center);
985 SkRect sk_dst = SkRect::MakeLTRB(dest.GetLeft(), dest.GetTop(),
986 dest.GetRight(), dest.GetBottom());
987 builder.DrawImageRect(DlImageImpeller::Make(boston), /*src=*/sk_bounds,
988 /*dst=*/sk_dst, DlImageSampling::kNearestNeighbor,
989 &paint);
990 return builder.Build();
991 };
992
993 ASSERT_TRUE(OpenPlaygroundHere(callback));
994 }
995
996 TEST_P(AiksTest, GaussianBlurOneDimension) {
997 DisplayListBuilder builder;
998
999 builder.Scale(GetContentScale().x, GetContentScale().y);
1000 builder.Scale(0.5, 0.5);
1001
1002 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1003 builder.DrawImage(DlImageImpeller::Make(boston), {100, 100}, {});
1004
1005 DlPaint paint;
1006 paint.setBlendMode(DlBlendMode::kSrc);
1007
1008 auto backdrop_filter = DlBlurImageFilter::Make(50, 0, DlTileMode::kClamp);
1009 builder.SaveLayer(nullptr, &paint, backdrop_filter.get());
1010 builder.Restore();
1011 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1012 }
1013
1014 // Smoketest to catch issues with the coverage hint.
1015 // Draws a rotated blurred image within a rectangle clip. The center of the clip
1016 // rectangle is the center of the rotated image. The entire area of the clip
1017 // rectangle should be filled with opaque colors output by the blur.
1018 TEST_P(AiksTest, GaussianBlurRotatedAndClipped) {
1019 DisplayListBuilder builder;
1020
1021 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1022 Rect bounds =
1023 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
1024
1025 DlPaint paint;
1026 paint.setImageFilter(DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal));
1027
1028 Vector2 image_center = Vector2(bounds.GetSize() / 2);
1029 Vector2 clip_size = {150, 75};
1030 Vector2 center = Vector2(1024, 768) / 2;
1031 builder.Scale(GetContentScale().x, GetContentScale().y);
1032
1033 auto clip_bounds =
1034 Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size);
1035 builder.ClipRect(SkRect::MakeLTRB(clip_bounds.GetLeft(), clip_bounds.GetTop(),
1036 clip_bounds.GetRight(),
1037 clip_bounds.GetBottom()));
1038 builder.Translate(center.x, center.y);
1039 builder.Scale(0.6, 0.6);
1040 builder.Rotate(25);
1041
1042 auto dst_rect = bounds.Shift(-image_center);
1043 builder.DrawImageRect(
1044 DlImageImpeller::Make(boston), /*src=*/
1045 SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(), bounds.GetRight(),
1046 bounds.GetBottom()),
1047 /*dst=*/
1048 SkRect::MakeLTRB(dst_rect.GetLeft(), dst_rect.GetTop(),
1049 dst_rect.GetRight(), dst_rect.GetBottom()),
1050 DlImageSampling::kMipmapLinear, &paint);
1051
1052 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1053 }
1054
1055 TEST_P(AiksTest, GaussianBlurRotatedNonUniform) {
1056 auto callback = [&]() -> sk_sp<DisplayList> {
1057 const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
1058 const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
1059 DlTileMode::kMirror, DlTileMode::kDecal};
1060
1061 static float rotation = 45;
1062 static float scale = 0.6;
1063 static int selected_tile_mode = 3;
1064
1065 if (AiksTest::ImGuiBegin("Controls", nullptr,
1066 ImGuiWindowFlags_AlwaysAutoResize)) {
1067 ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
1068 ImGui::SliderFloat("Scale", &scale, 0, 2.0);
1069 ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
1070 sizeof(tile_mode_names) / sizeof(char*));
1071 ImGui::End();
1072 }
1073
1074 DisplayListBuilder builder;
1075
1076 DlPaint paint;
1077 paint.setColor(DlColor::kGreen());
1078 paint.setImageFilter(
1079 DlBlurImageFilter::Make(50, 0, tile_modes[selected_tile_mode]));
1080
1081 Vector2 center = Vector2(1024, 768) / 2;
1082 builder.Scale(GetContentScale().x, GetContentScale().y);
1083 builder.Translate(center.x, center.y);
1084 builder.Scale(scale, scale);
1085 builder.Rotate(rotation);
1086
1087 SkRRect rrect =
1088 SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -100, 200, 200), 10, 10);
1089 builder.DrawRRect(rrect, paint);
1090 return builder.Build();
1091 };
1092
1093 ASSERT_TRUE(OpenPlaygroundHere(callback));
1094 }
1095
1096 TEST_P(AiksTest, BlurredRectangleWithShader) {
1097 DisplayListBuilder builder;
1098 builder.Scale(GetContentScale().x, GetContentScale().y);
1099
1100 auto paint_lines = [&builder](Scalar dx, Scalar dy, DlPaint paint) {
1101 auto draw_line = [&builder, &paint](SkPoint a, SkPoint b) {
1102 SkPath line = SkPath::Line(a, b);
1103 builder.DrawPath(line, paint);
1104 };
1105 paint.setStrokeWidth(5);
1106 paint.setDrawStyle(DlDrawStyle::kStroke);
1107 draw_line(SkPoint::Make(dx + 100, dy + 100),
1108 SkPoint::Make(dx + 200, dy + 200));
1109 draw_line(SkPoint::Make(dx + 100, dy + 200),
1110 SkPoint::Make(dx + 200, dy + 100));
1111 draw_line(SkPoint::Make(dx + 150, dy + 100),
1112 SkPoint::Make(dx + 200, dy + 150));
1113 draw_line(SkPoint::Make(dx + 100, dy + 150),
1114 SkPoint::Make(dx + 150, dy + 200));
1115 };
1116
1117 AiksContext renderer(GetContext(), nullptr);
1118 DisplayListBuilder recorder_builder;
1119 for (int x = 0; x < 5; ++x) {
1120 for (int y = 0; y < 5; ++y) {
1121 SkRect rect = SkRect::MakeXYWH(x * 20, y * 20, 20, 20);
1122 DlPaint paint;
1123 paint.setColor(((x + y) & 1) == 0 ? DlColor::kYellow()
1124 : DlColor::kBlue());
1125
1126 recorder_builder.DrawRect(rect, paint);
1127 }
1128 }
1129 auto texture =
1130 DisplayListToTexture(recorder_builder.Build(), {100, 100}, renderer);
1131
1132 auto image_source = std::make_shared<DlImageColorSource>(
1133 DlImageImpeller::Make(texture), DlTileMode::kRepeat, DlTileMode::kRepeat);
1134 auto blur_filter = DlBlurImageFilter::Make(5, 5, DlTileMode::kDecal);
1135
1136 DlPaint paint;
1137 paint.setColor(DlColor::kDarkGreen());
1138 builder.DrawRect(SkRect::MakeLTRB(0, 0, 300, 600), paint);
1139
1140 paint.setColorSource(image_source);
1141 builder.DrawRect(SkRect::MakeLTRB(100, 100, 200, 200), paint);
1142
1143 paint.setColorSource(nullptr);
1144 paint.setColor(DlColor::kRed());
1145 builder.DrawRect(SkRect::MakeLTRB(300, 0, 600, 600), paint);
1146
1147 paint.setColorSource(image_source);
1148 paint.setImageFilter(blur_filter);
1149 builder.DrawRect(SkRect::MakeLTRB(400, 100, 500, 200), paint);
1150
1151 paint.setImageFilter(nullptr);
1152 paint_lines(0, 300, paint);
1153
1154 paint.setImageFilter(blur_filter);
1155 paint_lines(300, 300, paint);
1156
1157 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1158 }
1159
1160 TEST_P(AiksTest, GaussianBlurWithoutDecalSupport) {
1161 if (GetParam() != PlaygroundBackend::kMetal) {
1162 GTEST_SKIP()
1163 << "This backend doesn't yet support setting device capabilities.";
1164 }
1165 if (!WillRenderSomething()) {
1166 // Sometimes these tests are run without playgrounds enabled which is
1167 // pointless for this test since we are asserting that
1168 // `SupportsDecalSamplerAddressMode` is called.
1169 GTEST_SKIP() << "This test requires playgrounds.";
1170 }
1171
1172 std::shared_ptr<const Capabilities> old_capabilities =
1173 GetContext()->GetCapabilities();
1174 auto mock_capabilities = std::make_shared<MockCapabilities>();
1175 EXPECT_CALL(*mock_capabilities, SupportsDecalSamplerAddressMode())
1176 .Times(::testing::AtLeast(1))
1177 .WillRepeatedly(::testing::Return(false));
1178 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultColorFormat);
1179 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultStencilFormat);
1180 FLT_FORWARD(mock_capabilities, old_capabilities,
1181 GetDefaultDepthStencilFormat);
1182 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsOffscreenMSAA);
1183 FLT_FORWARD(mock_capabilities, old_capabilities,
1184 SupportsImplicitResolvingMSAA);
1185 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsReadFromResolve);
1186 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsFramebufferFetch);
1187 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsSSBO);
1188 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsCompute);
1189 FLT_FORWARD(mock_capabilities, old_capabilities,
1190 SupportsTextureToTextureBlits);
1191 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultGlyphAtlasFormat);
1192 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsTriangleFan);
1193 ASSERT_TRUE(SetCapabilities(mock_capabilities).ok());
1194
1195 auto texture = DlImageImpeller::Make(CreateTextureForFixture("boston.jpg"));
1196
1197 DisplayListBuilder builder;
1198 builder.Scale(GetContentScale().x * 0.5, GetContentScale().y * 0.5);
1199
1200 DlPaint paint;
1201 paint.setColor(DlColor::kBlack());
1202 builder.DrawPaint(paint);
1203
1204 auto blur_filter = DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal);
1205 paint.setImageFilter(blur_filter);
1206 builder.DrawImage(texture, SkPoint::Make(200, 200), {}, &paint);
1207 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1208 }
1209
1210 // This addresses a bug where tiny blurs could result in mip maps that beyond
1211 // the limits for the textures used for blurring.
1212 // See also: b/323402168
1213 TEST_P(AiksTest, GaussianBlurSolidColorTinyMipMap) {
1214 AiksContext renderer(GetContext(), nullptr);
1215
1216 for (int32_t i = 1; i < 5; ++i) {
1217 DisplayListBuilder builder;
1218 Scalar fi = i;
1219 SkPath path;
1220 path.moveTo(100, 100);
1221 path.lineTo(100 + fi, 100 + fi);
1222
1223 DlPaint paint;
1224 paint.setColor(DlColor::kChartreuse());
1225 auto blur_filter = DlBlurImageFilter::Make(0.1, 0.1, DlTileMode::kClamp);
1226 paint.setImageFilter(blur_filter);
1227
1228 builder.DrawPath(path, paint);
1229
1230 auto image = DisplayListToTexture(builder.Build(), {1024, 768}, renderer);
1231 EXPECT_TRUE(image) << " length " << i;
1232 }
1233 }
1234
1235 // This addresses a bug where tiny blurs could result in mip maps that beyond
1236 // the limits for the textures used for blurring.
1237 // See also: b/323402168
1238 TEST_P(AiksTest, GaussianBlurBackdropTinyMipMap) {
1239 AiksContext renderer(GetContext(), nullptr);
1240 for (int32_t i = 1; i < 5; ++i) {
1241 DisplayListBuilder builder;
1242
1243 ISize clip_size = ISize(i, i);
1244 builder.Save();
1245 builder.ClipRect(
1246 SkRect::MakeXYWH(400, 400, clip_size.width, clip_size.height));
1247
1248 DlPaint paint;
1249 paint.setColor(DlColor::kGreen());
1250 auto blur_filter = DlBlurImageFilter::Make(0.1, 0.1, DlTileMode::kDecal);
1251 paint.setImageFilter(blur_filter);
1252
1253 builder.DrawCircle({400, 400}, 200, paint);
1254 builder.Restore();
1255
1256 auto image = DisplayListToTexture(builder.Build(), {1024, 768}, renderer);
1257 EXPECT_TRUE(image) << " length " << i;
1258 }
1259 }
1260
1261 } // namespace testing
1262 } // namespace impeller
impeller::ISize
ISize64 ISize
Definition: size.h:140
impeller::Sigma::sigma
Scalar sigma
Definition: sigma.h:33
impeller::testing::MaskBlurTestConfig::alpha
Scalar alpha
Definition: aiks_dl_blur_unittests.cc:397
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:396
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:394
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:596
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:19
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:484
impeller::TRect::GetLeft
constexpr auto GetLeft() const
Definition: rect.h:345
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:15
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:273
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:776
impeller::testing::MaskBlurTestConfig::blend_mode
DlBlendMode blend_mode
Definition: aiks_dl_blur_unittests.cc:400
impeller::TSize::width
Type width
Definition: size.h:22
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:78
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:321
impeller::TRect::GetRight
constexpr auto GetRight() const
Definition: rect.h:349
impeller::Playground::GetContentScale
Point GetContentScale() const
Definition: playground.cc:185
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:528
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:398
scale
const Scalar scale
Definition: stroke_path_geometry.cc:301
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:30
impeller::TRect::GetBottom
constexpr auto GetBottom() const
Definition: rect.h:351
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:395
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:1195
impeller::TSize::height
Type height
Definition: size.h:23
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:403
impeller
Definition: allocation.cc:12
impeller::TRect::GetTop
constexpr auto GetTop() const
Definition: rect.h:347
impeller::TRect< Scalar >
impeller::testing::MaskBlurTestConfig::invert_colors
bool invert_colors
Definition: aiks_dl_blur_unittests.cc:399
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:612
impeller::DrawPlaygroundLine
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
dl_image_impeller.h