1 // Unit tests for ResampleEffect.
4 #include <gtest/gtest.h>
7 #include "effect_chain.h"
8 #include "flat_input.h"
9 #include "image_format.h"
10 #include "resample_effect.h"
11 #include "test_util.h"
19 return sin(M_PI * x) / (M_PI * x);
22 float lanczos(float x, float a)
27 return sinc(x) * sinc(x / a);
33 TEST(ResampleEffectTest, IdentityTransformDoesNothing) {
36 float data[size * size] = {
42 float out_data[size * size];
44 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
45 Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
46 ASSERT_TRUE(resample_effect->set_int("width", 4));
47 ASSERT_TRUE(resample_effect->set_int("height", 4));
48 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
50 expect_equal(data, out_data, size, size);
53 TEST(ResampleEffectTest, UpscaleByTwoGetsCorrectPixelCenters) {
56 float data[size * size] = {
57 0.0, 0.0, 0.0, 0.0, 0.0,
58 0.0, 0.0, 0.0, 0.0, 0.0,
59 0.0, 0.0, 1.0, 0.0, 0.0,
60 0.0, 0.0, 0.0, 0.0, 0.0,
61 0.0, 0.0, 0.0, 0.0, 0.0,
63 float expected_data[size * size * 4], out_data[size * size * 4];
65 for (int y = 0; y < size * 2; ++y) {
66 for (int x = 0; x < size * 2; ++x) {
67 float weight = lanczos((x - size + 0.5f) * 0.5f, 3.0f);
68 weight *= lanczos((y - size + 0.5f) * 0.5f, 3.0f);
69 expected_data[y * (size * 2) + x] = weight;
73 EffectChainTester tester(NULL, size * 2, size * 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
76 format.color_space = COLORSPACE_sRGB;
77 format.gamma_curve = GAMMA_LINEAR;
79 FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size, size);
80 input->set_pixel_data(data);
81 tester.get_chain()->add_input(input);
83 Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
84 ASSERT_TRUE(resample_effect->set_int("width", size * 2));
85 ASSERT_TRUE(resample_effect->set_int("height", size * 2));
86 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
88 expect_equal(expected_data, out_data, size * 2, size * 2);
91 TEST(ResampleEffectTest, DownscaleByTwoGetsCorrectPixelCenters) {
94 // This isn't a perfect dot, since the Lanczos filter has a slight
95 // sharpening effect; the most important thing is that we have kept
96 // the texel center right (everything is nicely symmetric).
97 // The approximate magnitudes have been checked against ImageMagick.
98 float expected_data[size * size] = {
99 0.0045, -0.0067, -0.0598, -0.0067, 0.0045,
100 -0.0067, 0.0099, 0.0886, 0.0099, -0.0067,
101 -0.0598, 0.0886, 0.7930, 0.0886, -0.0598,
102 -0.0067, 0.0099, 0.0886, 0.0099, -0.0067,
103 0.0045, -0.0067, -0.0598, -0.0067, 0.0045,
105 float data[size * size * 4], out_data[size * size];
107 for (int y = 0; y < size * 2; ++y) {
108 for (int x = 0; x < size * 2; ++x) {
109 float weight = lanczos((x - size + 0.5f) * 0.5f, 3.0f);
110 weight *= lanczos((y - size + 0.5f) * 0.5f, 3.0f);
111 data[y * (size * 2) + x] = weight;
115 EffectChainTester tester(NULL, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
118 format.color_space = COLORSPACE_sRGB;
119 format.gamma_curve = GAMMA_LINEAR;
121 FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size * 2, size * 2);
122 input->set_pixel_data(data);
123 tester.get_chain()->add_input(input);
125 Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
126 ASSERT_TRUE(resample_effect->set_int("width", size));
127 ASSERT_TRUE(resample_effect->set_int("height", size));
128 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
130 expect_equal(expected_data, out_data, size, size);
133 TEST(ResampleEffectTest, UpscaleByThreeGetsCorrectPixelCenters) {
136 float data[size * size] = {
137 0.0, 0.0, 0.0, 0.0, 0.0,
138 0.0, 0.0, 0.0, 0.0, 0.0,
139 0.0, 0.0, 1.0, 0.0, 0.0,
140 0.0, 0.0, 0.0, 0.0, 0.0,
141 0.0, 0.0, 0.0, 0.0, 0.0,
143 float out_data[size * size * 9];
145 EffectChainTester tester(NULL, size * 3, size * 3, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
148 format.color_space = COLORSPACE_sRGB;
149 format.gamma_curve = GAMMA_LINEAR;
151 FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size, size);
152 input->set_pixel_data(data);
153 tester.get_chain()->add_input(input);
155 Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
156 ASSERT_TRUE(resample_effect->set_int("width", size * 3));
157 ASSERT_TRUE(resample_effect->set_int("height", size * 3));
158 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
160 // We only bother checking that the middle pixel is still correct,
161 // and that symmetry holds.
162 EXPECT_FLOAT_EQ(1.0, out_data[7 * (size * 3) + 7]);
163 for (unsigned y = 0; y < size * 3; ++y) {
164 for (unsigned x = 0; x < size * 3; ++x) {
165 EXPECT_FLOAT_EQ(out_data[y * (size * 3) + x], out_data[(size * 3 - y - 1) * (size * 3) + x]);
166 EXPECT_FLOAT_EQ(out_data[y * (size * 3) + x], out_data[y * (size * 3) + (size * 3 - x - 1)]);
171 TEST(ResampleEffectTest, HeavyResampleGetsSumRight) {
172 // Do only one resample pass, more specifically the last one, which goes to
173 // our fp32 output. This allows us to analyze the precision without intermediate
175 const int swidth = 1, sheight = 1280;
176 const int dwidth = 1, dheight = 64;
178 float data[swidth * sheight], out_data[dwidth * dheight], expected_data[dwidth * dheight];
179 for (int y = 0; y < sheight; ++y) {
180 for (int x = 0; x < swidth; ++x) {
181 data[y * swidth + x] = 1.0f;
184 for (int y = 0; y < dheight; ++y) {
185 for (int x = 0; x < dwidth; ++x) {
186 expected_data[y * dwidth + x] = 1.0f;
190 EffectChainTester tester(NULL, dwidth, dheight, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA32F);
193 format.color_space = COLORSPACE_sRGB;
194 format.gamma_curve = GAMMA_LINEAR;
196 FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, swidth, sheight);
197 input->set_pixel_data(data);
199 tester.get_chain()->add_input(input);
200 Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
201 ASSERT_TRUE(resample_effect->set_int("width", dwidth));
202 ASSERT_TRUE(resample_effect->set_int("height", dheight));
203 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
205 // Require that we are within 10-bit accuracy. Note that this limit is for
206 // one pass only, but the limit is tight enough that it should be good enough
207 // for 10-bit accuracy even after two passes.
208 expect_equal(expected_data, out_data, dwidth, dheight, 0.1 / 1023.0);
211 TEST(ResampleEffectTest, ReadWholePixelFromLeft) {
214 float data[size * size] = {
215 0.0, 0.0, 0.0, 0.0, 0.0,
216 0.0, 0.0, 0.0, 0.0, 0.0,
217 0.0, 0.0, 1.0, 0.0, 0.0,
218 0.0, 0.0, 0.0, 0.0, 0.0,
219 0.0, 0.0, 0.0, 0.0, 0.0,
221 float expected_data[size * size] = {
222 0.0, 0.0, 0.0, 0.0, 0.0,
223 0.0, 0.0, 0.0, 0.0, 0.0,
224 0.0, 1.0, 0.0, 0.0, 0.0,
225 0.0, 0.0, 0.0, 0.0, 0.0,
226 0.0, 0.0, 0.0, 0.0, 0.0,
228 float out_data[size * size];
230 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
231 Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
232 ASSERT_TRUE(resample_effect->set_int("width", size));
233 ASSERT_TRUE(resample_effect->set_int("height", size));
234 ASSERT_TRUE(resample_effect->set_float("left", 1.0f));
235 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
237 expect_equal(expected_data, out_data, size, size);
240 TEST(ResampleEffectTest, ReadQuarterPixelFromLeft) {
243 float data[size * size] = {
244 0.0, 0.0, 0.0, 0.0, 0.0,
245 0.0, 0.0, 0.0, 0.0, 0.0,
246 0.0, 0.0, 1.0, 0.0, 0.0,
247 0.0, 0.0, 0.0, 0.0, 0.0,
248 0.0, 0.0, 0.0, 0.0, 0.0,
251 float expected_data[size * size] = {
252 0.0, 0.0, 0.0, 0.0, 0.0,
253 0.0, 0.0, 0.0, 0.0, 0.0,
255 // sin(x*pi)/(x*pi) * sin(x*pi/3)/(x*pi/3) for
256 // x = -1.75, -0.75, 0.25, 1.25, 2.25.
257 // Note that the weight is mostly on the left side.
258 -0.06779, 0.27019, 0.89007, -0.13287, 0.03002,
260 0.0, 0.0, 0.0, 0.0, 0.0,
261 0.0, 0.0, 0.0, 0.0, 0.0,
263 float out_data[size * size];
265 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
266 Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
267 ASSERT_TRUE(resample_effect->set_int("width", size));
268 ASSERT_TRUE(resample_effect->set_int("height", size));
269 ASSERT_TRUE(resample_effect->set_float("left", 0.25f));
270 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
272 expect_equal(expected_data, out_data, size, size);
275 TEST(ResampleEffectTest, ReadQuarterPixelFromTop) {
277 const int height = 5;
279 float data[width * height] = {
287 // See ReadQuarterPixelFromLeft for explanation of the data.
288 float expected_data[width * height] = {
295 float out_data[width * height];
297 EffectChainTester tester(data, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
298 Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
299 ASSERT_TRUE(resample_effect->set_int("width", width));
300 ASSERT_TRUE(resample_effect->set_int("height", height));
301 ASSERT_TRUE(resample_effect->set_float("top", 0.25f));
302 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
304 expect_equal(expected_data, out_data, width, height);