Flutter Engine
The Flutter Engine
aiks_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/impeller/aiks/aiks_unittests.h"
6
13#include "third_party/imgui/imgui.h"
14
15////////////////////////////////////////////////////////////////////////////////
16// This is for tests of Canvas that are interested the results of rendering
17// blurs.
18////////////////////////////////////////////////////////////////////////////////
19
20namespace impeller {
21namespace testing {
22
23TEST_P(AiksTest, CanRenderMaskBlurHugeSigma) {
24 Canvas canvas;
25 canvas.DrawCircle({400, 400}, 300,
26 {.color = Color::Green(),
27 .mask_blur_descriptor = Paint::MaskBlurDescriptor{
29 .sigma = Sigma(99999),
30 }});
31 canvas.Restore();
32
33 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
34}
35
36TEST_P(AiksTest, CanRenderForegroundBlendWithMaskBlur) {
37 // This case triggers the ForegroundPorterDuffBlend path. The color filter
38 // should apply to the color only, and respect the alpha mask.
39 Canvas canvas;
40 canvas.ClipRect(Rect::MakeXYWH(100, 150, 400, 400));
41 canvas.DrawCircle({400, 400}, 200,
42 {
43 .color = Color::White(),
44 .color_filter = ColorFilter::MakeBlend(
46 .mask_blur_descriptor =
49 .sigma = Radius(20),
50 },
51 });
52 canvas.Restore();
53
54 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
55}
56
57TEST_P(AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur) {
58 // This case triggers the ForegroundAdvancedBlend path. The color filter
59 // should apply to the color only, and respect the alpha mask.
60 Canvas canvas;
61 canvas.ClipRect(Rect::MakeXYWH(100, 150, 400, 400));
62 canvas.DrawCircle({400, 400}, 200,
63 {
64 .color = Color::Grey(),
65 .color_filter = ColorFilter::MakeBlend(
67 .mask_blur_descriptor =
70 .sigma = Radius(20),
71 },
72 });
73 canvas.Restore();
74
75 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
76}
77
78TEST_P(AiksTest, CanRenderBackdropBlurInteractive) {
79 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
80 static PlaygroundPoint point_a(Point(50, 50), 30, Color::White());
81 static PlaygroundPoint point_b(Point(300, 200), 30, Color::White());
82 auto [a, b] = DrawPlaygroundLine(point_a, point_b);
83
84 Canvas canvas;
85 canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
86 canvas.DrawCircle({300, 200}, 100, {.color = Color::GreenYellow()});
87 canvas.DrawCircle({140, 170}, 75, {.color = Color::DarkMagenta()});
88 canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()});
89 canvas.ClipRRect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), {20, 20});
90 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
94 canvas.Restore();
95
96 return canvas.EndRecordingAsPicture();
97 };
98
99 ASSERT_TRUE(OpenPlaygroundHere(callback));
100}
101
102TEST_P(AiksTest, CanRenderBackdropBlur) {
103 Canvas canvas;
104 canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
105 canvas.DrawCircle({300, 200}, 100, {.color = Color::GreenYellow()});
106 canvas.DrawCircle({140, 170}, 75, {.color = Color::DarkMagenta()});
107 canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()});
108 canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), {20, 20});
109 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
110 ImageFilter::MakeBlur(Sigma(30.0), Sigma(30.0),
113 canvas.Restore();
114
115 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
116}
117
118TEST_P(AiksTest, CanRenderBackdropBlurHugeSigma) {
119 Canvas canvas;
120 canvas.DrawCircle({400, 400}, 300, {.color = Color::Green()});
121 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
122 ImageFilter::MakeBlur(Sigma(999999), Sigma(999999),
125 canvas.Restore();
126
127 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
128}
129
130TEST_P(AiksTest, CanRenderClippedBlur) {
131 Canvas canvas;
132 canvas.ClipRect(Rect::MakeXYWH(100, 150, 400, 400));
133 canvas.DrawCircle(
134 {400, 400}, 200,
135 {
136 .color = Color::Green(),
137 .image_filter = ImageFilter::MakeBlur(
140 });
141 canvas.Restore();
142
143 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
144}
145
146TEST_P(AiksTest, ClippedBlurFilterRendersCorrectlyInteractive) {
147 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
148 static PlaygroundPoint playground_point(Point(400, 400), 20,
149 Color::Green());
150 auto point = DrawPlaygroundPoint(playground_point);
151
152 Canvas canvas;
153 canvas.Translate(point - Point(400, 400));
154 Paint paint;
155 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
157 .sigma = Radius{120 * 3},
158 };
159 paint.color = Color::Red();
161 builder.AddRect(Rect::MakeLTRB(0, 0, 800, 800));
162 canvas.DrawPath(builder.TakePath(), paint);
163 return canvas.EndRecordingAsPicture();
164 };
165 ASSERT_TRUE(OpenPlaygroundHere(callback));
166}
167
168TEST_P(AiksTest, ClippedBlurFilterRendersCorrectly) {
169 Canvas canvas;
170 canvas.Translate(Point(0, -400));
171 Paint paint;
172 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
174 .sigma = Radius{120 * 3},
175 };
176 paint.color = Color::Red();
178 builder.AddRect(Rect::MakeLTRB(0, 0, 800, 800));
179 canvas.DrawPath(builder.TakePath(), paint);
180 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
181}
182
183TEST_P(AiksTest, ClearBlendWithBlur) {
184 Canvas canvas;
185 Paint white;
186 white.color = Color::Blue();
187 canvas.DrawRect(Rect::MakeXYWH(0, 0, 600.0, 600.0), white);
188
189 Paint clear;
190 clear.blend_mode = BlendMode::kClear;
191 clear.mask_blur_descriptor = Paint::MaskBlurDescriptor{
193 .sigma = Sigma(20),
194 };
195
196 canvas.DrawCircle(Point::MakeXY(300.0, 300.0), 200.0, clear);
197
198 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
199}
200
201TEST_P(AiksTest, BlurHasNoEdge) {
202 Scalar sigma = 47.6;
203 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
204 if (AiksTest::ImGuiBegin("Controls", nullptr,
205 ImGuiWindowFlags_AlwaysAutoResize)) {
206 ImGui::SliderFloat("Sigma", &sigma, 0, 50);
207 ImGui::End();
208 }
209 Canvas canvas;
210 canvas.Scale(GetContentScale());
211 canvas.DrawPaint({});
212 Paint blur = {
213 .color = Color::Green(),
214 .mask_blur_descriptor =
217 .sigma = Sigma(sigma),
218 },
219 };
220 canvas.DrawRect(Rect::MakeXYWH(300, 300, 200, 200), blur);
221 return canvas.EndRecordingAsPicture();
222 };
223
224 ASSERT_TRUE(OpenPlaygroundHere(callback));
225}
226
227TEST_P(AiksTest, BlurredRectangleWithShader) {
228 Canvas canvas;
229 canvas.Scale(GetContentScale());
230
231 auto paint_lines = [&canvas](Scalar dx, Scalar dy, Paint paint) {
232 auto draw_line = [&canvas, &paint](Point a, Point b) {
233 canvas.DrawPath(PathBuilder{}.AddLine(a, b).TakePath(), paint);
234 };
235 paint.stroke_width = 5;
237 draw_line(Point(dx + 100, dy + 100), Point(dx + 200, dy + 200));
238 draw_line(Point(dx + 100, dy + 200), Point(dx + 200, dy + 100));
239 draw_line(Point(dx + 150, dy + 100), Point(dx + 200, dy + 150));
240 draw_line(Point(dx + 100, dy + 150), Point(dx + 150, dy + 200));
241 };
242
243 AiksContext renderer(GetContext(), nullptr);
244 Canvas recorder_canvas;
245 for (int x = 0; x < 5; ++x) {
246 for (int y = 0; y < 5; ++y) {
247 Rect rect = Rect::MakeXYWH(x * 20, y * 20, 20, 20);
248 Paint paint{.color =
249 ((x + y) & 1) == 0 ? Color::Yellow() : Color::Blue()};
250 recorder_canvas.DrawRect(rect, paint);
251 }
252 }
253 Picture picture = recorder_canvas.EndRecordingAsPicture();
254 std::shared_ptr<Texture> texture =
255 picture.ToImage(renderer, ISize{100, 100})->GetTexture();
256
257 ColorSource image_source = ColorSource::MakeImage(
259 std::shared_ptr<ImageFilter> blur_filter = ImageFilter::MakeBlur(
262 canvas.DrawRect(Rect::MakeLTRB(0, 0, 300, 600),
263 Paint{.color = Color::DarkGreen()});
264 canvas.DrawRect(Rect::MakeLTRB(100, 100, 200, 200),
265 Paint{.color_source = image_source});
266 canvas.DrawRect(Rect::MakeLTRB(300, 0, 600, 600),
267 Paint{.color = Color::Red()});
268 canvas.DrawRect(
269 Rect::MakeLTRB(400, 100, 500, 200),
270 Paint{.color_source = image_source, .image_filter = blur_filter});
271 paint_lines(0, 300, Paint{.color_source = image_source});
272 paint_lines(300, 300,
273 Paint{.color_source = image_source, .image_filter = blur_filter});
274 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
275}
276
277TEST_P(AiksTest, MaskBlurWithZeroSigmaIsSkipped) {
278 Canvas canvas;
279
280 Paint paint = {
281 .color = Color::Blue(),
282 .mask_blur_descriptor =
285 .sigma = Sigma(0),
286 },
287 };
288
289 canvas.DrawCircle({300, 300}, 200, paint);
290 canvas.DrawRect(Rect::MakeLTRB(100, 300, 500, 600), paint);
291
292 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
293}
294
297 Scalar sigma = 1.0f;
298 Scalar alpha = 1.0f;
299 std::shared_ptr<ImageFilter> image_filter;
300 bool invert_colors = false;
302};
303
304static Picture MaskBlurVariantTest(const AiksTest& test_context,
305 const MaskBlurTestConfig& config) {
306 Canvas canvas;
307 canvas.Scale(test_context.GetContentScale());
308 canvas.Scale(Vector2{0.8f, 0.8f});
309 Paint paint;
310 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
312 .sigma = Sigma{1},
313 };
314
315 canvas.DrawPaint({.color = Color::AntiqueWhite()});
316
317 paint.mask_blur_descriptor->style = config.style;
318 paint.mask_blur_descriptor->sigma = Sigma{config.sigma};
319 paint.image_filter = config.image_filter;
320 paint.invert_colors = config.invert_colors;
321 paint.blend_mode = config.blend_mode;
322
323 const Scalar x = 50;
324 const Scalar radius = 20.0f;
325 const Scalar y_spacing = 100.0f;
326
327 Scalar y = 50;
328 paint.color = Color::Crimson().WithAlpha(config.alpha);
329 canvas.DrawRect(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
330 radius, 60.0f - radius),
331 paint);
332
333 y += y_spacing;
334 paint.color = Color::Blue().WithAlpha(config.alpha);
335 canvas.DrawCircle({x + 25, y + 25}, radius, paint);
336
337 y += y_spacing;
338 paint.color = Color::Green().WithAlpha(config.alpha);
339 canvas.DrawOval(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
340 radius, 60.0f - radius),
341 paint);
342
343 y += y_spacing;
344 paint.color = Color::Purple().WithAlpha(config.alpha);
345 canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
346 {radius, radius}, //
347 paint);
348
349 y += y_spacing;
350 paint.color = Color::Orange().WithAlpha(config.alpha);
351 canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
352 {radius, 5.0f}, paint);
353
354 y += y_spacing;
355 paint.color = Color::Maroon().WithAlpha(config.alpha);
356 canvas.DrawPath(PathBuilder{}
357 .MoveTo({x + 0, y + 60})
358 .LineTo({x + 30, y + 0})
359 .LineTo({x + 60, y + 60})
360 .Close()
361 .TakePath(),
362 paint);
363
364 y += y_spacing;
365 paint.color = Color::Maroon().WithAlpha(config.alpha);
366 canvas.DrawPath(PathBuilder{}
367 .AddArc(Rect::MakeXYWH(x + 5, y, 50, 50),
368 Radians{kPi / 2}, Radians{kPi})
369 .AddArc(Rect::MakeXYWH(x + 25, y, 50, 50),
370 Radians{kPi / 2}, Radians{kPi})
371 .Close()
372 .TakePath(),
373 paint);
374
375 return canvas.EndRecordingAsPicture();
376}
377
378static const std::map<std::string, MaskBlurTestConfig> kPaintVariations = {
379 // 1. Normal style, translucent, zero sigma.
380 {"NormalTranslucentZeroSigma",
382 .sigma = 0.0f,
383 .alpha = 0.5f}},
384 // 2. Normal style, translucent.
385 {"NormalTranslucent",
387 .sigma = 8.0f,
388 .alpha = 0.5f}},
389 // 3. Solid style, translucent.
390 {"SolidTranslucent",
392 .sigma = 8.0f,
393 .alpha = 0.5f}},
394 // 4. Solid style, opaque.
395 {"SolidOpaque",
396 {.style = FilterContents::BlurStyle::kSolid, .sigma = 8.0f}},
397 // 5. Solid style, translucent, color & image filtered.
398 {"SolidTranslucentWithFilters",
400 .sigma = 8.0f,
401 .alpha = 0.5f,
402 .image_filter = ImageFilter::MakeBlur(Sigma{3},
403 Sigma{3},
406 .invert_colors = true}},
407 // 6. Solid style, translucent, exclusion blended.
408 {"SolidTranslucentExclusionBlend",
410 .sigma = 8.0f,
411 .alpha = 0.5f,
412 .blend_mode = BlendMode::kExclusion}},
413 // 7. Inner style, translucent.
414 {"InnerTranslucent",
416 .sigma = 8.0f,
417 .alpha = 0.5f}},
418 // 8. Inner style, translucent, blurred.
419 {"InnerTranslucentWithBlurImageFilter",
421 .sigma = 8.0f,
422 .alpha = 0.5f,
423 .image_filter = ImageFilter::MakeBlur(Sigma{3},
424 Sigma{3},
427 // 9. Outer style, translucent.
428 {"OuterTranslucent",
430 .sigma = 8.0f,
431 .alpha = 0.5f}},
432 // 10. Outer style, opaque, image filtered.
433 {"OuterOpaqueWithBlurImageFilter",
435 .sigma = 8.0f,
436 .image_filter = ImageFilter::MakeBlur(Sigma{3},
437 Sigma{3},
440};
441
442#define MASK_BLUR_VARIANT_TEST(config) \
443 TEST_P(AiksTest, MaskBlurVariantTest##config) { \
444 ASSERT_TRUE(OpenPlaygroundHere( \
445 MaskBlurVariantTest(*this, kPaintVariations.at(#config)))); \
446 }
447
448MASK_BLUR_VARIANT_TEST(NormalTranslucentZeroSigma)
449MASK_BLUR_VARIANT_TEST(NormalTranslucent)
450MASK_BLUR_VARIANT_TEST(SolidTranslucent)
451MASK_BLUR_VARIANT_TEST(SolidOpaque)
452MASK_BLUR_VARIANT_TEST(SolidTranslucentWithFilters)
453MASK_BLUR_VARIANT_TEST(SolidTranslucentExclusionBlend)
454MASK_BLUR_VARIANT_TEST(InnerTranslucent)
455MASK_BLUR_VARIANT_TEST(InnerTranslucentWithBlurImageFilter)
456MASK_BLUR_VARIANT_TEST(OuterTranslucent)
457MASK_BLUR_VARIANT_TEST(OuterOpaqueWithBlurImageFilter)
458
459#undef MASK_BLUR_VARIANT_TEST
460
461TEST_P(AiksTest, GaussianBlurAtPeripheryVertical) {
462 Canvas canvas;
463
464 canvas.Scale(GetContentScale());
465 canvas.DrawRRect(Rect::MakeLTRB(0, 0, GetWindowSize().width, 100),
466 Size(10, 10), Paint{.color = Color::LimeGreen()});
467 canvas.DrawRRect(Rect::MakeLTRB(0, 110, GetWindowSize().width, 210),
468 Size(10, 10), Paint{.color = Color::Magenta()});
469 canvas.ClipRect(Rect::MakeLTRB(100, 0, 200, GetWindowSize().height));
470 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
471 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
474 canvas.Restore();
475
476 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
477}
478
479TEST_P(AiksTest, GaussianBlurAtPeripheryHorizontal) {
480 Canvas canvas;
481
482 canvas.Scale(GetContentScale());
483 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
484 canvas.DrawImageRect(
485 std::make_shared<Image>(boston),
486 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height),
487 Rect::MakeLTRB(0, 0, GetWindowSize().width, 100), Paint{});
488 canvas.DrawRRect(Rect::MakeLTRB(0, 110, GetWindowSize().width, 210),
489 Size(10, 10), Paint{.color = Color::Magenta()});
490 canvas.ClipRect(Rect::MakeLTRB(0, 50, GetWindowSize().width, 150));
491 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
492 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
495 canvas.Restore();
496 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
497}
498
499#define FLT_FORWARD(mock, real, method) \
500 EXPECT_CALL(*mock, method()) \
501 .WillRepeatedly(::testing::Return(real->method()));
502
503TEST_P(AiksTest, GaussianBlurWithoutDecalSupport) {
504 if (GetParam() != PlaygroundBackend::kMetal) {
505 GTEST_SKIP_(
506 "This backend doesn't yet support setting device capabilities.");
507 }
508 if (!WillRenderSomething()) {
509 // Sometimes these tests are run without playgrounds enabled which is
510 // pointless for this test since we are asserting that
511 // `SupportsDecalSamplerAddressMode` is called.
512 GTEST_SKIP_("This test requires playgrounds.");
513 }
514
515 std::shared_ptr<const Capabilities> old_capabilities =
516 GetContext()->GetCapabilities();
517 auto mock_capabilities = std::make_shared<MockCapabilities>();
518 EXPECT_CALL(*mock_capabilities, SupportsDecalSamplerAddressMode())
519 .Times(::testing::AtLeast(1))
520 .WillRepeatedly(::testing::Return(false));
521 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultColorFormat);
522 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultStencilFormat);
523 FLT_FORWARD(mock_capabilities, old_capabilities,
524 GetDefaultDepthStencilFormat);
525 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsOffscreenMSAA);
526 FLT_FORWARD(mock_capabilities, old_capabilities,
527 SupportsImplicitResolvingMSAA);
528 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsReadFromResolve);
529 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsFramebufferFetch);
530 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsSSBO);
531 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsCompute);
532 FLT_FORWARD(mock_capabilities, old_capabilities,
533 SupportsTextureToTextureBlits);
534 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultGlyphAtlasFormat);
535 ASSERT_TRUE(SetCapabilities(mock_capabilities).ok());
536
537 auto texture = std::make_shared<Image>(CreateTextureForFixture("boston.jpg"));
538 Canvas canvas;
539 canvas.Scale(GetContentScale() * 0.5);
540 canvas.DrawPaint({.color = Color::Black()});
541 canvas.DrawImage(
542 texture, Point(200, 200),
543 {
544 .image_filter = ImageFilter::MakeBlur(
547 });
548 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
549}
550
551TEST_P(AiksTest, GaussianBlurOneDimension) {
552 Canvas canvas;
553
554 canvas.Scale(GetContentScale());
555 canvas.Scale({0.5, 0.5, 1.0});
556 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
557 canvas.DrawImage(std::make_shared<Image>(boston), Point(100, 100), Paint{});
558 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
562 canvas.Restore();
563 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
564}
565
566// Smoketest to catch issues with the coverage hint.
567// Draws a rotated blurred image within a rectangle clip. The center of the clip
568// rectangle is the center of the rotated image. The entire area of the clip
569// rectangle should be filled with opaque colors output by the blur.
570TEST_P(AiksTest, GaussianBlurRotatedAndClipped) {
571 Canvas canvas;
572 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
573 Rect bounds =
574 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
575 Vector2 image_center = Vector2(bounds.GetSize() / 2);
576 Paint paint = {.image_filter =
577 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
580 Vector2 clip_size = {150, 75};
581 Vector2 center = Vector2(1024, 768) / 2;
582 canvas.Scale(GetContentScale());
583 canvas.ClipRect(
584 Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size));
585 canvas.Translate({center.x, center.y, 0});
586 canvas.Scale({0.6, 0.6, 1});
587 canvas.Rotate(Degrees(25));
588
589 canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,
590 /*dest=*/bounds.Shift(-image_center), paint);
591
592 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
593}
594
595TEST_P(AiksTest, GaussianBlurScaledAndClipped) {
596 Canvas canvas;
597 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
598 Rect bounds =
599 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
600 Vector2 image_center = Vector2(bounds.GetSize() / 2);
601 Paint paint = {.image_filter =
602 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
605 Vector2 clip_size = {150, 75};
606 Vector2 center = Vector2(1024, 768) / 2;
607 canvas.Scale(GetContentScale());
608 canvas.ClipRect(
609 Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size));
610 canvas.Translate({center.x, center.y, 0});
611 canvas.Scale({0.6, 0.6, 1});
612
613 canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,
614 /*dest=*/bounds.Shift(-image_center), paint);
615
616 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
617}
618
619TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) {
620 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
621
622 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
623 const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
624 const Entity::TileMode tile_modes[] = {
627
628 static float rotation = 0;
629 static float scale = 0.6;
630 static int selected_tile_mode = 3;
631
632 if (AiksTest::ImGuiBegin("Controls", nullptr,
633 ImGuiWindowFlags_AlwaysAutoResize)) {
634 ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
635 ImGui::SliderFloat("Scale", &scale, 0, 2.0);
636 ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
637 sizeof(tile_mode_names) / sizeof(char*));
638 ImGui::End();
639 }
640
641 Canvas canvas;
642 Rect bounds =
643 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
644 Vector2 image_center = Vector2(bounds.GetSize() / 2);
645 Paint paint = {.image_filter =
646 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
648 tile_modes[selected_tile_mode])};
649 static PlaygroundPoint point_a(Point(362, 309), 20, Color::Red());
650 static PlaygroundPoint point_b(Point(662, 459), 20, Color::Red());
651 auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
652 Vector2 center = Vector2(1024, 768) / 2;
653 canvas.Scale(GetContentScale());
654 canvas.ClipRect(
655 Rect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
656 canvas.Translate({center.x, center.y, 0});
657 canvas.Scale({scale, scale, 1});
658 canvas.Rotate(Degrees(rotation));
659
660 canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,
661 /*dest=*/bounds.Shift(-image_center), paint);
662 return canvas.EndRecordingAsPicture();
663 };
664
665 ASSERT_TRUE(OpenPlaygroundHere(callback));
666}
667
668TEST_P(AiksTest, GaussianBlurRotatedNonUniform) {
669 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
670 const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
671 const Entity::TileMode tile_modes[] = {
674
675 static float rotation = 45;
676 static float scale = 0.6;
677 static int selected_tile_mode = 3;
678
679 if (AiksTest::ImGuiBegin("Controls", nullptr,
680 ImGuiWindowFlags_AlwaysAutoResize)) {
681 ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
682 ImGui::SliderFloat("Scale", &scale, 0, 2.0);
683 ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
684 sizeof(tile_mode_names) / sizeof(char*));
685 ImGui::End();
686 }
687
688 Canvas canvas;
689 Paint paint = {.color = Color::Green(),
690 .image_filter =
693 tile_modes[selected_tile_mode])};
694 Vector2 center = Vector2(1024, 768) / 2;
695 canvas.Scale(GetContentScale());
696 canvas.Translate({center.x, center.y, 0});
697 canvas.Scale({scale, scale, 1});
698 canvas.Rotate(Degrees(rotation));
699
700 canvas.DrawRRect(Rect::MakeXYWH(-100, -100, 200, 200), Size(10, 10), paint);
701 return canvas.EndRecordingAsPicture();
702 };
703
704 ASSERT_TRUE(OpenPlaygroundHere(callback));
705}
706
707// This addresses a bug where tiny blurs could result in mip maps that beyond
708// the limits for the textures used for blurring.
709// See also: b/323402168
710TEST_P(AiksTest, GaussianBlurSolidColorTinyMipMap) {
711 for (int32_t i = 1; i < 5; ++i) {
712 Canvas canvas;
713 Scalar fi = i;
714 canvas.DrawPath(
716 .MoveTo({100, 100})
717 .LineTo({100.f + fi, 100.f + fi})
718 .TakePath(),
719 {.color = Color::Chartreuse(),
720 .image_filter = ImageFilter::MakeBlur(
723
724 Picture picture = canvas.EndRecordingAsPicture();
725 std::shared_ptr<RenderTargetCache> cache =
726 std::make_shared<RenderTargetCache>(
727 GetContext()->GetResourceAllocator());
728 AiksContext aiks_context(GetContext(), nullptr, cache);
729 std::shared_ptr<Image> image = picture.ToImage(aiks_context, {1024, 768});
730 EXPECT_TRUE(image) << " length " << i;
731 }
732}
733
734// This addresses a bug where tiny blurs could result in mip maps that beyond
735// the limits for the textures used for blurring.
736// See also: b/323402168
737TEST_P(AiksTest, GaussianBlurBackdropTinyMipMap) {
738 for (int32_t i = 0; i < 5; ++i) {
739 Canvas canvas;
740 ISize clip_size = ISize(i, i);
741 canvas.ClipRect(
742 Rect::MakeXYWH(400, 400, clip_size.width, clip_size.height));
743 canvas.DrawCircle(
744 {400, 400}, 200,
745 {
746 .color = Color::Green(),
747 .image_filter = ImageFilter::MakeBlur(
750 });
751 canvas.Restore();
752
753 Picture picture = canvas.EndRecordingAsPicture();
754 std::shared_ptr<RenderTargetCache> cache =
755 std::make_shared<RenderTargetCache>(
756 GetContext()->GetResourceAllocator());
757 AiksContext aiks_context(GetContext(), nullptr, cache);
758 std::shared_ptr<Image> image = picture.ToImage(aiks_context, {1024, 768});
759 EXPECT_TRUE(image) << " clip rect " << i;
760 }
761}
762
763TEST_P(AiksTest, GaussianBlurAnimatedBackdrop) {
764 // This test is for checking out how stable rendering is when content is
765 // translated underneath a blur. Animating under a blur can cause
766 // *shimmering* to happen as a result of pixel alignment.
767 // See also: https://github.com/flutter/flutter/issues/140193
768 auto boston = std::make_shared<Image>(
769 CreateTextureForFixture("boston.jpg", /*enable_mipmapping=*/true));
770 ASSERT_TRUE(boston);
771 int64_t count = 0;
772 Scalar sigma = 20.0;
773 Scalar freq = 0.1;
774 Scalar amp = 50.0;
775 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
776 if (AiksTest::ImGuiBegin("Controls", nullptr,
777 ImGuiWindowFlags_AlwaysAutoResize)) {
778 ImGui::SliderFloat("Sigma", &sigma, 0, 200);
779 ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0);
780 ImGui::SliderFloat("Amplitude", &amp, 1, 100);
781 ImGui::End();
782 }
783
784 Canvas canvas;
785 canvas.Scale(GetContentScale());
786 Scalar y = amp * sin(freq * 2.0 * M_PI * count / 60);
787 canvas.DrawImage(boston,
788 Point(1024 / 2 - boston->GetSize().width / 2,
789 (768 / 2 - boston->GetSize().height / 2) + y),
790 {});
791 static PlaygroundPoint point_a(Point(100, 100), 20, Color::Red());
792 static PlaygroundPoint point_b(Point(900, 700), 20, Color::Red());
793 auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
794 canvas.ClipRect(
795 Rect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
796 canvas.ClipRect(Rect::MakeLTRB(100, 100, 900, 700));
797 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
798 ImageFilter::MakeBlur(Sigma(sigma), Sigma(sigma),
801 count += 1;
802 return canvas.EndRecordingAsPicture();
803 };
804 ASSERT_TRUE(OpenPlaygroundHere(callback));
805}
806
807TEST_P(AiksTest, GaussianBlurStyleInnerGradient) {
808 Canvas canvas;
809 canvas.Scale(GetContentScale());
810
811 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
812
813 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
814 Color{0.7568, 0.2627, 0.2118, 1.0}};
815 std::vector<Scalar> stops = {0.0, 1.0};
816
817 Paint paint;
819 {0, 0}, {200, 200}, std::move(colors), std::move(stops),
821 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
823 .sigma = Sigma(30),
824 };
825 canvas.DrawPath(PathBuilder()
826 .MoveTo({200, 200})
827 .LineTo({300, 400})
828 .LineTo({100, 400})
829 .Close()
830 .TakePath(),
831 paint);
832
833 // Draw another thing to make sure the clip area is reset.
834 Paint red;
835 red.color = Color::Red();
836 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
837 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
838}
839
840TEST_P(AiksTest, GaussianBlurStyleSolidGradient) {
841 Canvas canvas;
842 canvas.Scale(GetContentScale());
843
844 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
845
846 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
847 Color{0.7568, 0.2627, 0.2118, 1.0}};
848 std::vector<Scalar> stops = {0.0, 1.0};
849
850 Paint paint;
852 {0, 0}, {200, 200}, std::move(colors), std::move(stops),
854 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
856 .sigma = Sigma(30),
857 };
858 canvas.DrawPath(PathBuilder()
859 .MoveTo({200, 200})
860 .LineTo({300, 400})
861 .LineTo({100, 400})
862 .Close()
863 .TakePath(),
864 paint);
865
866 // Draw another thing to make sure the clip area is reset.
867 Paint red;
868 red.color = Color::Red();
869 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
870 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
871}
872
873TEST_P(AiksTest, GaussianBlurStyleOuterGradient) {
874 Canvas canvas;
875 canvas.Scale(GetContentScale());
876
877 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
878
879 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
880 Color{0.7568, 0.2627, 0.2118, 1.0}};
881 std::vector<Scalar> stops = {0.0, 1.0};
882
883 Paint paint;
885 {0, 0}, {200, 200}, std::move(colors), std::move(stops),
887 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
889 .sigma = Sigma(30),
890 };
891 canvas.DrawPath(PathBuilder()
892 .MoveTo({200, 200})
893 .LineTo({300, 400})
894 .LineTo({100, 400})
895 .Close()
896 .TakePath(),
897 paint);
898
899 // Draw another thing to make sure the clip area is reset.
900 Paint red;
901 red.color = Color::Red();
902 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
903 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
904}
905
906TEST_P(AiksTest, GaussianBlurStyleInner) {
907 Canvas canvas;
908 canvas.Scale(GetContentScale());
909
910 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
911
912 Paint paint;
913 paint.color = Color::Green();
914 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
916 .sigma = Sigma(30),
917 };
918 canvas.DrawPath(PathBuilder()
919 .MoveTo({200, 200})
920 .LineTo({300, 400})
921 .LineTo({100, 400})
922 .Close()
923 .TakePath(),
924 paint);
925
926 // Draw another thing to make sure the clip area is reset.
927 Paint red;
928 red.color = Color::Red();
929 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
930
931 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
932}
933
934TEST_P(AiksTest, GaussianBlurStyleOuter) {
935 Canvas canvas;
936 canvas.Scale(GetContentScale());
937
938 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
939
940 Paint paint;
941 paint.color = Color::Green();
942 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
944 .sigma = Sigma(30),
945 };
946 canvas.DrawPath(PathBuilder()
947 .MoveTo({200, 200})
948 .LineTo({300, 400})
949 .LineTo({100, 400})
950 .Close()
951 .TakePath(),
952 paint);
953
954 // Draw another thing to make sure the clip area is reset.
955 Paint red;
956 red.color = Color::Red();
957 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
958
959 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
960}
961
962TEST_P(AiksTest, GaussianBlurStyleSolid) {
963 Canvas canvas;
964 canvas.Scale(GetContentScale());
965
966 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
967
968 Paint paint;
969 paint.color = Color::Green();
970 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
972 .sigma = Sigma(30),
973 };
974 canvas.DrawPath(PathBuilder()
975 .MoveTo({200, 200})
976 .LineTo({300, 400})
977 .LineTo({100, 400})
978 .Close()
979 .TakePath(),
980 paint);
981
982 // Draw another thing to make sure the clip area is reset.
983 Paint red;
984 red.color = Color::Red();
985 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
986
987 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
988}
989
990TEST_P(AiksTest, MaskBlurTexture) {
991 Scalar sigma = 30;
992 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
993 if (AiksTest::ImGuiBegin("Controls", nullptr,
994 ImGuiWindowFlags_AlwaysAutoResize)) {
995 ImGui::SliderFloat("Sigma", &sigma, 0, 500);
996 ImGui::End();
997 }
998 Canvas canvas;
999 canvas.Scale(GetContentScale());
1000 Paint paint;
1001 paint.color = Color::Green();
1002 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
1004 .sigma = Sigma(sigma),
1005 };
1006 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1007 canvas.DrawImage(std::make_shared<Image>(boston), {200, 200}, paint);
1008 Paint red;
1009 red.color = Color::Red();
1010 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
1011 return canvas.EndRecordingAsPicture();
1012 };
1013 ASSERT_TRUE(OpenPlaygroundHere(callback));
1014}
1015
1016TEST_P(AiksTest, GuassianBlurUpdatesMipmapContents) {
1017 // This makes sure if mip maps are recycled across invocations of blurs the
1018 // contents get updated each frame correctly. If they aren't updated the color
1019 // inside the blur and outside the blur will be different.
1020 //
1021 // If there is some change to render target caching this could display a false
1022 // positive in the future. Also, if the LOD that is rendered is 1 it could
1023 // present a false positive.
1024 int32_t count = 0;
1025 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1026 Canvas canvas;
1027 if (count++ == 0) {
1028 canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
1029 } else {
1030 canvas.DrawCircle({100, 100}, 50, {.color = Color::Chartreuse()});
1031 }
1032 canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), {20, 20});
1033 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
1034 ImageFilter::MakeBlur(Sigma(30.0), Sigma(30.0),
1037 canvas.Restore();
1038 return canvas.EndRecordingAsPicture();
1039 };
1040
1041 ASSERT_TRUE(OpenPlaygroundHere(callback));
1042}
1043
1044TEST_P(AiksTest, GaussianBlurSetsMipCountOnPass) {
1045 Canvas canvas;
1046 canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
1047 canvas.SaveLayer({}, std::nullopt,
1051 canvas.Restore();
1052
1053 Picture picture = canvas.EndRecordingAsPicture();
1054 EXPECT_EQ(4, picture.pass->GetRequiredMipCount());
1055}
1056
1057TEST_P(AiksTest, GaussianBlurAllocatesCorrectMipCountRenderTarget) {
1058 size_t blur_required_mip_count =
1059 GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1060
1061 Canvas canvas;
1062 canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
1063 canvas.SaveLayer({}, std::nullopt,
1067 canvas.Restore();
1068
1069 Picture picture = canvas.EndRecordingAsPicture();
1070 std::shared_ptr<RenderTargetCache> cache =
1071 std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1072 AiksContext aiks_context(GetContext(), nullptr, cache);
1073 picture.ToImage(aiks_context, {100, 100});
1074
1075 size_t max_mip_count = 0;
1076 for (auto it = cache->GetRenderTargetDataBegin();
1077 it != cache->GetRenderTargetDataEnd(); ++it) {
1078 max_mip_count = std::max(it->config.mip_count, max_mip_count);
1079 }
1080 EXPECT_EQ(max_mip_count, blur_required_mip_count);
1081}
1082
1083TEST_P(AiksTest, GaussianBlurMipMapNestedLayer) {
1084 fml::testing::LogCapture log_capture;
1085 size_t blur_required_mip_count =
1086 GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1087
1088 Canvas canvas;
1089 canvas.DrawPaint({.color = Color::Wheat()});
1090 canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
1091 canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
1092 canvas.SaveLayer({}, std::nullopt,
1096 canvas.DrawCircle({200, 200}, 50, {.color = Color::Chartreuse()});
1097
1098 Picture picture = canvas.EndRecordingAsPicture();
1099 std::shared_ptr<RenderTargetCache> cache =
1100 std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1101 AiksContext aiks_context(GetContext(), nullptr, cache);
1102 picture.ToImage(aiks_context, {100, 100});
1103
1104 size_t max_mip_count = 0;
1105 for (auto it = cache->GetRenderTargetDataBegin();
1106 it != cache->GetRenderTargetDataEnd(); ++it) {
1107 max_mip_count = std::max(it->config.mip_count, max_mip_count);
1108 }
1109 EXPECT_EQ(max_mip_count, blur_required_mip_count);
1110 // The log is FML_DLOG, so only check in debug builds.
1111#ifndef NDEBUG
1112 if (GetParam() != PlaygroundBackend::kOpenGLES) {
1113 EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1114 std::string::npos);
1115 } else {
1116 EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1117 std::string::npos);
1118 }
1119#endif
1120}
1121
1122TEST_P(AiksTest, GaussianBlurMipMapImageFilter) {
1123 size_t blur_required_mip_count =
1124 GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1125 fml::testing::LogCapture log_capture;
1126 Canvas canvas;
1127 canvas.SaveLayer(
1128 {.image_filter = ImageFilter::MakeBlur(Sigma(30), Sigma(30),
1131 canvas.DrawCircle({200, 200}, 50, {.color = Color::Chartreuse()});
1132
1133 Picture picture = canvas.EndRecordingAsPicture();
1134 std::shared_ptr<RenderTargetCache> cache =
1135 std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1136 AiksContext aiks_context(GetContext(), nullptr, cache);
1137 picture.ToImage(aiks_context, {1024, 768});
1138
1139 size_t max_mip_count = 0;
1140 for (auto it = cache->GetRenderTargetDataBegin();
1141 it != cache->GetRenderTargetDataEnd(); ++it) {
1142 max_mip_count = std::max(it->config.mip_count, max_mip_count);
1143 }
1144 EXPECT_EQ(max_mip_count, blur_required_mip_count);
1145 // The log is FML_DLOG, so only check in debug builds.
1146#ifndef NDEBUG
1147 if (GetParam() != PlaygroundBackend::kOpenGLES) {
1148 EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1149 std::string::npos);
1150 } else {
1151 EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1152 std::string::npos);
1153 }
1154#endif
1155}
1156
1157TEST_P(AiksTest, GaussianBlurMipMapSolidColor) {
1158 size_t blur_required_mip_count =
1159 GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1160 fml::testing::LogCapture log_capture;
1161 Canvas canvas;
1162 canvas.DrawPath(PathBuilder{}
1163 .MoveTo({100, 100})
1164 .LineTo({200, 100})
1165 .LineTo({150, 200})
1166 .LineTo({50, 200})
1167 .Close()
1168 .TakePath(),
1169 {.color = Color::Chartreuse(),
1170 .image_filter = ImageFilter::MakeBlur(
1173
1174 Picture picture = canvas.EndRecordingAsPicture();
1175 std::shared_ptr<RenderTargetCache> cache =
1176 std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1177 AiksContext aiks_context(GetContext(), nullptr, cache);
1178 picture.ToImage(aiks_context, {1024, 768});
1179
1180 size_t max_mip_count = 0;
1181 for (auto it = cache->GetRenderTargetDataBegin();
1182 it != cache->GetRenderTargetDataEnd(); ++it) {
1183 max_mip_count = std::max(it->config.mip_count, max_mip_count);
1184 }
1185 EXPECT_EQ(max_mip_count, blur_required_mip_count);
1186 // The log is FML_DLOG, so only check in debug builds.
1187#ifndef NDEBUG
1188 if (GetParam() != PlaygroundBackend::kOpenGLES) {
1189 EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1190 std::string::npos);
1191 } else {
1192 EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1193 std::string::npos);
1194 }
1195#endif
1196}
1197
1198TEST_P(AiksTest, MaskBlurDoesntStretchContents) {
1199 Scalar sigma = 70;
1200 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1201 if (AiksTest::ImGuiBegin("Controls", nullptr,
1202 ImGuiWindowFlags_AlwaysAutoResize)) {
1203 ImGui::SliderFloat("Sigma", &sigma, 0, 500);
1204 ImGui::End();
1205 }
1206 Canvas canvas;
1207 canvas.Scale(GetContentScale());
1208 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
1209
1210 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1211 ColorSource image_source = ColorSource::MakeImage(
1213
1214 canvas.Transform(Matrix::MakeTranslation({100, 100, 0}) *
1215 Matrix::MakeScale({0.5, 0.5, 1.0}));
1216 Paint paint = {
1217 .color_source = image_source,
1218 .mask_blur_descriptor =
1221 .sigma = Sigma(sigma),
1222 },
1223 };
1224 canvas.DrawRect(
1225 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height),
1226 paint);
1227
1228 return canvas.EndRecordingAsPicture();
1229 };
1230 ASSERT_TRUE(OpenPlaygroundHere(callback));
1231}
1232
1233} // namespace testing
1234} // namespace impeller
#define M_PI
int count
Definition: FontMgrTest.cpp:50
static bool ok(int result)
#define FLT_FORWARD(mock, real, method)
#define MASK_BLUR_VARIANT_TEST(config)
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
static std::shared_ptr< ColorFilter > MakeBlend(BlendMode blend_mode, Color color)
Definition: color_filter.cc:23
static ColorSource MakeLinearGradient(Point start_point, Point end_point, std::vector< Color > colors, std::vector< Scalar > stops, Entity::TileMode tile_mode, Matrix effect_transform)
static ColorSource MakeImage(std::shared_ptr< Texture > texture, Entity::TileMode x_tile_mode, Entity::TileMode y_tile_mode, SamplerDescriptor sampler_descriptor, Matrix effect_transform)
@ kNormal
Blurred inside and outside.
@ kOuter
Nothing inside, blurred outside.
@ kInner
Blurred inside, nothing outside.
@ kSolid
Solid inside, blurred outside.
static std::shared_ptr< ImageFilter > MakeBlur(Sigma sigma_x, Sigma sigma_y, FilterContents::BlurStyle blur_style, Entity::TileMode tile_mode)
Definition: image_filter.cc:20
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:22
PathBuilder & AddArc(const Rect &oval_bounds, Radians start, Radians sweep, bool use_center=false)
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:33
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
Point GetContentScale() const
Definition: playground.cc:192
const Paint & paint
Definition: color_source.cc:38
static bool b
struct MyStruct a[10]
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static void draw_line(SkCanvas *canvas, SkImage *, const SkRect &r, sk_sp< SkImageFilter > imf)
FlTexture * texture
double y
double x
SK_API GrDirectContext * GetContext(const SkImage *src)
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< const SkImage > image
Definition: SkRecords.h:269
sk_sp< const SkPicture > picture
Definition: SkRecords.h:299
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
PODArray< SkColor > colors
Definition: SkRecords.h:276
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
SK_API sk_sp< SkShader > Color(SkColor)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
static const std::map< std::string, MaskBlurTestConfig > kPaintVariations
static Picture MaskBlurVariantTest(const AiksTest &test_context, const MaskBlurTestConfig &config)
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer)
Point Vector2
Definition: point.h:326
constexpr float kPi
Definition: constants.h:26
float Scalar
Definition: scalar.h:18
Point DrawPlaygroundPoint(PlaygroundPoint &point)
Definition: widgets.cc:9
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
TPoint< Scalar > Point
Definition: point.h:322
BlendMode
Definition: color.h:59
TSize< Scalar > Size
Definition: size.h:137
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:20
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
ISize64 ISize
Definition: size.h:140
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
int32_t height
int32_t width
const Scalar scale
std::string str() const
Definition: logging.cc:122
static constexpr Color Crimson()
Definition: color.h:352
static constexpr Color Grey()
Definition: color.h:496
static constexpr Color LimeGreen()
Definition: color.h:604
static constexpr Color GreenYellow()
Definition: color.h:492
static constexpr Color Chartreuse()
Definition: color.h:332
static constexpr Color Black()
Definition: color.h:268
static constexpr Color CornflowerBlue()
Definition: color.h:344
static constexpr Color White()
Definition: color.h:266
static constexpr Color Magenta()
Definition: color.h:612
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:280
static constexpr Color Maroon()
Definition: color.h:616
static constexpr Color OrangeRed()
Definition: color.h:696
static constexpr Color DarkGreen()
Definition: color.h:376
static constexpr Color Orange()
Definition: color.h:692
static constexpr Color Purple()
Definition: color.h:744
static constexpr Color Wheat()
Definition: color.h:836
static constexpr Color Red()
Definition: color.h:274
static constexpr Color AntiqueWhite()
Definition: color.h:288
static constexpr Color DarkMagenta()
Definition: color.h:388
static constexpr Color Yellow()
Definition: color.h:844
static constexpr Color Blue()
Definition: color.h:278
static constexpr Color Green()
Definition: color.h:276
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
FilterContents::BlurStyle style
Definition: paint.h:49
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:48
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
static constexpr TPoint< Type > MakeXY(Type x, Type y)
Definition: point.h:46
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
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:605
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
Type height
Definition: size.h:23
Type width
Definition: size.h:22
std::shared_ptr< ImageFilter > image_filter
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678