1 // Unit tests for YCbCrInput.
2 // FIXME: This class really ought to support mipmaps.
6 #include "effect_chain.h"
7 #include "gtest/gtest.h"
9 #include "ycbcr_input.h"
11 TEST(YCbCrInput, Simple444) {
15 // Pure-color test inputs, calculated with the formulas in Rec. 601
17 unsigned char y[width * height] = {
20 unsigned char cb[width * height] = {
21 128, 128, 90, 54, 240,
23 unsigned char cr[width * height] = {
24 128, 128, 240, 34, 110,
26 float expected_data[4 * width * height] = {
33 float out_data[4 * width * height];
35 EffectChainTester tester(NULL, width, height);
38 format.color_space = COLORSPACE_sRGB;
39 format.gamma_curve = GAMMA_sRGB;
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;
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);
57 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
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);
64 TEST(YCbCrInput, FullRangeRec601) {
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] = {
74 unsigned char cb[width * height] = {
75 128, 128, 85, 44, 255,
77 unsigned char cr[width * height] = {
78 128, 128, 255, 21, 107,
80 float expected_data[4 * width * height] = {
87 float out_data[4 * width * height];
89 EffectChainTester tester(NULL, width, height);
92 format.color_space = COLORSPACE_sRGB;
93 format.gamma_curve = GAMMA_sRGB;
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;
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);
111 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
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);
118 TEST(YCbCrInput, Rec709) {
120 const int height = 5;
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,
127 unsigned char cb[width * height] = {
128 128, 128, 102, 42, 240,
130 unsigned char cr[width * height] = {
131 128, 128, 240, 26, 118,
133 float expected_data[4 * width * height] = {
140 float out_data[4 * width * height];
142 EffectChainTester tester(NULL, width, height);
145 format.color_space = COLORSPACE_sRGB;
146 format.gamma_curve = GAMMA_sRGB;
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;
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);
164 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
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);
171 TEST(YCbCrInput, Rec2020) {
173 const int height = 5;
175 // Pure-color test inputs, calculated with the formulas in Rec. 2020
176 // page 4, tables 4 and 5 (for conventional non-constant luminance).
177 // Note that we still use 8-bit inputs, even though Rec. 2020 is only
178 // defined for 10- and 12-bit.
179 unsigned char y[width * height] = {
180 16, 235, 74, 164, 29,
182 unsigned char cb[width * height] = {
183 128, 128, 97, 47, 240,
185 unsigned char cr[width * height] = {
186 128, 128, 240, 25, 119,
188 float expected_data[4 * width * height] = {
195 float out_data[4 * width * height];
197 EffectChainTester tester(NULL, width, height);
200 format.color_space = COLORSPACE_sRGB;
201 format.gamma_curve = GAMMA_sRGB;
203 YCbCrFormat ycbcr_format;
204 ycbcr_format.luma_coefficients = YCBCR_REC_2020;
205 ycbcr_format.full_range = false;
206 ycbcr_format.chroma_subsampling_x = 1;
207 ycbcr_format.chroma_subsampling_y = 1;
208 ycbcr_format.cb_x_position = 0.5f;
209 ycbcr_format.cb_y_position = 0.5f;
210 ycbcr_format.cr_x_position = 0.5f;
211 ycbcr_format.cr_y_position = 0.5f;
213 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
214 input->set_pixel_data(0, y);
215 input->set_pixel_data(1, cb);
216 input->set_pixel_data(2, cr);
217 tester.get_chain()->add_input(input);
219 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
221 // Y'CbCr isn't 100% accurate (the input values are rounded),
222 // so we need some leeway.
223 expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
226 TEST(YCbCrInput, Subsampling420) {
228 const int height = 4;
230 unsigned char y[width * height] = {
236 unsigned char cb[(width/2) * (height/2)] = {
240 unsigned char cr[(width/2) * (height/2)] = {
245 // Note: This is only the blue channel. The chroma samples (with associated
246 // values for blue) are marked off in comments.
247 float expected_data[width * height] = {
248 0.000, 0.125, 0.375, 0.500,
250 0.125, 0.250, 0.500, 0.625,
252 0.375, 0.500, 0.750, 0.875,
254 0.500, 0.625, 0.875, 1.000,
256 float out_data[width * height];
258 EffectChainTester tester(NULL, width, height);
261 format.color_space = COLORSPACE_sRGB;
262 format.gamma_curve = GAMMA_sRGB;
264 YCbCrFormat ycbcr_format;
265 ycbcr_format.luma_coefficients = YCBCR_REC_601;
266 ycbcr_format.full_range = false;
267 ycbcr_format.chroma_subsampling_x = 2;
268 ycbcr_format.chroma_subsampling_y = 2;
269 ycbcr_format.cb_x_position = 0.5f;
270 ycbcr_format.cb_y_position = 0.5f;
271 ycbcr_format.cr_x_position = 0.5f;
272 ycbcr_format.cr_y_position = 0.5f;
274 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
275 input->set_pixel_data(0, y);
276 input->set_pixel_data(1, cb);
277 input->set_pixel_data(2, cr);
278 tester.get_chain()->add_input(input);
280 tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
282 // Y'CbCr isn't 100% accurate (the input values are rounded),
283 // so we need some leeway.
284 expect_equal(expected_data, out_data, width, height, 0.01, 0.001);
287 TEST(YCbCrInput, Subsampling420WithNonCenteredSamples) {
289 const int height = 4;
291 unsigned char y[width * height] = {
297 unsigned char cb[(width/2) * (height/2)] = {
301 unsigned char cr[(width/2) * (height/2)] = {
306 // Note: This is only the blue channel. The chroma samples (with associated
307 // values for blue) are marked off in comments.
308 float expected_data[width * height] = {
309 0.000, 0.250, 0.500, 0.500,
311 0.125, 0.375, 0.625, 0.625,
313 0.375, 0.625, 0.875, 0.875,
315 0.500, 0.750, 1.000, 1.000,
317 float out_data[width * height];
319 EffectChainTester tester(NULL, width, height);
322 format.color_space = COLORSPACE_sRGB;
323 format.gamma_curve = GAMMA_sRGB;
325 YCbCrFormat ycbcr_format;
326 ycbcr_format.luma_coefficients = YCBCR_REC_601;
327 ycbcr_format.full_range = false;
328 ycbcr_format.chroma_subsampling_x = 2;
329 ycbcr_format.chroma_subsampling_y = 2;
330 ycbcr_format.cb_x_position = 0.0f;
331 ycbcr_format.cb_y_position = 0.5f;
332 ycbcr_format.cr_x_position = 0.0f;
333 ycbcr_format.cr_y_position = 0.5f;
335 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
336 input->set_pixel_data(0, y);
337 input->set_pixel_data(1, cb);
338 input->set_pixel_data(2, cr);
339 tester.get_chain()->add_input(input);
341 tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
343 // Y'CbCr isn't 100% accurate (the input values are rounded),
344 // so we need some leeway.
345 expect_equal(expected_data, out_data, width, height, 0.01, 0.001);
348 // Yes, some 4:2:2 formats actually have this craziness.
349 TEST(YCbCrInput, DifferentCbAndCrPositioning) {
351 const int height = 4;
353 unsigned char y[width * height] = {
359 unsigned char cb[(width/2) * height] = {
365 unsigned char cr[(width/2) * height] = {
372 // Chroma samples in this csae are always co-sited with a luma sample;
373 // their associated color values and position are marked off in comments.
374 float expected_data_blue[width * height] = {
375 0.000 /* 0.0 */, 0.250, 0.500 /* 0.5 */, 0.500,
376 0.500 /* 0.5 */, 0.750, 1.000 /* 1.0 */, 1.000,
377 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */, 0.500,
378 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */, 0.500,
380 float expected_data_red[width * height] = {
381 0.000, 0.000 /* 0.0 */, 0.250, 0.500 /* 0.5 */,
382 0.500, 0.500 /* 0.5 */, 0.750, 1.000 /* 1.0 */,
383 0.500, 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */,
384 0.500, 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */,
386 float out_data[width * height];
388 EffectChainTester tester(NULL, width, height);
391 format.color_space = COLORSPACE_sRGB;
392 format.gamma_curve = GAMMA_sRGB;
394 YCbCrFormat ycbcr_format;
395 ycbcr_format.luma_coefficients = YCBCR_REC_601;
396 ycbcr_format.full_range = false;
397 ycbcr_format.chroma_subsampling_x = 2;
398 ycbcr_format.chroma_subsampling_y = 1;
399 ycbcr_format.cb_x_position = 0.0f;
400 ycbcr_format.cb_y_position = 0.5f;
401 ycbcr_format.cr_x_position = 1.0f;
402 ycbcr_format.cr_y_position = 0.5f;
404 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
405 input->set_pixel_data(0, y);
406 input->set_pixel_data(1, cb);
407 input->set_pixel_data(2, cr);
408 tester.get_chain()->add_input(input);
410 // Y'CbCr isn't 100% accurate (the input values are rounded),
411 // so we need some leeway.
412 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
413 expect_equal(expected_data_red, out_data, width, height, 0.02, 0.002);
415 tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
416 expect_equal(expected_data_blue, out_data, width, height, 0.01, 0.001);