diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 09177ba2d1108..e02a1131ae792 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -145,7 +145,6 @@ ../../../flutter/impeller/display_list/aiks_dl_atlas_unittests.cc ../../../flutter/impeller/display_list/aiks_dl_basic_unittests.cc ../../../flutter/impeller/display_list/aiks_dl_blend_unittests.cc -../../../flutter/impeller/display_list/aiks_dl_blur_unittests.cc ../../../flutter/impeller/display_list/aiks_dl_clip_unittests.cc ../../../flutter/impeller/display_list/aiks_dl_gradient_unittests.cc ../../../flutter/impeller/display_list/aiks_dl_opacity_unittests.cc diff --git a/display_list/dl_color.h b/display_list/dl_color.h index be13257740a56..c7fd49b0857ac 100644 --- a/display_list/dl_color.h +++ b/display_list/dl_color.h @@ -58,12 +58,6 @@ struct DlColor { static constexpr DlColor kCornflowerBlue() {return DlColor(0xFF6495ED);}; static constexpr DlColor kCrimson() {return DlColor(0xFFFF5733);}; static constexpr DlColor kAqua() {return DlColor(0xFF00FFFF);}; - static constexpr DlColor kOrange() {return DlColor(0xFFFFA500);}; - static constexpr DlColor kPurple() {return DlColor(0xFF800080);}; - static constexpr DlColor kLimeGreen() {return DlColor(0xFF32CD32);}; - static constexpr DlColor kGreenYellow() {return DlColor(0xFFADFF2F);}; - static constexpr DlColor kDarkMagenta() {return DlColor(0xFF8B008B);}; - static constexpr DlColor kOrangeRed() {return DlColor(0xFFFF4500);}; // clang-format on constexpr bool isOpaque() const { return getAlpha() == 0xFF; } diff --git a/impeller/aiks/aiks_blur_unittests.cc b/impeller/aiks/aiks_blur_unittests.cc index 640d8281ec5dd..95d7b1d10f568 100644 --- a/impeller/aiks/aiks_blur_unittests.cc +++ b/impeller/aiks/aiks_blur_unittests.cc @@ -5,6 +5,7 @@ #include "flutter/impeller/aiks/aiks_unittests.h" #include "impeller/aiks/canvas.h" +#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" #include "impeller/entity/render_target_cache.h" #include "impeller/geometry/path_builder.h" #include "impeller/playground/widgets.h" @@ -19,6 +20,210 @@ namespace impeller { namespace testing { +TEST_P(AiksTest, CanRenderMaskBlurHugeSigma) { + Canvas canvas; + canvas.DrawCircle({400, 400}, 300, + {.color = Color::Green(), + .mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Sigma(99999), + }}); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, CanRenderForegroundBlendWithMaskBlur) { + // This case triggers the ForegroundPorterDuffBlend path. The color filter + // should apply to the color only, and respect the alpha mask. + Canvas canvas; + canvas.ClipRect(Rect::MakeXYWH(100, 150, 400, 400)); + canvas.DrawCircle({400, 400}, 200, + { + .color = Color::White(), + .color_filter = ColorFilter::MakeBlend( + BlendMode::kSource, Color::Green()), + .mask_blur_descriptor = + Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Radius(20), + }, + }); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur) { + // This case triggers the ForegroundAdvancedBlend path. The color filter + // should apply to the color only, and respect the alpha mask. + Canvas canvas; + canvas.ClipRect(Rect::MakeXYWH(100, 150, 400, 400)); + canvas.DrawCircle({400, 400}, 200, + { + .color = Color::Grey(), + .color_filter = ColorFilter::MakeBlend( + BlendMode::kColor, Color::Green()), + .mask_blur_descriptor = + Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Radius(20), + }, + }); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, CanRenderBackdropBlurInteractive) { + auto callback = [&](AiksContext& renderer) -> std::optional { + static PlaygroundPoint point_a(Point(50, 50), 30, Color::White()); + static PlaygroundPoint point_b(Point(300, 200), 30, Color::White()); + auto [a, b] = DrawPlaygroundLine(point_a, point_b); + + Canvas canvas; + canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()}); + canvas.DrawCircle({300, 200}, 100, {.color = Color::GreenYellow()}); + canvas.DrawCircle({140, 170}, 75, {.color = Color::DarkMagenta()}); + canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()}); + canvas.ClipRRect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), {20, 20}); + canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, + ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); + canvas.Restore(); + + return canvas.EndRecordingAsPicture(); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(AiksTest, CanRenderBackdropBlur) { + Canvas canvas; + canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()}); + canvas.DrawCircle({300, 200}, 100, {.color = Color::GreenYellow()}); + canvas.DrawCircle({140, 170}, 75, {.color = Color::DarkMagenta()}); + canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()}); + canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), {20, 20}); + canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, + ImageFilter::MakeBlur(Sigma(30.0), Sigma(30.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, CanRenderBackdropBlurHugeSigma) { + Canvas canvas; + canvas.DrawCircle({400, 400}, 300, {.color = Color::Green()}); + canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, + ImageFilter::MakeBlur(Sigma(999999), Sigma(999999), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, CanRenderClippedBlur) { + Canvas canvas; + canvas.ClipRect(Rect::MakeXYWH(100, 150, 400, 400)); + canvas.DrawCircle( + {400, 400}, 200, + { + .color = Color::Green(), + .image_filter = ImageFilter::MakeBlur( + Sigma(20.0), Sigma(20.0), FilterContents::BlurStyle::kNormal, + Entity::TileMode::kDecal), + }); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, ClippedBlurFilterRendersCorrectlyInteractive) { + auto callback = [&](AiksContext& renderer) -> std::optional { + static PlaygroundPoint playground_point(Point(400, 400), 20, + Color::Green()); + auto point = DrawPlaygroundPoint(playground_point); + + Canvas canvas; + canvas.Translate(point - Point(400, 400)); + Paint paint; + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Radius{120 * 3}, + }; + paint.color = Color::Red(); + PathBuilder builder{}; + builder.AddRect(Rect::MakeLTRB(0, 0, 800, 800)); + canvas.DrawPath(builder.TakePath(), paint); + return canvas.EndRecordingAsPicture(); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(AiksTest, ClippedBlurFilterRendersCorrectly) { + Canvas canvas; + canvas.Translate(Point(0, -400)); + Paint paint; + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Radius{120 * 3}, + }; + paint.color = Color::Red(); + PathBuilder builder{}; + builder.AddRect(Rect::MakeLTRB(0, 0, 800, 800)); + canvas.DrawPath(builder.TakePath(), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, ClearBlendWithBlur) { + Canvas canvas; + Paint white; + white.color = Color::Blue(); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 600.0, 600.0), white); + + Paint clear; + clear.blend_mode = BlendMode::kClear; + clear.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Sigma(20), + }; + + canvas.DrawCircle(Point::MakeXY(300.0, 300.0), 200.0, clear); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, BlurHasNoEdge) { + Scalar sigma = 47.6; + auto callback = [&](AiksContext& renderer) -> std::optional { + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::SliderFloat("Sigma", &sigma, 0, 50); + ImGui::End(); + } + Canvas canvas; + canvas.Scale(GetContentScale()); + canvas.DrawPaint({}); + Paint blur = { + .color = Color::Green(), + .mask_blur_descriptor = + Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Sigma(sigma), + }, + }; + canvas.DrawRect(Rect::MakeXYWH(300, 300, 200, 200), blur); + return canvas.EndRecordingAsPicture(); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + TEST_P(AiksTest, BlurredRectangleWithShader) { Canvas canvas; canvas.Scale(GetContentScale()); @@ -69,6 +274,228 @@ TEST_P(AiksTest, BlurredRectangleWithShader) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, MaskBlurWithZeroSigmaIsSkipped) { + Canvas canvas; + + Paint paint = { + .color = Color::Blue(), + .mask_blur_descriptor = + Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Sigma(0), + }, + }; + + canvas.DrawCircle({300, 300}, 200, paint); + canvas.DrawRect(Rect::MakeLTRB(100, 300, 500, 600), paint); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +struct MaskBlurTestConfig { + FilterContents::BlurStyle style = FilterContents::BlurStyle::kNormal; + Scalar sigma = 1.0f; + Scalar alpha = 1.0f; + std::shared_ptr image_filter; + bool invert_colors = false; + BlendMode blend_mode = BlendMode::kSourceOver; +}; + +static Picture MaskBlurVariantTest(const AiksTest& test_context, + const MaskBlurTestConfig& config) { + Canvas canvas; + canvas.Scale(test_context.GetContentScale()); + canvas.Scale(Vector2{0.8f, 0.8f}); + Paint paint; + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Sigma{1}, + }; + + canvas.DrawPaint({.color = Color::AntiqueWhite()}); + + paint.mask_blur_descriptor->style = config.style; + paint.mask_blur_descriptor->sigma = Sigma{config.sigma}; + paint.image_filter = config.image_filter; + paint.invert_colors = config.invert_colors; + paint.blend_mode = config.blend_mode; + + const Scalar x = 50; + const Scalar radius = 20.0f; + const Scalar y_spacing = 100.0f; + + Scalar y = 50; + paint.color = Color::Crimson().WithAlpha(config.alpha); + canvas.DrawRect(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, // + radius, 60.0f - radius), + paint); + + y += y_spacing; + paint.color = Color::Blue().WithAlpha(config.alpha); + canvas.DrawCircle({x + 25, y + 25}, radius, paint); + + y += y_spacing; + paint.color = Color::Green().WithAlpha(config.alpha); + canvas.DrawOval(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, // + radius, 60.0f - radius), + paint); + + y += y_spacing; + paint.color = Color::Purple().WithAlpha(config.alpha); + canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), // + {radius, radius}, // + paint); + + y += y_spacing; + paint.color = Color::Orange().WithAlpha(config.alpha); + canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), // + {radius, 5.0f}, paint); + + y += y_spacing; + paint.color = Color::Maroon().WithAlpha(config.alpha); + canvas.DrawPath(PathBuilder{} + .MoveTo({x + 0, y + 60}) + .LineTo({x + 30, y + 0}) + .LineTo({x + 60, y + 60}) + .Close() + .TakePath(), + paint); + + y += y_spacing; + paint.color = Color::Maroon().WithAlpha(config.alpha); + canvas.DrawPath(PathBuilder{} + .AddArc(Rect::MakeXYWH(x + 5, y, 50, 50), + Radians{kPi / 2}, Radians{kPi}) + .AddArc(Rect::MakeXYWH(x + 25, y, 50, 50), + Radians{kPi / 2}, Radians{kPi}) + .Close() + .TakePath(), + paint); + + return canvas.EndRecordingAsPicture(); +} + +static const std::map kPaintVariations = { + // 1. Normal style, translucent, zero sigma. + {"NormalTranslucentZeroSigma", + {.style = FilterContents::BlurStyle::kNormal, + .sigma = 0.0f, + .alpha = 0.5f}}, + // 2. Normal style, translucent. + {"NormalTranslucent", + {.style = FilterContents::BlurStyle::kNormal, + .sigma = 8.0f, + .alpha = 0.5f}}, + // 3. Solid style, translucent. + {"SolidTranslucent", + {.style = FilterContents::BlurStyle::kSolid, + .sigma = 8.0f, + .alpha = 0.5f}}, + // 4. Solid style, opaque. + {"SolidOpaque", + {.style = FilterContents::BlurStyle::kSolid, .sigma = 8.0f}}, + // 5. Solid style, translucent, color & image filtered. + {"SolidTranslucentWithFilters", + {.style = FilterContents::BlurStyle::kSolid, + .sigma = 8.0f, + .alpha = 0.5f, + .image_filter = ImageFilter::MakeBlur(Sigma{3}, + Sigma{3}, + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp), + .invert_colors = true}}, + // 6. Solid style, translucent, exclusion blended. + {"SolidTranslucentExclusionBlend", + {.style = FilterContents::BlurStyle::kSolid, + .sigma = 8.0f, + .alpha = 0.5f, + .blend_mode = BlendMode::kExclusion}}, + // 7. Inner style, translucent. + {"InnerTranslucent", + {.style = FilterContents::BlurStyle::kInner, + .sigma = 8.0f, + .alpha = 0.5f}}, + // 8. Inner style, translucent, blurred. + {"InnerTranslucentWithBlurImageFilter", + {.style = FilterContents::BlurStyle::kInner, + .sigma = 8.0f, + .alpha = 0.5f, + .image_filter = ImageFilter::MakeBlur(Sigma{3}, + Sigma{3}, + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)}}, + // 9. Outer style, translucent. + {"OuterTranslucent", + {.style = FilterContents::BlurStyle::kOuter, + .sigma = 8.0f, + .alpha = 0.5f}}, + // 10. Outer style, opaque, image filtered. + {"OuterOpaqueWithBlurImageFilter", + {.style = FilterContents::BlurStyle::kOuter, + .sigma = 8.0f, + .image_filter = ImageFilter::MakeBlur(Sigma{3}, + Sigma{3}, + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)}}, +}; + +#define MASK_BLUR_VARIANT_TEST(config) \ + TEST_P(AiksTest, MaskBlurVariantTest##config) { \ + ASSERT_TRUE(OpenPlaygroundHere( \ + MaskBlurVariantTest(*this, kPaintVariations.at(#config)))); \ + } + +MASK_BLUR_VARIANT_TEST(NormalTranslucentZeroSigma) +MASK_BLUR_VARIANT_TEST(NormalTranslucent) +MASK_BLUR_VARIANT_TEST(SolidTranslucent) +MASK_BLUR_VARIANT_TEST(SolidOpaque) +MASK_BLUR_VARIANT_TEST(SolidTranslucentWithFilters) +MASK_BLUR_VARIANT_TEST(SolidTranslucentExclusionBlend) +MASK_BLUR_VARIANT_TEST(InnerTranslucent) +MASK_BLUR_VARIANT_TEST(InnerTranslucentWithBlurImageFilter) +MASK_BLUR_VARIANT_TEST(OuterTranslucent) +MASK_BLUR_VARIANT_TEST(OuterOpaqueWithBlurImageFilter) + +#undef MASK_BLUR_VARIANT_TEST + +TEST_P(AiksTest, GaussianBlurAtPeripheryVertical) { + Canvas canvas; + + canvas.Scale(GetContentScale()); + canvas.DrawRRect(Rect::MakeLTRB(0, 0, GetWindowSize().width, 100), + Size(10, 10), Paint{.color = Color::LimeGreen()}); + canvas.DrawRRect(Rect::MakeLTRB(0, 110, GetWindowSize().width, 210), + Size(10, 10), Paint{.color = Color::Magenta()}); + canvas.ClipRect(Rect::MakeLTRB(100, 0, 200, GetWindowSize().height)); + canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, + ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, GaussianBlurAtPeripheryHorizontal) { + Canvas canvas; + + canvas.Scale(GetContentScale()); + std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); + canvas.DrawImageRect( + std::make_shared(boston), + Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height), + Rect::MakeLTRB(0, 0, GetWindowSize().width, 100), Paint{}); + canvas.DrawRRect(Rect::MakeLTRB(0, 110, GetWindowSize().width, 210), + Size(10, 10), Paint{.color = Color::Magenta()}); + canvas.ClipRect(Rect::MakeLTRB(0, 50, GetWindowSize().width, 150)); + canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, + ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); + canvas.Restore(); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + #define FLT_FORWARD(mock, real, method) \ EXPECT_CALL(*mock, method()) \ .WillRepeatedly(::testing::Return(real->method())); @@ -121,6 +548,162 @@ TEST_P(AiksTest, GaussianBlurWithoutDecalSupport) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, GaussianBlurOneDimension) { + Canvas canvas; + + canvas.Scale(GetContentScale()); + canvas.Scale({0.5, 0.5, 1.0}); + std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); + canvas.DrawImage(std::make_shared(boston), Point(100, 100), Paint{}); + canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, + ImageFilter::MakeBlur(Sigma(50.0), Sigma(0.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); + canvas.Restore(); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +// Smoketest to catch issues with the coverage hint. +// Draws a rotated blurred image within a rectangle clip. The center of the clip +// rectangle is the center of the rotated image. The entire area of the clip +// rectangle should be filled with opaque colors output by the blur. +TEST_P(AiksTest, GaussianBlurRotatedAndClipped) { + Canvas canvas; + std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); + Rect bounds = + Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height); + Vector2 image_center = Vector2(bounds.GetSize() / 2); + Paint paint = {.image_filter = + ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kDecal)}; + Vector2 clip_size = {150, 75}; + Vector2 center = Vector2(1024, 768) / 2; + canvas.Scale(GetContentScale()); + canvas.ClipRect( + Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size)); + canvas.Translate({center.x, center.y, 0}); + canvas.Scale({0.6, 0.6, 1}); + canvas.Rotate(Degrees(25)); + + canvas.DrawImageRect(std::make_shared(boston), /*source=*/bounds, + /*dest=*/bounds.Shift(-image_center), paint); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, GaussianBlurScaledAndClipped) { + Canvas canvas; + std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); + Rect bounds = + Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height); + Vector2 image_center = Vector2(bounds.GetSize() / 2); + Paint paint = {.image_filter = + ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kDecal)}; + Vector2 clip_size = {150, 75}; + Vector2 center = Vector2(1024, 768) / 2; + canvas.Scale(GetContentScale()); + canvas.ClipRect( + Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size)); + canvas.Translate({center.x, center.y, 0}); + canvas.Scale({0.6, 0.6, 1}); + + canvas.DrawImageRect(std::make_shared(boston), /*source=*/bounds, + /*dest=*/bounds.Shift(-image_center), paint); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) { + std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); + + auto callback = [&](AiksContext& renderer) -> std::optional { + const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; + const Entity::TileMode tile_modes[] = { + Entity::TileMode::kClamp, Entity::TileMode::kRepeat, + Entity::TileMode::kMirror, Entity::TileMode::kDecal}; + + static float rotation = 0; + static float scale = 0.6; + static int selected_tile_mode = 3; + + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180); + ImGui::SliderFloat("Scale", &scale, 0, 2.0); + ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, + sizeof(tile_mode_names) / sizeof(char*)); + ImGui::End(); + } + + Canvas canvas; + Rect bounds = + Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height); + Vector2 image_center = Vector2(bounds.GetSize() / 2); + Paint paint = {.image_filter = + ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0), + FilterContents::BlurStyle::kNormal, + tile_modes[selected_tile_mode])}; + static PlaygroundPoint point_a(Point(362, 309), 20, Color::Red()); + static PlaygroundPoint point_b(Point(662, 459), 20, Color::Red()); + auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b); + Vector2 center = Vector2(1024, 768) / 2; + canvas.Scale(GetContentScale()); + canvas.ClipRect( + Rect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y)); + canvas.Translate({center.x, center.y, 0}); + canvas.Scale({scale, scale, 1}); + canvas.Rotate(Degrees(rotation)); + + canvas.DrawImageRect(std::make_shared(boston), /*source=*/bounds, + /*dest=*/bounds.Shift(-image_center), paint); + return canvas.EndRecordingAsPicture(); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(AiksTest, GaussianBlurRotatedNonUniform) { + auto callback = [&](AiksContext& renderer) -> std::optional { + const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; + const Entity::TileMode tile_modes[] = { + Entity::TileMode::kClamp, Entity::TileMode::kRepeat, + Entity::TileMode::kMirror, Entity::TileMode::kDecal}; + + static float rotation = 45; + static float scale = 0.6; + static int selected_tile_mode = 3; + + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180); + ImGui::SliderFloat("Scale", &scale, 0, 2.0); + ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, + sizeof(tile_mode_names) / sizeof(char*)); + ImGui::End(); + } + + Canvas canvas; + Paint paint = {.color = Color::Green(), + .image_filter = + ImageFilter::MakeBlur(Sigma(50.0), Sigma(0.0), + FilterContents::BlurStyle::kNormal, + tile_modes[selected_tile_mode])}; + Vector2 center = Vector2(1024, 768) / 2; + canvas.Scale(GetContentScale()); + canvas.Translate({center.x, center.y, 0}); + canvas.Scale({scale, scale, 1}); + canvas.Rotate(Degrees(rotation)); + + canvas.DrawRRect(Rect::MakeXYWH(-100, -100, 200, 200), Size(10, 10), paint); + return canvas.EndRecordingAsPicture(); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + // This addresses a bug where tiny blurs could result in mip maps that beyond // the limits for the textures used for blurring. // See also: b/323402168 @@ -177,5 +760,321 @@ TEST_P(AiksTest, GaussianBlurBackdropTinyMipMap) { } } +TEST_P(AiksTest, GaussianBlurAnimatedBackdrop) { + // This test is for checking out how stable rendering is when content is + // translated underneath a blur. Animating under a blur can cause + // *shimmering* to happen as a result of pixel alignment. + // See also: https://github.com/flutter/flutter/issues/140193 + auto boston = std::make_shared( + CreateTextureForFixture("boston.jpg", /*enable_mipmapping=*/true)); + ASSERT_TRUE(boston); + int64_t count = 0; + Scalar sigma = 20.0; + Scalar freq = 0.1; + Scalar amp = 50.0; + auto callback = [&](AiksContext& renderer) -> std::optional { + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::SliderFloat("Sigma", &sigma, 0, 200); + ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0); + ImGui::SliderFloat("Amplitude", &, 1, 100); + ImGui::End(); + } + + Canvas canvas; + canvas.Scale(GetContentScale()); + Scalar y = amp * sin(freq * 2.0 * M_PI * count / 60); + canvas.DrawImage(boston, + Point(1024 / 2 - boston->GetSize().width / 2, + (768 / 2 - boston->GetSize().height / 2) + y), + {}); + static PlaygroundPoint point_a(Point(100, 100), 20, Color::Red()); + static PlaygroundPoint point_b(Point(900, 700), 20, Color::Red()); + auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b); + canvas.ClipRect( + Rect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y)); + canvas.ClipRect(Rect::MakeLTRB(100, 100, 900, 700)); + canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, + ImageFilter::MakeBlur(Sigma(sigma), Sigma(sigma), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); + count += 1; + return canvas.EndRecordingAsPicture(); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(AiksTest, GaussianBlurStyleInnerGradient) { + Canvas canvas; + canvas.Scale(GetContentScale()); + + canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)}); + + std::vector colors = {Color{0.9568, 0.2627, 0.2118, 1.0}, + Color{0.7568, 0.2627, 0.2118, 1.0}}; + std::vector stops = {0.0, 1.0}; + + Paint paint; + paint.color_source = ColorSource::MakeLinearGradient( + {0, 0}, {200, 200}, std::move(colors), std::move(stops), + Entity::TileMode::kMirror, {}); + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kInner, + .sigma = Sigma(30), + }; + canvas.DrawPath(PathBuilder() + .MoveTo({200, 200}) + .LineTo({300, 400}) + .LineTo({100, 400}) + .Close() + .TakePath(), + paint); + + // Draw another thing to make sure the clip area is reset. + Paint red; + red.color = Color::Red(); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, GaussianBlurStyleSolidGradient) { + Canvas canvas; + canvas.Scale(GetContentScale()); + + canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)}); + + std::vector colors = {Color{0.9568, 0.2627, 0.2118, 1.0}, + Color{0.7568, 0.2627, 0.2118, 1.0}}; + std::vector stops = {0.0, 1.0}; + + Paint paint; + paint.color_source = ColorSource::MakeLinearGradient( + {0, 0}, {200, 200}, std::move(colors), std::move(stops), + Entity::TileMode::kMirror, {}); + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kSolid, + .sigma = Sigma(30), + }; + canvas.DrawPath(PathBuilder() + .MoveTo({200, 200}) + .LineTo({300, 400}) + .LineTo({100, 400}) + .Close() + .TakePath(), + paint); + + // Draw another thing to make sure the clip area is reset. + Paint red; + red.color = Color::Red(); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, GaussianBlurStyleOuterGradient) { + Canvas canvas; + canvas.Scale(GetContentScale()); + + canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)}); + + std::vector colors = {Color{0.9568, 0.2627, 0.2118, 1.0}, + Color{0.7568, 0.2627, 0.2118, 1.0}}; + std::vector stops = {0.0, 1.0}; + + Paint paint; + paint.color_source = ColorSource::MakeLinearGradient( + {0, 0}, {200, 200}, std::move(colors), std::move(stops), + Entity::TileMode::kMirror, {}); + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kOuter, + .sigma = Sigma(30), + }; + canvas.DrawPath(PathBuilder() + .MoveTo({200, 200}) + .LineTo({300, 400}) + .LineTo({100, 400}) + .Close() + .TakePath(), + paint); + + // Draw another thing to make sure the clip area is reset. + Paint red; + red.color = Color::Red(); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, GaussianBlurStyleInner) { + Canvas canvas; + canvas.Scale(GetContentScale()); + + canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)}); + + Paint paint; + paint.color = Color::Green(); + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kInner, + .sigma = Sigma(30), + }; + canvas.DrawPath(PathBuilder() + .MoveTo({200, 200}) + .LineTo({300, 400}) + .LineTo({100, 400}) + .Close() + .TakePath(), + paint); + + // Draw another thing to make sure the clip area is reset. + Paint red; + red.color = Color::Red(); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, GaussianBlurStyleOuter) { + Canvas canvas; + canvas.Scale(GetContentScale()); + + canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)}); + + Paint paint; + paint.color = Color::Green(); + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kOuter, + .sigma = Sigma(30), + }; + canvas.DrawPath(PathBuilder() + .MoveTo({200, 200}) + .LineTo({300, 400}) + .LineTo({100, 400}) + .Close() + .TakePath(), + paint); + + // Draw another thing to make sure the clip area is reset. + Paint red; + red.color = Color::Red(); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, GaussianBlurStyleSolid) { + Canvas canvas; + canvas.Scale(GetContentScale()); + + canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)}); + + Paint paint; + paint.color = Color::Green(); + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kSolid, + .sigma = Sigma(30), + }; + canvas.DrawPath(PathBuilder() + .MoveTo({200, 200}) + .LineTo({300, 400}) + .LineTo({100, 400}) + .Close() + .TakePath(), + paint); + + // Draw another thing to make sure the clip area is reset. + Paint red; + red.color = Color::Red(); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, MaskBlurTexture) { + Scalar sigma = 30; + auto callback = [&](AiksContext& renderer) -> std::optional { + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::SliderFloat("Sigma", &sigma, 0, 500); + ImGui::End(); + } + Canvas canvas; + canvas.Scale(GetContentScale()); + Paint paint; + paint.color = Color::Green(); + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Sigma(sigma), + }; + std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); + canvas.DrawImage(std::make_shared(boston), {200, 200}, paint); + Paint red; + red.color = Color::Red(); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red); + return canvas.EndRecordingAsPicture(); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(AiksTest, GuassianBlurUpdatesMipmapContents) { + // This makes sure if mip maps are recycled across invocations of blurs the + // contents get updated each frame correctly. If they aren't updated the color + // inside the blur and outside the blur will be different. + // + // If there is some change to render target caching this could display a false + // positive in the future. Also, if the LOD that is rendered is 1 it could + // present a false positive. + int32_t count = 0; + auto callback = [&](AiksContext& renderer) -> std::optional { + Canvas canvas; + if (count++ == 0) { + canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()}); + } else { + canvas.DrawCircle({100, 100}, 50, {.color = Color::Chartreuse()}); + } + canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), {20, 20}); + canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, + ImageFilter::MakeBlur(Sigma(30.0), Sigma(30.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); + canvas.Restore(); + return canvas.EndRecordingAsPicture(); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(AiksTest, MaskBlurDoesntStretchContents) { + Scalar sigma = 70; + auto callback = [&](AiksContext& renderer) -> std::optional { + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::SliderFloat("Sigma", &sigma, 0, 500); + ImGui::End(); + } + Canvas canvas; + canvas.Scale(GetContentScale()); + canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)}); + + std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); + ColorSource image_source = ColorSource::MakeImage( + boston, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {}, {}); + + canvas.Transform(Matrix::MakeTranslation({100, 100, 0}) * + Matrix::MakeScale({0.5, 0.5, 1.0})); + Paint paint = { + .color_source = image_source, + .mask_blur_descriptor = + Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Sigma(sigma), + }, + }; + canvas.DrawRect( + Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height), + paint); + + return canvas.EndRecordingAsPicture(); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/display_list/BUILD.gn b/impeller/display_list/BUILD.gn index e60ec4e9f37de..712e1bb723974 100644 --- a/impeller/display_list/BUILD.gn +++ b/impeller/display_list/BUILD.gn @@ -55,7 +55,6 @@ template("display_list_unittests_component") { "aiks_dl_atlas_unittests.cc", "aiks_dl_basic_unittests.cc", "aiks_dl_blend_unittests.cc", - "aiks_dl_blur_unittests.cc", "aiks_dl_clip_unittests.cc", "aiks_dl_gradient_unittests.cc", "aiks_dl_opacity_unittests.cc", diff --git a/impeller/display_list/aiks_dl_blur_unittests.cc b/impeller/display_list/aiks_dl_blur_unittests.cc deleted file mode 100644 index 0319bba01ddba..0000000000000 --- a/impeller/display_list/aiks_dl_blur_unittests.cc +++ /dev/null @@ -1,977 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "display_list/display_list.h" -#include "display_list/dl_blend_mode.h" -#include "display_list/dl_builder.h" -#include "display_list/dl_color.h" -#include "display_list/dl_paint.h" -#include "display_list/dl_sampling_options.h" -#include "display_list/dl_tile_mode.h" -#include "display_list/effects/dl_color_filter.h" -#include "display_list/effects/dl_color_source.h" -#include "display_list/effects/dl_image_filter.h" -#include "display_list/effects/dl_mask_filter.h" -#include "flutter/impeller/aiks/aiks_unittests.h" - -#include "impeller/display_list/dl_image_impeller.h" -#include "impeller/playground/widgets.h" -#include "include/core/SkRRect.h" -#include "include/core/SkRect.h" -#include "third_party/imgui/imgui.h" - -//////////////////////////////////////////////////////////////////////////////// -// This is for tests of Canvas that are interested the results of rendering -// blurs. -//////////////////////////////////////////////////////////////////////////////// - -namespace impeller { -namespace testing { - -using namespace flutter; - -TEST_P(AiksTest, CanRenderMaskBlurHugeSigma) { - DisplayListBuilder builder; - - DlPaint paint; - paint.setColor(DlColor::kGreen()); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 99999)); - builder.DrawCircle({400, 400}, 300, paint); - builder.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, CanRenderForegroundBlendWithMaskBlur) { - // This case triggers the ForegroundPorterDuffBlend path. The color filter - // should apply to the color only, and respect the alpha mask. - DisplayListBuilder builder; - builder.ClipRect(SkRect::MakeXYWH(100, 150, 400, 400)); - - DlPaint paint; - paint.setColor(DlColor::kWhite()); - - Sigma sigma = Radius(20); - paint.setMaskFilter( - DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma)); - paint.setColorFilter( - DlBlendColorFilter::Make(DlColor::kGreen(), DlBlendMode::kSrc)); - builder.DrawCircle({400, 400}, 200, paint); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur) { - // This case triggers the ForegroundAdvancedBlend path. The color filter - // should apply to the color only, and respect the alpha mask. - DisplayListBuilder builder; - builder.ClipRect(SkRect::MakeXYWH(100, 150, 400, 400)); - - DlPaint paint; - paint.setColor( - DlColor::RGBA(128.0f / 255.0f, 128.0f / 255.0f, 128.0f / 255.0f, 1.0f)); - - Sigma sigma = Radius(20); - paint.setMaskFilter( - DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma)); - paint.setColorFilter( - DlBlendColorFilter::Make(DlColor::kGreen(), DlBlendMode::kColor)); - builder.DrawCircle({400, 400}, 200, paint); - builder.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, CanRenderBackdropBlurInteractive) { - auto callback = [&]() -> sk_sp { - static PlaygroundPoint point_a(Point(50, 50), 30, Color::White()); - static PlaygroundPoint point_b(Point(300, 200), 30, Color::White()); - auto [a, b] = DrawPlaygroundLine(point_a, point_b); - - DisplayListBuilder builder; - DlPaint paint; - paint.setColor(DlColor::kCornflowerBlue()); - builder.DrawCircle({100, 100}, 50, paint); - - paint.setColor(DlColor::kGreenYellow()); - builder.DrawCircle({300, 200}, 100, paint); - - paint.setColor(DlColor::kDarkMagenta()); - builder.DrawCircle({140, 170}, 75, paint); - - paint.setColor(DlColor::kOrangeRed()); - builder.DrawCircle({180, 120}, 100, paint); - - SkRRect rrect = - SkRRect::MakeRectXY(SkRect::MakeLTRB(a.x, a.y, b.x, b.y), 20, 20); - builder.ClipRRect(rrect); - - DlPaint save_paint; - save_paint.setBlendMode(DlBlendMode::kSrc); - - auto backdrop_filter = DlBlurImageFilter::Make(20, 20, DlTileMode::kClamp); - builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get()); - builder.Restore(); - - return builder.Build(); - }; - - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -TEST_P(AiksTest, CanRenderBackdropBlur) { - DisplayListBuilder builder; - - DlPaint paint; - paint.setColor(DlColor::kCornflowerBlue()); - builder.DrawCircle({100, 100}, 50, paint); - - paint.setColor(DlColor::kGreenYellow()); - builder.DrawCircle({300, 200}, 100, paint); - - paint.setColor(DlColor::kDarkMagenta()); - builder.DrawCircle({140, 170}, 75, paint); - - paint.setColor(DlColor::kOrangeRed()); - builder.DrawCircle({180, 120}, 100, paint); - - SkRRect rrect = - SkRRect::MakeRectXY(SkRect::MakeLTRB(75, 50, 375, 275), 20, 20); - builder.ClipRRect(rrect); - - DlPaint save_paint; - save_paint.setBlendMode(DlBlendMode::kSrc); - auto backdrop_filter = DlBlurImageFilter::Make(30, 30, DlTileMode::kClamp); - builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get()); - builder.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, CanRenderBackdropBlurHugeSigma) { - DisplayListBuilder builder; - - DlPaint paint; - paint.setColor(DlColor::kGreen()); - builder.DrawCircle({400, 400}, 300, paint); - - DlPaint save_paint; - save_paint.setBlendMode(DlBlendMode::kSrc); - - auto backdrop_filter = - DlBlurImageFilter::Make(999999, 999999, DlTileMode::kClamp); - builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get()); - builder.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, CanRenderClippedBlur) { - DisplayListBuilder builder; - builder.ClipRect(SkRect::MakeXYWH(100, 150, 400, 400)); - - DlPaint paint; - paint.setColor(DlColor::kGreen()); - paint.setImageFilter(DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal)); - builder.DrawCircle({400, 400}, 200, paint); - builder.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, ClippedBlurFilterRendersCorrectlyInteractive) { - auto callback = [&]() -> sk_sp { - static PlaygroundPoint playground_point(Point(400, 400), 20, - Color::Green()); - auto point = DrawPlaygroundPoint(playground_point); - - DisplayListBuilder builder; - auto location = point - Point(400, 400); - builder.Translate(location.x, location.y); - - DlPaint paint; - Sigma sigma = Radius{120 * 3}; - paint.setMaskFilter( - DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma)); - paint.setColor(DlColor::kRed()); - - SkPath path = SkPath::Rect(SkRect::MakeLTRB(0, 0, 800, 800)); - path.addCircle(0, 0, 0.5); - builder.DrawPath(path, paint); - return builder.Build(); - }; - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -TEST_P(AiksTest, ClippedBlurFilterRendersCorrectly) { - DisplayListBuilder builder; - builder.Translate(0, -400); - DlPaint paint; - - Sigma sigma = Radius{120 * 3}; - paint.setMaskFilter( - DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma.sigma)); - paint.setColor(DlColor::kRed()); - - SkPath path = SkPath::Rect(SkRect::MakeLTRB(0, 0, 800, 800)); - path.addCircle(0, 0, 0.5); - builder.DrawPath(path, paint); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, ClearBlendWithBlur) { - DisplayListBuilder builder; - DlPaint paint; - paint.setColor(DlColor::kBlue()); - builder.DrawRect(SkRect::MakeXYWH(0, 0, 600.0, 600.0), paint); - - DlPaint clear; - clear.setBlendMode(DlBlendMode::kClear); - clear.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20)); - - builder.DrawCircle({300.0, 300.0}, 200.0, clear); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, BlurHasNoEdge) { - Scalar sigma = 47.6; - auto callback = [&]() -> sk_sp { - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::SliderFloat("Sigma", &sigma, 0, 50); - ImGui::End(); - } - DisplayListBuilder builder; - builder.Scale(GetContentScale().x, GetContentScale().y); - builder.DrawPaint({}); - - DlPaint paint; - paint.setColor(DlColor::kGreen()); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma)); - - builder.DrawRect(SkRect::MakeXYWH(300, 300, 200, 200), paint); - return builder.Build(); - }; - - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -TEST_P(AiksTest, MaskBlurWithZeroSigmaIsSkipped) { - DisplayListBuilder builder; - - DlPaint paint; - paint.setColor(DlColor::kBlue()); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 0)); - - builder.DrawCircle({300, 300}, 200, paint); - builder.DrawRect(SkRect::MakeLTRB(100, 300, 500, 600), paint); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -struct MaskBlurTestConfig { - DlBlurStyle style = DlBlurStyle::kNormal; - Scalar sigma = 1.0f; - Scalar alpha = 1.0f; - std::shared_ptr image_filter; - bool invert_colors = false; - DlBlendMode blend_mode = DlBlendMode::kSrcOver; -}; - -static sk_sp MaskBlurVariantTest( - const AiksTest& test_context, - const MaskBlurTestConfig& config) { - DisplayListBuilder builder; - builder.Scale(test_context.GetContentScale().x, - test_context.GetContentScale().y); - builder.Scale(0.8f, 0.8f); - - DlPaint draw_paint; - draw_paint.setColor( - DlColor::RGBA(Color::AntiqueWhite().red, Color::AntiqueWhite().green, - Color::AntiqueWhite().blue, Color::AntiqueWhite().alpha)); - builder.DrawPaint(draw_paint); - - DlPaint paint; - paint.setMaskFilter(DlBlurMaskFilter::Make(config.style, config.sigma)); - paint.setInvertColors(config.invert_colors); - paint.setImageFilter(config.image_filter); - paint.setBlendMode(config.blend_mode); - - const Scalar x = 50; - const Scalar radius = 20.0f; - const Scalar y_spacing = 100.0f; - Scalar alpha = config.alpha * 255; - - Scalar y = 50; - paint.setColor(DlColor::kCrimson().withAlpha(alpha)); - builder.DrawRect(SkRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, // - radius, 60.0f - radius), - paint); - - y += y_spacing; - paint.setColor(DlColor::kBlue().withAlpha(alpha)); - builder.DrawCircle({x + 25, y + 25}, radius, paint); - - y += y_spacing; - paint.setColor(DlColor::kGreen().withAlpha(alpha)); - builder.DrawOval(SkRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, // - radius, 60.0f - radius), - paint); - - y += y_spacing; - paint.setColor(DlColor::kPurple().withAlpha(alpha)); - SkRRect rrect = - SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, 60.0f, 60.0f), radius, radius); - builder.DrawRRect(rrect, paint); - - y += y_spacing; - paint.setColor(DlColor::kOrange().withAlpha(alpha)); - - rrect = - SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, 60.0f, 60.0f), radius, 5.0); - builder.DrawRRect(rrect, paint); - - y += y_spacing; - paint.setColor(DlColor::kMaroon().withAlpha(alpha)); - - { - SkPath path; - path.moveTo(x + 0, y + 60); - path.lineTo(x + 30, y + 0); - path.lineTo(x + 60, y + 60); - path.close(); - - builder.DrawPath(path, paint); - } - - y += y_spacing; - paint.setColor(DlColor::kMaroon().withAlpha(alpha)); - { - SkPath path; - path.addArc(SkRect::MakeXYWH(x + 5, y, 50, 50), 90, 180); - path.addArc(SkRect::MakeXYWH(x + 25, y, 50, 50), 90, 180); - path.close(); - builder.DrawPath(path, paint); - } - - return builder.Build(); -} - -static const std::map kPaintVariations = { - // 1. Normal style, translucent, zero sigma. - {"NormalTranslucentZeroSigma", - {.style = DlBlurStyle::kNormal, .sigma = 0.0f, .alpha = 0.5f}}, - // 2. Normal style, translucent. - {"NormalTranslucent", - {.style = DlBlurStyle::kNormal, .sigma = 8.0f, .alpha = 0.5f}}, - // 3. Solid style, translucent. - {"SolidTranslucent", - {.style = DlBlurStyle::kSolid, .sigma = 8.0f, .alpha = 0.5f}}, - // 4. Solid style, opaque. - {"SolidOpaque", {.style = DlBlurStyle::kSolid, .sigma = 8.0f}}, - // 5. Solid style, translucent, color & image filtered. - {"SolidTranslucentWithFilters", - {.style = DlBlurStyle::kSolid, - .sigma = 8.0f, - .alpha = 0.5f, - .image_filter = DlBlurImageFilter::Make(3, 3, DlTileMode::kClamp), - .invert_colors = true}}, - // 6. Solid style, translucent, exclusion blended. - {"SolidTranslucentExclusionBlend", - {.style = DlBlurStyle::kSolid, - .sigma = 8.0f, - .alpha = 0.5f, - .blend_mode = DlBlendMode::kExclusion}}, - // 7. Inner style, translucent. - {"InnerTranslucent", - {.style = DlBlurStyle::kInner, .sigma = 8.0f, .alpha = 0.5f}}, - // 8. Inner style, translucent, blurred. - {"InnerTranslucentWithBlurImageFilter", - {.style = DlBlurStyle::kInner, - .sigma = 8.0f, - .alpha = 0.5f, - .image_filter = DlBlurImageFilter::Make(3, 3, DlTileMode::kClamp)}}, - // 9. Outer style, translucent. - {"OuterTranslucent", - {.style = DlBlurStyle::kOuter, .sigma = 8.0f, .alpha = 0.5f}}, - // 10. Outer style, opaque, image filtered. - {"OuterOpaqueWithBlurImageFilter", - {.style = DlBlurStyle::kOuter, - .sigma = 8.0f, - .image_filter = DlBlurImageFilter::Make(3, 3, DlTileMode::kClamp)}}, -}; - -#define MASK_BLUR_VARIANT_TEST(config) \ - TEST_P(AiksTest, MaskBlurVariantTest##config) { \ - ASSERT_TRUE(OpenPlaygroundHere( \ - MaskBlurVariantTest(*this, kPaintVariations.at(#config)))); \ - } - -MASK_BLUR_VARIANT_TEST(NormalTranslucentZeroSigma) -MASK_BLUR_VARIANT_TEST(NormalTranslucent) -MASK_BLUR_VARIANT_TEST(SolidTranslucent) -MASK_BLUR_VARIANT_TEST(SolidOpaque) -MASK_BLUR_VARIANT_TEST(SolidTranslucentWithFilters) -MASK_BLUR_VARIANT_TEST(SolidTranslucentExclusionBlend) -MASK_BLUR_VARIANT_TEST(InnerTranslucent) -MASK_BLUR_VARIANT_TEST(InnerTranslucentWithBlurImageFilter) -MASK_BLUR_VARIANT_TEST(OuterTranslucent) -MASK_BLUR_VARIANT_TEST(OuterOpaqueWithBlurImageFilter) - -#undef MASK_BLUR_VARIANT_TEST - -TEST_P(AiksTest, GaussianBlurStyleInner) { - DisplayListBuilder builder; - builder.Scale(GetContentScale().x, GetContentScale().y); - - DlPaint paint; - paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1)); - builder.DrawPaint(paint); - - paint.setColor(DlColor::kGreen()); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kInner, 30)); - - SkPath path; - path.moveTo(200, 200); - path.lineTo(300, 400); - path.lineTo(100, 400); - path.close(); - - builder.DrawPath(path, paint); - - // Draw another thing to make sure the clip area is reset. - DlPaint red; - red.setColor(DlColor::kRed()); - builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, GaussianBlurStyleOuter) { - DisplayListBuilder builder; - builder.Scale(GetContentScale().x, GetContentScale().y); - - DlPaint paint; - paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0)); - builder.DrawPaint(paint); - - paint.setColor(DlColor::kGreen()); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kOuter, 30)); - - SkPath path; - path.moveTo(200, 200); - path.lineTo(300, 400); - path.lineTo(100, 400); - path.close(); - - builder.DrawPath(path, paint); - - // Draw another thing to make sure the clip area is reset. - DlPaint red; - red.setColor(DlColor::kRed()); - builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, GaussianBlurStyleSolid) { - DisplayListBuilder builder; - builder.Scale(GetContentScale().x, GetContentScale().y); - - DlPaint paint; - paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0)); - builder.DrawPaint(paint); - - paint.setColor(DlColor::kGreen()); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kSolid, 30)); - - SkPath path; - path.moveTo(200, 200); - path.lineTo(300, 400); - path.lineTo(100, 400); - path.close(); - - builder.DrawPath(path, paint); - - // Draw another thing to make sure the clip area is reset. - DlPaint red; - red.setColor(DlColor::kRed()); - builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, MaskBlurTexture) { - Scalar sigma = 30; - auto callback = [&]() -> sk_sp { - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::SliderFloat("Sigma", &sigma, 0, 500); - ImGui::End(); - } - - DisplayListBuilder builder; - builder.Scale(GetContentScale().x, GetContentScale().y); - - DlPaint paint; - paint.setColor(DlColor::kGreen()); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma)); - - builder.DrawImage( - DlImageImpeller::Make(CreateTextureForFixture("boston.jpg")), - {200, 200}, DlImageSampling::kNearestNeighbor, &paint); - - DlPaint red; - red.setColor(DlColor::kRed()); - builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red); - - return builder.Build(); - }; - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -TEST_P(AiksTest, MaskBlurDoesntStretchContents) { - Scalar sigma = 70; - auto callback = [&]() -> sk_sp { - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::SliderFloat("Sigma", &sigma, 0, 500); - ImGui::End(); - } - - DisplayListBuilder builder; - builder.Scale(GetContentScale().x, GetContentScale().y); - - DlPaint paint; - paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0)); - builder.DrawPaint(paint); - - std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); - - builder.Transform(SkMatrix::Translate(100, 100) * - SkMatrix::Scale(0.5, 0.5)); - - paint.setColorSource(std::make_shared( - DlImageImpeller::Make(boston), DlTileMode::kRepeat, DlTileMode::kRepeat, - DlImageSampling::kMipmapLinear)); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, sigma)); - - builder.DrawRect(SkRect::MakeXYWH(0, 0, boston->GetSize().width, - boston->GetSize().height), - paint); - - return builder.Build(); - }; - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -TEST_P(AiksTest, GaussianBlurAtPeripheryVertical) { - DisplayListBuilder builder; - - DlPaint paint; - builder.Scale(GetContentScale().x, GetContentScale().y); - - paint.setColor(DlColor::kLimeGreen()); - SkRRect rrect = SkRRect::MakeRectXY( - SkRect::MakeLTRB(0, 0, GetWindowSize().width, 100), 10, 10); - builder.DrawRRect(rrect, paint); - - paint.setColor(DlColor::kMagenta()); - rrect = SkRRect::MakeRectXY( - SkRect::MakeLTRB(0, 110, GetWindowSize().width, 210), 10, 10); - builder.DrawRRect(rrect, paint); - builder.ClipRect(SkRect::MakeLTRB(100, 0, 200, GetWindowSize().height)); - - DlPaint save_paint; - save_paint.setBlendMode(DlBlendMode::kSrc); - - auto backdrop_filter = DlBlurImageFilter::Make(20, 20, DlTileMode::kClamp); - - builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get()); - builder.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, GaussianBlurAtPeripheryHorizontal) { - DisplayListBuilder builder; - - builder.Scale(GetContentScale().x, GetContentScale().y); - std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); - builder.DrawImageRect( - DlImageImpeller::Make(boston), - SkRect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height), - SkRect::MakeLTRB(0, 0, GetWindowSize().width, 100), - DlImageSampling::kNearestNeighbor); - - DlPaint paint; - paint.setColor(DlColor::kMagenta()); - - SkRRect rrect = SkRRect::MakeRectXY( - SkRect::MakeLTRB(0, 110, GetWindowSize().width, 210), 10, 10); - builder.DrawRRect(rrect, paint); - builder.ClipRect(SkRect::MakeLTRB(0, 50, GetWindowSize().width, 150)); - - DlPaint save_paint; - save_paint.setBlendMode(DlBlendMode::kSrc); - - auto backdrop_filter = DlBlurImageFilter::Make(20, 20, DlTileMode::kClamp); - builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get()); - - builder.Restore(); - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, GaussianBlurAnimatedBackdrop) { - // This test is for checking out how stable rendering is when content is - // translated underneath a blur. Animating under a blur can cause - // *shimmering* to happen as a result of pixel alignment. - // See also: https://github.com/flutter/flutter/issues/140193 - auto boston = - CreateTextureForFixture("boston.jpg", /*enable_mipmapping=*/true); - ASSERT_TRUE(boston); - int64_t count = 0; - Scalar sigma = 20.0; - Scalar freq = 0.1; - Scalar amp = 50.0; - auto callback = [&]() -> sk_sp { - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::SliderFloat("Sigma", &sigma, 0, 200); - ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0); - ImGui::SliderFloat("Amplitude", &, 1, 100); - ImGui::End(); - } - - DisplayListBuilder builder; - builder.Scale(GetContentScale().x, GetContentScale().y); - Scalar y = amp * sin(freq * 2.0 * M_PI * count / 60); - builder.DrawImage( - DlImageImpeller::Make(boston), - SkPoint::Make(1024 / 2 - boston->GetSize().width / 2, - (768 / 2 - boston->GetSize().height / 2) + y), - DlImageSampling::kMipmapLinear); - static PlaygroundPoint point_a(Point(100, 100), 20, Color::Red()); - static PlaygroundPoint point_b(Point(900, 700), 20, Color::Red()); - auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b); - - builder.ClipRect( - SkRect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y)); - builder.ClipRect(SkRect::MakeLTRB(100, 100, 900, 700)); - - DlPaint paint; - paint.setBlendMode(DlBlendMode::kSrc); - - auto backdrop_filter = - DlBlurImageFilter::Make(sigma, sigma, DlTileMode::kClamp); - builder.SaveLayer(nullptr, &paint, backdrop_filter.get()); - count += 1; - return builder.Build(); - }; - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -TEST_P(AiksTest, GaussianBlurStyleInnerGradient) { - DisplayListBuilder builder; - - builder.Scale(GetContentScale().x, GetContentScale().y); - - DlPaint paint; - paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0)); - builder.DrawPaint(paint); - - std::vector colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0), - DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)}; - std::vector stops = {0.0, 1.0}; - - paint = DlPaint{}; - paint.setColorSource(DlColorSource::MakeLinear( - /*start_point=*/{0, 0}, - /*end_point=*/{200, 200}, - /*stop_count=*/colors.size(), - /*colors=*/colors.data(), - /*stops=*/stops.data(), - /*tile_mode=*/DlTileMode::kMirror)); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kInner, 30)); - - SkPath path; - path.moveTo(200, 200); - path.lineTo(300, 400); - path.lineTo(100, 400); - path.close(); - builder.DrawPath(path, paint); - - // Draw another thing to make sure the clip area is reset. - DlPaint red; - red.setColor(DlColor::kRed()); - builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, GaussianBlurStyleSolidGradient) { - DisplayListBuilder builder; - builder.Scale(GetContentScale().x, GetContentScale().y); - - DlPaint paint; - paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0)); - builder.DrawPaint(paint); - - std::vector colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0), - DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)}; - std::vector stops = {0.0, 1.0}; - - paint = DlPaint{}; - paint.setColorSource(DlColorSource::MakeLinear( - /*start_point=*/{0, 0}, - /*end_point=*/{200, 200}, - /*stop_count=*/colors.size(), - /*colors=*/colors.data(), - /*stops=*/stops.data(), - /*tile_mode=*/DlTileMode::kMirror)); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kSolid, 30)); - - SkPath path; - path.moveTo(200, 200); - path.lineTo(300, 400); - path.lineTo(100, 400); - path.close(); - builder.DrawPath(path, paint); - - // Draw another thing to make sure the clip area is reset. - DlPaint red; - red.setColor(DlColor::kRed()); - builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red); - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, GaussianBlurStyleOuterGradient) { - DisplayListBuilder builder; - builder.Scale(GetContentScale().x, GetContentScale().y); - - DlPaint paint; - paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 1.0)); - builder.DrawPaint(paint); - - std::vector colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0), - DlColor::RGBA(0.7568, 0.2627, 0.2118, 1.0)}; - std::vector stops = {0.0, 1.0}; - - paint = DlPaint{}; - paint.setColorSource(DlColorSource::MakeLinear( - /*start_point=*/{0, 0}, - /*end_point=*/{200, 200}, - /*stop_count=*/colors.size(), - /*colors=*/colors.data(), - /*stops=*/stops.data(), - /*tile_mode=*/DlTileMode::kMirror)); - paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kOuter, 30)); - - SkPath path; - path.moveTo(200, 200); - path.lineTo(300, 400); - path.lineTo(100, 400); - path.close(); - builder.DrawPath(path, paint); - - // Draw another thing to make sure the clip area is reset. - DlPaint red; - red.setColor(DlColor::kRed()); - builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), red); - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, GaussianBlurScaledAndClipped) { - DisplayListBuilder builder; - std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); - Rect bounds = - Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height); - Vector2 image_center = Vector2(bounds.GetSize() / 2); - - DlPaint paint; - paint.setImageFilter(DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal)); - - Vector2 clip_size = {150, 75}; - Vector2 center = Vector2(1024, 768) / 2; - builder.Scale(GetContentScale().x, GetContentScale().y); - - auto rect = - Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size); - builder.ClipRect(SkRect::MakeLTRB(rect.GetLeft(), rect.GetTop(), - rect.GetRight(), rect.GetBottom())); - builder.Translate(center.x, center.y); - builder.Scale(0.6, 0.6); - - SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(), - bounds.GetRight(), bounds.GetBottom()); - Rect dest = bounds.Shift(-image_center); - SkRect sk_dst = SkRect::MakeLTRB(dest.GetLeft(), dest.GetTop(), - dest.GetRight(), dest.GetBottom()); - builder.DrawImageRect(DlImageImpeller::Make(boston), /*src=*/sk_bounds, - /*dst=*/sk_dst, DlImageSampling::kNearestNeighbor, - &paint); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) { - std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); - - auto callback = [&]() -> sk_sp { - const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; - const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat, - DlTileMode::kMirror, DlTileMode::kDecal}; - - static float rotation = 0; - static float scale = 0.6; - static int selected_tile_mode = 3; - - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180); - ImGui::SliderFloat("Scale", &scale, 0, 2.0); - ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, - sizeof(tile_mode_names) / sizeof(char*)); - ImGui::End(); - } - - DisplayListBuilder builder; - Rect bounds = - Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height); - Vector2 image_center = Vector2(bounds.GetSize() / 2); - DlPaint paint; - paint.setImageFilter( - DlBlurImageFilter::Make(20, 20, tile_modes[selected_tile_mode])); - - static PlaygroundPoint point_a(Point(362, 309), 20, Color::Red()); - static PlaygroundPoint point_b(Point(662, 459), 20, Color::Red()); - auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b); - Vector2 center = Vector2(1024, 768) / 2; - - builder.Scale(GetContentScale().x, GetContentScale().y); - builder.ClipRect( - SkRect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y)); - builder.Translate(center.x, center.y); - builder.Scale(scale, scale); - builder.Rotate(rotation); - - SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(), - bounds.GetRight(), bounds.GetBottom()); - Rect dest = bounds.Shift(-image_center); - SkRect sk_dst = SkRect::MakeLTRB(dest.GetLeft(), dest.GetTop(), - dest.GetRight(), dest.GetBottom()); - builder.DrawImageRect(DlImageImpeller::Make(boston), /*src=*/sk_bounds, - /*dst=*/sk_dst, DlImageSampling::kNearestNeighbor, - &paint); - return builder.Build(); - }; - - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -TEST_P(AiksTest, GaussianBlurOneDimension) { - DisplayListBuilder builder; - - builder.Scale(GetContentScale().x, GetContentScale().y); - builder.Scale(0.5, 0.5); - - std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); - builder.DrawImage(DlImageImpeller::Make(boston), {100, 100}, {}); - - DlPaint paint; - paint.setBlendMode(DlBlendMode::kSrc); - - auto backdrop_filter = DlBlurImageFilter::Make(50, 0, DlTileMode::kClamp); - builder.SaveLayer(nullptr, &paint, backdrop_filter.get()); - builder.Restore(); - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -// Smoketest to catch issues with the coverage hint. -// Draws a rotated blurred image within a rectangle clip. The center of the clip -// rectangle is the center of the rotated image. The entire area of the clip -// rectangle should be filled with opaque colors output by the blur. -TEST_P(AiksTest, GaussianBlurRotatedAndClipped) { - DisplayListBuilder builder; - - std::shared_ptr boston = CreateTextureForFixture("boston.jpg"); - Rect bounds = - Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height); - - DlPaint paint; - paint.setImageFilter(DlBlurImageFilter::Make(20, 20, DlTileMode::kDecal)); - - Vector2 image_center = Vector2(bounds.GetSize() / 2); - Vector2 clip_size = {150, 75}; - Vector2 center = Vector2(1024, 768) / 2; - builder.Scale(GetContentScale().x, GetContentScale().y); - - auto clip_bounds = - Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size); - builder.ClipRect(SkRect::MakeLTRB(clip_bounds.GetLeft(), clip_bounds.GetTop(), - clip_bounds.GetRight(), - clip_bounds.GetBottom())); - builder.Translate(center.x, center.y); - builder.Scale(0.6, 0.6); - builder.Rotate(25); - - auto dst_rect = bounds.Shift(-image_center); - builder.DrawImageRect( - DlImageImpeller::Make(boston), /*src=*/ - SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(), bounds.GetRight(), - bounds.GetBottom()), - /*dst=*/ - SkRect::MakeLTRB(dst_rect.GetLeft(), dst_rect.GetTop(), - dst_rect.GetRight(), dst_rect.GetBottom()), - DlImageSampling::kMipmapLinear, &paint); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, GaussianBlurRotatedNonUniform) { - auto callback = [&]() -> sk_sp { - const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; - const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat, - DlTileMode::kMirror, DlTileMode::kDecal}; - - static float rotation = 45; - static float scale = 0.6; - static int selected_tile_mode = 3; - - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180); - ImGui::SliderFloat("Scale", &scale, 0, 2.0); - ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, - sizeof(tile_mode_names) / sizeof(char*)); - ImGui::End(); - } - - DisplayListBuilder builder; - - DlPaint paint; - paint.setColor(DlColor::kGreen()); - paint.setImageFilter( - DlBlurImageFilter::Make(50, 0, tile_modes[selected_tile_mode])); - - Vector2 center = Vector2(1024, 768) / 2; - builder.Scale(GetContentScale().x, GetContentScale().y); - builder.Translate(center.x, center.y); - builder.Scale(scale, scale); - builder.Rotate(rotation); - - SkRRect rrect = - SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -100, 200, 200), 10, 10); - builder.DrawRRect(rrect, paint); - return builder.Build(); - }; - - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -} // namespace testing -} // namespace impeller diff --git a/testing/impeller_golden_tests_output.txt b/testing/impeller_golden_tests_output.txt index 70fb554fb3774..d357f4a4fed6c 100644 --- a/testing/impeller_golden_tests_output.txt +++ b/testing/impeller_golden_tests_output.txt @@ -702,6 +702,9 @@ impeller_Play_AiksTest_GaussianBlurWithoutDecalSupport_Metal.png impeller_Play_AiksTest_GradientStrokesRenderCorrectly_Metal.png impeller_Play_AiksTest_GradientStrokesRenderCorrectly_OpenGLES.png impeller_Play_AiksTest_GradientStrokesRenderCorrectly_Vulkan.png +impeller_Play_AiksTest_GuassianBlurUpdatesMipmapContents_Metal.png +impeller_Play_AiksTest_GuassianBlurUpdatesMipmapContents_OpenGLES.png +impeller_Play_AiksTest_GuassianBlurUpdatesMipmapContents_Vulkan.png impeller_Play_AiksTest_ImageColorSourceEffectTransform_Metal.png impeller_Play_AiksTest_ImageColorSourceEffectTransform_OpenGLES.png impeller_Play_AiksTest_ImageColorSourceEffectTransform_Vulkan.png