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();
127 std::optional<Rect> coverage =
129 ASSERT_FALSE(coverage.has_value());
139 std::optional<Rect> coverage =
146 fml::StatusOr<Scalar> sigma_radius_1 =
147 CalculateSigmaForBlurRadius(1.0,
Matrix());
148 ASSERT_TRUE(sigma_radius_1.ok());
150 sigma_radius_1.value(),
156 std::optional<Rect> coverage =
159 EXPECT_TRUE(coverage.has_value());
160 if (coverage.has_value()) {
166 fml::StatusOr<Scalar> sigma_radius_1 =
167 CalculateSigmaForBlurRadius(1.0,
Matrix());
168 ASSERT_TRUE(sigma_radius_1.ok());
170 sigma_radius_1.value(),
173 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
177 std::optional<Rect> coverage =
180 EXPECT_TRUE(coverage.has_value());
181 if (coverage.has_value()) {
188 fml::StatusOr<Scalar> sigma_radius_1 =
189 CalculateSigmaForBlurRadius(1.0, effect_transform);
190 ASSERT_TRUE(sigma_radius_1.ok());
192 sigma_radius_1.value(),
195 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
199 std::optional<Rect> coverage =
201 EXPECT_TRUE(coverage.has_value());
202 if (coverage.has_value()) {
209 fml::StatusOr<Scalar> sigma_radius_1 =
210 CalculateSigmaForBlurRadius(1.0,
Matrix());
211 ASSERT_TRUE(sigma_radius_1.ok());
212 auto contents = std::make_unique<GaussianBlurFilterContents>(
215 std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
218 EXPECT_TRUE(coverage.has_value());
219 if (coverage.has_value()) {
239 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
240 fml::StatusOr<Scalar> sigma_radius_1 =
241 CalculateSigmaForBlurRadius(1.0,
Matrix());
242 ASSERT_TRUE(sigma_radius_1.ok());
243 auto contents = std::make_unique<GaussianBlurFilterContents>(
247 std::shared_ptr<ContentContext> renderer = GetContentContext();
250 std::optional<Entity> result =
251 contents->GetEntity(*renderer, entity, {});
252 EXPECT_TRUE(result.has_value());
253 if (result.has_value()) {
255 std::optional<Rect> result_coverage = result.value().GetCoverage();
256 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
257 EXPECT_TRUE(result_coverage.has_value());
258 EXPECT_TRUE(contents_coverage.has_value());
259 if (result_coverage.has_value() && contents_coverage.has_value()) {
260 EXPECT_TRUE(
RectNear(contents_coverage.value(),
269 RenderCoverageMatchesGetCoverageTranslate) {
270 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
271 fml::StatusOr<Scalar> sigma_radius_1 =
272 CalculateSigmaForBlurRadius(1.0,
Matrix());
273 ASSERT_TRUE(sigma_radius_1.ok());
274 auto contents = std::make_unique<GaussianBlurFilterContents>(
278 std::shared_ptr<ContentContext> renderer = GetContentContext();
282 std::optional<Entity> result =
283 contents->GetEntity(*renderer, entity, {});
285 EXPECT_TRUE(result.has_value());
286 if (result.has_value()) {
288 std::optional<Rect> result_coverage = result.value().GetCoverage();
289 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
290 EXPECT_TRUE(result_coverage.has_value());
291 EXPECT_TRUE(contents_coverage.has_value());
292 if (result_coverage.has_value() && contents_coverage.has_value()) {
293 EXPECT_TRUE(
RectNear(contents_coverage.value(),
302 RenderCoverageMatchesGetCoverageRotated) {
303 std::shared_ptr<Texture> texture = MakeTexture(
ISize(400, 300));
304 fml::StatusOr<Scalar> sigma_radius_1 =
305 CalculateSigmaForBlurRadius(1.0,
Matrix());
306 auto contents = std::make_unique<GaussianBlurFilterContents>(
310 std::shared_ptr<ContentContext> renderer = GetContentContext();
316 std::optional<Entity> result =
317 contents->GetEntity(*renderer, entity, {});
318 EXPECT_TRUE(result.has_value());
319 if (result.has_value()) {
321 std::optional<Rect> result_coverage = result.value().GetCoverage();
322 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
323 EXPECT_TRUE(result_coverage.has_value());
324 EXPECT_TRUE(contents_coverage.has_value());
325 if (result_coverage.has_value() && contents_coverage.has_value()) {
326 EXPECT_TRUE(
RectNear(contents_coverage.value(),
335 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
341 EXPECT_TRUE(uvs_bounds.has_value());
342 if (uvs_bounds.has_value()) {
348 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
349 auto texture_contents = std::make_shared<TextureContents>();
350 texture_contents->SetSourceRect(
Rect::MakeSize(texture->GetSize()));
351 texture_contents->SetTexture(texture);
353 50, 40, texture->GetSize().width, texture->GetSize().height));
355 fml::StatusOr<Scalar> sigma_radius_1 =
356 CalculateSigmaForBlurRadius(1.0,
Matrix());
357 auto contents = std::make_unique<GaussianBlurFilterContents>(
361 std::shared_ptr<ContentContext> renderer = GetContentContext();
364 std::optional<Entity> result =
365 contents->GetEntity(*renderer, entity, {});
366 EXPECT_TRUE(result.has_value());
367 if (result.has_value()) {
369 std::optional<Rect> result_coverage = result.value().GetCoverage();
370 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
371 EXPECT_TRUE(result_coverage.has_value());
372 EXPECT_TRUE(contents_coverage.has_value());
373 if (result_coverage.has_value() && contents_coverage.has_value()) {
374 EXPECT_TRUE(
RectNear(result_coverage.value(), contents_coverage.value()));
375 EXPECT_TRUE(
RectNear(result_coverage.value(),
382 TextureContentsWithDestinationRectScaled) {
383 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
384 auto texture_contents = std::make_shared<TextureContents>();
385 texture_contents->SetSourceRect(
Rect::MakeSize(texture->GetSize()));
386 texture_contents->SetTexture(texture);
388 50, 40, texture->GetSize().width, texture->GetSize().height));
390 fml::StatusOr<Scalar> sigma_radius_1 =
391 CalculateSigmaForBlurRadius(1.0,
Matrix());
392 auto contents = std::make_unique<GaussianBlurFilterContents>(
397 std::shared_ptr<ContentContext> renderer = GetContentContext();
401 std::optional<Entity> result =
402 contents->GetEntity(*renderer, entity, {});
403 EXPECT_TRUE(result.has_value());
404 if (result.has_value()) {
406 std::optional<Rect> result_coverage = result.value().GetCoverage();
407 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
408 EXPECT_TRUE(result_coverage.has_value());
409 EXPECT_TRUE(contents_coverage.has_value());
410 if (result_coverage.has_value() && contents_coverage.has_value()) {
411 EXPECT_TRUE(
RectNear(result_coverage.value(), contents_coverage.value()));
415 EXPECT_TRUE(
RectNear(contents_coverage.value(),
423 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
424 auto texture_contents = std::make_shared<TextureContents>();
425 texture_contents->SetSourceRect(
Rect::MakeSize(texture->GetSize()));
426 texture_contents->SetTexture(texture);
428 50, 40, texture->GetSize().width, texture->GetSize().height));
430 fml::StatusOr<Scalar> sigma_radius_1 =
431 CalculateSigmaForBlurRadius(1.0, effect_transform);
432 ASSERT_TRUE(sigma_radius_1.ok());
433 auto contents = std::make_unique<GaussianBlurFilterContents>(
437 contents->SetEffectTransform(effect_transform);
438 std::shared_ptr<ContentContext> renderer = GetContentContext();
441 std::optional<Entity> result =
442 contents->GetEntity(*renderer, entity, {});
443 EXPECT_TRUE(result.has_value());
444 if (result.has_value()) {
446 std::optional<Rect> result_coverage = result.value().GetCoverage();
447 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
448 EXPECT_TRUE(result_coverage.has_value());
449 EXPECT_TRUE(contents_coverage.has_value());
450 if (result_coverage.has_value() && contents_coverage.has_value()) {
451 EXPECT_TRUE(
RectNear(result_coverage.value(), contents_coverage.value()));
452 EXPECT_TRUE(
RectNear(contents_coverage.value(),
462 fml::StatusOr<Scalar> derived_sigma =
463 CalculateSigmaForBlurRadius(radius,
Matrix());
464 ASSERT_TRUE(derived_sigma.ok());
465 EXPECT_NEAR(sigma, derived_sigma.value(), 0.01f);
481 EXPECT_FLOAT_EQ(tally, 1.0f);
484 for (
int i = 0; i < 4; ++i) {
520 GaussianBlurPipeline::FragmentShader::KernelSamples blur_info =
522 EXPECT_EQ(blur_info.sample_count, 3);
530 Point(-1.3333333, 0));
531 EXPECT_FLOAT_EQ(GetCoefficient(blur_info.sample_data[0]), 0.3);
534 EXPECT_FLOAT_EQ(GetCoefficient(blur_info.sample_data[1]), 0.4);
537 EXPECT_FLOAT_EQ(GetCoefficient(blur_info.sample_data[2]), 0.3);
550 Scalar fract = fabsf(modf(point.
x, &int_part));
552 return left * fract + right * (1.0 - fract);
554 return left * (1.0 - fract) + right * fract;
558 lerp(GetUVOffset(blur_info.sample_data[0]),
data[0],
data[1]) *
559 GetCoefficient(blur_info.sample_data[0]) +
560 data[2] * GetCoefficient(blur_info.sample_data[1]) +
561 lerp(GetUVOffset(blur_info.sample_data[2]),
data[3],
data[4]) *
562 GetCoefficient(blur_info.sample_data[2]);
564 EXPECT_NEAR(original_output, fast_output, 0.01);
577 GaussianBlurPipeline::FragmentShader::KernelSamples fast_kernel_samples =
579 EXPECT_EQ(fast_kernel_samples.sample_count, 17);
582 for (
int i = 0; i < 33; i++) {
587 FML_CHECK(point.y == 0.0f);
588 FML_CHECK(point.x >= -16);
589 FML_CHECK(point.x <= 16);
591 Scalar fract = fabsf(modf(point.x, &fint_part));
593 int32_t int_part =
static_cast<int32_t
>(fint_part) + 16;
594 return data[int_part];
596 int32_t left =
static_cast<int32_t
>(floor(point.x)) + 16;
597 int32_t right =
static_cast<int32_t
>(ceil(point.x)) + 16;
599 return fract *
data[left] + (1.0 - fract) *
data[right];
601 return (1.0 - fract) *
data[left] + fract *
data[right];
608 auto sample = kernel_samples.
samples[i];
609 output += sample.
coefficient * sampler(sample.uv_offset);
613 for (
int i = 0; i < fast_kernel_samples.sample_count; i++) {
614 fast_output += GetCoefficient(fast_kernel_samples.sample_data[i]) *
615 sampler(GetUVOffset(fast_kernel_samples.sample_data[i]));
618 EXPECT_NEAR(output, fast_output, 0.1);
630 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.
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