5 #include "flutter/testing/testing.h"
6 #include "fml/status_or.h"
7 #include "gmock/gmock.h"
14 #include "impeller/renderer/testing/mocks.h"
17 #define IMPELLER_RAND arc4random
19 #define IMPELLER_RAND rand
30 fml::StatusOr<float> LowerBoundNewtonianMethod(
31 const std::function<
float(
float)>& func,
35 const double delta = 1e-6;
38 static const int kMaxIterations = 1000;
42 fx = func(
x) - target;
43 double derivative = (func(
x + delta) - func(
x)) / delta;
44 x =
x - fx / derivative;
45 if (++count > kMaxIterations) {
46 return fml::Status(fml::StatusCode::kDeadlineExceeded,
47 "Did not converge on answer.");
49 }
while (std::abs(fx) > tolerance ||
55 Scalar GetCoefficient(
const Vector4& vec) {
59 Vector2 GetUVOffset(
const Vector4& vec) {
63 fml::StatusOr<Scalar> CalculateSigmaForBlurRadius(
65 const Matrix& effect_transform) {
78 return LowerBoundNewtonianMethod(f, radius, 2.f, 0.001f);
87 std::shared_ptr<CommandBuffer> command_buffer =
89 if (!command_buffer) {
94 "Clear Subpass", size, command_buffer,
100 ->Submit({command_buffer})
105 if (render_target.ok()) {
106 return render_target.value().GetRenderTargetTexture();
129 std::optional<Rect> coverage =
131 ASSERT_FALSE(coverage.has_value());
142 std::optional<Rect> coverage =
149 fml::StatusOr<Scalar> sigma_radius_1 =
150 CalculateSigmaForBlurRadius(1.0,
Matrix());
151 ASSERT_TRUE(sigma_radius_1.ok());
153 sigma_radius_1.value(),
160 std::optional<Rect> coverage =
163 EXPECT_TRUE(coverage.has_value());
164 if (coverage.has_value()) {
170 fml::StatusOr<Scalar> sigma_radius_1 =
171 CalculateSigmaForBlurRadius(1.0,
Matrix());
172 ASSERT_TRUE(sigma_radius_1.ok());
174 sigma_radius_1.value(),
178 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
182 std::optional<Rect> coverage =
185 EXPECT_TRUE(coverage.has_value());
186 if (coverage.has_value()) {
193 fml::StatusOr<Scalar> sigma_radius_1 =
194 CalculateSigmaForBlurRadius(1.0, effect_transform);
195 ASSERT_TRUE(sigma_radius_1.ok());
197 sigma_radius_1.value(),
201 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
205 std::optional<Rect> coverage =
207 EXPECT_TRUE(coverage.has_value());
208 if (coverage.has_value()) {
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::optional<Rect> coverage = contents->GetFilterSourceCoverage(
225 EXPECT_TRUE(coverage.has_value());
226 if (coverage.has_value()) {
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>(
255 std::shared_ptr<ContentContext> renderer = GetContentContext();
258 std::optional<Entity> result =
259 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 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>(
287 std::shared_ptr<ContentContext> renderer = GetContentContext();
291 std::optional<Entity> result =
292 contents->GetEntity(*renderer, entity, {});
294 EXPECT_TRUE(result.has_value());
295 if (result.has_value()) {
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(),
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>(
320 std::shared_ptr<ContentContext> renderer = GetContentContext();
326 std::optional<Entity> result =
327 contents->GetEntity(*renderer, entity, {});
328 EXPECT_TRUE(result.has_value());
329 if (result.has_value()) {
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(),
345 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
351 EXPECT_TRUE(uvs_bounds.has_value());
352 if (uvs_bounds.has_value()) {
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>(
372 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(result_coverage.value(),
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);
399 50, 40, texture->GetSize().width, texture->GetSize().height));
401 fml::StatusOr<Scalar> sigma_radius_1 =
402 CalculateSigmaForBlurRadius(1.0,
Matrix());
403 auto contents = std::make_unique<GaussianBlurFilterContents>(
408 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()));
426 EXPECT_TRUE(
RectNear(contents_coverage.value(),
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);
439 50, 40, texture->GetSize().width, texture->GetSize().height));
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>(
449 contents->SetEffectTransform(effect_transform);
450 std::shared_ptr<ContentContext> renderer = GetContentContext();
453 std::optional<Entity> result =
454 contents->GetEntity(*renderer, entity, {});
455 EXPECT_TRUE(result.has_value());
456 if (result.has_value()) {
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(),
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);
493 EXPECT_FLOAT_EQ(tally, 1.0f);
496 for (
int i = 0; i < 4; ++i) {
532 GaussianBlurPipeline::FragmentShader::KernelSamples blur_info =
534 EXPECT_EQ(blur_info.sample_count, 3);
542 Point(-1.3333333, 0));
543 EXPECT_FLOAT_EQ(GetCoefficient(blur_info.sample_data[0]), 0.3);
546 EXPECT_FLOAT_EQ(GetCoefficient(blur_info.sample_data[1]), 0.4);
549 EXPECT_FLOAT_EQ(GetCoefficient(blur_info.sample_data[2]), 0.3);
562 Scalar fract = fabsf(modf(point.
x, &int_part));
564 return left * fract + right * (1.0 - fract);
566 return left * (1.0 - fract) + right * fract;
570 lerp(GetUVOffset(blur_info.sample_data[0]),
data[0],
data[1]) *
571 GetCoefficient(blur_info.sample_data[0]) +
572 data[2] * GetCoefficient(blur_info.sample_data[1]) +
573 lerp(GetUVOffset(blur_info.sample_data[2]),
data[3],
data[4]) *
574 GetCoefficient(blur_info.sample_data[2]);
576 EXPECT_NEAR(original_output, fast_output, 0.01);
589 GaussianBlurPipeline::FragmentShader::KernelSamples fast_kernel_samples =
591 EXPECT_EQ(fast_kernel_samples.sample_count, 17);
594 for (
int i = 0; i < 33; i++) {
599 FML_CHECK(point.y == 0.0f);
600 FML_CHECK(point.x >= -16);
601 FML_CHECK(point.x <= 16);
603 Scalar fract = fabsf(modf(point.x, &fint_part));
605 int32_t int_part =
static_cast<int32_t
>(fint_part) + 16;
606 return data[int_part];
608 int32_t left =
static_cast<int32_t
>(floor(point.x)) + 16;
609 int32_t right =
static_cast<int32_t
>(ceil(point.x)) + 16;
611 return fract *
data[left] + (1.0 - fract) *
data[right];
613 return (1.0 - fract) *
data[left] + fract *
data[right];
620 auto sample = kernel_samples.
samples[i];
621 output += sample.
coefficient * sampler(sample.uv_offset);
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]));
630 EXPECT_NEAR(output, fast_output, 0.1);
642 GaussianBlurPipeline::FragmentShader::KernelSamples frag_kernel_samples =
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
std::shared_ptr< ContentContext > GetContentContext() const
@ kNormal
Blurred inside and outside.
Performs a bidirectional Gaussian blur.
static Scalar CalculateBlurRadius(Scalar sigma)
static Scalar ScaleSigma(Scalar sigma)
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 Scalar CalculateScale(Scalar sigma)
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
Render passes encode render commands directed as one specific render target into an underlying comman...
std::shared_ptr< Texture > MakeTexture(ISize size)
Create a texture that has been cleared to transparent black.
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)
ScopedObject< Object > Create(CtorArgs &&... args)
TEST(AllocationSizeTest, CanCreateTypedAllocations)
TEST_P(AiksTest, DrawAtlasNoColor)
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
static constexpr int32_t kGaussianBlurMaxKernelSize
KernelSamples GenerateBlurInfo(BlurParameters parameters)
std::array< Point, 4 > Quad
GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(KernelSamples parameters)
KernelSample samples[kMaxKernelSize]
A 4x4 matrix using column-major storage.
static constexpr Matrix MakeTranslation(const Vector3 &t)
static Matrix MakeRotationZ(Radians r)
static constexpr Matrix MakeScale(const Vector3 &s)
constexpr static std::optional< TRect > MakePointBounds(const U &value)
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
constexpr static TRect MakeSize(const TSize< U > &size)
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
std::shared_ptr< const fml::Mapping > data