5 #include "flutter/testing/testing.h"
6 #include "fml/status_or.h"
7 #include "gmock/gmock.h"
12 #include "impeller/renderer/testing/mocks.h"
15 #define IMPELLER_RAND arc4random
17 #define IMPELLER_RAND rand
28 fml::StatusOr<float> LowerBoundNewtonianMethod(
29 const std::function<
float(
float)>& func,
33 const double delta = 1e-6;
36 static const int kMaxIterations = 1000;
40 fx = func(x) - target;
41 double derivative = (func(x + delta) - func(x)) / delta;
42 x = x - fx / derivative;
43 if (++count > kMaxIterations) {
44 return fml::Status(fml::StatusCode::kDeadlineExceeded,
45 "Did not converge on answer.");
47 }
while (std::abs(fx) > tolerance ||
53 fml::StatusOr<Scalar> CalculateSigmaForBlurRadius(
55 const Matrix& effect_transform) {
57 Vector2 scaled_sigma = (effect_transform.Basis() *
64 return std::max(blur_radius.x, blur_radius.y);
68 return LowerBoundNewtonianMethod(f, radius, 2.f, 0.001f);
78 "Clear Subpass", size,
80 if (render_target.ok()) {
81 return render_target.value().GetRenderTargetTexture();
102 std::optional<Rect> coverage =
104 ASSERT_FALSE(coverage.has_value());
114 std::optional<Rect> coverage =
121 fml::StatusOr<Scalar> sigma_radius_1 =
122 CalculateSigmaForBlurRadius(1.0,
Matrix());
123 ASSERT_TRUE(sigma_radius_1.ok());
125 sigma_radius_1.value(),
131 std::optional<Rect> coverage =
134 EXPECT_TRUE(coverage.has_value());
135 if (coverage.has_value()) {
141 fml::StatusOr<Scalar> sigma_radius_1 =
142 CalculateSigmaForBlurRadius(1.0,
Matrix());
143 ASSERT_TRUE(sigma_radius_1.ok());
145 sigma_radius_1.value(),
148 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
152 std::optional<Rect> coverage =
155 EXPECT_TRUE(coverage.has_value());
156 if (coverage.has_value()) {
163 fml::StatusOr<Scalar> sigma_radius_1 =
164 CalculateSigmaForBlurRadius(1.0, effect_transform);
165 ASSERT_TRUE(sigma_radius_1.ok());
167 sigma_radius_1.value(),
170 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
174 std::optional<Rect> coverage =
176 EXPECT_TRUE(coverage.has_value());
177 if (coverage.has_value()) {
184 fml::StatusOr<Scalar> sigma_radius_1 =
185 CalculateSigmaForBlurRadius(1.0,
Matrix());
186 ASSERT_TRUE(sigma_radius_1.ok());
187 auto contents = std::make_unique<GaussianBlurFilterContents>(
190 std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
193 EXPECT_TRUE(coverage.has_value());
194 if (coverage.has_value()) {
214 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
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>(
222 std::shared_ptr<ContentContext> renderer = GetContentContext();
225 std::optional<Entity> result =
226 contents->GetEntity(*renderer, entity, {});
227 EXPECT_TRUE(result.has_value());
228 if (result.has_value()) {
230 std::optional<Rect> result_coverage = result.value().GetCoverage();
231 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
232 EXPECT_TRUE(result_coverage.has_value());
233 EXPECT_TRUE(contents_coverage.has_value());
234 if (result_coverage.has_value() && contents_coverage.has_value()) {
235 EXPECT_TRUE(
RectNear(contents_coverage.value(),
244 RenderCoverageMatchesGetCoverageTranslate) {
245 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
246 fml::StatusOr<Scalar> sigma_radius_1 =
247 CalculateSigmaForBlurRadius(1.0,
Matrix());
248 ASSERT_TRUE(sigma_radius_1.ok());
249 auto contents = std::make_unique<GaussianBlurFilterContents>(
253 std::shared_ptr<ContentContext> renderer = GetContentContext();
257 std::optional<Entity> result =
258 contents->GetEntity(*renderer, entity, {});
260 EXPECT_TRUE(result.has_value());
261 if (result.has_value()) {
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(),
277 RenderCoverageMatchesGetCoverageRotated) {
278 std::shared_ptr<Texture> texture = MakeTexture(
ISize(400, 300));
279 fml::StatusOr<Scalar> sigma_radius_1 =
280 CalculateSigmaForBlurRadius(1.0,
Matrix());
281 auto contents = std::make_unique<GaussianBlurFilterContents>(
285 std::shared_ptr<ContentContext> renderer = GetContentContext();
291 std::optional<Entity> result =
292 contents->GetEntity(*renderer, entity, {});
293 EXPECT_TRUE(result.has_value());
294 if (result.has_value()) {
296 std::optional<Rect> result_coverage = result.value().GetCoverage();
297 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
298 EXPECT_TRUE(result_coverage.has_value());
299 EXPECT_TRUE(contents_coverage.has_value());
300 if (result_coverage.has_value() && contents_coverage.has_value()) {
301 EXPECT_TRUE(
RectNear(contents_coverage.value(),
310 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
316 EXPECT_TRUE(uvs_bounds.has_value());
317 if (uvs_bounds.has_value()) {
323 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
324 auto texture_contents = std::make_shared<TextureContents>();
325 texture_contents->SetSourceRect(
Rect::MakeSize(texture->GetSize()));
326 texture_contents->SetTexture(texture);
328 50, 40, texture->GetSize().width, texture->GetSize().height));
330 fml::StatusOr<Scalar> sigma_radius_1 =
331 CalculateSigmaForBlurRadius(1.0,
Matrix());
332 auto contents = std::make_unique<GaussianBlurFilterContents>(
336 std::shared_ptr<ContentContext> renderer = GetContentContext();
339 std::optional<Entity> result =
340 contents->GetEntity(*renderer, entity, {});
341 EXPECT_TRUE(result.has_value());
342 if (result.has_value()) {
344 std::optional<Rect> result_coverage = result.value().GetCoverage();
345 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
346 EXPECT_TRUE(result_coverage.has_value());
347 EXPECT_TRUE(contents_coverage.has_value());
348 if (result_coverage.has_value() && contents_coverage.has_value()) {
349 EXPECT_TRUE(
RectNear(result_coverage.value(), contents_coverage.value()));
350 EXPECT_TRUE(
RectNear(result_coverage.value(),
357 TextureContentsWithDestinationRectScaled) {
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);
363 50, 40, texture->GetSize().width, texture->GetSize().height));
365 fml::StatusOr<Scalar> sigma_radius_1 =
366 CalculateSigmaForBlurRadius(1.0,
Matrix());
367 auto contents = std::make_unique<GaussianBlurFilterContents>(
371 std::shared_ptr<ContentContext> renderer = GetContentContext();
375 std::optional<Entity> result =
376 contents->GetEntity(*renderer, entity, {});
377 EXPECT_TRUE(result.has_value());
378 if (result.has_value()) {
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(contents_coverage.value(),
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);
399 50, 40, texture->GetSize().width, texture->GetSize().height));
401 fml::StatusOr<Scalar> sigma_radius_1 =
402 CalculateSigmaForBlurRadius(1.0, effect_transform);
403 ASSERT_TRUE(sigma_radius_1.ok());
404 auto contents = std::make_unique<GaussianBlurFilterContents>(
408 contents->SetEffectTransform(effect_transform);
409 std::shared_ptr<ContentContext> renderer = GetContentContext();
412 std::optional<Entity> result =
413 contents->GetEntity(*renderer, entity, {});
414 EXPECT_TRUE(result.has_value());
415 if (result.has_value()) {
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 EXPECT_TRUE(
RectNear(contents_coverage.value(),
433 fml::StatusOr<Scalar> derived_sigma =
434 CalculateSigmaForBlurRadius(radius,
Matrix());
435 ASSERT_TRUE(derived_sigma.ok());
436 EXPECT_NEAR(sigma, derived_sigma.value(), 0.01f);
444 KernelPipeline::FragmentShader::KernelSamples samples =
446 EXPECT_EQ(samples.sample_count, 9);
450 for (
int i = 0; i < samples.sample_count; ++i) {
451 tally += samples.samples[i].coefficient;
453 EXPECT_FLOAT_EQ(tally, 1.0f);
456 for (
int i = 0; i < 4; ++i) {
457 EXPECT_FLOAT_EQ(samples.samples[i].coefficient,
458 samples.samples[8 - i].coefficient);
459 EXPECT_TRUE(samples.samples[i + 1].coefficient >
460 samples.samples[i].coefficient);
465 KernelPipeline::FragmentShader::KernelSamples kernel_samples = {
492 KernelPipeline::FragmentShader::KernelSamples fast_kernel_samples =
494 EXPECT_EQ(fast_kernel_samples.sample_count, 3);
496 KernelPipeline::FragmentShader::KernelSample* samples =
497 kernel_samples.samples;
498 KernelPipeline::FragmentShader::KernelSample* fast_samples =
499 fast_kernel_samples.samples;
504 EXPECT_FLOAT_EQ(fast_samples[0].uv_offset.x, -1.3333333);
505 EXPECT_FLOAT_EQ(fast_samples[0].uv_offset.y, 0);
506 EXPECT_FLOAT_EQ(fast_samples[0].coefficient, 0.3);
507 EXPECT_FLOAT_EQ(fast_samples[1].uv_offset.x, 0);
508 EXPECT_FLOAT_EQ(fast_samples[1].uv_offset.y, 0);
509 EXPECT_FLOAT_EQ(fast_samples[1].coefficient, 0.4);
510 EXPECT_FLOAT_EQ(fast_samples[2].uv_offset.x, 1.3333333);
511 EXPECT_FLOAT_EQ(fast_samples[2].uv_offset.y, 0);
512 EXPECT_FLOAT_EQ(fast_samples[2].coefficient, 0.3);
517 Scalar data[5] = {0.25, 0.5, 0.5, 1.0, 0.2};
519 samples[0].coefficient * data[0] + samples[1].coefficient * data[1] +
520 samples[2].coefficient * data[2] + samples[3].coefficient * data[3] +
521 samples[4].coefficient * data[4];
525 Scalar fract = fabsf(modf(point.
x, &int_part));
527 return left * fract + right * (1.0 - fract);
529 return left * (1.0 - fract) + right * fract;
533 lerp(fast_samples[0].uv_offset, data[0], data[1]) *
534 fast_samples[0].coefficient +
535 data[2] * fast_samples[1].coefficient +
536 lerp(fast_samples[2].uv_offset, data[3], data[4]) *
537 fast_samples[2].coefficient;
539 EXPECT_NEAR(original_output, fast_output, 0.01);
544 int32_t blur_radius =
static_cast<int32_t
>(
548 .blur_radius = blur_radius,
550 KernelPipeline::FragmentShader::KernelSamples kernel_samples =
552 EXPECT_EQ(kernel_samples.sample_count, 33);
553 KernelPipeline::FragmentShader::KernelSamples fast_kernel_samples =
555 EXPECT_EQ(fast_kernel_samples.sample_count, 17);
558 for (
int i = 0; i < 33; i++) {
559 data[i] = 255.0 *
static_cast<double>(
IMPELLER_RAND()) / RAND_MAX;
563 FML_CHECK(point.y == 0.0f);
564 FML_CHECK(point.x >= -16);
565 FML_CHECK(point.x <= 16);
567 Scalar fract = fabsf(modf(point.x, &fint_part));
569 int32_t int_part =
static_cast<int32_t
>(fint_part) + 16;
570 return data[int_part];
572 int32_t left =
static_cast<int32_t
>(floor(point.x)) + 16;
573 int32_t right =
static_cast<int32_t
>(ceil(point.x)) + 16;
575 return fract * data[left] + (1.0 - fract) * data[right];
577 return (1.0 - fract) * data[left] + fract * data[right];
583 for (
int i = 0; i < kernel_samples.sample_count; ++i) {
584 auto sample = kernel_samples.samples[i];
585 output += sample.coefficient * sampler(sample.uv_offset);
589 for (
int i = 0; i < fast_kernel_samples.sample_count; ++i) {
590 auto sample = fast_kernel_samples.samples[i];
591 fast_output += sample.coefficient * sampler(sample.uv_offset);
594 EXPECT_NEAR(output, fast_output, 0.1);