@@ -19,29 +19,43 @@ namespace {
19
19
// Use newtonian method to give the closest answer to target where
20
20
// f(x) is less than the target. We do this because the value is `ceil`'d to
21
21
// 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 ;
29
32
30
33
do {
31
34
fx = func (x) - target;
32
- float derivative = (func (x + delta) - func (x)) / delta;
35
+ double derivative = (func (x + delta) - func (x)) / delta;
33
36
x = x - fx / derivative;
34
-
37
+ if (++count > kMaxIterations ) {
38
+ return fml::Status (fml::StatusCode::kDeadlineExceeded ,
39
+ " Did not converge on answer." );
40
+ }
35
41
} while (std::abs (fx) > tolerance ||
36
42
fx < 0.0 ); // fx < 0.0 makes this lower bound.
37
43
38
44
return x;
39
45
}
40
46
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 );
45
59
};
46
60
// The newtonian method is used here since inverting the function is
47
61
// non-trivial because of conditional logic and would be fragile to changes.
@@ -90,9 +104,11 @@ TEST(GaussianBlurFilterContentsTest, CoverageSimple) {
90
104
}
91
105
92
106
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 (),
96
112
Entity::TileMode::kDecal );
97
113
FilterInput::Vector inputs = {
98
114
FilterInput::Make (Rect::MakeLTRB (100 , 100 , 200 , 200 ))};
@@ -111,9 +127,11 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithTexture) {
111
127
.format = PixelFormat::kB8G8R8A8UNormInt ,
112
128
.size = ISize (100 , 100 ),
113
129
};
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 (),
117
135
Entity::TileMode::kDecal );
118
136
std::shared_ptr<Texture> texture =
119
137
GetContentContext ()->GetContext ()->GetResourceAllocator ()->CreateTexture (
@@ -135,33 +153,39 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithEffectTransform) {
135
153
.format = PixelFormat::kB8G8R8A8UNormInt ,
136
154
.size = ISize (100 , 100 ),
137
155
};
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 (),
141
162
Entity::TileMode::kDecal );
142
163
std::shared_ptr<Texture> texture =
143
164
GetContentContext ()->GetContext ()->GetResourceAllocator ()->CreateTexture (
144
165
desc);
145
166
FilterInput::Vector inputs = {FilterInput::Make (texture)};
146
167
Entity entity;
147
168
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);
150
171
EXPECT_TRUE (coverage.has_value ());
151
172
if (coverage.has_value ()) {
152
173
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 ));
154
175
}
155
176
}
156
177
157
178
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 ());
159
182
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 );
161
184
std::optional<Rect> coverage = contents->GetFilterSourceCoverage (
162
185
/* effect_transform=*/ Matrix::MakeScale ({2.0 , 2.0 , 1.0 }),
163
186
/* output_limit=*/ Rect::MakeLTRB (100 , 100 , 200 , 200 ));
164
- ASSERT_EQ (coverage, Rect::MakeLTRB (100 - 2 , 100 - 2 , 200 + 2 , 200 + 2 ));
187
+ EXPECT_RECT_NEAR (coverage.value (),
188
+ Rect::MakeLTRB (100 - 2 , 100 - 2 , 200 + 2 , 200 + 2 ));
165
189
}
166
190
167
191
TEST (GaussianBlurFilterContentsTest, CalculateSigmaValues) {
@@ -180,9 +204,11 @@ TEST_P(GaussianBlurFilterContentsTest, RenderCoverageMatchesGetCoverage) {
180
204
.size = ISize (100 , 100 ),
181
205
};
182
206
std::shared_ptr<Texture> texture = MakeTexture (desc);
183
- Scalar sigma_radius_1 = CalculateSigmaForBlurRadius (1.0 );
207
+ fml::StatusOr<Scalar> sigma_radius_1 =
208
+ CalculateSigmaForBlurRadius (1.0 , Matrix ());
209
+ ASSERT_TRUE (sigma_radius_1.ok ());
184
210
auto contents = std::make_unique<GaussianBlurFilterContents>(
185
- sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal );
211
+ sigma_radius_1. value () , sigma_radius_1. value () , Entity::TileMode::kDecal );
186
212
contents->SetInputs ({FilterInput::Make (texture)});
187
213
std::shared_ptr<ContentContext> renderer = GetContentContext ();
188
214
@@ -213,9 +239,11 @@ TEST_P(GaussianBlurFilterContentsTest,
213
239
.size = ISize (100 , 100 ),
214
240
};
215
241
std::shared_ptr<Texture> texture = MakeTexture (desc);
216
- Scalar sigma_radius_1 = CalculateSigmaForBlurRadius (1.0 );
242
+ fml::StatusOr<Scalar> sigma_radius_1 =
243
+ CalculateSigmaForBlurRadius (1.0 , Matrix ());
244
+ ASSERT_TRUE (sigma_radius_1.ok ());
217
245
auto contents = std::make_unique<GaussianBlurFilterContents>(
218
- sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal );
246
+ sigma_radius_1. value () , sigma_radius_1. value () , Entity::TileMode::kDecal );
219
247
contents->SetInputs ({FilterInput::Make (texture)});
220
248
std::shared_ptr<ContentContext> renderer = GetContentContext ();
221
249
@@ -248,9 +276,10 @@ TEST_P(GaussianBlurFilterContentsTest,
248
276
.size = ISize (400 , 300 ),
249
277
};
250
278
std::shared_ptr<Texture> texture = MakeTexture (desc);
251
- Scalar sigma_radius_1 = CalculateSigmaForBlurRadius (1.0 );
279
+ fml::StatusOr<Scalar> sigma_radius_1 =
280
+ CalculateSigmaForBlurRadius (1.0 , Matrix ());
252
281
auto contents = std::make_unique<GaussianBlurFilterContents>(
253
- sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal );
282
+ sigma_radius_1. value () , sigma_radius_1. value () , Entity::TileMode::kDecal );
254
283
contents->SetInputs ({FilterInput::Make (texture)});
255
284
std::shared_ptr<ContentContext> renderer = GetContentContext ();
256
285
@@ -308,9 +337,10 @@ TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithDestinationRect) {
308
337
texture_contents->SetDestinationRect (Rect::MakeXYWH (
309
338
50 , 40 , texture->GetSize ().width , texture->GetSize ().height ));
310
339
311
- Scalar sigma_radius_1 = CalculateSigmaForBlurRadius (1.0 );
340
+ fml::StatusOr<Scalar> sigma_radius_1 =
341
+ CalculateSigmaForBlurRadius (1.0 , Matrix ());
312
342
auto contents = std::make_unique<GaussianBlurFilterContents>(
313
- sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal );
343
+ sigma_radius_1. value () , sigma_radius_1. value () , Entity::TileMode::kDecal );
314
344
contents->SetInputs ({FilterInput::Make (texture_contents)});
315
345
std::shared_ptr<ContentContext> renderer = GetContentContext ();
316
346
@@ -347,9 +377,10 @@ TEST_P(GaussianBlurFilterContentsTest,
347
377
texture_contents->SetDestinationRect (Rect::MakeXYWH (
348
378
50 , 40 , texture->GetSize ().width , texture->GetSize ().height ));
349
379
350
- Scalar sigma_radius_1 = CalculateSigmaForBlurRadius (1.0 );
380
+ fml::StatusOr<Scalar> sigma_radius_1 =
381
+ CalculateSigmaForBlurRadius (1.0 , Matrix ());
351
382
auto contents = std::make_unique<GaussianBlurFilterContents>(
352
- sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal );
383
+ sigma_radius_1. value () , sigma_radius_1. value () , Entity::TileMode::kDecal );
353
384
contents->SetInputs ({FilterInput::Make (texture_contents)});
354
385
std::shared_ptr<ContentContext> renderer = GetContentContext ();
355
386
@@ -372,13 +403,56 @@ TEST_P(GaussianBlurFilterContentsTest,
372
403
}
373
404
}
374
405
406
+ TEST_P (GaussianBlurFilterContentsTest, TextureContentsWithEffectTransform) {
407
+ TextureDescriptor desc = {
408
+ .storage_mode = StorageMode::kDevicePrivate ,
409
+ .format = PixelFormat::kB8G8R8A8UNormInt ,
410
+ .size = ISize (100 , 100 ),
411
+ };
412
+
413
+ Matrix effect_transform = Matrix::MakeScale ({2.0 , 2.0 , 1.0 });
414
+ std::shared_ptr<Texture> texture = MakeTexture (desc);
415
+ auto texture_contents = std::make_shared<TextureContents>();
416
+ texture_contents->SetSourceRect (Rect::MakeSize (texture->GetSize ()));
417
+ texture_contents->SetTexture (texture);
418
+ texture_contents->SetDestinationRect (Rect::MakeXYWH (
419
+ 50 , 40 , texture->GetSize ().width , texture->GetSize ().height ));
420
+
421
+ fml::StatusOr<Scalar> sigma_radius_1 =
422
+ CalculateSigmaForBlurRadius (1.0 , effect_transform);
423
+ ASSERT_TRUE (sigma_radius_1.ok ());
424
+ auto contents = std::make_unique<GaussianBlurFilterContents>(
425
+ sigma_radius_1.value (), sigma_radius_1.value (), Entity::TileMode::kDecal );
426
+ contents->SetInputs ({FilterInput::Make (texture_contents)});
427
+ contents->SetEffectTransform (effect_transform);
428
+ std::shared_ptr<ContentContext> renderer = GetContentContext ();
429
+
430
+ Entity entity;
431
+ std::optional<Entity> result =
432
+ contents->GetEntity (*renderer, entity, /* coverage_hint=*/ {});
433
+ EXPECT_TRUE (result.has_value ());
434
+ if (result.has_value ()) {
435
+ EXPECT_EQ (result.value ().GetBlendMode (), BlendMode::kSourceOver );
436
+ std::optional<Rect> result_coverage = result.value ().GetCoverage ();
437
+ std::optional<Rect> contents_coverage = contents->GetCoverage (entity);
438
+ EXPECT_TRUE (result_coverage.has_value ());
439
+ EXPECT_TRUE (contents_coverage.has_value ());
440
+ if (result_coverage.has_value () && contents_coverage.has_value ()) {
441
+ EXPECT_TRUE (RectNear (result_coverage.value (), contents_coverage.value ()));
442
+ EXPECT_TRUE (RectNear (contents_coverage.value (),
443
+ Rect::MakeXYWH (49 .f , 39 .f , 102 .f , 102 .f )));
444
+ }
445
+ }
446
+ }
447
+
375
448
TEST (GaussianBlurFilterContentsTest, CalculateSigmaForBlurRadius) {
376
449
Scalar sigma = 1.0 ;
377
450
Scalar radius = GaussianBlurFilterContents::CalculateBlurRadius (
378
451
GaussianBlurFilterContents::ScaleSigma (sigma));
379
- Scalar derived_sigma = CalculateSigmaForBlurRadius (radius);
380
-
381
- EXPECT_NEAR (sigma, derived_sigma, 0 .01f );
452
+ fml::StatusOr<Scalar> derived_sigma =
453
+ CalculateSigmaForBlurRadius (radius, Matrix ());
454
+ ASSERT_TRUE (derived_sigma.ok ());
455
+ EXPECT_NEAR (sigma, derived_sigma.value (), 0 .01f );
382
456
}
383
457
384
458
TEST (GaussianBlurFilterContentsTest, Coefficients) {
0 commit comments