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