Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
gaussian_blur_filter_contents_unittests.cc
Go to the documentation of this file.
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
6#include "fml/status_or.h"
7#include "gmock/gmock.h"
15
16#if FML_OS_MACOSX
17#define IMPELLER_RAND arc4random
18#else
19#define IMPELLER_RAND rand
20#endif
21
22namespace impeller {
23namespace testing {
24
25namespace {
26
27// Use newtonian method to give the closest answer to target where
28// f(x) is less than the target. We do this because the value is `ceil`'d to
29// grab fractional pixels.
30fml::StatusOr<float> LowerBoundNewtonianMethod(
31 const std::function<float(float)>& func,
32 float target,
33 float guess,
34 float tolerance) {
35 const double delta = 1e-6;
36 double x = guess;
37 double fx;
38 static const int kMaxIterations = 1000;
39 int count = 0;
40
41 do {
42 fx = func(x) - target;
43 double derivative = (func(x + delta) - func(x)) / delta;
44 x = x - fx / derivative;
45 if (++count > kMaxIterations) {
47 "Did not converge on answer.");
48 }
49 } while (std::abs(fx) > tolerance ||
50 fx < 0.0); // fx < 0.0 makes this lower bound.
51
52 return x;
53}
54
55Scalar GetCoefficient(const Vector4& vec) {
56 return vec.z;
57}
58
59Vector2 GetUVOffset(const Vector4& vec) {
60 return vec.xy();
61}
62
63fml::StatusOr<Scalar> CalculateSigmaForBlurRadius(
64 Scalar radius,
65 const Matrix& effect_transform) {
66 auto f = [effect_transform](Scalar x) -> Scalar {
67 Vector2 scaled_sigma = (effect_transform.Basis() *
70 .Abs();
74 return std::max(blur_radius.x, blur_radius.y);
75 };
76 // The newtonian method is used here since inverting the function is
77 // non-trivial because of conditional logic and would be fragile to changes.
78 return LowerBoundNewtonianMethod(f, radius, 2.f, 0.001f);
79}
80
81} // namespace
82
84 public:
85 /// Create a texture that has been cleared to transparent black.
86 std::shared_ptr<Texture> MakeTexture(ISize size) {
87 std::shared_ptr<CommandBuffer> command_buffer =
88 GetContentContext()->GetContext()->CreateCommandBuffer();
89 if (!command_buffer) {
90 return nullptr;
91 }
92
93 auto render_target = GetContentContext()->MakeSubpass(
94 "Clear Subpass", size, command_buffer,
95 [](const ContentContext&, RenderPass&) { return true; });
96
98 ->GetContext()
99 ->GetCommandQueue()
100 ->Submit(/*buffers=*/{command_buffer})
101 .ok()) {
102 return nullptr;
103 }
104
105 if (render_target.ok()) {
106 return render_target.value().GetRenderTargetTexture();
107 }
108 return nullptr;
109 }
110};
112
115 /*sigma_x=*/0.0, /*sigma_y=*/0.0, Entity::TileMode::kDecal,
116 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
117 /*mask_geometry=*/nullptr);
118 EXPECT_EQ(contents.GetSigmaX(), 0.0);
119 EXPECT_EQ(contents.GetSigmaY(), 0.0);
120}
121
124 /*sigma_x=*/0.0, /*sigma_y=*/0.0, Entity::TileMode::kDecal,
125 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
126 /*mask_geometry=*/nullptr);
127 FilterInput::Vector inputs = {};
128 Entity entity;
129 std::optional<Rect> coverage =
130 contents.GetFilterCoverage(inputs, entity, /*effect_transform=*/Matrix());
131 ASSERT_FALSE(coverage.has_value());
132}
133
136 /*sigma_x=*/0.0, /*sigma_y=*/0.0, Entity::TileMode::kDecal,
137 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
138 /*mask_geometry=*/nullptr);
139 FilterInput::Vector inputs = {
140 FilterInput::Make(Rect::MakeLTRB(10, 10, 110, 110))};
141 Entity entity;
142 std::optional<Rect> coverage =
143 contents.GetFilterCoverage(inputs, entity, /*effect_transform=*/Matrix());
144
145 ASSERT_EQ(coverage, Rect::MakeLTRB(10, 10, 110, 110));
146}
147
149 fml::StatusOr<Scalar> sigma_radius_1 =
150 CalculateSigmaForBlurRadius(1.0, Matrix());
151 ASSERT_TRUE(sigma_radius_1.ok());
153 /*sigma_x=*/sigma_radius_1.value(),
154 /*sigma_y=*/sigma_radius_1.value(), Entity::TileMode::kDecal,
155 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
156 /*mask_geometry=*/nullptr);
157 FilterInput::Vector inputs = {
158 FilterInput::Make(Rect::MakeLTRB(100, 100, 200, 200))};
159 Entity entity;
160 std::optional<Rect> coverage =
161 contents.GetFilterCoverage(inputs, entity, /*effect_transform=*/Matrix());
162
163 EXPECT_TRUE(coverage.has_value());
164 if (coverage.has_value()) {
165 EXPECT_RECT_NEAR(coverage.value(), Rect::MakeLTRB(99, 99, 201, 201));
166 }
167}
168
170 fml::StatusOr<Scalar> sigma_radius_1 =
171 CalculateSigmaForBlurRadius(1.0, Matrix());
172 ASSERT_TRUE(sigma_radius_1.ok());
174 /*sigma_X=*/sigma_radius_1.value(),
175 /*sigma_y=*/sigma_radius_1.value(), Entity::TileMode::kDecal,
176 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
177 /*mask_geometry=*/nullptr);
178 std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
180 Entity entity;
181 entity.SetTransform(Matrix::MakeTranslation({100, 100, 0}));
182 std::optional<Rect> coverage =
183 contents.GetFilterCoverage(inputs, entity, /*effect_transform=*/Matrix());
184
185 EXPECT_TRUE(coverage.has_value());
186 if (coverage.has_value()) {
187 EXPECT_RECT_NEAR(coverage.value(), Rect::MakeLTRB(99, 99, 201, 201));
188 }
189}
190
191TEST_P(GaussianBlurFilterContentsTest, CoverageWithEffectTransform) {
192 Matrix effect_transform = Matrix::MakeScale({2.0, 2.0, 1.0});
193 fml::StatusOr<Scalar> sigma_radius_1 =
194 CalculateSigmaForBlurRadius(1.0, effect_transform);
195 ASSERT_TRUE(sigma_radius_1.ok());
197 /*sigma_x=*/sigma_radius_1.value(),
198 /*sigma_y=*/sigma_radius_1.value(), Entity::TileMode::kDecal,
199 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
200 /*mask_geometry=*/nullptr);
201 std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
203 Entity entity;
204 entity.SetTransform(Matrix::MakeTranslation({100, 100, 0}));
205 std::optional<Rect> coverage =
206 contents.GetFilterCoverage(inputs, entity, effect_transform);
207 EXPECT_TRUE(coverage.has_value());
208 if (coverage.has_value()) {
209 EXPECT_RECT_NEAR(coverage.value(),
210 Rect::MakeLTRB(100 - 1, 100 - 1, 200 + 1, 200 + 1));
211 }
212}
213
214TEST(GaussianBlurFilterContentsTest, FilterSourceCoverage) {
215 fml::StatusOr<Scalar> sigma_radius_1 =
216 CalculateSigmaForBlurRadius(1.0, Matrix());
217 ASSERT_TRUE(sigma_radius_1.ok());
218 auto contents = std::make_unique<GaussianBlurFilterContents>(
219 sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal,
220 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
221 /*mask_geometry=*/nullptr);
222 std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
223 /*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}),
224 /*output_limit=*/Rect::MakeLTRB(100, 100, 200, 200));
225 EXPECT_TRUE(coverage.has_value());
226 if (coverage.has_value()) {
227 EXPECT_RECT_NEAR(coverage.value(),
228 Rect::MakeLTRB(100 - 2, 100 - 2, 200 + 2, 200 + 2));
229 }
230}
231
232TEST(GaussianBlurFilterContentsTest, CalculateSigmaValues) {
237 EXPECT_EQ(GaussianBlurFilterContents::CalculateScale(16.0f), 0.25);
238 // Hang on to 1/8 as long as possible.
239 EXPECT_EQ(GaussianBlurFilterContents::CalculateScale(95.0f), 0.125);
240 EXPECT_EQ(GaussianBlurFilterContents::CalculateScale(96.0f), 0.0625);
241 // Downsample clamped to 1/16th.
242 EXPECT_EQ(GaussianBlurFilterContents::CalculateScale(1024.0f), 0.0625);
243}
244
245TEST_P(GaussianBlurFilterContentsTest, RenderCoverageMatchesGetCoverage) {
246 std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
247 fml::StatusOr<Scalar> sigma_radius_1 =
248 CalculateSigmaForBlurRadius(1.0, Matrix());
249 ASSERT_TRUE(sigma_radius_1.ok());
250 auto contents = std::make_unique<GaussianBlurFilterContents>(
251 sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal,
252 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
253 /*mask_geometry=*/nullptr);
254 contents->SetInputs({FilterInput::Make(texture)});
255 std::shared_ptr<ContentContext> renderer = GetContentContext();
256
257 Entity entity;
258 std::optional<Entity> result =
259 contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
260 EXPECT_TRUE(result.has_value());
261 if (result.has_value()) {
262 EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSrcOver);
263 std::optional<Rect> result_coverage = result.value().GetCoverage();
264 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
265 EXPECT_TRUE(result_coverage.has_value());
266 EXPECT_TRUE(contents_coverage.has_value());
267 if (result_coverage.has_value() && contents_coverage.has_value()) {
268 EXPECT_TRUE(RectNear(contents_coverage.value(),
269 Rect::MakeLTRB(-1, -1, 101, 101)));
270 EXPECT_TRUE(
271 RectNear(result_coverage.value(), Rect::MakeLTRB(-1, -1, 101, 101)));
272 }
273 }
274}
275
277 RenderCoverageMatchesGetCoverageTranslate) {
278 std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
279 fml::StatusOr<Scalar> sigma_radius_1 =
280 CalculateSigmaForBlurRadius(1.0, Matrix());
281 ASSERT_TRUE(sigma_radius_1.ok());
282 auto contents = std::make_unique<GaussianBlurFilterContents>(
283 sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal,
284 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
285 /*mask_geometry=*/nullptr);
286 contents->SetInputs({FilterInput::Make(texture)});
287 std::shared_ptr<ContentContext> renderer = GetContentContext();
288
289 Entity entity;
290 entity.SetTransform(Matrix::MakeTranslation({100, 200, 0}));
291 std::optional<Entity> result =
292 contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
293
294 EXPECT_TRUE(result.has_value());
295 if (result.has_value()) {
296 EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSrcOver);
297 std::optional<Rect> result_coverage = result.value().GetCoverage();
298 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
299 EXPECT_TRUE(result_coverage.has_value());
300 EXPECT_TRUE(contents_coverage.has_value());
301 if (result_coverage.has_value() && contents_coverage.has_value()) {
302 EXPECT_TRUE(RectNear(contents_coverage.value(),
303 Rect::MakeLTRB(99, 199, 201, 301)));
304 EXPECT_TRUE(
305 RectNear(result_coverage.value(), Rect::MakeLTRB(99, 199, 201, 301)));
306 }
307 }
308}
309
311 RenderCoverageMatchesGetCoverageRotated) {
312 std::shared_ptr<Texture> texture = MakeTexture(ISize(400, 300));
313 fml::StatusOr<Scalar> sigma_radius_1 =
314 CalculateSigmaForBlurRadius(1.0, Matrix());
315 auto contents = std::make_unique<GaussianBlurFilterContents>(
316 sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal,
317 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
318 /*mask_geometry=*/nullptr);
319 contents->SetInputs({FilterInput::Make(texture)});
320 std::shared_ptr<ContentContext> renderer = GetContentContext();
321
322 Entity entity;
323 // Rotate around the top left corner, then push it over to (100, 100).
324 entity.SetTransform(Matrix::MakeTranslation({400, 100, 0}) *
326 std::optional<Entity> result =
327 contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
328 EXPECT_TRUE(result.has_value());
329 if (result.has_value()) {
330 EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSrcOver);
331 std::optional<Rect> result_coverage = result.value().GetCoverage();
332 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
333 EXPECT_TRUE(result_coverage.has_value());
334 EXPECT_TRUE(contents_coverage.has_value());
335 if (result_coverage.has_value() && contents_coverage.has_value()) {
336 EXPECT_TRUE(RectNear(contents_coverage.value(),
337 Rect::MakeLTRB(99, 99, 401, 501)));
338 EXPECT_TRUE(
339 RectNear(result_coverage.value(), Rect::MakeLTRB(99, 99, 401, 501)));
340 }
341 }
342}
343
345 std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
346 auto filter_input = FilterInput::Make(texture);
347 Entity entity;
349 filter_input, entity, Rect::MakeSize(ISize(100, 100)), ISize(100, 100));
350 std::optional<Rect> uvs_bounds = Rect::MakePointBounds(uvs);
351 EXPECT_TRUE(uvs_bounds.has_value());
352 if (uvs_bounds.has_value()) {
353 EXPECT_TRUE(RectNear(uvs_bounds.value(), Rect::MakeXYWH(0, 0, 1, 1)));
354 }
355}
356
357TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithDestinationRect) {
358 std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
359 auto texture_contents = std::make_shared<TextureContents>();
360 texture_contents->SetSourceRect(Rect::MakeSize(texture->GetSize()));
361 texture_contents->SetTexture(texture);
362 texture_contents->SetDestinationRect(Rect::MakeXYWH(
363 50, 40, texture->GetSize().width, texture->GetSize().height));
364
365 fml::StatusOr<Scalar> sigma_radius_1 =
366 CalculateSigmaForBlurRadius(1.0, Matrix());
367 auto contents = std::make_unique<GaussianBlurFilterContents>(
368 sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal,
369 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
370 /*mask_geometry=*/nullptr);
371 contents->SetInputs({FilterInput::Make(texture_contents)});
372 std::shared_ptr<ContentContext> renderer = GetContentContext();
373
374 Entity entity;
375 std::optional<Entity> result =
376 contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
377 EXPECT_TRUE(result.has_value());
378 if (result.has_value()) {
379 EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSrcOver);
380 std::optional<Rect> result_coverage = result.value().GetCoverage();
381 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
382 EXPECT_TRUE(result_coverage.has_value());
383 EXPECT_TRUE(contents_coverage.has_value());
384 if (result_coverage.has_value() && contents_coverage.has_value()) {
385 EXPECT_TRUE(RectNear(result_coverage.value(), contents_coverage.value()));
386 EXPECT_TRUE(RectNear(result_coverage.value(),
387 Rect::MakeLTRB(49.f, 39.f, 151.f, 141.f)));
388 }
389 }
390}
391
393 TextureContentsWithDestinationRectScaled) {
394 std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
395 auto texture_contents = std::make_shared<TextureContents>();
396 texture_contents->SetSourceRect(Rect::MakeSize(texture->GetSize()));
397 texture_contents->SetTexture(texture);
398 texture_contents->SetDestinationRect(Rect::MakeXYWH(
399 50, 40, texture->GetSize().width, texture->GetSize().height));
400
401 fml::StatusOr<Scalar> sigma_radius_1 =
402 CalculateSigmaForBlurRadius(1.0, Matrix());
403 auto contents = std::make_unique<GaussianBlurFilterContents>(
404 sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal,
405 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
406 /*mask_geometry=*/nullptr);
407 contents->SetInputs({FilterInput::Make(texture_contents)});
408 std::shared_ptr<ContentContext> renderer = GetContentContext();
409
410 Entity entity;
411 entity.SetTransform(Matrix::MakeScale({2.0, 2.0, 1.0}));
412 std::optional<Entity> result =
413 contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
414 EXPECT_TRUE(result.has_value());
415 if (result.has_value()) {
416 EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSrcOver);
417 std::optional<Rect> result_coverage = result.value().GetCoverage();
418 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
419 EXPECT_TRUE(result_coverage.has_value());
420 EXPECT_TRUE(contents_coverage.has_value());
421 if (result_coverage.has_value() && contents_coverage.has_value()) {
422 EXPECT_TRUE(RectNear(result_coverage.value(), contents_coverage.value()));
423 // Scaling a blurred entity doesn't seem to scale the blur radius linearly
424 // when comparing results with rrect_blur. That's why this is not
425 // Rect::MakeXYWH(98.f, 78.f, 204.0f, 204.f).
426 EXPECT_TRUE(RectNear(contents_coverage.value(),
427 Rect::MakeXYWH(94.f, 74.f, 212.0f, 212.f)));
428 }
429 }
430}
431
432TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithEffectTransform) {
433 Matrix effect_transform = Matrix::MakeScale({2.0, 2.0, 1.0});
434 std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
435 auto texture_contents = std::make_shared<TextureContents>();
436 texture_contents->SetSourceRect(Rect::MakeSize(texture->GetSize()));
437 texture_contents->SetTexture(texture);
438 texture_contents->SetDestinationRect(Rect::MakeXYWH(
439 50, 40, texture->GetSize().width, texture->GetSize().height));
440
441 fml::StatusOr<Scalar> sigma_radius_1 =
442 CalculateSigmaForBlurRadius(1.0, effect_transform);
443 ASSERT_TRUE(sigma_radius_1.ok());
444 auto contents = std::make_unique<GaussianBlurFilterContents>(
445 sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal,
446 /*bounds=*/std::nullopt, FilterContents::BlurStyle::kNormal,
447 /*mask_geometry=*/nullptr);
448 contents->SetInputs({FilterInput::Make(texture_contents)});
449 contents->SetEffectTransform(effect_transform);
450 std::shared_ptr<ContentContext> renderer = GetContentContext();
451
452 Entity entity;
453 std::optional<Entity> result =
454 contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
455 EXPECT_TRUE(result.has_value());
456 if (result.has_value()) {
457 EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSrcOver);
458 std::optional<Rect> result_coverage = result.value().GetCoverage();
459 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
460 EXPECT_TRUE(result_coverage.has_value());
461 EXPECT_TRUE(contents_coverage.has_value());
462 if (result_coverage.has_value() && contents_coverage.has_value()) {
463 EXPECT_TRUE(RectNear(result_coverage.value(), contents_coverage.value()));
464 EXPECT_TRUE(RectNear(contents_coverage.value(),
465 Rect::MakeXYWH(49.f, 39.f, 102.f, 102.f)));
466 }
467 }
468}
469
470TEST(GaussianBlurFilterContentsTest, CalculateSigmaForBlurRadius) {
471 Scalar sigma = 1.0;
474 fml::StatusOr<Scalar> derived_sigma =
475 CalculateSigmaForBlurRadius(radius, Matrix());
476 ASSERT_TRUE(derived_sigma.ok());
477 EXPECT_NEAR(sigma, derived_sigma.value(), 0.01f);
478}
479
481 BlurParameters parameters = {.blur_uv_offset = Point(1, 0),
482 .blur_sigma = 1,
483 .blur_radius = 5,
484 .step_size = 1};
485 KernelSamples samples = GenerateBlurInfo(parameters);
486 EXPECT_EQ(samples.sample_count, 11);
487
488 // Coefficients should add up to 1.
489 Scalar tally = 0;
490 for (int i = 0; i < samples.sample_count; ++i) {
491 tally += samples.samples[i].coefficient;
492 }
493 EXPECT_FLOAT_EQ(tally, 1.0f);
494
495 // Verify the shape of the curve.
496 for (int i = 0; i < 4; ++i) {
497 EXPECT_FLOAT_EQ(samples.samples[i].coefficient,
498 samples.samples[10 - i].coefficient);
499 EXPECT_TRUE(samples.samples[i + 1].coefficient >
500 samples.samples[i].coefficient);
501 }
502}
503
504TEST(GaussianBlurFilterContentsTest, LerpHackKernelSamplesSimple) {
505 KernelSamples kernel_samples = {
506 .sample_count = 5,
507 .samples =
508 {
509 {
510 .uv_offset = Vector2(-2, 0),
511 .coefficient = 0.1f,
512 },
513 {
514 .uv_offset = Vector2(-1, 0),
515 .coefficient = 0.2f,
516 },
517 {
518 .uv_offset = Vector2(0, 0),
519 .coefficient = 0.4f,
520 },
521 {
522 .uv_offset = Vector2(1, 0),
523 .coefficient = 0.2f,
524 },
525 {
526 .uv_offset = Vector2(2, 0),
527 .coefficient = 0.1f,
528 },
529 },
530 };
531
532 GaussianBlurPipeline::FragmentShader::KernelSamples blur_info =
533 LerpHackKernelSamples(kernel_samples);
534 EXPECT_EQ(blur_info.sample_count, 3);
535
536 KernelSample* samples = kernel_samples.samples;
537
538 //////////////////////////////////////////////////////////////////////////////
539 // Check output kernel.
540
541 EXPECT_POINT_NEAR(GetUVOffset(blur_info.sample_data[0]),
542 Point(-1.3333333, 0));
543 EXPECT_FLOAT_EQ(GetCoefficient(blur_info.sample_data[0]), 0.3);
544
545 EXPECT_POINT_NEAR(GetUVOffset(blur_info.sample_data[1]), Point(0, 0));
546 EXPECT_FLOAT_EQ(GetCoefficient(blur_info.sample_data[1]), 0.4);
547
548 EXPECT_POINT_NEAR(GetUVOffset(blur_info.sample_data[2]), Point(1.333333, 0));
549 EXPECT_FLOAT_EQ(GetCoefficient(blur_info.sample_data[2]), 0.3);
550
551 //////////////////////////////////////////////////////////////////////////////
552 // Check output of fast kernel versus original kernel.
553
554 Scalar data[5] = {0.25, 0.5, 0.5, 1.0, 0.2};
555 Scalar original_output =
556 samples[0].coefficient * data[0] + samples[1].coefficient * data[1] +
557 samples[2].coefficient * data[2] + samples[3].coefficient * data[3] +
558 samples[4].coefficient * data[4];
559
560 auto lerp = [](const Point& point, Scalar left, Scalar right) {
561 Scalar int_part;
562 Scalar fract = fabsf(modf(point.x, &int_part));
563 if (point.x < 0) {
564 return left * fract + right * (1.0 - fract);
565 } else {
566 return left * (1.0 - fract) + right * fract;
567 }
568 };
569 Scalar fast_output =
570 /*1st*/ lerp(GetUVOffset(blur_info.sample_data[0]), data[0], data[1]) *
571 GetCoefficient(blur_info.sample_data[0]) +
572 /*2nd*/ data[2] * GetCoefficient(blur_info.sample_data[1]) +
573 /*3rd*/ lerp(GetUVOffset(blur_info.sample_data[2]), data[3], data[4]) *
574 GetCoefficient(blur_info.sample_data[2]);
575
576 EXPECT_NEAR(original_output, fast_output, 0.01);
577}
578
579TEST(GaussianBlurFilterContentsTest, LerpHackKernelSamplesComplex) {
580 Scalar sigma = 10.0f;
581 int32_t blur_radius = static_cast<int32_t>(
583 BlurParameters parameters = {.blur_uv_offset = Point(1, 0),
584 .blur_sigma = sigma,
585 .blur_radius = blur_radius,
586 .step_size = 1};
587 KernelSamples kernel_samples = GenerateBlurInfo(parameters);
588 EXPECT_EQ(kernel_samples.sample_count, 33);
589 GaussianBlurPipeline::FragmentShader::KernelSamples fast_kernel_samples =
590 LerpHackKernelSamples(kernel_samples);
591 EXPECT_EQ(fast_kernel_samples.sample_count, 17);
592 float data[33];
593 srand(0);
594 for (int i = 0; i < 33; i++) {
595 data[i] = 255.0 * static_cast<double>(IMPELLER_RAND()) / RAND_MAX;
596 }
597
598 auto sampler = [data](Point point) -> Scalar {
599 FML_CHECK(point.y == 0.0f);
600 FML_CHECK(point.x >= -16);
601 FML_CHECK(point.x <= 16);
602 Scalar fint_part;
603 Scalar fract = fabsf(modf(point.x, &fint_part));
604 if (fract == 0) {
605 int32_t int_part = static_cast<int32_t>(fint_part) + 16;
606 return data[int_part];
607 } else {
608 int32_t left = static_cast<int32_t>(floor(point.x)) + 16;
609 int32_t right = static_cast<int32_t>(ceil(point.x)) + 16;
610 if (point.x < 0) {
611 return fract * data[left] + (1.0 - fract) * data[right];
612 } else {
613 return (1.0 - fract) * data[left] + fract * data[right];
614 }
615 }
616 };
617
618 Scalar output = 0.0;
619 for (int i = 0; i < kernel_samples.sample_count; ++i) {
620 auto sample = kernel_samples.samples[i];
621 output += sample.coefficient * sampler(sample.uv_offset);
622 }
623
624 Scalar fast_output = 0.0;
625 for (int i = 0; i < fast_kernel_samples.sample_count; i++) {
626 fast_output += GetCoefficient(fast_kernel_samples.sample_data[i]) *
627 sampler(GetUVOffset(fast_kernel_samples.sample_data[i]));
628 }
629
630 EXPECT_NEAR(output, fast_output, 0.1);
631}
632
634 Scalar sigma = 30.5f;
635 int32_t blur_radius = static_cast<int32_t>(
637 BlurParameters parameters = {.blur_uv_offset = Point(1, 0),
638 .blur_sigma = sigma,
639 .blur_radius = blur_radius,
640 .step_size = 1};
641 KernelSamples kernel_samples = GenerateBlurInfo(parameters);
642 GaussianBlurPipeline::FragmentShader::KernelSamples frag_kernel_samples =
643 LerpHackKernelSamples(kernel_samples);
644 EXPECT_TRUE(frag_kernel_samples.sample_count <= kGaussianBlurMaxKernelSize);
645}
646
647} // namespace testing
648} // namespace impeller
double x() const
Definition geometry.h:22
const T & value() const
Definition status_or.h:77
bool ok() const
Definition status_or.h:75
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition entity.cc:62
std::shared_ptr< ContentContext > GetContentContext() const
@ kNormal
Blurred inside and outside.
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
std::vector< FilterInput::Ref > Vector
Performs a bidirectional Gaussian blur.
std::optional< Rect > GetFilterCoverage(const FilterInput::Vector &inputs, const Entity &entity, const Matrix &effect_transform) const override
Internal utility method for |GetLocalCoverage| that computes the output coverage of this filter acros...
static Quad CalculateUVs(const std::shared_ptr< FilterInput > &filter_input, const Entity &entity, const Rect &source_rect, const ISize &texture_size)
std::shared_ptr< Context > GetContext() const
Definition playground.cc:91
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition render_pass.h:30
std::shared_ptr< Texture > MakeTexture(ISize size)
Create a texture that has been cleared to transparent black.
int32_t x
uint32_t * target
#define FML_CHECK(condition)
Definition logging.h:104
Vector2 blur_radius
Blur radius in source pixels based on scaled_sigma.
Vector2 scaled_sigma
Sigma when considering an entity's scale and the effect transform.
inline ::testing::AssertionResult RectNear(impeller::Rect a, impeller::Rect b)
#define EXPECT_RECT_NEAR(a, b)
#define EXPECT_POINT_NEAR(a, b)
FlTexture * texture
TEST(FrameTimingsRecorderTest, RecordVsync)
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
TEST_P(AiksTest, DrawAtlasNoColor)
Point Vector2
Definition point.h:429
static constexpr int32_t kGaussianBlurMaxKernelSize
float Scalar
Definition scalar.h:19
KernelSamples GenerateBlurInfo(BlurParameters parameters)
ISize64 ISize
Definition size.h:162
std::array< Point, 4 > Quad
Definition point.h:430
GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(KernelSamples parameters)
#define INSTANTIATE_PLAYGROUND_SUITE(playground)
KernelSample samples[kMaxKernelSize]
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
static Matrix MakeRotationZ(Radians r)
Definition matrix.h:223
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
static constexpr std::optional< TRect > MakePointBounds(const U &value)
Definition rect.h:165
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129