diff --git a/impeller/display_list/aiks_dl_blur_unittests.cc b/impeller/display_list/aiks_dl_blur_unittests.cc index 84c9dc2e09149..4185b173086bc 100644 --- a/impeller/display_list/aiks_dl_blur_unittests.cc +++ b/impeller/display_list/aiks_dl_blur_unittests.cc @@ -60,6 +60,72 @@ TEST_P(AiksTest, SolidColorOvalsMaskBlurTinySigma) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +sk_sp DoGradientOvalStrokeMaskBlur(Vector2 content_Scale, + Scalar sigma, + DlBlurStyle style) { + DisplayListBuilder builder; + builder.Scale(content_Scale.x, content_Scale.y); + + DlPaint background_paint; + background_paint.setColor(DlColor(1, 0.1, 0.1, 0.1, DlColorSpace::kSRGB)); + builder.DrawPaint(background_paint); + + std::vector colors = {DlColor::kRed(), DlColor::kBlue()}; + std::vector stops = {0.0, 1.0}; + + DlPaint paint; + paint.setMaskFilter(DlBlurMaskFilter::Make(style, sigma)); + auto gradient = DlColorSource::MakeLinear( + {0, 0}, {200, 200}, 2, colors.data(), stops.data(), DlTileMode::kClamp); + paint.setColorSource(gradient); + paint.setColor(DlColor::kWhite()); + paint.setDrawStyle(DlDrawStyle::kStroke); + paint.setStrokeWidth(20); + + builder.Save(); + builder.Translate(100, 100); + + { + DlPaint line_paint; + line_paint.setColor(DlColor::kWhite()); + builder.DrawLine({100, 0}, {100, 60}, line_paint); + builder.DrawLine({0, 30}, {200, 30}, line_paint); + } + + SkRRect rrect = + SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 200.0f, 60.0f), 50, 100); + builder.DrawRRect(rrect, paint); + builder.Restore(); + + return builder.Build(); +} + +// https://github.com/flutter/flutter/issues/155930 +TEST_P(AiksTest, GradientOvalStrokeMaskBlur) { + ASSERT_TRUE(OpenPlaygroundHere(DoGradientOvalStrokeMaskBlur( + GetContentScale(), /*sigma=*/10, DlBlurStyle::kNormal))); +} + +TEST_P(AiksTest, GradientOvalStrokeMaskBlurSigmaZero) { + ASSERT_TRUE(OpenPlaygroundHere(DoGradientOvalStrokeMaskBlur( + GetContentScale(), /*sigma=*/0, DlBlurStyle::kNormal))); +} + +TEST_P(AiksTest, GradientOvalStrokeMaskBlurOuter) { + ASSERT_TRUE(OpenPlaygroundHere(DoGradientOvalStrokeMaskBlur( + GetContentScale(), /*sigma=*/10, DlBlurStyle::kOuter))); +} + +TEST_P(AiksTest, GradientOvalStrokeMaskBlurInner) { + ASSERT_TRUE(OpenPlaygroundHere(DoGradientOvalStrokeMaskBlur( + GetContentScale(), /*sigma=*/10, DlBlurStyle::kInner))); +} + +TEST_P(AiksTest, GradientOvalStrokeMaskBlurSolid) { + ASSERT_TRUE(OpenPlaygroundHere(DoGradientOvalStrokeMaskBlur( + GetContentScale(), /*sigma=*/10, DlBlurStyle::kSolid))); +} + TEST_P(AiksTest, SolidColorCircleMaskBlurTinySigma) { DisplayListBuilder builder; builder.Scale(GetContentScale().x, GetContentScale().y); diff --git a/impeller/display_list/aiks_dl_text_unittests.cc b/impeller/display_list/aiks_dl_text_unittests.cc index 03c79971b2b12..2119841ebd0ab 100644 --- a/impeller/display_list/aiks_dl_text_unittests.cc +++ b/impeller/display_list/aiks_dl_text_unittests.cc @@ -283,6 +283,7 @@ TEST_P(AiksTest, CanRenderEmojiTextFrame) { TEST_P(AiksTest, CanRenderEmojiTextFrameWithBlur) { DisplayListBuilder builder; + builder.Scale(GetContentScale().x, GetContentScale().y); DlPaint paint; paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1)); builder.DrawPaint(paint); diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index efdd24254f07e..d2e39ead1817b 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -74,6 +74,9 @@ Vector2 ExtractScale(const Matrix& matrix) { struct BlurInfo { /// The scalar that is used to get from source space to unrotated local space. Vector2 source_space_scalar; + /// The translation that is used to get from source space to unrotated local + /// space. + Vector2 source_space_offset; /// Sigma when considering an entity's scale and the effect transform. Vector2 scaled_sigma; /// Blur radius in source pixels based on scaled_sigma. @@ -95,6 +98,8 @@ BlurInfo CalculateBlurInfo(const Entity& entity, // applied to the result of the blur as part of the result's transform. const Vector2 source_space_scalar = ExtractScale(entity.GetTransform().Basis()); + const Vector2 source_space_offset = + Vector2(entity.GetTransform().m[12], entity.GetTransform().m[13]); Vector2 scaled_sigma = (effect_transform.Basis() * Matrix::MakeScale(source_space_scalar) * // @@ -110,6 +115,7 @@ BlurInfo CalculateBlurInfo(const Entity& entity, (Matrix::MakeScale(source_space_scalar) * padding).Abs(); return { .source_space_scalar = source_space_scalar, + .source_space_offset = source_space_offset, .scaled_sigma = scaled_sigma, .blur_radius = blur_radius, .padding = padding, @@ -545,7 +551,8 @@ Entity ApplyBlurStyle(FilterContents::BlurStyle blur_style, const Snapshot& input_snapshot, Entity blur_entity, const Geometry* geometry, - Vector2 source_space_scalar) { + Vector2 source_space_scalar, + Vector2 source_space_offset) { switch (blur_style) { case FilterContents::BlurStyle::kNormal: return blur_entity; @@ -563,9 +570,11 @@ Entity ApplyBlurStyle(FilterContents::BlurStyle blur_style, Entity::FromSnapshot(input_snapshot, entity.GetBlendMode()); Entity result; Matrix blurred_transform = blur_entity.GetTransform(); - Matrix snapshot_transform = entity.GetTransform() * // - Matrix::MakeScale(1.f / source_space_scalar) * - input_snapshot.transform; + Matrix snapshot_transform = + entity.GetTransform() * // + Matrix::MakeScale(1.f / source_space_scalar) * + Matrix::MakeTranslation(-1 * source_space_offset) * + input_snapshot.transform; result.SetContents(Contents::MakeAnonymous( fml::MakeCopyable([blur_entity = blur_entity.Clone(), blurred_transform, snapshot_transform, @@ -697,11 +706,13 @@ std::optional GaussianBlurFilterContents::RenderFilter( Entity snapshot_entity = entity.Clone(); snapshot_entity.SetTransform( + Matrix::MakeTranslation(blur_info.source_space_offset) * Matrix::MakeScale(blur_info.source_space_scalar)); std::optional source_expanded_coverage_hint; if (expanded_coverage_hint.has_value()) { source_expanded_coverage_hint = expanded_coverage_hint->TransformBounds( + Matrix::MakeTranslation(blur_info.source_space_offset) * Matrix::MakeScale(blur_info.source_space_scalar) * entity.GetTransform().Invert()); } @@ -717,9 +728,11 @@ std::optional GaussianBlurFilterContents::RenderFilter( Entity result = Entity::FromSnapshot(input_snapshot.value(), entity.GetBlendMode()); // No blur to render. - result.SetTransform(entity.GetTransform() * - Matrix::MakeScale(1.f / blur_info.source_space_scalar) * - input_snapshot->transform); + result.SetTransform( + entity.GetTransform() * + Matrix::MakeScale(1.f / blur_info.source_space_scalar) * + Matrix::MakeTranslation(-1 * blur_info.source_space_offset) * + input_snapshot->transform); return result; } @@ -827,7 +840,8 @@ std::optional GaussianBlurFilterContents::RenderFilter( .transform = entity.GetTransform() * // Matrix::MakeScale(1.f / blur_info.source_space_scalar) * // - downsample_pass_args.transform * // + Matrix::MakeTranslation(-1 * blur_info.source_space_offset) * + downsample_pass_args.transform * // Matrix::MakeScale(1 / downsample_pass_args.effective_scalar), .sampler_descriptor = sampler_desc, .opacity = input_snapshot->opacity}, @@ -835,7 +849,8 @@ std::optional GaussianBlurFilterContents::RenderFilter( return ApplyBlurStyle(mask_blur_style_, entity, inputs[0], input_snapshot.value(), std::move(blur_output_entity), - mask_geometry_, blur_info.source_space_scalar); + mask_geometry_, blur_info.source_space_scalar, + blur_info.source_space_offset); } Scalar GaussianBlurFilterContents::CalculateBlurRadius(Scalar sigma) { diff --git a/testing/impeller_golden_tests_output.txt b/testing/impeller_golden_tests_output.txt index 7d56e0f10a301..00c4ad46566cb 100644 --- a/testing/impeller_golden_tests_output.txt +++ b/testing/impeller_golden_tests_output.txt @@ -715,6 +715,21 @@ impeller_Play_AiksTest_GaussianBlurStyleSolid_Metal.png impeller_Play_AiksTest_GaussianBlurStyleSolid_OpenGLES.png impeller_Play_AiksTest_GaussianBlurStyleSolid_Vulkan.png impeller_Play_AiksTest_GaussianBlurWithoutDecalSupport_Metal.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurInner_Metal.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurInner_OpenGLES.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurInner_Vulkan.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurOuter_Metal.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurOuter_OpenGLES.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurOuter_Vulkan.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurSigmaZero_Metal.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurSigmaZero_OpenGLES.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurSigmaZero_Vulkan.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurSolid_Metal.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurSolid_OpenGLES.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlurSolid_Vulkan.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlur_Metal.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlur_OpenGLES.png +impeller_Play_AiksTest_GradientOvalStrokeMaskBlur_Vulkan.png impeller_Play_AiksTest_GradientStrokesRenderCorrectly_Metal.png impeller_Play_AiksTest_GradientStrokesRenderCorrectly_OpenGLES.png impeller_Play_AiksTest_GradientStrokesRenderCorrectly_Vulkan.png