e4c23b10782c09e96fa91cd2a4cb030a7faf9c6c
[movit] / resample_effect_test.cpp
1 // Unit tests for ResampleEffect.
2
3 #include <GL/glew.h>
4 #include <math.h>
5
6 #include "effect_chain.h"
7 #include "flat_input.h"
8 #include "glew.h"
9 #include "gtest/gtest.h"
10 #include "image_format.h"
11 #include "resample_effect.h"
12 #include "test_util.h"
13
14 namespace {
15
16 float sinc(float x)
17 {
18         return sin(M_PI * x) / (M_PI * x);
19 }
20
21 float lanczos(float x, float a)
22 {
23         if (fabs(x) >= a) {
24                 return 0.0f;
25         } else {
26                 return sinc(x) * sinc(x / a);
27         }
28 }
29
30 }  // namespace
31
32 TEST(ResampleEffectTest, IdentityTransformDoesNothing) {
33         const int size = 4;
34
35         float data[size * size] = {
36                 0.0, 1.0, 0.0, 1.0,
37                 0.0, 1.0, 1.0, 0.0,
38                 0.0, 0.5, 1.0, 0.5,
39                 0.0, 0.0, 0.0, 0.0,
40         };
41         float out_data[size * size];
42
43         EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
44         Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
45         ASSERT_TRUE(resample_effect->set_int("width", 4));
46         ASSERT_TRUE(resample_effect->set_int("height", 4));
47         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
48
49         expect_equal(data, out_data, size, size);
50 }
51
52 TEST(ResampleEffectTest, UpscaleByTwoGetsCorrectPixelCenters) {
53         const int size = 5;
54
55         float data[size * size] = {
56                 0.0, 0.0, 0.0, 0.0, 0.0,
57                 0.0, 0.0, 0.0, 0.0, 0.0,
58                 0.0, 0.0, 1.0, 0.0, 0.0,
59                 0.0, 0.0, 0.0, 0.0, 0.0,
60                 0.0, 0.0, 0.0, 0.0, 0.0,
61         };
62         float expected_data[size * size * 4], out_data[size * size * 4];
63
64         for (int y = 0; y < size * 2; ++y) {
65                 for (int x = 0; x < size * 2; ++x) {
66                         float weight = lanczos((x - size + 0.5f) * 0.5f, 3.0f);
67                         weight *= lanczos((y - size + 0.5f) * 0.5f, 3.0f);
68                         expected_data[y * (size * 2) + x] = weight;
69                 }
70         }
71
72         EffectChainTester tester(NULL, size * 2, size * 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
73
74         ImageFormat format;
75         format.color_space = COLORSPACE_sRGB;
76         format.gamma_curve = GAMMA_LINEAR;
77
78         FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size, size);
79         input->set_pixel_data(data);
80         tester.get_chain()->add_input(input);
81
82         Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
83         ASSERT_TRUE(resample_effect->set_int("width", size * 2));
84         ASSERT_TRUE(resample_effect->set_int("height", size * 2));
85         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
86
87         expect_equal(expected_data, out_data, size * 2, size * 2);
88 }
89
90 TEST(ResampleEffectTest, DownscaleByTwoGetsCorrectPixelCenters) {
91         const int size = 5;
92
93         // This isn't a perfect dot, since the Lanczos filter has a slight
94         // sharpening effect; the most important thing is that we have kept
95         // the texel center right (everything is nicely symmetric).
96         // The approximate magnitudes have been checked against ImageMagick.
97         float expected_data[size * size] = {
98                  0.0045, -0.0067, -0.0598, -0.0067,  0.0045, 
99                 -0.0067,  0.0099,  0.0886,  0.0099, -0.0067, 
100                 -0.0598,  0.0886,  0.7930,  0.0886, -0.0598, 
101                 -0.0067,  0.0099,  0.0886,  0.0099, -0.0067, 
102                  0.0045, -0.0067, -0.0598, -0.0067,  0.0045, 
103         };
104         float data[size * size * 4], out_data[size * size];
105
106         for (int y = 0; y < size * 2; ++y) {
107                 for (int x = 0; x < size * 2; ++x) {
108                         float weight = lanczos((x - size + 0.5f) * 0.5f, 3.0f);
109                         weight *= lanczos((y - size + 0.5f) * 0.5f, 3.0f);
110                         data[y * (size * 2) + x] = weight;
111                 }
112         }
113
114         EffectChainTester tester(NULL, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
115
116         ImageFormat format;
117         format.color_space = COLORSPACE_sRGB;
118         format.gamma_curve = GAMMA_LINEAR;
119
120         FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size * 2, size * 2);
121         input->set_pixel_data(data);
122         tester.get_chain()->add_input(input);
123
124         Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
125         ASSERT_TRUE(resample_effect->set_int("width", size));
126         ASSERT_TRUE(resample_effect->set_int("height", size));
127         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
128
129         expect_equal(expected_data, out_data, size, size);
130 }
131
132 TEST(ResampleEffectTest, UpscaleByThreeGetsCorrectPixelCenters) {
133         const int size = 5;
134
135         float data[size * size] = {
136                 0.0, 0.0, 0.0, 0.0, 0.0,
137                 0.0, 0.0, 0.0, 0.0, 0.0,
138                 0.0, 0.0, 1.0, 0.0, 0.0,
139                 0.0, 0.0, 0.0, 0.0, 0.0,
140                 0.0, 0.0, 0.0, 0.0, 0.0,
141         };
142         float out_data[size * size * 9];
143
144         EffectChainTester tester(NULL, size * 3, size * 3, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
145
146         ImageFormat format;
147         format.color_space = COLORSPACE_sRGB;
148         format.gamma_curve = GAMMA_LINEAR;
149
150         FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size, size);
151         input->set_pixel_data(data);
152         tester.get_chain()->add_input(input);
153
154         Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
155         ASSERT_TRUE(resample_effect->set_int("width", size * 3));
156         ASSERT_TRUE(resample_effect->set_int("height", size * 3));
157         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
158
159         // We only bother checking that the middle pixel is still correct,
160         // and that symmetry holds.
161         EXPECT_FLOAT_EQ(1.0, out_data[7 * (size * 3) + 7]);
162         for (unsigned y = 0; y < size * 3; ++y) {
163                 for (unsigned x = 0; x < size * 3; ++x) {
164                         EXPECT_FLOAT_EQ(out_data[y * (size * 3) + x], out_data[(size * 3 - y - 1) * (size * 3) + x]);
165                         EXPECT_FLOAT_EQ(out_data[y * (size * 3) + x], out_data[y * (size * 3) + (size * 3 - x - 1)]);
166                 }
167         }
168 }
169
170 TEST(ResampleEffectTest, HeavyResampleGetsSumRight) {
171         const int swidth = 1280, sheight = 720;
172         const int dwidth = 36, dheight = 20;
173
174         float data[swidth * sheight], out_data[dwidth * dheight], expected_data[dwidth * dheight];
175         for (int y = 0; y < sheight; ++y) {
176                 for (int x = 0; x < swidth; ++x) {
177                         data[y * swidth + x] = 0.5f;
178                 }
179         }
180         for (int y = 0; y < dheight; ++y) {
181                 for (int x = 0; x < dwidth; ++x) {
182                         expected_data[y * dwidth + x] = 0.5f;
183                 }
184         }
185
186         EffectChainTester tester(NULL, dwidth, dheight, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
187
188         ImageFormat format;
189         format.color_space = COLORSPACE_sRGB;
190         format.gamma_curve = GAMMA_LINEAR;
191
192         FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, swidth, sheight);
193         input->set_pixel_data(data);
194
195         tester.get_chain()->add_input(input);
196         Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
197         ASSERT_TRUE(resample_effect->set_int("width", dwidth));
198         ASSERT_TRUE(resample_effect->set_int("height", dheight));
199         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
200
201         expect_equal(expected_data, out_data, dwidth, dheight, 0.001);
202 }
203