@@ -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
92106TEST (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
157178TEST (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
167194TEST (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+
375451TEST (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
384461TEST (GaussianBlurFilterContentsTest, Coefficients) {
0 commit comments