diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index f396c42e25ba4..9f7544a952826 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -909,8 +909,8 @@ void Canvas::DrawTextFrame(const std::shared_ptr& text_frame, // needs to be reworked in order to interact correctly with // mask filters. // https://github.com/flutter/flutter/issues/133297 - entity.SetContents( - paint.WithFilters(paint.WithMaskBlur(std::move(text_contents), true))); + entity.SetContents(paint.WithFilters(paint.WithMaskBlur( + std::move(text_contents), true, GetCurrentTransform()))); AddRenderEntityToCurrentPass(std::move(entity)); } diff --git a/impeller/aiks/experimental_canvas.cc b/impeller/aiks/experimental_canvas.cc index d3c547bb3aa3f..0de5da7aa7e98 100644 --- a/impeller/aiks/experimental_canvas.cc +++ b/impeller/aiks/experimental_canvas.cc @@ -408,8 +408,8 @@ void ExperimentalCanvas::DrawTextFrame( // needs to be reworked in order to interact correctly with // mask filters. // https://github.com/flutter/flutter/issues/133297 - entity.SetContents( - paint.WithFilters(paint.WithMaskBlur(std::move(text_contents), true))); + entity.SetContents(paint.WithFilters(paint.WithMaskBlur( + std::move(text_contents), true, GetCurrentTransform()))); AddRenderEntityToCurrentPass(std::move(entity), false); } diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index aa7350c031493..12a5a0adc045a 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -78,10 +78,11 @@ std::shared_ptr Paint::WithFiltersForSubpassTarget( } std::shared_ptr Paint::WithMaskBlur(std::shared_ptr input, - bool is_solid_color) const { + bool is_solid_color, + const Matrix& ctm) const { if (mask_blur_descriptor.has_value()) { input = mask_blur_descriptor->CreateMaskBlur(FilterInput::Make(input), - is_solid_color); + is_solid_color, ctm); } return input; } @@ -203,12 +204,19 @@ std::shared_ptr Paint::MaskBlurDescriptor::CreateMaskBlur( std::shared_ptr Paint::MaskBlurDescriptor::CreateMaskBlur( const FilterInput::Ref& input, - bool is_solid_color) const { + bool is_solid_color, + const Matrix& ctm) const { + Vector2 blur_sigma(sigma.sigma, sigma.sigma); + if (!respect_ctm) { + blur_sigma /= Vector2(ctm.GetBasisX().Length(), ctm.GetBasisY().Length()); + } if (is_solid_color) { - return FilterContents::MakeGaussianBlur(input, sigma, sigma, + return FilterContents::MakeGaussianBlur(input, Sigma(blur_sigma.x), + Sigma(blur_sigma.y), Entity::TileMode::kDecal, style); } - return FilterContents::MakeBorderMaskBlur(input, sigma, sigma, style); + return FilterContents::MakeBorderMaskBlur(input, Sigma(blur_sigma.x), + Sigma(blur_sigma.y), style); } std::shared_ptr Paint::GetColorFilter() const { diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index 66049e1c0acd9..684c87c95f18e 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -48,6 +48,9 @@ struct Paint { struct MaskBlurDescriptor { FilterContents::BlurStyle style; Sigma sigma; + /// Text mask blurs need to not apply the CTM to the blur kernel. + /// See: https://github.com/flutter/flutter/issues/115112 + bool respect_ctm = true; std::shared_ptr CreateMaskBlur( std::shared_ptr color_source_contents, @@ -58,7 +61,8 @@ struct Paint { std::shared_ptr CreateMaskBlur( const FilterInput::Ref& input, - bool is_solid_color) const; + bool is_solid_color, + const Matrix& ctm) const; }; Color color = Color::Black(); @@ -105,7 +109,8 @@ struct Paint { bool HasColorFilter() const; std::shared_ptr WithMaskBlur(std::shared_ptr input, - bool is_solid_color) const; + bool is_solid_color, + const Matrix& ctm) const; std::shared_ptr WithImageFilter( const FilterInput::Variant& input, diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 5c5e30b53a383..6dbf67c41c669 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -511,6 +511,7 @@ void DlDispatcherBase::setMaskFilter(const flutter::DlMaskFilter* filter) { paint_.mask_blur_descriptor = { .style = ToBlurStyle(blur->style()), .sigma = Sigma(blur->sigma()), + .respect_ctm = blur->respectCTM(), }; break; } diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 918d145a75dfd..51f3cf2390b87 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -379,6 +379,14 @@ std::optional GaussianBlurFilterContents::GetFilterCoverage( return expanded_source_coverage.TransformBounds(entity.GetTransform()); } +namespace { +Vector2 ExtractScale(const Matrix& matrix) { + Vector2 entity_scale_x = matrix * Vector2(1.0, 0.0); + Vector2 entity_scale_y = matrix * Vector2(0.0, 1.0); + return Vector2(entity_scale_x.GetLength(), entity_scale_y.GetLength()); +} +} // namespace + std::optional GaussianBlurFilterContents::RenderFilter( const FilterInput::Vector& inputs, const ContentContext& renderer, @@ -390,7 +398,14 @@ std::optional GaussianBlurFilterContents::RenderFilter( return std::nullopt; } + const Vector2 source_space_scalar = + ExtractScale(entity.GetTransform().Basis()); + Vector2 scaled_sigma = (effect_transform.Basis() * + // Uncomment the following line to break the text + // shadows, but fix GaussianVsRRectBlur tests. + Matrix::MakeScale({source_space_scalar.x, + source_space_scalar.y, 1}) * // Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_))) .Abs(); scaled_sigma.x = std::min(scaled_sigma.x, kMaxSigma); @@ -417,11 +432,13 @@ std::optional GaussianBlurFilterContents::RenderFilter( } Entity snapshot_entity = entity.Clone(); - snapshot_entity.SetTransform(Matrix()); + snapshot_entity.SetTransform( + Matrix::MakeScale({source_space_scalar.x, source_space_scalar.y, 1})); std::optional source_expanded_coverage_hint; if (expanded_coverage_hint.has_value()) { - source_expanded_coverage_hint = - expanded_coverage_hint->TransformBounds(entity.GetTransform().Invert()); + source_expanded_coverage_hint = expanded_coverage_hint->TransformBounds( + Matrix::MakeScale({source_space_scalar.x, source_space_scalar.y, 1}) * + entity.GetTransform().Invert()); } std::optional input_snapshot = @@ -436,7 +453,10 @@ std::optional GaussianBlurFilterContents::RenderFilter( Entity result = Entity::FromSnapshot(input_snapshot.value(), entity.GetBlendMode()); // No blur to render. - result.SetTransform(entity.GetTransform() * input_snapshot->transform); + result.SetTransform(entity.GetTransform() * + Matrix::MakeScale({1.f / source_space_scalar.x, + 1.f / source_space_scalar.y, 1}) * + input_snapshot->transform); return result; } @@ -562,12 +582,16 @@ std::optional GaussianBlurFilterContents::RenderFilter( MinMagFilter::kLinear, SamplerAddressMode::kClampToEdge); Entity blur_output_entity = Entity::FromSnapshot( - Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(), - .transform = entity.GetTransform() * input_snapshot->transform * - padding_snapshot_adjustment * - Matrix::MakeScale(1 / effective_scalar), - .sampler_descriptor = sampler_desc, - .opacity = input_snapshot->opacity}, + Snapshot{ + .texture = pass3_out.value().GetRenderTargetTexture(), + .transform = entity.GetTransform() * // + Matrix::MakeScale({1.f / source_space_scalar.x, + 1.f / source_space_scalar.y, 1}) * // + input_snapshot->transform * // + padding_snapshot_adjustment * // + Matrix::MakeScale(1 / effective_scalar), + .sampler_descriptor = sampler_desc, + .opacity = input_snapshot->opacity}, entity.GetBlendMode()); return ApplyBlurStyle(mask_blur_style_, entity, inputs[0], 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 cf4b955f1311a..93468e037a7ec 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc @@ -401,7 +401,7 @@ TEST_P(GaussianBlurFilterContentsTest, if (result_coverage.has_value() && contents_coverage.has_value()) { EXPECT_TRUE(RectNear(result_coverage.value(), contents_coverage.value())); EXPECT_TRUE(RectNear(contents_coverage.value(), - Rect::MakeXYWH(98.f, 78.f, 204.0f, 204.f))); + Rect::MakeXYWH(94.f, 74.f, 212.0f, 212.f))); } } } @@ -438,7 +438,7 @@ TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithEffectTransform) { if (result_coverage.has_value() && contents_coverage.has_value()) { EXPECT_TRUE(RectNear(result_coverage.value(), contents_coverage.value())); EXPECT_TRUE(RectNear(contents_coverage.value(), - Rect::MakeXYWH(49.f, 39.f, 102.f, 102.f))); + Rect::MakeXYWH(94.f, 74.f, 212.f, 212.f))); } } }