5#include "flutter/testing/testing.h"
7#include "gmock/gmock.h"
16#define IMPELLER_RAND arc4random
18#define IMPELLER_RAND rand
30 const std::function<
float(
float)>& func,
34 const double delta = 1e-6;
37 static const int kMaxIterations = 1000;
42 double derivative = (func(
x + delta) - func(
x)) / delta;
43 x =
x - fx / derivative;
44 if (++
count > kMaxIterations) {
46 "Did not converge on answer.");
48 }
while (std::abs(fx) > tolerance ||
56 const Matrix& effect_transform) {
58 Vector2 scaled_sigma = (effect_transform.Basis() *
65 return std::max(blur_radius.x, blur_radius.y);
69 return LowerBoundNewtonianMethod(f, radius, 2.f, 0.001f);
78 std::shared_ptr<CommandBuffer> command_buffer =
80 if (!command_buffer) {
85 "Clear Subpass", size, command_buffer,
91 ->Submit({command_buffer})
96 if (render_target.ok()) {
97 return render_target.value().GetRenderTargetTexture();
118 std::optional<Rect> coverage =
120 ASSERT_FALSE(coverage.has_value());
130 std::optional<Rect> coverage =
138 CalculateSigmaForBlurRadius(1.0,
Matrix());
139 ASSERT_TRUE(sigma_radius_1.
ok());
141 sigma_radius_1.
value(),
147 std::optional<Rect> coverage =
151 if (coverage.has_value()) {
158 CalculateSigmaForBlurRadius(1.0,
Matrix());
159 ASSERT_TRUE(sigma_radius_1.
ok());
161 sigma_radius_1.
value(),
164 std::shared_ptr<Texture>
texture = MakeTexture(
ISize(100, 100));
168 std::optional<Rect> coverage =
172 if (coverage.has_value()) {
180 CalculateSigmaForBlurRadius(1.0, effect_transform);
181 ASSERT_TRUE(sigma_radius_1.ok());
183 sigma_radius_1.value(),
186 std::shared_ptr<Texture>
texture = MakeTexture(
ISize(100, 100));
190 std::optional<Rect> coverage =
193 if (coverage.has_value()) {
201 CalculateSigmaForBlurRadius(1.0,
Matrix());
202 ASSERT_TRUE(sigma_radius_1.
ok());
203 auto contents = std::make_unique<GaussianBlurFilterContents>(
206 std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
210 if (coverage.has_value()) {
230 std::shared_ptr<Texture>
texture = MakeTexture(
ISize(100, 100));
232 CalculateSigmaForBlurRadius(1.0,
Matrix());
233 ASSERT_TRUE(sigma_radius_1.
ok());
234 auto contents = std::make_unique<GaussianBlurFilterContents>(
238 std::shared_ptr<ContentContext> renderer = GetContentContext();
241 std::optional<Entity>
result =
242 contents->GetEntity(*renderer, entity, {});
246 std::optional<Rect> result_coverage =
result.value().GetCoverage();
247 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
250 if (result_coverage.has_value() && contents_coverage.has_value()) {
260 RenderCoverageMatchesGetCoverageTranslate) {
261 std::shared_ptr<Texture>
texture = MakeTexture(
ISize(100, 100));
263 CalculateSigmaForBlurRadius(1.0,
Matrix());
264 ASSERT_TRUE(sigma_radius_1.
ok());
265 auto contents = std::make_unique<GaussianBlurFilterContents>(
269 std::shared_ptr<ContentContext> renderer = GetContentContext();
273 std::optional<Entity>
result =
274 contents->GetEntity(*renderer, entity, {});
279 std::optional<Rect> result_coverage =
result.value().GetCoverage();
280 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
283 if (result_coverage.has_value() && contents_coverage.has_value()) {
293 RenderCoverageMatchesGetCoverageRotated) {
294 std::shared_ptr<Texture>
texture = MakeTexture(
ISize(400, 300));
296 CalculateSigmaForBlurRadius(1.0,
Matrix());
297 auto contents = std::make_unique<GaussianBlurFilterContents>(
301 std::shared_ptr<ContentContext> renderer = GetContentContext();
307 std::optional<Entity>
result =
308 contents->GetEntity(*renderer, entity, {});
312 std::optional<Rect> result_coverage =
result.value().GetCoverage();
313 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
316 if (result_coverage.has_value() && contents_coverage.has_value()) {
326 std::shared_ptr<Texture>
texture = MakeTexture(
ISize(100, 100));
333 if (uvs_bounds.has_value()) {
339 std::shared_ptr<Texture>
texture = MakeTexture(
ISize(100, 100));
340 auto texture_contents = std::make_shared<TextureContents>();
342 texture_contents->SetTexture(
texture);
347 CalculateSigmaForBlurRadius(1.0,
Matrix());
348 auto contents = std::make_unique<GaussianBlurFilterContents>(
352 std::shared_ptr<ContentContext> renderer = GetContentContext();
355 std::optional<Entity>
result =
356 contents->GetEntity(*renderer, entity, {});
360 std::optional<Rect> result_coverage =
result.value().GetCoverage();
361 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
364 if (result_coverage.has_value() && contents_coverage.has_value()) {
373 TextureContentsWithDestinationRectScaled) {
374 std::shared_ptr<Texture>
texture = MakeTexture(
ISize(100, 100));
375 auto texture_contents = std::make_shared<TextureContents>();
377 texture_contents->SetTexture(
texture);
382 CalculateSigmaForBlurRadius(1.0,
Matrix());
383 auto contents = std::make_unique<GaussianBlurFilterContents>(
387 std::shared_ptr<ContentContext> renderer = GetContentContext();
391 std::optional<Entity>
result =
392 contents->GetEntity(*renderer, entity, {});
396 std::optional<Rect> result_coverage =
result.value().GetCoverage();
397 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
400 if (result_coverage.has_value() && contents_coverage.has_value()) {
410 std::shared_ptr<Texture>
texture = MakeTexture(
ISize(100, 100));
411 auto texture_contents = std::make_shared<TextureContents>();
413 texture_contents->SetTexture(
texture);
418 CalculateSigmaForBlurRadius(1.0, effect_transform);
419 ASSERT_TRUE(sigma_radius_1.
ok());
420 auto contents = std::make_unique<GaussianBlurFilterContents>(
424 contents->SetEffectTransform(effect_transform);
425 std::shared_ptr<ContentContext> renderer = GetContentContext();
428 std::optional<Entity>
result =
429 contents->GetEntity(*renderer, entity, {});
433 std::optional<Rect> result_coverage =
result.value().GetCoverage();
434 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
437 if (result_coverage.has_value() && contents_coverage.has_value()) {
450 CalculateSigmaForBlurRadius(radius,
Matrix());
451 ASSERT_TRUE(derived_sigma.
ok());
452 EXPECT_NEAR(sigma, derived_sigma.
value(), 0.01f);
460 GaussianBlurPipeline::FragmentShader::KernelSamples samples =
462 EXPECT_EQ(samples.sample_count, 9);
466 for (
int i = 0; i < samples.sample_count; ++i) {
467 tally += samples.samples[i].coefficient;
469 EXPECT_FLOAT_EQ(tally, 1.0f);
472 for (
int i = 0; i < 4; ++i) {
473 EXPECT_FLOAT_EQ(samples.samples[i].coefficient,
474 samples.samples[8 - i].coefficient);
476 samples.samples[i].coefficient);
481 GaussianBlurPipeline::FragmentShader::KernelSamples kernel_samples = {
508 GaussianBlurPipeline::FragmentShader::KernelSamples fast_kernel_samples =
510 EXPECT_EQ(fast_kernel_samples.sample_count, 3);
512 GaussianBlurPipeline::FragmentShader::KernelSample* samples =
513 kernel_samples.samples;
514 GaussianBlurPipeline::FragmentShader::KernelSample* fast_samples =
515 fast_kernel_samples.samples;
520 EXPECT_FLOAT_EQ(fast_samples[0].uv_offset.x, -1.3333333);
521 EXPECT_FLOAT_EQ(fast_samples[0].uv_offset.y, 0);
522 EXPECT_FLOAT_EQ(fast_samples[0].coefficient, 0.3);
523 EXPECT_FLOAT_EQ(fast_samples[1].uv_offset.x, 0);
524 EXPECT_FLOAT_EQ(fast_samples[1].uv_offset.y, 0);
525 EXPECT_FLOAT_EQ(fast_samples[1].coefficient, 0.4);
526 EXPECT_FLOAT_EQ(fast_samples[2].uv_offset.x, 1.3333333);
527 EXPECT_FLOAT_EQ(fast_samples[2].uv_offset.y, 0);
528 EXPECT_FLOAT_EQ(fast_samples[2].coefficient, 0.3);
533 Scalar data[5] = {0.25, 0.5, 0.5, 1.0, 0.2};
535 samples[0].coefficient * data[0] + samples[1].coefficient * data[1] +
536 samples[2].coefficient * data[2] + samples[3].coefficient * data[3] +
537 samples[4].coefficient * data[4];
541 Scalar fract = fabsf(modf(point.
x, &int_part));
543 return left * fract +
right * (1.0 - fract);
545 return left * (1.0 - fract) +
right * fract;
549 lerp(fast_samples[0].uv_offset, data[0], data[1]) *
550 fast_samples[0].coefficient +
551 data[2] * fast_samples[1].coefficient +
552 lerp(fast_samples[2].uv_offset, data[3], data[4]) *
553 fast_samples[2].coefficient;
555 EXPECT_NEAR(original_output, fast_output, 0.01);
560 int32_t blur_radius =
static_cast<int32_t
>(
564 .blur_radius = blur_radius,
566 GaussianBlurPipeline::FragmentShader::KernelSamples kernel_samples =
568 EXPECT_EQ(kernel_samples.sample_count, 33);
569 GaussianBlurPipeline::FragmentShader::KernelSamples fast_kernel_samples =
571 EXPECT_EQ(fast_kernel_samples.sample_count, 17);
574 for (
int i = 0; i < 33; i++) {
575 data[i] = 255.0 *
static_cast<double>(
IMPELLER_RAND()) / RAND_MAX;
583 Scalar fract = fabsf(modf(point.x, &fint_part));
585 int32_t int_part =
static_cast<int32_t
>(fint_part) + 16;
586 return data[int_part];
588 int32_t
left =
static_cast<int32_t
>(floor(point.x)) + 16;
589 int32_t
right =
static_cast<int32_t
>(ceil(point.x)) + 16;
591 return fract * data[
left] + (1.0 - fract) * data[
right];
593 return (1.0 - fract) * data[
left] + fract * data[
right];
599 for (
int i = 0; i < kernel_samples.sample_count; ++i) {
600 auto sample = kernel_samples.samples[i];
601 output += sample.coefficient * sampler(sample.uv_offset);
605 for (
int i = 0; i < fast_kernel_samples.sample_count; ++i) {
606 auto sample = fast_kernel_samples.samples[i];
607 fast_output += sample.coefficient * sampler(sample.uv_offset);
610 EXPECT_NEAR(output, fast_output, 0.1);
#define TEST(S, s, D, expected)
static sk_sp< Effect > Create()
static bool ok(int result)
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
std::shared_ptr< ContentContext > GetContentContext() const
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
@ 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.
#define FML_CHECK(condition)
inline ::testing::AssertionResult RectNear(impeller::Rect a, impeller::Rect b)
#define EXPECT_RECT_NEAR(a, b)
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer)
GaussianBlurPipeline::FragmentShader::KernelSamples GenerateBlurInfo(BlurParameters parameters)
GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(GaussianBlurPipeline::FragmentShader::KernelSamples parameters)
std::array< Point, 4 > Quad
#define INSTANTIATE_PLAYGROUND_SUITE(playground)
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)
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
static constexpr TRect MakeSize(const TSize< U > &size)
static constexpr std::optional< TRect > MakePointBounds(const U &value)
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
static SkPoint lerp(const SkPoint &a, const SkPoint &b, float T)
#define EXPECT_TRUE(handle)