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