]> git.sesse.net Git - movit/blob - ycbcr_input_test.cpp
Add a distclean target.
[movit] / ycbcr_input_test.cpp
1 // Unit tests for YCbCrInput.
2 // FIXME: This class really ought to support mipmaps.
3
4 #include <stddef.h>
5
6 #include "effect_chain.h"
7 #include "gtest/gtest.h"
8 #include "test_util.h"
9 #include "ycbcr_input.h"
10
11 TEST(YCbCrInput, Simple444) {
12         const int width = 1;
13         const int height = 5;
14
15         // Pure-color test inputs, calculated with the formulas in Rec. 601
16         // section 2.5.4.
17         unsigned char y[width * height] = {
18                 16, 235, 81, 145, 41,
19         };
20         unsigned char cb[width * height] = {
21                 128, 128, 90, 54, 240,
22         };
23         unsigned char cr[width * height] = {
24                 128, 128, 240, 34, 110,
25         };
26         float expected_data[4 * width * height] = {
27                 0.0, 0.0, 0.0, 1.0,
28                 1.0, 1.0, 1.0, 1.0,
29                 1.0, 0.0, 0.0, 1.0,
30                 0.0, 1.0, 0.0, 1.0,
31                 0.0, 0.0, 1.0, 1.0,
32         };
33         float out_data[4 * width * height];
34
35         EffectChainTester tester(NULL, width, height);
36
37         ImageFormat format;
38         format.color_space = COLORSPACE_sRGB;
39         format.gamma_curve = GAMMA_sRGB;
40
41         YCbCrFormat ycbcr_format;
42         ycbcr_format.luma_coefficients = YCBCR_REC_601;
43         ycbcr_format.full_range = false;
44         ycbcr_format.chroma_subsampling_x = 1;
45         ycbcr_format.chroma_subsampling_y = 1;
46         ycbcr_format.cb_x_position = 0.5f;
47         ycbcr_format.cb_y_position = 0.5f;
48         ycbcr_format.cr_x_position = 0.5f;
49         ycbcr_format.cr_y_position = 0.5f;
50
51         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
52         input->set_pixel_data(0, y);
53         input->set_pixel_data(1, cb);
54         input->set_pixel_data(2, cr);
55         tester.get_chain()->add_input(input);
56
57         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
58
59         // Y'CbCr isn't 100% accurate (the input values are rounded),
60         // so we need some leeway.
61         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
62 }
63
64 TEST(YCbCrInput, FullRangeRec601) {
65         const int width = 1;
66         const int height = 5;
67
68         // Pure-color test inputs, calculated with the formulas in Rec. 601
69         // section 2.5.4 but without the scaling factors applied
70         // (so both R, G, B, Y, Cb and R vary from 0 to 255).
71         unsigned char y[width * height] = {
72                 0, 255, 76, 150, 29,
73         };
74         unsigned char cb[width * height] = {
75                 128, 128, 85, 44, 255,
76         };
77         unsigned char cr[width * height] = {
78                 128, 128, 255, 21, 107,
79         };
80         float expected_data[4 * width * height] = {
81                 0.0, 0.0, 0.0, 1.0,
82                 1.0, 1.0, 1.0, 1.0,
83                 1.0, 0.0, 0.0, 1.0,
84                 0.0, 1.0, 0.0, 1.0,
85                 0.0, 0.0, 1.0, 1.0,
86         };
87         float out_data[4 * width * height];
88
89         EffectChainTester tester(NULL, width, height);
90
91         ImageFormat format;
92         format.color_space = COLORSPACE_sRGB;
93         format.gamma_curve = GAMMA_sRGB;
94
95         YCbCrFormat ycbcr_format;
96         ycbcr_format.luma_coefficients = YCBCR_REC_601;
97         ycbcr_format.full_range = true;
98         ycbcr_format.chroma_subsampling_x = 1;
99         ycbcr_format.chroma_subsampling_y = 1;
100         ycbcr_format.cb_x_position = 0.5f;
101         ycbcr_format.cb_y_position = 0.5f;
102         ycbcr_format.cr_x_position = 0.5f;
103         ycbcr_format.cr_y_position = 0.5f;
104
105         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
106         input->set_pixel_data(0, y);
107         input->set_pixel_data(1, cb);
108         input->set_pixel_data(2, cr);
109         tester.get_chain()->add_input(input);
110
111         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
112
113         // Y'CbCr isn't 100% accurate (the input values are rounded),
114         // so we need some leeway.
115         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
116 }
117
118 TEST(YCbCrInput, Rec709) {
119         const int width = 1;
120         const int height = 5;
121
122         // Pure-color test inputs, calculated with the formulas in Rec. 709
123         // page 19, items 3.4 and 3.5.
124         unsigned char y[width * height] = {
125                 16, 235, 63, 173, 32, 
126         };
127         unsigned char cb[width * height] = {
128                 128, 128, 102, 42, 240,
129         };
130         unsigned char cr[width * height] = {
131                 128, 128, 240, 26, 118,
132         };
133         float expected_data[4 * width * height] = {
134                 0.0, 0.0, 0.0, 1.0,
135                 1.0, 1.0, 1.0, 1.0,
136                 1.0, 0.0, 0.0, 1.0,
137                 0.0, 1.0, 0.0, 1.0,
138                 0.0, 0.0, 1.0, 1.0,
139         };
140         float out_data[4 * width * height];
141
142         EffectChainTester tester(NULL, width, height);
143
144         ImageFormat format;
145         format.color_space = COLORSPACE_sRGB;
146         format.gamma_curve = GAMMA_sRGB;
147
148         YCbCrFormat ycbcr_format;
149         ycbcr_format.luma_coefficients = YCBCR_REC_709;
150         ycbcr_format.full_range = false;
151         ycbcr_format.chroma_subsampling_x = 1;
152         ycbcr_format.chroma_subsampling_y = 1;
153         ycbcr_format.cb_x_position = 0.5f;
154         ycbcr_format.cb_y_position = 0.5f;
155         ycbcr_format.cr_x_position = 0.5f;
156         ycbcr_format.cr_y_position = 0.5f;
157
158         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
159         input->set_pixel_data(0, y);
160         input->set_pixel_data(1, cb);
161         input->set_pixel_data(2, cr);
162         tester.get_chain()->add_input(input);
163
164         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
165
166         // Y'CbCr isn't 100% accurate (the input values are rounded),
167         // so we need some leeway.
168         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
169 }
170
171 TEST(YCbCrInput, Subsampling420) {
172         const int width = 4;
173         const int height = 4;
174
175         unsigned char y[width * height] = {
176                 126, 126, 126, 126,
177                 126, 126, 126, 126,
178                 126, 126, 126, 126,
179                 126, 126, 126, 126,
180         };
181         unsigned char cb[(width/2) * (height/2)] = {
182                 64, 128,
183                 128, 192,
184         };
185         unsigned char cr[(width/2) * (height/2)] = {
186                 128, 128,
187                 128, 128,
188         };
189
190         // Note: This is only the blue channel. The chroma samples (with associated
191         // values for blue) are marked off in comments.
192         float expected_data[width * height] = {
193                 0.000, 0.125, 0.375, 0.500, 
194                  /* 0.0 */      /* 0.5 */
195                 0.125, 0.250, 0.500, 0.625,
196
197                 0.375, 0.500, 0.750, 0.875,
198                  /* 0.5 */      /* 1.0 */
199                 0.500, 0.625, 0.875, 1.000,
200         };
201         float out_data[width * height];
202
203         EffectChainTester tester(NULL, width, height);
204
205         ImageFormat format;
206         format.color_space = COLORSPACE_sRGB;
207         format.gamma_curve = GAMMA_sRGB;
208
209         YCbCrFormat ycbcr_format;
210         ycbcr_format.luma_coefficients = YCBCR_REC_601;
211         ycbcr_format.full_range = false;
212         ycbcr_format.chroma_subsampling_x = 2;
213         ycbcr_format.chroma_subsampling_y = 2;
214         ycbcr_format.cb_x_position = 0.5f;
215         ycbcr_format.cb_y_position = 0.5f;
216         ycbcr_format.cr_x_position = 0.5f;
217         ycbcr_format.cr_y_position = 0.5f;
218
219         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
220         input->set_pixel_data(0, y);
221         input->set_pixel_data(1, cb);
222         input->set_pixel_data(2, cr);
223         tester.get_chain()->add_input(input);
224
225         tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
226
227         // Y'CbCr isn't 100% accurate (the input values are rounded),
228         // so we need some leeway.
229         expect_equal(expected_data, out_data, width, height, 0.01, 0.001);
230 }
231
232 TEST(YCbCrInput, Subsampling420WithNonCenteredSamples) {
233         const int width = 4;
234         const int height = 4;
235
236         unsigned char y[width * height] = {
237                 126, 126, 126, 126,
238                 126, 126, 126, 126,
239                 126, 126, 126, 126,
240                 126, 126, 126, 126,
241         };
242         unsigned char cb[(width/2) * (height/2)] = {
243                 64, 128,
244                 128, 192,
245         };
246         unsigned char cr[(width/2) * (height/2)] = {
247                 128, 128,
248                 128, 128,
249         };
250
251         // Note: This is only the blue channel. The chroma samples (with associated
252         // values for blue) are marked off in comments.
253         float expected_data[width * height] = {
254                    0.000, 0.250, 0.500, 0.500, 
255                 /* 0.0 */     /* 0.5 */
256                    0.125, 0.375, 0.625, 0.625,
257
258                    0.375, 0.625, 0.875, 0.875,
259                 /* 0.5 */     /* 1.0 */
260                    0.500, 0.750, 1.000, 1.000,
261         };
262         float out_data[width * height];
263
264         EffectChainTester tester(NULL, width, height);
265
266         ImageFormat format;
267         format.color_space = COLORSPACE_sRGB;
268         format.gamma_curve = GAMMA_sRGB;
269
270         YCbCrFormat ycbcr_format;
271         ycbcr_format.luma_coefficients = YCBCR_REC_601;
272         ycbcr_format.full_range = false;
273         ycbcr_format.chroma_subsampling_x = 2;
274         ycbcr_format.chroma_subsampling_y = 2;
275         ycbcr_format.cb_x_position = 0.0f;
276         ycbcr_format.cb_y_position = 0.5f;
277         ycbcr_format.cr_x_position = 0.0f;
278         ycbcr_format.cr_y_position = 0.5f;
279
280         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
281         input->set_pixel_data(0, y);
282         input->set_pixel_data(1, cb);
283         input->set_pixel_data(2, cr);
284         tester.get_chain()->add_input(input);
285
286         tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
287
288         // Y'CbCr isn't 100% accurate (the input values are rounded),
289         // so we need some leeway.
290         expect_equal(expected_data, out_data, width, height, 0.01, 0.001);
291 }
292
293 // Yes, some 4:2:2 formats actually have this craziness.
294 TEST(YCbCrInput, DifferentCbAndCrPositioning) {
295         const int width = 4;
296         const int height = 4;
297
298         unsigned char y[width * height] = {
299                 126, 126, 126, 126,
300                 126, 126, 126, 126,
301                 126, 126, 126, 126,
302                 126, 126, 126, 126,
303         };
304         unsigned char cb[(width/2) * height] = {
305                 64, 128,
306                 128, 192,
307                 128, 128,
308                 128, 128,
309         };
310         unsigned char cr[(width/2) * height] = {
311                 48, 128,
312                 128, 208,
313                 128, 128,
314                 128, 128,
315         };
316
317         // Chroma samples in this csae are always co-sited with a luma sample;
318         // their associated color values and position are marked off in comments.
319         float expected_data_blue[width * height] = {
320                    0.000 /* 0.0 */, 0.250,           0.500 /* 0.5 */, 0.500, 
321                    0.500 /* 0.5 */, 0.750,           1.000 /* 1.0 */, 1.000, 
322                    0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 0.500, 
323                    0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 0.500, 
324         };
325         float expected_data_red[width * height] = {
326                    0.000,           0.000 /* 0.0 */, 0.250,           0.500 /* 0.5 */, 
327                    0.500,           0.500 /* 0.5 */, 0.750,           1.000 /* 1.0 */, 
328                    0.500,           0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 
329                    0.500,           0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 
330         };
331         float out_data[width * height];
332
333         EffectChainTester tester(NULL, width, height);
334
335         ImageFormat format;
336         format.color_space = COLORSPACE_sRGB;
337         format.gamma_curve = GAMMA_sRGB;
338
339         YCbCrFormat ycbcr_format;
340         ycbcr_format.luma_coefficients = YCBCR_REC_601;
341         ycbcr_format.full_range = false;
342         ycbcr_format.chroma_subsampling_x = 2;
343         ycbcr_format.chroma_subsampling_y = 1;
344         ycbcr_format.cb_x_position = 0.0f;
345         ycbcr_format.cb_y_position = 0.5f;
346         ycbcr_format.cr_x_position = 1.0f;
347         ycbcr_format.cr_y_position = 0.5f;
348
349         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
350         input->set_pixel_data(0, y);
351         input->set_pixel_data(1, cb);
352         input->set_pixel_data(2, cr);
353         tester.get_chain()->add_input(input);
354
355         // Y'CbCr isn't 100% accurate (the input values are rounded),
356         // so we need some leeway.
357         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
358         expect_equal(expected_data_red, out_data, width, height, 0.02, 0.002);
359
360         tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
361         expect_equal(expected_data_blue, out_data, width, height, 0.01, 0.001);
362 }