1 // Unit tests for YCbCrInput.
2 // FIXME: This class really ought to support mipmaps.
5 #include "gtest/gtest.h"
6 #include "ycbcr_input.h"
8 TEST(YCbCrInput, Simple444) {
12 // Pure-color test inputs, calculated with the formulas in Rec. 601
14 unsigned char y[width * height] = {
17 unsigned char cb[width * height] = {
18 128, 128, 90, 54, 240,
20 unsigned char cr[width * height] = {
21 128, 128, 240, 34, 110,
23 float expected_data[4 * width * height] = {
30 float out_data[4 * width * height];
32 EffectChainTester tester(NULL, width, height);
35 format.color_space = COLORSPACE_sRGB;
36 format.gamma_curve = GAMMA_sRGB;
38 YCbCrFormat ycbcr_format;
39 ycbcr_format.luma_coefficients = YCBCR_REC_601;
40 ycbcr_format.full_range = false;
41 ycbcr_format.chroma_subsampling_x = 1;
42 ycbcr_format.chroma_subsampling_y = 1;
43 ycbcr_format.cb_x_position = 0.5f;
44 ycbcr_format.cb_y_position = 0.5f;
45 ycbcr_format.cr_x_position = 0.5f;
46 ycbcr_format.cr_y_position = 0.5f;
48 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
49 input->set_pixel_data(0, y);
50 input->set_pixel_data(1, cb);
51 input->set_pixel_data(2, cr);
52 tester.get_chain()->add_input(input);
54 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
56 // Y'CbCr isn't 100% accurate (the input values are rounded),
57 // so we need some leeway.
58 expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
61 TEST(YCbCrInput, FullRangeRec601) {
65 // Pure-color test inputs, calculated with the formulas in Rec. 601
66 // section 2.5.4 but without the scaling factors applied
67 // (so both R, G, B, Y, Cb and R vary from 0 to 255).
68 unsigned char y[width * height] = {
71 unsigned char cb[width * height] = {
72 128, 128, 85, 44, 255,
74 unsigned char cr[width * height] = {
75 128, 128, 255, 21, 107,
77 float expected_data[4 * width * height] = {
84 float out_data[4 * width * height];
86 EffectChainTester tester(NULL, width, height);
89 format.color_space = COLORSPACE_sRGB;
90 format.gamma_curve = GAMMA_sRGB;
92 YCbCrFormat ycbcr_format;
93 ycbcr_format.luma_coefficients = YCBCR_REC_601;
94 ycbcr_format.full_range = true;
95 ycbcr_format.chroma_subsampling_x = 1;
96 ycbcr_format.chroma_subsampling_y = 1;
97 ycbcr_format.cb_x_position = 0.5f;
98 ycbcr_format.cb_y_position = 0.5f;
99 ycbcr_format.cr_x_position = 0.5f;
100 ycbcr_format.cr_y_position = 0.5f;
102 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
103 input->set_pixel_data(0, y);
104 input->set_pixel_data(1, cb);
105 input->set_pixel_data(2, cr);
106 tester.get_chain()->add_input(input);
108 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
110 // Y'CbCr isn't 100% accurate (the input values are rounded),
111 // so we need some leeway.
112 expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
115 TEST(YCbCrInput, Rec709) {
117 const int height = 5;
119 // Pure-color test inputs, calculated with the formulas in Rec. 709
120 // page 19, items 3.4 and 3.5.
121 unsigned char y[width * height] = {
122 16, 235, 63, 173, 32,
124 unsigned char cb[width * height] = {
125 128, 128, 102, 42, 240,
127 unsigned char cr[width * height] = {
128 128, 128, 240, 26, 118,
130 float expected_data[4 * width * height] = {
137 float out_data[4 * width * height];
139 EffectChainTester tester(NULL, width, height);
142 format.color_space = COLORSPACE_sRGB;
143 format.gamma_curve = GAMMA_sRGB;
145 YCbCrFormat ycbcr_format;
146 ycbcr_format.luma_coefficients = YCBCR_REC_709;
147 ycbcr_format.full_range = false;
148 ycbcr_format.chroma_subsampling_x = 1;
149 ycbcr_format.chroma_subsampling_y = 1;
150 ycbcr_format.cb_x_position = 0.5f;
151 ycbcr_format.cb_y_position = 0.5f;
152 ycbcr_format.cr_x_position = 0.5f;
153 ycbcr_format.cr_y_position = 0.5f;
155 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
156 input->set_pixel_data(0, y);
157 input->set_pixel_data(1, cb);
158 input->set_pixel_data(2, cr);
159 tester.get_chain()->add_input(input);
161 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
163 // Y'CbCr isn't 100% accurate (the input values are rounded),
164 // so we need some leeway.
165 expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
168 TEST(YCbCrInput, Subsampling420) {
170 const int height = 4;
172 unsigned char y[width * height] = {
178 unsigned char cb[(width/2) * (height/2)] = {
182 unsigned char cr[(width/2) * (height/2)] = {
187 // Note: This is only the blue channel. The chroma samples (with associated
188 // values for blue) are marked off in comments.
189 float expected_data[width * height] = {
190 0.000, 0.125, 0.375, 0.500,
192 0.125, 0.250, 0.500, 0.625,
194 0.375, 0.500, 0.750, 0.875,
196 0.500, 0.625, 0.875, 1.000,
198 float out_data[width * height];
200 EffectChainTester tester(NULL, width, height);
203 format.color_space = COLORSPACE_sRGB;
204 format.gamma_curve = GAMMA_sRGB;
206 YCbCrFormat ycbcr_format;
207 ycbcr_format.luma_coefficients = YCBCR_REC_601;
208 ycbcr_format.full_range = false;
209 ycbcr_format.chroma_subsampling_x = 2;
210 ycbcr_format.chroma_subsampling_y = 2;
211 ycbcr_format.cb_x_position = 0.5f;
212 ycbcr_format.cb_y_position = 0.5f;
213 ycbcr_format.cr_x_position = 0.5f;
214 ycbcr_format.cr_y_position = 0.5f;
216 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
217 input->set_pixel_data(0, y);
218 input->set_pixel_data(1, cb);
219 input->set_pixel_data(2, cr);
220 tester.get_chain()->add_input(input);
222 tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
224 // Y'CbCr isn't 100% accurate (the input values are rounded),
225 // so we need some leeway.
226 expect_equal(expected_data, out_data, width, height, 0.01, 0.001);
229 TEST(YCbCrInput, Subsampling420WithNonCenteredSamples) {
231 const int height = 4;
233 unsigned char y[width * height] = {
239 unsigned char cb[(width/2) * (height/2)] = {
243 unsigned char cr[(width/2) * (height/2)] = {
248 // Note: This is only the blue channel. The chroma samples (with associated
249 // values for blue) are marked off in comments.
250 float expected_data[width * height] = {
251 0.000, 0.250, 0.500, 0.500,
253 0.125, 0.375, 0.625, 0.625,
255 0.375, 0.625, 0.875, 0.875,
257 0.500, 0.750, 1.000, 1.000,
259 float out_data[width * height];
261 EffectChainTester tester(NULL, width, height);
264 format.color_space = COLORSPACE_sRGB;
265 format.gamma_curve = GAMMA_sRGB;
267 YCbCrFormat ycbcr_format;
268 ycbcr_format.luma_coefficients = YCBCR_REC_601;
269 ycbcr_format.full_range = false;
270 ycbcr_format.chroma_subsampling_x = 2;
271 ycbcr_format.chroma_subsampling_y = 2;
272 ycbcr_format.cb_x_position = 0.0f;
273 ycbcr_format.cb_y_position = 0.5f;
274 ycbcr_format.cr_x_position = 0.0f;
275 ycbcr_format.cr_y_position = 0.5f;
277 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
278 input->set_pixel_data(0, y);
279 input->set_pixel_data(1, cb);
280 input->set_pixel_data(2, cr);
281 tester.get_chain()->add_input(input);
283 tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
285 // Y'CbCr isn't 100% accurate (the input values are rounded),
286 // so we need some leeway.
287 expect_equal(expected_data, out_data, width, height, 0.01, 0.001);
290 // Yes, some 4:2:2 formats actually have this craziness.
291 TEST(YCbCrInput, DifferentCbAndCrPositioning) {
293 const int height = 4;
295 unsigned char y[width * height] = {
301 unsigned char cb[(width/2) * height] = {
307 unsigned char cr[(width/2) * height] = {
314 // Chroma samples in this csae are always co-sited with a luma sample;
315 // their associated color values and position are marked off in comments.
316 float expected_data_blue[width * height] = {
317 0.000 /* 0.0 */, 0.250, 0.500 /* 0.5 */, 0.500,
318 0.500 /* 0.5 */, 0.750, 1.000 /* 1.0 */, 1.000,
319 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */, 0.500,
320 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */, 0.500,
322 float expected_data_red[width * height] = {
323 0.000, 0.000 /* 0.0 */, 0.250, 0.500 /* 0.5 */,
324 0.500, 0.500 /* 0.5 */, 0.750, 1.000 /* 1.0 */,
325 0.500, 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */,
326 0.500, 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */,
328 float out_data[width * height];
330 EffectChainTester tester(NULL, width, height);
333 format.color_space = COLORSPACE_sRGB;
334 format.gamma_curve = GAMMA_sRGB;
336 YCbCrFormat ycbcr_format;
337 ycbcr_format.luma_coefficients = YCBCR_REC_601;
338 ycbcr_format.full_range = false;
339 ycbcr_format.chroma_subsampling_x = 2;
340 ycbcr_format.chroma_subsampling_y = 1;
341 ycbcr_format.cb_x_position = 0.0f;
342 ycbcr_format.cb_y_position = 0.5f;
343 ycbcr_format.cr_x_position = 1.0f;
344 ycbcr_format.cr_y_position = 0.5f;
346 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
347 input->set_pixel_data(0, y);
348 input->set_pixel_data(1, cb);
349 input->set_pixel_data(2, cr);
350 tester.get_chain()->add_input(input);
352 // Y'CbCr isn't 100% accurate (the input values are rounded),
353 // so we need some leeway.
354 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
355 expect_equal(expected_data_red, out_data, width, height, 0.02, 0.002);
357 tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
358 expect_equal(expected_data_blue, out_data, width, height, 0.01, 0.001);