]> git.sesse.net Git - movit/blobdiff - resample_effect_test.cpp
Add an effect for Lanczos resampling.
[movit] / resample_effect_test.cpp
diff --git a/resample_effect_test.cpp b/resample_effect_test.cpp
new file mode 100644 (file)
index 0000000..462f5c9
--- /dev/null
@@ -0,0 +1,162 @@
+// Unit tests for ResampleEffect.
+
+#include "test_util.h"
+#include "gtest/gtest.h"
+#include "resample_effect.h"
+#include "flat_input.h"
+
+namespace {
+
+float sinc(float x)
+{
+       return sin(M_PI * x) / (M_PI * x);
+}
+
+float lanczos(float x, float a)
+{
+       if (fabs(x) >= a) {
+               return 0.0f;
+       } else {
+               return sinc(x) * sinc(x / a);
+       }
+}
+
+}  // namespace
+
+TEST(ResampleEffectTest, IdentityTransformDoesNothing) {
+       const int size = 4;
+
+       float data[size * size] = {
+               0.0, 1.0, 0.0, 1.0,
+               0.0, 1.0, 1.0, 0.0,
+               0.0, 0.5, 1.0, 0.5,
+               0.0, 0.0, 0.0, 0.0,
+       };
+       float out_data[size * size];
+
+       EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+       Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
+       ASSERT_TRUE(resample_effect->set_int("width", 4));
+       ASSERT_TRUE(resample_effect->set_int("height", 4));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       expect_equal(data, out_data, size, size);
+}
+
+TEST(ResampleEffectTest, UpscaleByTwoGetsCorrectPixelCenters) {
+       const int size = 5;
+
+       float data[size * size] = {
+               0.0, 0.0, 0.0, 0.0, 0.0,
+               0.0, 0.0, 0.0, 0.0, 0.0,
+               0.0, 0.0, 1.0, 0.0, 0.0,
+               0.0, 0.0, 0.0, 0.0, 0.0,
+               0.0, 0.0, 0.0, 0.0, 0.0,
+       };
+       float expected_data[size * size * 4], out_data[size * size * 4];
+
+       for (int y = 0; y < size * 2; ++y) {
+               for (int x = 0; x < size * 2; ++x) {
+                       float weight = lanczos((x - size + 0.5f) * 0.5f, 3.0f);
+                       weight *= lanczos((y - size + 0.5f) * 0.5f, 3.0f);
+                       expected_data[y * (size * 2) + x] = weight;
+               }
+       }
+
+       EffectChainTester tester(NULL, size * 2, size * 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       ImageFormat format;
+       format.color_space = COLORSPACE_sRGB;
+       format.gamma_curve = GAMMA_LINEAR;
+
+       FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size, size);
+       input->set_pixel_data(data);
+       tester.get_chain()->add_input(input);
+
+       Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
+       ASSERT_TRUE(resample_effect->set_int("width", size * 2));
+       ASSERT_TRUE(resample_effect->set_int("height", size * 2));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       expect_equal(expected_data, out_data, size * 2, size * 2);
+}
+
+TEST(ResampleEffectTest, DownscaleByTwoGetsCorrectPixelCenters) {
+       const int size = 5;
+
+       // This isn't a perfect dot, since the Lanczos filter has a slight
+       // sharpening effect; the most important thing is that we have kept
+       // the texel center right (everything is nicely symmetric).
+       // The approximate magnitudes have been checked against ImageMagick.
+       float expected_data[size * size] = {
+                0.0045, -0.0067, -0.0598, -0.0067,  0.0045, 
+               -0.0067,  0.0099,  0.0886,  0.0099, -0.0067, 
+               -0.0598,  0.0886,  0.7930,  0.0886, -0.0598, 
+               -0.0067,  0.0099,  0.0886,  0.0099, -0.0067, 
+                0.0045, -0.0067, -0.0598, -0.0067,  0.0045, 
+       };
+       float data[size * size * 4], out_data[size * size];
+
+       for (int y = 0; y < size * 2; ++y) {
+               for (int x = 0; x < size * 2; ++x) {
+                       float weight = lanczos((x - size + 0.5f) * 0.5f, 3.0f);
+                       weight *= lanczos((y - size + 0.5f) * 0.5f, 3.0f);
+                       data[y * (size * 2) + x] = weight;
+               }
+       }
+
+       EffectChainTester tester(NULL, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       ImageFormat format;
+       format.color_space = COLORSPACE_sRGB;
+       format.gamma_curve = GAMMA_LINEAR;
+
+       FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size * 2, size * 2);
+       input->set_pixel_data(data);
+       tester.get_chain()->add_input(input);
+
+       Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
+       ASSERT_TRUE(resample_effect->set_int("width", size));
+       ASSERT_TRUE(resample_effect->set_int("height", size));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       expect_equal(expected_data, out_data, size, size);
+}
+
+TEST(ResampleEffectTest, UpscaleByThreeGetsCorrectPixelCenters) {
+       const int size = 5;
+
+       float data[size * size] = {
+               0.0, 0.0, 0.0, 0.0, 0.0,
+               0.0, 0.0, 0.0, 0.0, 0.0,
+               0.0, 0.0, 1.0, 0.0, 0.0,
+               0.0, 0.0, 0.0, 0.0, 0.0,
+               0.0, 0.0, 0.0, 0.0, 0.0,
+       };
+       float out_data[size * size * 9];
+
+       EffectChainTester tester(NULL, size * 3, size * 3, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       ImageFormat format;
+       format.color_space = COLORSPACE_sRGB;
+       format.gamma_curve = GAMMA_LINEAR;
+
+       FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size, size);
+       input->set_pixel_data(data);
+       tester.get_chain()->add_input(input);
+
+       Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
+       ASSERT_TRUE(resample_effect->set_int("width", size * 3));
+       ASSERT_TRUE(resample_effect->set_int("height", size * 3));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       // We only bother checking that the middle pixel is still correct,
+       // and that symmetry holds.
+       EXPECT_FLOAT_EQ(1.0, out_data[7 * (size * 3) + 7]);
+       for (unsigned y = 0; y < size * 3; ++y) {
+               for (unsigned x = 0; x < size * 3; ++x) {
+                       EXPECT_FLOAT_EQ(out_data[y * (size * 3) + x], out_data[(size * 3 - y - 1) * (size * 3) + x]);
+                       EXPECT_FLOAT_EQ(out_data[y * (size * 3) + x], out_data[y * (size * 3) + (size * 3 - x - 1)]);
+               }
+       }
+}