]> git.sesse.net Git - movit/blob - ycbcr_conversion_effect_test.cpp
Run init_lanczos_table using std::call_once, now that we have C++11.
[movit] / ycbcr_conversion_effect_test.cpp
1 // Unit tests for YCbCrConversionEffect. Mostly done by leveraging
2 // YCbCrInput and seeing that the right thing comes out at the
3 // other end.
4
5 #include <epoxy/gl.h>
6 #include <math.h>
7
8 #include "effect_chain.h"
9 #include "gtest/gtest.h"
10 #include "image_format.h"
11 #include "test_util.h"
12 #include "util.h"
13 #include "ycbcr_input.h"
14
15 namespace movit {
16
17 TEST(YCbCrConversionEffectTest, BasicInOut) {
18         const int width = 1;
19         const int height = 5;
20
21         // Pure-color test inputs, calculated with the formulas in Rec. 601
22         // section 2.5.4.
23         unsigned char y[width * height] = {
24                 16, 235, 81, 145, 41,
25         };
26         unsigned char cb[width * height] = {
27                 128, 128, 90, 54, 240,
28         };
29         unsigned char cr[width * height] = {
30                 128, 128, 240, 34, 110,
31         };
32         unsigned char expected_data[width * height * 4] = {
33                 // The same data, just rearranged.
34                  16, 128, 128, 255,
35                 235, 128, 128, 255,
36                  81,  90, 240, 255,
37                 145,  54,  34, 255,
38                  41, 240, 110, 255
39         };
40
41         unsigned char out_data[width * height * 4];
42
43         EffectChainTester tester(nullptr, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
44
45         ImageFormat format;
46         format.color_space = COLORSPACE_sRGB;
47         format.gamma_curve = GAMMA_sRGB;
48
49         YCbCrFormat ycbcr_format;
50         ycbcr_format.luma_coefficients = YCBCR_REC_601;
51         ycbcr_format.full_range = false;
52         ycbcr_format.num_levels = 256;
53         ycbcr_format.chroma_subsampling_x = 1;
54         ycbcr_format.chroma_subsampling_y = 1;
55         ycbcr_format.cb_x_position = 0.5f;
56         ycbcr_format.cb_y_position = 0.5f;
57         ycbcr_format.cr_x_position = 0.5f;
58         ycbcr_format.cr_y_position = 0.5f;
59
60         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format);
61
62         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
63         input->set_pixel_data(0, y);
64         input->set_pixel_data(1, cb);
65         input->set_pixel_data(2, cr);
66         tester.get_chain()->add_input(input);
67
68         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
69         expect_equal(expected_data, out_data, 4 * width, height);
70 }
71
72 TEST(YCbCrConversionEffectTest, ClampToValidRange) {
73         const int width = 1;
74         const int height = 6;
75
76         // Some out-of-range of at-range values.
77         // Y should be clamped to 16-235 and Cb/Cr to 16-240.
78         // (Alpha should still be 255.)
79         unsigned char y[width * height] = {
80                 0, 10, 16, 235, 240, 255
81         };
82         unsigned char cb[width * height] = {
83                 0, 10, 16, 235, 240, 255,
84         };
85         unsigned char cr[width * height] = {
86                 255, 240, 235, 16, 10, 0,
87         };
88         unsigned char expected_data[width * height * 4] = {
89                 16, 16, 240, 255,
90                 16, 16, 240, 255,
91                 16, 16, 235, 255,
92                 235, 235, 16, 255,
93                 235, 240, 16, 255,
94                 235, 240, 16, 255,
95         };
96
97         unsigned char out_data[width * height * 4];
98
99         EffectChainTester tester(nullptr, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
100
101         ImageFormat format;
102         format.color_space = COLORSPACE_sRGB;
103         format.gamma_curve = GAMMA_sRGB;
104
105         YCbCrFormat ycbcr_format;
106         ycbcr_format.luma_coefficients = YCBCR_REC_601;
107         ycbcr_format.full_range = false;
108         ycbcr_format.num_levels = 256;
109         ycbcr_format.chroma_subsampling_x = 1;
110         ycbcr_format.chroma_subsampling_y = 1;
111         ycbcr_format.cb_x_position = 0.5f;
112         ycbcr_format.cb_y_position = 0.5f;
113         ycbcr_format.cr_x_position = 0.5f;
114         ycbcr_format.cr_y_position = 0.5f;
115
116         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format);
117
118         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
119         input->set_pixel_data(0, y);
120         input->set_pixel_data(1, cb);
121         input->set_pixel_data(2, cr);
122         tester.get_chain()->add_input(input);
123
124         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
125         expect_equal(expected_data, out_data, 4 * width, height);
126 }
127
128 TEST(YCbCrConversionEffectTest, LimitedRangeToFullRange) {
129         const int width = 1;
130         const int height = 5;
131
132         // Pure-color test inputs, calculated with the formulas in Rec. 601
133         // section 2.5.4.
134         unsigned char y[width * height] = {
135                 16, 235, 81, 145, 41,
136         };
137         unsigned char cb[width * height] = {
138                 128, 128, 90, 54, 240,
139         };
140         unsigned char cr[width * height] = {
141                 128, 128, 240, 34, 110,
142         };
143         unsigned char expected_data[width * height * 4] = {
144                 // Range now from 0-255 for all components, and values in-between
145                 // also adjusted a bit.
146                   0, 128, 128, 255,
147                 255, 128, 128, 255,
148                  76,  85, 255, 255,
149                 150,  44,  21, 255,
150                  29, 255, 108, 255
151         };
152
153         unsigned char out_data[width * height * 4];
154
155         EffectChainTester tester(nullptr, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
156
157         ImageFormat format;
158         format.color_space = COLORSPACE_sRGB;
159         format.gamma_curve = GAMMA_sRGB;
160
161         YCbCrFormat ycbcr_format;
162         ycbcr_format.luma_coefficients = YCBCR_REC_601;
163         ycbcr_format.full_range = true;
164         ycbcr_format.num_levels = 256;
165         ycbcr_format.chroma_subsampling_x = 1;
166         ycbcr_format.chroma_subsampling_y = 1;
167         ycbcr_format.cb_x_position = 0.5f;
168         ycbcr_format.cb_y_position = 0.5f;
169         ycbcr_format.cr_x_position = 0.5f;
170         ycbcr_format.cr_y_position = 0.5f;
171
172         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format);
173
174         ycbcr_format.full_range = false;
175         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
176         input->set_pixel_data(0, y);
177         input->set_pixel_data(1, cb);
178         input->set_pixel_data(2, cr);
179         tester.get_chain()->add_input(input);
180
181         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
182
183         // This specific data element has the correct value (110-128)*(255/224) + 128 = 107.509,
184         // which rounds the wrong way on some cards. In normal use, we detect this and round off
185         // in DitherEffect instead (so it's not a problem in pratice), but in unit tests like this,
186         // we don't run with dither, so we simply fudge this one value instead.
187         if (out_data[18] == 107) {
188                 out_data[18] = 108;
189         }
190
191         expect_equal(expected_data, out_data, 4 * width, height);
192 }
193
194 TEST(YCbCrConversionEffectTest, PlanarOutput) {
195         const int width = 1;
196         const int height = 5;
197
198         // Pure-color test inputs, calculated with the formulas in Rec. 601
199         // section 2.5.4.
200         unsigned char y[width * height] = {
201                 16, 235, 81, 145, 41,
202         };
203         unsigned char cb[width * height] = {
204                 128, 128, 90, 54, 240,
205         };
206         unsigned char cr[width * height] = {
207                 128, 128, 240, 34, 110,
208         };
209
210         unsigned char out_y[width * height], out_cb[width * height], out_cr[width * height];
211
212         EffectChainTester tester(nullptr, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
213
214         ImageFormat format;
215         format.color_space = COLORSPACE_sRGB;
216         format.gamma_curve = GAMMA_sRGB;
217
218         YCbCrFormat ycbcr_format;
219         ycbcr_format.luma_coefficients = YCBCR_REC_601;
220         ycbcr_format.full_range = false;
221         ycbcr_format.num_levels = 256;
222         ycbcr_format.chroma_subsampling_x = 1;
223         ycbcr_format.chroma_subsampling_y = 1;
224         ycbcr_format.cb_x_position = 0.5f;
225         ycbcr_format.cb_y_position = 0.5f;
226         ycbcr_format.cr_x_position = 0.5f;
227         ycbcr_format.cr_y_position = 0.5f;
228
229         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format, YCBCR_OUTPUT_PLANAR);
230
231         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
232         input->set_pixel_data(0, y);
233         input->set_pixel_data(1, cb);
234         input->set_pixel_data(2, cr);
235         tester.get_chain()->add_input(input);
236
237         tester.run(out_y, out_cb, out_cr, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
238         expect_equal(y, out_y, width, height);
239         expect_equal(cb, out_cb, width, height);
240         expect_equal(cr, out_cr, width, height);
241 }
242
243 TEST(YCbCrConversionEffectTest, SplitLumaAndChroma) {
244         const int width = 1;
245         const int height = 5;
246
247         // Pure-color test inputs, calculated with the formulas in Rec. 601
248         // section 2.5.4.
249         unsigned char y[width * height] = {
250                 16, 235, 81, 145, 41,
251         };
252         unsigned char cb[width * height] = {
253                 128, 128, 90, 54, 240,
254         };
255         unsigned char cr[width * height] = {
256                 128, 128, 240, 34, 110,
257         };
258
259         // The R and A data, rearranged. Note: The G and B channels
260         // (the middle columns) are undefined. If we change the behavior,
261         // the test will need to be updated, but a failure is expected.
262         unsigned char expected_y[width * height * 4] = {
263                  16, /*undefined:*/  16, /*undefined:*/  16, 255,
264                 235, /*undefined:*/ 235, /*undefined:*/ 235, 255,
265                  81, /*undefined:*/  81, /*undefined:*/  81, 255,
266                 145, /*undefined:*/ 145, /*undefined:*/ 145, 255,
267                  41, /*undefined:*/  41, /*undefined:*/  41, 255,
268         };
269
270         // Just the Cb and Cr data, rearranged. The B and A channels
271         // are undefined, as below.
272         unsigned char expected_cbcr[width * height * 4] = {
273                 128, 128, /*undefined:*/ 128, /*undefined:*/ 255,
274                 128, 128, /*undefined:*/ 128, /*undefined:*/ 255,
275                  90, 240, /*undefined:*/ 240, /*undefined:*/ 255,
276                  54,  34, /*undefined:*/  34, /*undefined:*/ 255,
277                 240, 110, /*undefined:*/ 110, /*undefined:*/ 255,
278         };
279
280         unsigned char out_y[width * height * 4], out_cbcr[width * height * 4];
281
282         EffectChainTester tester(nullptr, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
283
284         ImageFormat format;
285         format.color_space = COLORSPACE_sRGB;
286         format.gamma_curve = GAMMA_sRGB;
287
288         YCbCrFormat ycbcr_format;
289         ycbcr_format.luma_coefficients = YCBCR_REC_601;
290         ycbcr_format.full_range = false;
291         ycbcr_format.num_levels = 256;
292         ycbcr_format.chroma_subsampling_x = 1;
293         ycbcr_format.chroma_subsampling_y = 1;
294         ycbcr_format.cb_x_position = 0.5f;
295         ycbcr_format.cb_y_position = 0.5f;
296         ycbcr_format.cr_x_position = 0.5f;
297         ycbcr_format.cr_y_position = 0.5f;
298
299         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format, YCBCR_OUTPUT_SPLIT_Y_AND_CBCR);
300
301         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
302         input->set_pixel_data(0, y);
303         input->set_pixel_data(1, cb);
304         input->set_pixel_data(2, cr);
305         tester.get_chain()->add_input(input);
306
307         tester.run(out_y, out_cbcr, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
308         expect_equal(expected_y, out_y, width * 4, height);
309         expect_equal(expected_cbcr, out_cbcr, width * 4, height);
310 }
311
312 TEST(YCbCrConversionEffectTest, OutputChunkyAndRGBA) {
313         const int width = 1;
314         const int height = 5;
315
316         // Pure-color test inputs, calculated with the formulas in Rec. 601
317         // section 2.5.4.
318         unsigned char y[width * height] = {
319                 16, 235, 81, 145, 41,
320         };
321         unsigned char cb[width * height] = {
322                 128, 128, 90, 54, 240,
323         };
324         unsigned char cr[width * height] = {
325                 128, 128, 240, 34, 110,
326         };
327         unsigned char expected_ycbcr[width * height * 4] = {
328                 // The same data, just rearranged.
329                  16, 128, 128, 255,
330                 235, 128, 128, 255,
331                  81,  90, 240, 255,
332                 145,  54,  34, 255,
333                  41, 240, 110, 255
334         };
335         unsigned char expected_rgba[width * height * 4] = {
336                   0,   0,   0, 255,
337                 255, 255, 255, 255,
338                 255,   0,   0, 255,
339                   0, 255,   0, 255,
340                   0,   0, 255, 255,
341         };
342
343         unsigned char out_ycbcr[width * height * 4];
344         unsigned char out_rgba[width * height * 4];
345
346         EffectChainTester tester(nullptr, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
347
348         ImageFormat format;
349         format.color_space = COLORSPACE_sRGB;
350         format.gamma_curve = GAMMA_sRGB;
351
352         YCbCrFormat ycbcr_format;
353         ycbcr_format.luma_coefficients = YCBCR_REC_601;
354         ycbcr_format.full_range = false;
355         ycbcr_format.num_levels = 256;
356         ycbcr_format.chroma_subsampling_x = 1;
357         ycbcr_format.chroma_subsampling_y = 1;
358         ycbcr_format.cb_x_position = 0.5f;
359         ycbcr_format.cb_y_position = 0.5f;
360         ycbcr_format.cr_x_position = 0.5f;
361         ycbcr_format.cr_y_position = 0.5f;
362
363         tester.add_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
364         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format);
365
366         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
367         input->set_pixel_data(0, y);
368         input->set_pixel_data(1, cb);
369         input->set_pixel_data(2, cr);
370         tester.get_chain()->add_input(input);
371
372         // Note: We don't test that the values actually get dithered,
373         // just that the shader compiles and doesn't mess up badly.
374         tester.get_chain()->set_dither_bits(8);
375
376         tester.run(out_ycbcr, out_rgba, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
377         expect_equal(expected_ycbcr, out_ycbcr, width * 4, height);
378
379         // Y'CbCr isn't 100% accurate (the input values are rounded),
380         // so we need some leeway.
381         expect_equal(expected_rgba, out_rgba, 4 * width, height, 7, 255 * 0.002);
382 }
383
384 TEST(YCbCrConversionEffectTest, MultipleOutputsAndRGBA) {
385         const int width = 1;
386         const int height = 5;
387
388         // Pure-color test inputs, calculated with the formulas in Rec. 601
389         // section 2.5.4.
390         unsigned char y[width * height] = {
391                 16, 235, 81, 145, 41,
392         };
393         unsigned char cb[width * height] = {
394                 128, 128, 90, 54, 240,
395         };
396         unsigned char cr[width * height] = {
397                 128, 128, 240, 34, 110,
398         };
399         unsigned char expected_ycbcr[width * height * 4] = {
400                 // The same data, just rearranged.
401                  16, 128, 128, 255,
402                 235, 128, 128, 255,
403                  81,  90, 240, 255,
404                 145,  54,  34, 255,
405                  41, 240, 110, 255
406         };
407         unsigned char expected_rgba[width * height * 4] = {
408                   0,   0,   0, 255,
409                 255, 255, 255, 255,
410                 255,   0,   0, 255,
411                   0, 255,   0, 255,
412                   0,   0, 255, 255,
413         };
414
415         unsigned char out_ycbcr[width * height * 4];
416         unsigned char out_y[width * height * 4];
417         unsigned char out_cbcr[width * height * 4];
418         unsigned char out_rgba[width * height * 4];
419
420         EffectChainTester tester(nullptr, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
421
422         ImageFormat format;
423         format.color_space = COLORSPACE_sRGB;
424         format.gamma_curve = GAMMA_sRGB;
425
426         YCbCrFormat ycbcr_format;
427         ycbcr_format.luma_coefficients = YCBCR_REC_601;
428         ycbcr_format.full_range = false;
429         ycbcr_format.num_levels = 256;
430         ycbcr_format.chroma_subsampling_x = 1;
431         ycbcr_format.chroma_subsampling_y = 1;
432         ycbcr_format.cb_x_position = 0.5f;
433         ycbcr_format.cb_y_position = 0.5f;
434         ycbcr_format.cr_x_position = 0.5f;
435         ycbcr_format.cr_y_position = 0.5f;
436
437         tester.add_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
438         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format);
439         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format, YCBCR_OUTPUT_SPLIT_Y_AND_CBCR);
440
441         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
442         input->set_pixel_data(0, y);
443         input->set_pixel_data(1, cb);
444         input->set_pixel_data(2, cr);
445         tester.get_chain()->add_input(input);
446
447         // Note: We don't test that the values actually get dithered,
448         // just that the shader compiles and doesn't mess up badly.
449         tester.get_chain()->set_dither_bits(8);
450
451         tester.run(out_ycbcr, out_y, out_cbcr, out_rgba, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
452         expect_equal(expected_ycbcr, out_ycbcr, width * 4, height);
453
454         // Check that the extra Y' and CbCr outputs also are fine.
455         for (unsigned i = 0; i < width * height; ++i) {
456                 out_ycbcr[i * 4] = out_y[i * 4];
457                 out_ycbcr[i * 4 + 1] = out_cbcr[i * 4 + 0];
458                 out_ycbcr[i * 4 + 2] = out_cbcr[i * 4 + 1];
459         }
460         expect_equal(expected_ycbcr, out_ycbcr, width * 4, height);
461
462         // Y'CbCr isn't 100% accurate (the input values are rounded),
463         // so we need some leeway.
464         expect_equal(expected_rgba, out_rgba, 4 * width, height, 7, 255 * 0.002);
465 }
466
467 // Very similar to PlanarOutput.
468 TEST(YCbCrConversionEffectTest, ChangeOutputFormat) {
469         const int width = 1;
470         const int height = 5;
471
472         // Pure-color test inputs, calculated with the formulas in Rec. 601
473         // section 2.5.4.
474         unsigned char y[width * height] = {
475                 16, 235, 81, 145, 41,
476         };
477         unsigned char cb[width * height] = {
478                 128, 128, 90, 54, 240,
479         };
480         unsigned char cr[width * height] = {
481                 128, 128, 240, 34, 110,
482         };
483
484         unsigned char out_y[width * height], out_cb[width * height], out_cr[width * height];
485
486         EffectChainTester tester(nullptr, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
487
488         ImageFormat format;
489         format.color_space = COLORSPACE_sRGB;
490         format.gamma_curve = GAMMA_sRGB;
491
492         YCbCrFormat ycbcr_format;
493         ycbcr_format.luma_coefficients = YCBCR_REC_709;  // Deliberately wrong at first.
494         ycbcr_format.full_range = false;
495         ycbcr_format.num_levels = 256;
496         ycbcr_format.chroma_subsampling_x = 1;
497         ycbcr_format.chroma_subsampling_y = 1;
498         ycbcr_format.cb_x_position = 0.5f;
499         ycbcr_format.cb_y_position = 0.5f;
500         ycbcr_format.cr_x_position = 0.5f;
501         ycbcr_format.cr_y_position = 0.5f;
502
503         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format, YCBCR_OUTPUT_PLANAR);
504
505         ycbcr_format.luma_coefficients = YCBCR_REC_601;
506         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
507         input->set_pixel_data(0, y);
508         input->set_pixel_data(1, cb);
509         input->set_pixel_data(2, cr);
510         tester.get_chain()->add_input(input);
511
512         tester.run(out_y, out_cb, out_cr, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
513
514         // Now change the output format to match what we gave the input, and re-run.
515         tester.get_chain()->change_ycbcr_output_format(ycbcr_format);
516         tester.run(out_y, out_cb, out_cr, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
517
518         expect_equal(y, out_y, width, height);
519         expect_equal(cb, out_cb, width, height);
520         expect_equal(cr, out_cr, width, height);
521 }
522
523 TEST(YCbCrConversionEffectTest, TenBitOutput) {
524         const int width = 1;
525         const int height = 5;
526
527         // Pure-color test inputs.
528         float data[width * height * 4] = {
529                 0.0f, 0.0f, 0.0f, 1.0f,
530                 1.0f, 1.0f, 1.0f, 1.0f,
531                 1.0f, 0.0f, 0.0f, 1.0f,
532                 0.0f, 1.0f, 0.0f, 1.0f,
533                 0.0f, 0.0f, 1.0f, 1.0f,
534         };
535         uint32_t out_data[width * height];
536         int expanded_out_data[width * height * 4];
537         int expected_data[width * height * 4] = {
538                 // Expected results, calculated using formulas 3.2, 3.3 and 3.4
539                 // from Rec. 709. (Except the first two, which are obvious
540                 // given the 64–940 range of luminance.)
541                  64, 512, 512, 3,
542                 940, 512, 512, 3,
543                 250, 409, 960, 3,
544                 691, 167, 105, 3,
545                 127, 960, 471, 3,
546         };
547
548         EffectChainTester tester(nullptr, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGB10_A2);
549         tester.add_input(data, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_sRGB);
550
551         ImageFormat format;
552         format.color_space = COLORSPACE_sRGB;
553         format.gamma_curve = GAMMA_sRGB;
554
555         YCbCrFormat ycbcr_format;
556         ycbcr_format.luma_coefficients = YCBCR_REC_709;
557         ycbcr_format.full_range = false;
558         ycbcr_format.num_levels = 1024;
559         ycbcr_format.chroma_subsampling_x = 1;
560         ycbcr_format.chroma_subsampling_y = 1;
561         ycbcr_format.cb_x_position = 0.5f;
562         ycbcr_format.cb_y_position = 0.5f;
563         ycbcr_format.cr_x_position = 0.5f;
564         ycbcr_format.cr_y_position = 0.5f;
565
566         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format);
567         tester.run_10_10_10_2(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
568
569         // Unpack 10:10:10:2 to 32:32:32:32.
570         for (unsigned i = 0; i < width * height; ++i) {
571                 expanded_out_data[i * 4 + 0] = out_data[i] & 0x3ff;
572                 expanded_out_data[i * 4 + 1] = (out_data[i] >> 10) & 0x3ff;
573                 expanded_out_data[i * 4 + 2] = (out_data[i] >> 20) & 0x3ff;
574                 expanded_out_data[i * 4 + 3] = (out_data[i] >> 30);
575         }
576         expect_equal(expected_data, expanded_out_data, 4 * width, height);
577 }
578
579 TEST(YCbCrConversionEffectTest, TenBitOutputInSixteen) {
580         const int width = 1;
581         const int height = 5;
582
583         // Same test inputs and outputs as TenBitOutput, except that alpha
584         // is 16 bits instead of two.
585         float data[width * height * 4] = {
586                 0.0f, 0.0f, 0.0f, 1.0f,
587                 1.0f, 1.0f, 1.0f, 1.0f,
588                 1.0f, 0.0f, 0.0f, 1.0f,
589                 0.0f, 1.0f, 0.0f, 1.0f,
590                 0.0f, 0.0f, 1.0f, 1.0f,
591         };
592         uint16_t out_data[width * height * 4];
593         uint16_t expected_data[width * height * 4] = {
594                  64, 512, 512, 65535,
595                 940, 512, 512, 65535,
596                 250, 409, 960, 65535,
597                 691, 167, 105, 65535,
598                 127, 960, 471, 65535,
599         };
600
601         EffectChainTester tester(nullptr, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA16);
602         tester.add_input(data, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_sRGB);
603
604         ImageFormat format;
605         format.color_space = COLORSPACE_sRGB;
606         format.gamma_curve = GAMMA_sRGB;
607
608         YCbCrFormat ycbcr_format;
609         ycbcr_format.luma_coefficients = YCBCR_REC_709;
610         ycbcr_format.full_range = false;
611         ycbcr_format.num_levels = 1024;
612         ycbcr_format.chroma_subsampling_x = 1;
613         ycbcr_format.chroma_subsampling_y = 1;
614         ycbcr_format.cb_x_position = 0.5f;
615         ycbcr_format.cb_y_position = 0.5f;
616         ycbcr_format.cr_x_position = 0.5f;
617         ycbcr_format.cr_y_position = 0.5f;
618
619         tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format, YCBCR_OUTPUT_INTERLEAVED, GL_UNSIGNED_SHORT);
620         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
621
622         // Add some slight leeway for the benefit of cards that don't
623         // round correctly (would be fixed by DitherEffect if we had dither).
624         expect_equal(expected_data, out_data, 4 * width, height, 2);
625 }
626
627 }  // namespace movit