@@ -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,39 @@ 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+ ASSERT_RECT_NEAR (coverage.value (),
188+ Rect::MakeLTRB (100 - 2 , 100 - 2 , 200 + 2 , 200 + 2 ));
165189}
166190
167191TEST (GaussianBlurFilterContentsTest, CalculateSigmaValues) {
@@ -180,9 +204,11 @@ TEST_P(GaussianBlurFilterContentsTest, RenderCoverageMatchesGetCoverage) {
180204 .size = ISize (100 , 100 ),
181205 };
182206 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 ());
184210 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 );
186212 contents->SetInputs ({FilterInput::Make (texture)});
187213 std::shared_ptr<ContentContext> renderer = GetContentContext ();
188214
@@ -213,9 +239,11 @@ TEST_P(GaussianBlurFilterContentsTest,
213239 .size = ISize (100 , 100 ),
214240 };
215241 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 ());
217245 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 );
219247 contents->SetInputs ({FilterInput::Make (texture)});
220248 std::shared_ptr<ContentContext> renderer = GetContentContext ();
221249
@@ -248,9 +276,10 @@ TEST_P(GaussianBlurFilterContentsTest,
248276 .size = ISize (400 , 300 ),
249277 };
250278 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 ());
252281 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 );
254283 contents->SetInputs ({FilterInput::Make (texture)});
255284 std::shared_ptr<ContentContext> renderer = GetContentContext ();
256285
@@ -308,9 +337,10 @@ TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithDestinationRect) {
308337 texture_contents->SetDestinationRect (Rect::MakeXYWH (
309338 50 , 40 , texture->GetSize ().width , texture->GetSize ().height ));
310339
311- Scalar sigma_radius_1 = CalculateSigmaForBlurRadius (1.0 );
340+ fml::StatusOr<Scalar> sigma_radius_1 =
341+ CalculateSigmaForBlurRadius (1.0 , Matrix ());
312342 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 );
314344 contents->SetInputs ({FilterInput::Make (texture_contents)});
315345 std::shared_ptr<ContentContext> renderer = GetContentContext ();
316346
@@ -347,9 +377,10 @@ TEST_P(GaussianBlurFilterContentsTest,
347377 texture_contents->SetDestinationRect (Rect::MakeXYWH (
348378 50 , 40 , texture->GetSize ().width , texture->GetSize ().height ));
349379
350- Scalar sigma_radius_1 = CalculateSigmaForBlurRadius (1.0 );
380+ fml::StatusOr<Scalar> sigma_radius_1 =
381+ CalculateSigmaForBlurRadius (1.0 , Matrix ());
351382 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 );
353384 contents->SetInputs ({FilterInput::Make (texture_contents)});
354385 std::shared_ptr<ContentContext> renderer = GetContentContext ();
355386
@@ -372,13 +403,55 @@ TEST_P(GaussianBlurFilterContentsTest,
372403 }
373404}
374405
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+ auto contents = std::make_unique<GaussianBlurFilterContents>(
424+ sigma_radius_1.value (), sigma_radius_1.value (), Entity::TileMode::kDecal );
425+ contents->SetInputs ({FilterInput::Make (texture_contents)});
426+ contents->SetEffectTransform (effect_transform);
427+ std::shared_ptr<ContentContext> renderer = GetContentContext ();
428+
429+ Entity entity;
430+ std::optional<Entity> result =
431+ contents->GetEntity (*renderer, entity, /* coverage_hint=*/ {});
432+ EXPECT_TRUE (result.has_value ());
433+ if (result.has_value ()) {
434+ EXPECT_EQ (result.value ().GetBlendMode (), BlendMode::kSourceOver );
435+ std::optional<Rect> result_coverage = result.value ().GetCoverage ();
436+ std::optional<Rect> contents_coverage = contents->GetCoverage (entity);
437+ EXPECT_TRUE (result_coverage.has_value ());
438+ EXPECT_TRUE (contents_coverage.has_value ());
439+ if (result_coverage.has_value () && contents_coverage.has_value ()) {
440+ EXPECT_TRUE (RectNear (result_coverage.value (), contents_coverage.value ()));
441+ EXPECT_TRUE (RectNear (contents_coverage.value (),
442+ Rect::MakeXYWH (49 .f , 39 .f , 102 .f , 102 .f )));
443+ }
444+ }
445+ }
446+
375447TEST (GaussianBlurFilterContentsTest, CalculateSigmaForBlurRadius) {
376448 Scalar sigma = 1.0 ;
377449 Scalar radius = GaussianBlurFilterContents::CalculateBlurRadius (
378450 GaussianBlurFilterContents::ScaleSigma (sigma));
379- Scalar derived_sigma = CalculateSigmaForBlurRadius (radius);
380-
381- EXPECT_NEAR (sigma, derived_sigma, 0 .01f );
451+ fml::StatusOr<Scalar> derived_sigma =
452+ CalculateSigmaForBlurRadius (radius, Matrix ());
453+ ASSERT_TRUE (derived_sigma.ok ());
454+ EXPECT_NEAR (sigma, derived_sigma.value (), 0 .01f );
382455}
383456
384457TEST (GaussianBlurFilterContentsTest, Coefficients) {
0 commit comments