1 // Unit tests for ResampleEffect.
6 #include "effect_chain.h"
7 #include "flat_input.h"
9 #include "gtest/gtest.h"
10 #include "image_format.h"
11 #include "resample_effect.h"
12 #include "test_util.h"
18 return sin(M_PI * x) / (M_PI * x);
21 float lanczos(float x, float a)
26 return sinc(x) * sinc(x / a);
32 TEST(ResampleEffectTest, IdentityTransformDoesNothing) {
35 float data[size * size] = {
41 float out_data[size * size];
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);
49 expect_equal(data, out_data, size, size);
52 TEST(ResampleEffectTest, UpscaleByTwoGetsCorrectPixelCenters) {
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,
62 float expected_data[size * size * 4], out_data[size * size * 4];
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;
72 EffectChainTester tester(NULL, size * 2, size * 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
75 format.color_space = COLORSPACE_sRGB;
76 format.gamma_curve = GAMMA_LINEAR;
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);
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);
87 expect_equal(expected_data, out_data, size * 2, size * 2);
90 TEST(ResampleEffectTest, DownscaleByTwoGetsCorrectPixelCenters) {
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,
104 float data[size * size * 4], out_data[size * size];
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;
114 EffectChainTester tester(NULL, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
117 format.color_space = COLORSPACE_sRGB;
118 format.gamma_curve = GAMMA_LINEAR;
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);
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);
129 expect_equal(expected_data, out_data, size, size);
132 TEST(ResampleEffectTest, UpscaleByThreeGetsCorrectPixelCenters) {
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,
142 float out_data[size * size * 9];
144 EffectChainTester tester(NULL, size * 3, size * 3, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
147 format.color_space = COLORSPACE_sRGB;
148 format.gamma_curve = GAMMA_LINEAR;
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);
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);
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)]);
170 TEST(ResampleEffectTest, HeavyResampleGetsSumRight) {
171 // Do only one resample pass, more specifically the last one, which goes to
172 // our fp32 output. This allows us to analyze the precision without intermediate
174 const int swidth = 1, sheight = 1280;
175 const int dwidth = 1, dheight = 64;
177 float data[swidth * sheight], out_data[dwidth * dheight], expected_data[dwidth * dheight];
178 for (int y = 0; y < sheight; ++y) {
179 for (int x = 0; x < swidth; ++x) {
180 data[y * swidth + x] = 1.0f;
183 for (int y = 0; y < dheight; ++y) {
184 for (int x = 0; x < dwidth; ++x) {
185 expected_data[y * dwidth + x] = 1.0f;
189 EffectChainTester tester(NULL, dwidth, dheight, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA32F);
192 format.color_space = COLORSPACE_sRGB;
193 format.gamma_curve = GAMMA_LINEAR;
195 FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, swidth, sheight);
196 input->set_pixel_data(data);
198 tester.get_chain()->add_input(input);
199 Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect());
200 ASSERT_TRUE(resample_effect->set_int("width", dwidth));
201 ASSERT_TRUE(resample_effect->set_int("height", dheight));
202 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
204 // Require that we are within 10-bit accuracy. Note that this is for
205 // one pass only; some cards that don't have correct fp32 -> fp16
206 // rounding in the intermediate framebuffers will go outside this after
207 // a 2D resize. This limit is tight enough that it will be good enough
208 // for 8-bit accuracy, though.
209 expect_equal(expected_data, out_data, dwidth, dheight, 0.5 / 1023.0);