diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index c1c46ecde95f4..b10c7af1f3492 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -4551,7 +4551,21 @@ TEST_P(AiksTest, GaussianBlurWithoutDecalSupport) { Sigma(20.0), Sigma(20.0), FilterContents::BlurStyle::kNormal, Entity::TileMode::kDecal), }); + 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())); } diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 795d7843943fb..5f2c310f78d9e 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -66,8 +66,8 @@ std::shared_ptr FilterContents::MakeGaussianBlur( // TODO(https://github.com/flutter/flutter/issues/131580): Remove once the new // blur handles all cases. if (use_new_filter) { - auto blur = - std::make_shared(sigma_x.sigma, tile_mode); + auto blur = std::make_shared( + sigma_x.sigma, sigma_y.sigma, tile_mode); blur->SetInputs({input}); return blur; } diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 857184c8b1859..f61439d7f047e 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -148,6 +148,10 @@ std::shared_ptr MakeBlurSubpass( const SamplerDescriptor& sampler_descriptor, Entity::TileMode tile_mode, const GaussianBlurFragmentShader::BlurInfo& blur_info) { + if (blur_info.blur_sigma < kEhCloseEnough) { + return input_texture; + } + // TODO(gaaclarke): This blurs the whole image, but because we know the clip // region we could focus on just blurring that. ISize subpass_size = input_texture->GetSize(); @@ -202,9 +206,10 @@ std::shared_ptr MakeBlurSubpass( } // namespace GaussianBlurFilterContents::GaussianBlurFilterContents( - Scalar sigma, + Scalar sigma_x, + Scalar sigma_y, Entity::TileMode tile_mode) - : sigma_(sigma), tile_mode_(tile_mode) {} + : sigma_x_(sigma_x), sigma_y_(sigma_y), tile_mode_(tile_mode) {} // This value was extracted from Skia, see: // * https://github.com/google/skia/blob/d29cc3fe182f6e8a8539004a6a4ee8251677a6fd/src/gpu/ganesh/GrBlurUtils.cpp#L2561-L2576 @@ -219,10 +224,11 @@ Scalar GaussianBlurFilterContents::CalculateScale(Scalar sigma) { std::optional GaussianBlurFilterContents::GetFilterSourceCoverage( const Matrix& effect_transform, const Rect& output_limit) const { - Scalar scaled_sigma = ScaleSigma(sigma_); - Scalar blur_radius = CalculateBlurRadius(scaled_sigma); + Vector2 scaled_sigma = {ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)}; + Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x), + CalculateBlurRadius(scaled_sigma.y)}; Vector3 blur_radii = - effect_transform.Basis() * Vector3{blur_radius, blur_radius, 0.0}; + effect_transform.Basis() * Vector3{blur_radius.x, blur_radius.y, 0.0}; return output_limit.Expand(Point(blur_radii.x, blur_radii.y)); } @@ -239,11 +245,12 @@ std::optional GaussianBlurFilterContents::GetFilterCoverage( return {}; } - Scalar scaled_sigma = ScaleSigma(sigma_); - Scalar blur_radius = CalculateBlurRadius(scaled_sigma); + Vector2 scaled_sigma = {ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)}; + Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x), + CalculateBlurRadius(scaled_sigma.y)}; Vector3 blur_radii = (inputs[0]->GetTransform(entity).Basis() * effect_transform.Basis() * - Vector3{blur_radius, blur_radius, 0.0}) + Vector3{blur_radius.x, blur_radius.y, 0.0}) .Abs(); return input_coverage.value().Expand(Point(blur_radii.x, blur_radii.y)); } @@ -259,9 +266,10 @@ std::optional GaussianBlurFilterContents::RenderFilter( return std::nullopt; } - Scalar scaled_sigma = ScaleSigma(sigma_); - Scalar blur_radius = CalculateBlurRadius(scaled_sigma); - Vector2 padding(ceil(blur_radius), ceil(blur_radius)); + Vector2 scaled_sigma = {ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)}; + Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x), + CalculateBlurRadius(scaled_sigma.y)}; + Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y)); // Apply as much of the desired padding as possible from the source. This may // be ignored so must be accounted for in the downsample pass by adding a @@ -281,12 +289,13 @@ std::optional GaussianBlurFilterContents::RenderFilter( return std::nullopt; } - if (scaled_sigma < kEhCloseEnough) { + if (scaled_sigma.x < kEhCloseEnough && scaled_sigma.y < kEhCloseEnough) { return Entity::FromSnapshot(input_snapshot.value(), entity.GetBlendMode(), entity.GetClipDepth()); // No blur to render. } - Scalar desired_scalar = CalculateScale(scaled_sigma); + Scalar desired_scalar = + std::min(CalculateScale(scaled_sigma.x), CalculateScale(scaled_sigma.y)); // TODO(jonahwilliams): If desired_scalar is 1.0 and we fully acquired the // gutter from the expanded_coverage_hint, we can skip the downsample pass. // pass. @@ -314,8 +323,8 @@ std::optional GaussianBlurFilterContents::RenderFilter( input_snapshot->sampler_descriptor, tile_mode_, GaussianBlurFragmentShader::BlurInfo{ .blur_uv_offset = Point(0.0, pass1_pixel_size.y), - .blur_sigma = scaled_sigma * effective_scalar.y, - .blur_radius = blur_radius * effective_scalar.y, + .blur_sigma = scaled_sigma.y * effective_scalar.y, + .blur_radius = blur_radius.y * effective_scalar.y, .step_size = 1.0, }); @@ -325,8 +334,8 @@ std::optional GaussianBlurFilterContents::RenderFilter( input_snapshot->sampler_descriptor, tile_mode_, GaussianBlurFragmentShader::BlurInfo{ .blur_uv_offset = Point(pass1_pixel_size.x, 0.0), - .blur_sigma = scaled_sigma * effective_scalar.x, - .blur_radius = blur_radius * effective_scalar.x, + .blur_sigma = scaled_sigma.x * effective_scalar.x, + .blur_radius = blur_radius.x * effective_scalar.x, .step_size = 1.0, }); diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index 143c6f5ab0c6a..7932291fd9ee8 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -16,9 +16,12 @@ namespace impeller { /// Note: This will replace `DirectionalGaussianBlurFilterContents`. class GaussianBlurFilterContents final : public FilterContents { public: - explicit GaussianBlurFilterContents(Scalar sigma, Entity::TileMode tile_mode); + explicit GaussianBlurFilterContents(Scalar sigma_x, + Scalar sigma_y, + Entity::TileMode tile_mode); - Scalar GetSigma() const { return sigma_; } + Scalar GetSigmaX() const { return sigma_x_; } + Scalar GetSigmaY() const { return sigma_y_; } // |FilterContents| std::optional GetFilterSourceCoverage( @@ -67,7 +70,8 @@ class GaussianBlurFilterContents final : public FilterContents { const Rect& coverage, const std::optional& coverage_hint) const override; - const Scalar sigma_ = 0.0; + const Scalar sigma_x_ = 0.0; + const Scalar sigma_y_ = 0.0; const Entity::TileMode tile_mode_; }; diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc index 356870ccba947..40e36406cf065 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc @@ -62,12 +62,15 @@ class GaussianBlurFilterContentsTest : public EntityPlayground { INSTANTIATE_PLAYGROUND_SUITE(GaussianBlurFilterContentsTest); TEST(GaussianBlurFilterContentsTest, Create) { - GaussianBlurFilterContents contents(/*sigma=*/0.0, Entity::TileMode::kDecal); - ASSERT_EQ(contents.GetSigma(), 0.0); + GaussianBlurFilterContents contents(/*sigma_x=*/0.0, /*sigma_y=*/0.0, + Entity::TileMode::kDecal); + EXPECT_EQ(contents.GetSigmaX(), 0.0); + EXPECT_EQ(contents.GetSigmaY(), 0.0); } TEST(GaussianBlurFilterContentsTest, CoverageEmpty) { - GaussianBlurFilterContents contents(/*sigma=*/0.0, Entity::TileMode::kDecal); + GaussianBlurFilterContents contents(/*sigma_x=*/0.0, /*sigma_y=*/0.0, + Entity::TileMode::kDecal); FilterInput::Vector inputs = {}; Entity entity; std::optional coverage = @@ -76,7 +79,8 @@ TEST(GaussianBlurFilterContentsTest, CoverageEmpty) { } TEST(GaussianBlurFilterContentsTest, CoverageSimple) { - GaussianBlurFilterContents contents(/*sigma=*/0.0, Entity::TileMode::kDecal); + GaussianBlurFilterContents contents(/*sigma_x=*/0.0, /*sigma_y=*/0.0, + Entity::TileMode::kDecal); FilterInput::Vector inputs = { FilterInput::Make(Rect::MakeLTRB(10, 10, 110, 110))}; Entity entity; @@ -87,7 +91,8 @@ TEST(GaussianBlurFilterContentsTest, CoverageSimple) { TEST(GaussianBlurFilterContentsTest, CoverageWithSigma) { Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); - GaussianBlurFilterContents contents(/*sigma=*/sigma_radius_1, + GaussianBlurFilterContents contents(/*sigma_x=*/sigma_radius_1, + /*sigma_y=*/sigma_radius_1, Entity::TileMode::kDecal); FilterInput::Vector inputs = { FilterInput::Make(Rect::MakeLTRB(100, 100, 200, 200))}; @@ -107,7 +112,8 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithTexture) { .size = ISize(100, 100), }; Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); - GaussianBlurFilterContents contents(/*sigma=*/sigma_radius_1, + GaussianBlurFilterContents contents(/*sigma_X=*/sigma_radius_1, + /*sigma_y=*/sigma_radius_1, Entity::TileMode::kDecal); std::shared_ptr texture = GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture( @@ -130,7 +136,8 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithEffectTransform) { .size = ISize(100, 100), }; Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); - GaussianBlurFilterContents contents(/*sigma=*/sigma_radius_1, + GaussianBlurFilterContents contents(/*sigma_x=*/sigma_radius_1, + /*sigma_y=*/sigma_radius_1, Entity::TileMode::kDecal); std::shared_ptr texture = GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture( @@ -150,7 +157,7 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithEffectTransform) { TEST(GaussianBlurFilterContentsTest, FilterSourceCoverage) { Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); auto contents = std::make_unique( - sigma_radius_1, Entity::TileMode::kDecal); + sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal); std::optional coverage = contents->GetFilterSourceCoverage( /*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}), /*output_limit=*/Rect::MakeLTRB(100, 100, 200, 200)); @@ -175,7 +182,7 @@ TEST_P(GaussianBlurFilterContentsTest, RenderCoverageMatchesGetCoverage) { std::shared_ptr texture = MakeTexture(desc); Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); auto contents = std::make_unique( - sigma_radius_1, Entity::TileMode::kDecal); + sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal); contents->SetInputs({FilterInput::Make(texture)}); std::shared_ptr renderer = GetContentContext(); @@ -208,7 +215,7 @@ TEST_P(GaussianBlurFilterContentsTest, std::shared_ptr texture = MakeTexture(desc); Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); auto contents = std::make_unique( - sigma_radius_1, Entity::TileMode::kDecal); + sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal); contents->SetInputs({FilterInput::Make(texture)}); std::shared_ptr renderer = GetContentContext(); @@ -243,7 +250,7 @@ TEST_P(GaussianBlurFilterContentsTest, std::shared_ptr texture = MakeTexture(desc); Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); auto contents = std::make_unique( - sigma_radius_1, Entity::TileMode::kDecal); + sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal); contents->SetInputs({FilterInput::Make(texture)}); std::shared_ptr renderer = GetContentContext(); @@ -303,7 +310,7 @@ TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithDestinationRect) { Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); auto contents = std::make_unique( - sigma_radius_1, Entity::TileMode::kDecal); + sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal); contents->SetInputs({FilterInput::Make(texture_contents)}); std::shared_ptr renderer = GetContentContext(); @@ -342,7 +349,7 @@ TEST_P(GaussianBlurFilterContentsTest, Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); auto contents = std::make_unique( - sigma_radius_1, Entity::TileMode::kDecal); + sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal); contents->SetInputs({FilterInput::Make(texture_contents)}); std::shared_ptr renderer = GetContentContext(); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index c265686491437..025bc0467b3a4 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -1088,7 +1088,8 @@ TEST_P(EntityTest, GaussianBlurFilter) { switch (selected_pass_variation) { case 0: blur = std::make_shared( - blur_sigma_x.sigma, tile_modes[selected_tile_mode]); + blur_sigma_x.sigma, blur_sigma_y.sigma, + tile_modes[selected_tile_mode]); blur->SetInputs({FilterInput::Make(input)}); break; case 1: