diff --git a/impeller/aiks/aiks_blur_unittests.cc b/impeller/aiks/aiks_blur_unittests.cc index e3253b39488b9..df9a937cc18a6 100644 --- a/impeller/aiks/aiks_blur_unittests.cc +++ b/impeller/aiks/aiks_blur_unittests.cc @@ -665,6 +665,45 @@ TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) { 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 diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 0dfbdf2211db4..918d145a75dfd 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -263,7 +263,8 @@ Entity ApplyBlurStyle(FilterContents::BlurStyle blur_style, Entity::FromSnapshot(input_snapshot, entity.GetBlendMode()); Entity result; Matrix blurred_transform = blur_entity.GetTransform(); - Matrix snapshot_transform = snapshot_entity.GetTransform(); + Matrix snapshot_transform = + entity.GetTransform() * snapshot_entity.GetTransform(); result.SetContents(Contents::MakeAnonymous( fml::MakeCopyable([blur_entity = blur_entity.Clone(), blurred_transform, snapshot_transform, @@ -359,15 +360,14 @@ std::optional GaussianBlurFilterContents::GetFilterCoverage( return {}; } - std::optional input_coverage = inputs[0]->GetCoverage(entity); - if (!input_coverage.has_value()) { + Entity snapshot_entity = entity.Clone(); + snapshot_entity.SetTransform(Matrix()); + std::optional source_coverage = inputs[0]->GetCoverage(snapshot_entity); + if (!source_coverage.has_value()) { return {}; } - Vector2 entity_scale_x = entity.GetTransform().Basis() * Vector2(1.0, 0.0); - Vector2 entity_scale_y = entity.GetTransform().Basis() * Vector2(0.0, 1.0); - Vector2 scaled_sigma = (Matrix::MakeScale({entity_scale_x.GetLength(), - entity_scale_y.GetLength(), 1.0}) * + Vector2 scaled_sigma = (effect_transform.Basis() * Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_))) .Abs(); scaled_sigma.x = std::min(scaled_sigma.x, kMaxSigma); @@ -375,8 +375,8 @@ std::optional GaussianBlurFilterContents::GetFilterCoverage( Vector2 blur_radius = Vector2(CalculateBlurRadius(scaled_sigma.x), CalculateBlurRadius(scaled_sigma.y)); Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y)); - Vector2 local_padding = (entity.GetTransform().Basis() * padding).Abs(); - return input_coverage.value().Expand(Point(local_padding.x, local_padding.y)); + Rect expanded_source_coverage = source_coverage->Expand(padding); + return expanded_source_coverage.TransformBounds(entity.GetTransform()); } std::optional GaussianBlurFilterContents::RenderFilter( @@ -390,11 +390,7 @@ std::optional GaussianBlurFilterContents::RenderFilter( return std::nullopt; } - Vector2 entity_scale_x = entity.GetTransform().Basis() * Vector2(1.0, 0.0); - Vector2 entity_scale_y = entity.GetTransform().Basis() * Vector2(0.0, 1.0); Vector2 scaled_sigma = (effect_transform.Basis() * - Matrix::MakeScale({entity_scale_x.GetLength(), - entity_scale_y.GetLength(), 1.0}) * Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_))) .Abs(); scaled_sigma.x = std::min(scaled_sigma.x, kMaxSigma); @@ -420,17 +416,28 @@ std::optional GaussianBlurFilterContents::RenderFilter( mip_count = 1; } + Entity snapshot_entity = entity.Clone(); + snapshot_entity.SetTransform(Matrix()); + std::optional source_expanded_coverage_hint; + if (expanded_coverage_hint.has_value()) { + source_expanded_coverage_hint = + expanded_coverage_hint->TransformBounds(entity.GetTransform().Invert()); + } + std::optional input_snapshot = - inputs[0]->GetSnapshot("GaussianBlur", renderer, entity, - /*coverage_limit=*/expanded_coverage_hint, + inputs[0]->GetSnapshot("GaussianBlur", renderer, snapshot_entity, + /*coverage_limit=*/source_expanded_coverage_hint, /*mip_count=*/mip_count); if (!input_snapshot.has_value()) { return std::nullopt; } if (scaled_sigma.x < kEhCloseEnough && scaled_sigma.y < kEhCloseEnough) { - return Entity::FromSnapshot(input_snapshot.value(), - entity.GetBlendMode()); // No blur to render. + Entity result = + Entity::FromSnapshot(input_snapshot.value(), + entity.GetBlendMode()); // No blur to render. + result.SetTransform(entity.GetTransform() * input_snapshot->transform); + return result; } // In order to avoid shimmering in downsampling step, we should have mips. @@ -462,7 +469,7 @@ std::optional GaussianBlurFilterContents::RenderFilter( Vector2 effective_scalar = Vector2(subpass_size) / source_rect_padded.GetSize(); - Quad uvs = CalculateUVs(inputs[0], entity, source_rect_padded, + Quad uvs = CalculateUVs(inputs[0], snapshot_entity, source_rect_padded, input_snapshot->texture->GetSize()); std::shared_ptr command_buffer = @@ -484,18 +491,14 @@ std::optional GaussianBlurFilterContents::RenderFilter( std::optional input_snapshot_coverage = input_snapshot->GetCoverage(); Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)}; - if (expanded_coverage_hint.has_value() && - input_snapshot_coverage.has_value() && - // TODO(https://github.com/flutter/flutter/issues/140890): Remove this - // condition. There is some flaw in coverage stopping us from using this - // today. I attempted to use source coordinates to calculate the uvs, - // but that didn't work either. - input_snapshot.has_value() && - input_snapshot.value().transform.IsTranslationScaleOnly()) { + FML_DCHECK(input_snapshot.value().transform.IsTranslationScaleOnly()); + if (source_expanded_coverage_hint.has_value() && + input_snapshot_coverage.has_value()) { // Only process the uvs where the blur is happening, not the whole texture. - std::optional uvs = MakeReferenceUVs(input_snapshot_coverage.value(), - expanded_coverage_hint.value()) - .Intersection(Rect::MakeSize(Size(1, 1))); + std::optional uvs = + MakeReferenceUVs(input_snapshot_coverage.value(), + source_expanded_coverage_hint.value()) + .Intersection(Rect::MakeSize(Size(1, 1))); FML_DCHECK(uvs.has_value()); if (uvs.has_value()) { blur_uvs[0] = uvs->GetLeftTop(); @@ -560,7 +563,7 @@ std::optional GaussianBlurFilterContents::RenderFilter( Entity blur_output_entity = Entity::FromSnapshot( Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(), - .transform = input_snapshot->transform * + .transform = entity.GetTransform() * input_snapshot->transform * padding_snapshot_adjustment * Matrix::MakeScale(1 / effective_scalar), .sampler_descriptor = sampler_desc, 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 dad6c5bc0ba6d..cf4b955f1311a 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc @@ -382,7 +382,8 @@ TEST_P(GaussianBlurFilterContentsTest, CalculateSigmaForBlurRadius(1.0, Matrix()); auto contents = std::make_unique( sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal, - FilterContents::BlurStyle::kNormal, /*mask_geometry=*/nullptr); + FilterContents::BlurStyle::kNormal, + /*mask_geometry=*/nullptr); contents->SetInputs({FilterInput::Make(texture_contents)}); std::shared_ptr renderer = GetContentContext(); @@ -400,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(94.f, 74.f, 212.f, 212.f))); + Rect::MakeXYWH(98.f, 78.f, 204.0f, 204.f))); } } } diff --git a/testing/impeller_golden_tests_output.txt b/testing/impeller_golden_tests_output.txt index c3c1caba8337b..eb4cf2355e680 100644 --- a/testing/impeller_golden_tests_output.txt +++ b/testing/impeller_golden_tests_output.txt @@ -617,6 +617,9 @@ impeller_Play_AiksTest_GaussianBlurRotatedAndClippedInteractive_Vulkan.png impeller_Play_AiksTest_GaussianBlurRotatedAndClipped_Metal.png impeller_Play_AiksTest_GaussianBlurRotatedAndClipped_OpenGLES.png impeller_Play_AiksTest_GaussianBlurRotatedAndClipped_Vulkan.png +impeller_Play_AiksTest_GaussianBlurRotatedNonUniform_Metal.png +impeller_Play_AiksTest_GaussianBlurRotatedNonUniform_OpenGLES.png +impeller_Play_AiksTest_GaussianBlurRotatedNonUniform_Vulkan.png impeller_Play_AiksTest_GaussianBlurScaledAndClipped_Metal.png impeller_Play_AiksTest_GaussianBlurScaledAndClipped_OpenGLES.png impeller_Play_AiksTest_GaussianBlurScaledAndClipped_Vulkan.png