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

Commit 941f268

Browse files
authored
[Impeller] made sure to scale the blur radius by the effect transform (#49645)
fixes flutter/flutter#141204 ## screenshot after pr <img width="1023" alt="Screenshot 2024-01-09 at 2 31 10 PM" src="https://github.com/flutter/engine/assets/30870216/46fc2a63-67dc-4723-b394-2b2a6958f7ba"> ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I signed the [CLA]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 80a984a commit 941f268

File tree

3 files changed

+135
-57
lines changed

3 files changed

+135
-57
lines changed

impeller/entity/contents/filters/gaussian_blur_filter_contents.cc

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,14 @@ std::optional<Rect> GaussianBlurFilterContents::GetFilterCoverage(
235235
return {};
236236
}
237237

238-
Vector2 scaled_sigma = {ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)};
239-
Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x),
240-
CalculateBlurRadius(scaled_sigma.y)};
241-
Vector3 blur_radii =
242-
(inputs[0]->GetTransform(entity).Basis() * effect_transform.Basis() *
243-
Vector3{blur_radius.x, blur_radius.y, 0.0})
244-
.Abs();
245-
return input_coverage.value().Expand(Point(blur_radii.x, blur_radii.y));
238+
Vector2 scaled_sigma = (effect_transform.Basis() *
239+
Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)))
240+
.Abs();
241+
Vector2 blur_radius = Vector2(CalculateBlurRadius(scaled_sigma.x),
242+
CalculateBlurRadius(scaled_sigma.y));
243+
Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
244+
Vector2 local_padding = (entity.GetTransform().Basis() * padding).Abs();
245+
return input_coverage.value().Expand(Point(local_padding.x, local_padding.y));
246246
}
247247

248248
std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
@@ -256,13 +256,13 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
256256
return std::nullopt;
257257
}
258258

259-
Vector2 scaled_sigma = {ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)};
260-
Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x),
261-
CalculateBlurRadius(scaled_sigma.y)};
259+
Vector2 scaled_sigma = (effect_transform.Basis() *
260+
Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)))
261+
.Abs();
262+
Vector2 blur_radius = Vector2(CalculateBlurRadius(scaled_sigma.x),
263+
CalculateBlurRadius(scaled_sigma.y));
262264
Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
263-
Vector2 local_padding =
264-
(entity.GetTransform().Basis() * effect_transform.Basis() * padding)
265-
.Abs();
265+
Vector2 local_padding = (entity.GetTransform().Basis() * padding).Abs();
266266

267267
// Apply as much of the desired padding as possible from the source. This may
268268
// be ignored so must be accounted for in the downsample pass by adding a
@@ -437,7 +437,8 @@ KernelPipeline::FragmentShader::KernelSamples GenerateBlurInfo(
437437
KernelPipeline::FragmentShader::KernelSamples result;
438438
result.sample_count =
439439
((2 * parameters.blur_radius) / parameters.step_size) + 1;
440-
FML_CHECK(result.sample_count < 24);
440+
// 32 comes from kernel.glsl.
441+
FML_CHECK(result.sample_count < 32);
441442

442443
// Chop off the last samples if the radius >= 3 where they account for < 1.56%
443444
// of the result.

impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc

Lines changed: 118 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,43 @@ namespace {
1919
// Use newtonian method to give the closest answer to target where
2020
// f(x) is less than the target. We do this because the value is `ceil`'d to
2121
// grab fractional pixels.
22-
float LowerBoundNewtonianMethod(const std::function<float(float)>& func,
23-
float target,
24-
float guess,
25-
float tolerance) {
26-
const float delta = 1e-6;
27-
float x = guess;
28-
float fx;
22+
fml::StatusOr<float> LowerBoundNewtonianMethod(
23+
const std::function<float(float)>& func,
24+
float target,
25+
float guess,
26+
float tolerance) {
27+
const double delta = 1e-6;
28+
double x = guess;
29+
double fx;
30+
static const int kMaxIterations = 1000;
31+
int count = 0;
2932

3033
do {
3134
fx = func(x) - target;
32-
float derivative = (func(x + delta) - func(x)) / delta;
35+
double derivative = (func(x + delta) - func(x)) / delta;
3336
x = x - fx / derivative;
34-
37+
if (++count > kMaxIterations) {
38+
return fml::Status(fml::StatusCode::kDeadlineExceeded,
39+
"Did not converge on answer.");
40+
}
3541
} while (std::abs(fx) > tolerance ||
3642
fx < 0.0); // fx < 0.0 makes this lower bound.
3743

3844
return x;
3945
}
4046

41-
Scalar CalculateSigmaForBlurRadius(Scalar radius) {
42-
auto f = [](Scalar x) -> Scalar {
43-
return GaussianBlurFilterContents::CalculateBlurRadius(
44-
GaussianBlurFilterContents::ScaleSigma(x));
47+
fml::StatusOr<Scalar> CalculateSigmaForBlurRadius(
48+
Scalar radius,
49+
const Matrix& effect_transform) {
50+
auto f = [effect_transform](Scalar x) -> Scalar {
51+
Vector2 scaled_sigma = (effect_transform.Basis() *
52+
Vector2(GaussianBlurFilterContents::ScaleSigma(x),
53+
GaussianBlurFilterContents::ScaleSigma(x)))
54+
.Abs();
55+
Vector2 blur_radius = Vector2(
56+
GaussianBlurFilterContents::CalculateBlurRadius(scaled_sigma.x),
57+
GaussianBlurFilterContents::CalculateBlurRadius(scaled_sigma.y));
58+
return std::max(blur_radius.x, blur_radius.y);
4559
};
4660
// The newtonian method is used here since inverting the function is
4761
// non-trivial because of conditional logic and would be fragile to changes.
@@ -90,9 +104,11 @@ TEST(GaussianBlurFilterContentsTest, CoverageSimple) {
90104
}
91105

92106
TEST(GaussianBlurFilterContentsTest, CoverageWithSigma) {
93-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
94-
GaussianBlurFilterContents contents(/*sigma_x=*/sigma_radius_1,
95-
/*sigma_y=*/sigma_radius_1,
107+
fml::StatusOr<Scalar> sigma_radius_1 =
108+
CalculateSigmaForBlurRadius(1.0, Matrix());
109+
ASSERT_TRUE(sigma_radius_1.ok());
110+
GaussianBlurFilterContents contents(/*sigma_x=*/sigma_radius_1.value(),
111+
/*sigma_y=*/sigma_radius_1.value(),
96112
Entity::TileMode::kDecal);
97113
FilterInput::Vector inputs = {
98114
FilterInput::Make(Rect::MakeLTRB(100, 100, 200, 200))};
@@ -111,9 +127,11 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithTexture) {
111127
.format = PixelFormat::kB8G8R8A8UNormInt,
112128
.size = ISize(100, 100),
113129
};
114-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
115-
GaussianBlurFilterContents contents(/*sigma_X=*/sigma_radius_1,
116-
/*sigma_y=*/sigma_radius_1,
130+
fml::StatusOr<Scalar> sigma_radius_1 =
131+
CalculateSigmaForBlurRadius(1.0, Matrix());
132+
ASSERT_TRUE(sigma_radius_1.ok());
133+
GaussianBlurFilterContents contents(/*sigma_X=*/sigma_radius_1.value(),
134+
/*sigma_y=*/sigma_radius_1.value(),
117135
Entity::TileMode::kDecal);
118136
std::shared_ptr<Texture> texture =
119137
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
@@ -135,33 +153,42 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithEffectTransform) {
135153
.format = PixelFormat::kB8G8R8A8UNormInt,
136154
.size = ISize(100, 100),
137155
};
138-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
139-
GaussianBlurFilterContents contents(/*sigma_x=*/sigma_radius_1,
140-
/*sigma_y=*/sigma_radius_1,
156+
Matrix effect_transform = Matrix::MakeScale({2.0, 2.0, 1.0});
157+
fml::StatusOr<Scalar> sigma_radius_1 =
158+
CalculateSigmaForBlurRadius(1.0, effect_transform);
159+
ASSERT_TRUE(sigma_radius_1.ok());
160+
GaussianBlurFilterContents contents(/*sigma_x=*/sigma_radius_1.value(),
161+
/*sigma_y=*/sigma_radius_1.value(),
141162
Entity::TileMode::kDecal);
142163
std::shared_ptr<Texture> texture =
143164
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
144165
desc);
145166
FilterInput::Vector inputs = {FilterInput::Make(texture)};
146167
Entity entity;
147168
entity.SetTransform(Matrix::MakeTranslation({100, 100, 0}));
148-
std::optional<Rect> coverage = contents.GetFilterCoverage(
149-
inputs, entity, /*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}));
169+
std::optional<Rect> coverage =
170+
contents.GetFilterCoverage(inputs, entity, effect_transform);
150171
EXPECT_TRUE(coverage.has_value());
151172
if (coverage.has_value()) {
152173
EXPECT_RECT_NEAR(coverage.value(),
153-
Rect::MakeLTRB(100 - 2, 100 - 2, 200 + 2, 200 + 2));
174+
Rect::MakeLTRB(100 - 1, 100 - 1, 200 + 1, 200 + 1));
154175
}
155176
}
156177

157178
TEST(GaussianBlurFilterContentsTest, FilterSourceCoverage) {
158-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
179+
fml::StatusOr<Scalar> sigma_radius_1 =
180+
CalculateSigmaForBlurRadius(1.0, Matrix());
181+
ASSERT_TRUE(sigma_radius_1.ok());
159182
auto contents = std::make_unique<GaussianBlurFilterContents>(
160-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
183+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
161184
std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
162185
/*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}),
163186
/*output_limit=*/Rect::MakeLTRB(100, 100, 200, 200));
164-
ASSERT_EQ(coverage, Rect::MakeLTRB(100 - 2, 100 - 2, 200 + 2, 200 + 2));
187+
EXPECT_TRUE(coverage.has_value());
188+
if (coverage.has_value()) {
189+
EXPECT_RECT_NEAR(coverage.value(),
190+
Rect::MakeLTRB(100 - 2, 100 - 2, 200 + 2, 200 + 2));
191+
}
165192
}
166193

167194
TEST(GaussianBlurFilterContentsTest, CalculateSigmaValues) {
@@ -180,9 +207,11 @@ TEST_P(GaussianBlurFilterContentsTest, RenderCoverageMatchesGetCoverage) {
180207
.size = ISize(100, 100),
181208
};
182209
std::shared_ptr<Texture> texture = MakeTexture(desc);
183-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
210+
fml::StatusOr<Scalar> sigma_radius_1 =
211+
CalculateSigmaForBlurRadius(1.0, Matrix());
212+
ASSERT_TRUE(sigma_radius_1.ok());
184213
auto contents = std::make_unique<GaussianBlurFilterContents>(
185-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
214+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
186215
contents->SetInputs({FilterInput::Make(texture)});
187216
std::shared_ptr<ContentContext> renderer = GetContentContext();
188217

@@ -213,9 +242,11 @@ TEST_P(GaussianBlurFilterContentsTest,
213242
.size = ISize(100, 100),
214243
};
215244
std::shared_ptr<Texture> texture = MakeTexture(desc);
216-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
245+
fml::StatusOr<Scalar> sigma_radius_1 =
246+
CalculateSigmaForBlurRadius(1.0, Matrix());
247+
ASSERT_TRUE(sigma_radius_1.ok());
217248
auto contents = std::make_unique<GaussianBlurFilterContents>(
218-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
249+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
219250
contents->SetInputs({FilterInput::Make(texture)});
220251
std::shared_ptr<ContentContext> renderer = GetContentContext();
221252

@@ -248,9 +279,10 @@ TEST_P(GaussianBlurFilterContentsTest,
248279
.size = ISize(400, 300),
249280
};
250281
std::shared_ptr<Texture> texture = MakeTexture(desc);
251-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
282+
fml::StatusOr<Scalar> sigma_radius_1 =
283+
CalculateSigmaForBlurRadius(1.0, Matrix());
252284
auto contents = std::make_unique<GaussianBlurFilterContents>(
253-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
285+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
254286
contents->SetInputs({FilterInput::Make(texture)});
255287
std::shared_ptr<ContentContext> renderer = GetContentContext();
256288

@@ -308,9 +340,10 @@ TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithDestinationRect) {
308340
texture_contents->SetDestinationRect(Rect::MakeXYWH(
309341
50, 40, texture->GetSize().width, texture->GetSize().height));
310342

311-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
343+
fml::StatusOr<Scalar> sigma_radius_1 =
344+
CalculateSigmaForBlurRadius(1.0, Matrix());
312345
auto contents = std::make_unique<GaussianBlurFilterContents>(
313-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
346+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
314347
contents->SetInputs({FilterInput::Make(texture_contents)});
315348
std::shared_ptr<ContentContext> renderer = GetContentContext();
316349

@@ -347,9 +380,10 @@ TEST_P(GaussianBlurFilterContentsTest,
347380
texture_contents->SetDestinationRect(Rect::MakeXYWH(
348381
50, 40, texture->GetSize().width, texture->GetSize().height));
349382

350-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
383+
fml::StatusOr<Scalar> sigma_radius_1 =
384+
CalculateSigmaForBlurRadius(1.0, Matrix());
351385
auto contents = std::make_unique<GaussianBlurFilterContents>(
352-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
386+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
353387
contents->SetInputs({FilterInput::Make(texture_contents)});
354388
std::shared_ptr<ContentContext> renderer = GetContentContext();
355389

@@ -372,13 +406,56 @@ TEST_P(GaussianBlurFilterContentsTest,
372406
}
373407
}
374408

409+
TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithEffectTransform) {
410+
TextureDescriptor desc = {
411+
.storage_mode = StorageMode::kDevicePrivate,
412+
.format = PixelFormat::kB8G8R8A8UNormInt,
413+
.size = ISize(100, 100),
414+
};
415+
416+
Matrix effect_transform = Matrix::MakeScale({2.0, 2.0, 1.0});
417+
std::shared_ptr<Texture> texture = MakeTexture(desc);
418+
auto texture_contents = std::make_shared<TextureContents>();
419+
texture_contents->SetSourceRect(Rect::MakeSize(texture->GetSize()));
420+
texture_contents->SetTexture(texture);
421+
texture_contents->SetDestinationRect(Rect::MakeXYWH(
422+
50, 40, texture->GetSize().width, texture->GetSize().height));
423+
424+
fml::StatusOr<Scalar> sigma_radius_1 =
425+
CalculateSigmaForBlurRadius(1.0, effect_transform);
426+
ASSERT_TRUE(sigma_radius_1.ok());
427+
auto contents = std::make_unique<GaussianBlurFilterContents>(
428+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
429+
contents->SetInputs({FilterInput::Make(texture_contents)});
430+
contents->SetEffectTransform(effect_transform);
431+
std::shared_ptr<ContentContext> renderer = GetContentContext();
432+
433+
Entity entity;
434+
std::optional<Entity> result =
435+
contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
436+
EXPECT_TRUE(result.has_value());
437+
if (result.has_value()) {
438+
EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSourceOver);
439+
std::optional<Rect> result_coverage = result.value().GetCoverage();
440+
std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
441+
EXPECT_TRUE(result_coverage.has_value());
442+
EXPECT_TRUE(contents_coverage.has_value());
443+
if (result_coverage.has_value() && contents_coverage.has_value()) {
444+
EXPECT_TRUE(RectNear(result_coverage.value(), contents_coverage.value()));
445+
EXPECT_TRUE(RectNear(contents_coverage.value(),
446+
Rect::MakeXYWH(49.f, 39.f, 102.f, 102.f)));
447+
}
448+
}
449+
}
450+
375451
TEST(GaussianBlurFilterContentsTest, CalculateSigmaForBlurRadius) {
376452
Scalar sigma = 1.0;
377453
Scalar radius = GaussianBlurFilterContents::CalculateBlurRadius(
378454
GaussianBlurFilterContents::ScaleSigma(sigma));
379-
Scalar derived_sigma = CalculateSigmaForBlurRadius(radius);
380-
381-
EXPECT_NEAR(sigma, derived_sigma, 0.01f);
455+
fml::StatusOr<Scalar> derived_sigma =
456+
CalculateSigmaForBlurRadius(radius, Matrix());
457+
ASSERT_TRUE(derived_sigma.ok());
458+
EXPECT_NEAR(sigma, derived_sigma.value(), 0.01f);
382459
}
383460

384461
TEST(GaussianBlurFilterContentsTest, Coefficients) {

impeller/entity/shaders/gaussian_blur/kernel.glsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ struct KernelSample {
1616

1717
uniform KernelSamples {
1818
int sample_count;
19-
KernelSample samples[24];
19+
KernelSample samples[32];
2020
}
2121
blur_info;
2222

0 commit comments

Comments
 (0)