Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 99fc852

Browse files
authored
[Impeller] scales blur coverage to match rendered output (#47621)
in pursuit of: flutter/flutter#131580 I'm starting to write tests for the new blur, but I wanted to make tests for the old blur to make sure I understood the coverage rules. I decided to check this in separately while I work on the other blur. Even though the goal is to delete this class eventually. Most of this work transfers over to the new blur. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 5d178b2 commit 99fc852

File tree

4 files changed

+134
-1
lines changed

4 files changed

+134
-1
lines changed

ci/licenses_golden/excluded_files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
../../../flutter/impeller/display_list/skia_conversions_unittests.cc
136136
../../../flutter/impeller/docs
137137
../../../flutter/impeller/entity/contents/checkerboard_contents_unittests.cc
138+
../../../flutter/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents_unittests.cc
138139
../../../flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc
139140
../../../flutter/impeller/entity/contents/test
140141
../../../flutter/impeller/entity/contents/vertices_contents_unittests.cc

impeller/entity/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ impeller_component("entity_unittests") {
280280

281281
sources = [
282282
"contents/checkerboard_contents_unittests.cc",
283+
"contents/filters/directional_gaussian_blur_filter_contents_unittests.cc",
283284
"contents/filters/inputs/filter_input_unittests.cc",
284285
"contents/vertices_contents_unittests.cc",
285286
"entity_pass_target_unittests.cc",

impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,9 @@ std::optional<Rect> DirectionalGaussianBlurFilterContents::GetFilterCoverage(
313313

314314
auto transform = inputs[0]->GetTransform(entity) * effect_transform.Basis();
315315
auto transformed_blur_vector =
316-
transform.TransformDirection(blur_direction_ * Radius{blur_sigma_}.radius)
316+
transform
317+
.TransformDirection(blur_direction_ *
318+
Radius{ScaleSigma(blur_sigma_)}.radius)
317319
.Abs();
318320
return coverage->Expand(transformed_blur_vector);
319321
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/testing/testing.h"
6+
#include "gmock/gmock.h"
7+
#include "impeller/entity/contents/content_context.h"
8+
#include "impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.h"
9+
#include "impeller/entity/entity_playground.h"
10+
#include "impeller/renderer/testing/mocks.h"
11+
12+
namespace impeller {
13+
namespace testing {
14+
15+
using ::testing::Return;
16+
17+
namespace {
18+
19+
Scalar CalculateSigmaForBlurRadius(Scalar blur_radius) {
20+
// See Sigma.h
21+
return (blur_radius / kKernelRadiusPerSigma) + 0.5;
22+
}
23+
} // namespace
24+
25+
class DirectionalGaussianBlurFilterContentsTest : public EntityPlayground {
26+
public:
27+
// Stubs in the minimal support to make rendering pass.
28+
void SetupMinimalMockContext() {
29+
// This mocking code was removed since it wasn't strictly needed yet. If it
30+
// is needed you can find it here:
31+
// https://gist.github.com/gaaclarke/c2f6bf5fc6ecb10678da03789abc5843.
32+
}
33+
};
34+
35+
INSTANTIATE_PLAYGROUND_SUITE(DirectionalGaussianBlurFilterContentsTest);
36+
37+
TEST_P(DirectionalGaussianBlurFilterContentsTest, CoverageWithEffectTransform) {
38+
TextureDescriptor desc = {
39+
.format = PixelFormat::kB8G8R8A8UNormInt,
40+
.size = ISize(100, 100),
41+
};
42+
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
43+
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
44+
contents->SetSigma(Sigma{sigma_radius_1});
45+
contents->SetDirection({1.0, 0.0});
46+
std::shared_ptr<Texture> texture =
47+
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
48+
desc);
49+
FilterInput::Vector inputs = {FilterInput::Make(texture)};
50+
Entity entity;
51+
entity.SetTransformation(Matrix::MakeTranslation({100, 100, 0}));
52+
std::optional<Rect> coverage = contents->GetFilterCoverage(
53+
inputs, entity, /*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}));
54+
EXPECT_TRUE(coverage.has_value());
55+
if (coverage.has_value()) {
56+
EXPECT_NEAR(coverage->GetLeft(), 100 - 2,
57+
0.5); // Higher tolerance for sigma scaling.
58+
EXPECT_NEAR(coverage->GetTop(), 100, 0.01);
59+
EXPECT_NEAR(coverage->GetRight(), 200 + 2,
60+
0.5); // Higher tolerance for sigma scaling.
61+
EXPECT_NEAR(coverage->GetBottom(), 200, 0.01);
62+
}
63+
}
64+
65+
TEST(DirectionalGaussianBlurFilterContentsTest, FilterSourceCoverage) {
66+
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
67+
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
68+
contents->SetSigma(Sigma{sigma_radius_1});
69+
contents->SetDirection({1.0, 0.0});
70+
std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
71+
/*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}),
72+
/*output_limit=*/Rect::MakeLTRB(100, 100, 200, 200));
73+
ASSERT_EQ(coverage, Rect::MakeLTRB(100 - 2, 100, 200 + 2, 200));
74+
}
75+
76+
TEST_P(DirectionalGaussianBlurFilterContentsTest, RenderNoCoverage) {
77+
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
78+
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
79+
contents->SetSigma(Sigma{sigma_radius_1});
80+
contents->SetDirection({1.0, 0.0});
81+
std::shared_ptr<ContentContext> renderer = GetContentContext();
82+
Entity entity;
83+
Rect coverage_hint = Rect::MakeLTRB(0, 0, 0, 0);
84+
std::optional<Entity> result =
85+
contents->GetEntity(*renderer, entity, coverage_hint);
86+
ASSERT_FALSE(result.has_value());
87+
}
88+
89+
TEST_P(DirectionalGaussianBlurFilterContentsTest,
90+
RenderCoverageMatchesGetCoverage) {
91+
TextureDescriptor desc = {
92+
.format = PixelFormat::kB8G8R8A8UNormInt,
93+
.size = ISize(100, 100),
94+
};
95+
std::shared_ptr<Texture> texture =
96+
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
97+
desc);
98+
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
99+
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
100+
contents->SetSigma(Sigma{sigma_radius_1});
101+
contents->SetDirection({1.0, 0.0});
102+
contents->SetInputs({FilterInput::Make(texture)});
103+
std::shared_ptr<ContentContext> renderer = GetContentContext();
104+
105+
Entity entity;
106+
std::optional<Entity> result =
107+
contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
108+
EXPECT_TRUE(result.has_value());
109+
if (result.has_value()) {
110+
EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSourceOver);
111+
std::optional<Rect> result_coverage = result.value().GetCoverage();
112+
std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
113+
EXPECT_TRUE(result_coverage.has_value());
114+
EXPECT_TRUE(contents_coverage.has_value());
115+
if (result_coverage.has_value() && contents_coverage.has_value()) {
116+
EXPECT_NEAR(result_coverage.value().GetLeft(),
117+
contents_coverage.value().GetLeft(), kEhCloseEnough);
118+
EXPECT_NEAR(result_coverage.value().GetTop(),
119+
contents_coverage.value().GetTop(), kEhCloseEnough);
120+
EXPECT_NEAR(result_coverage.value().GetRight(),
121+
contents_coverage.value().GetRight(), kEhCloseEnough);
122+
EXPECT_NEAR(result_coverage.value().GetBottom(),
123+
contents_coverage.value().GetBottom(), kEhCloseEnough);
124+
}
125+
}
126+
}
127+
128+
} // namespace testing
129+
} // namespace impeller

0 commit comments

Comments
 (0)