1 // Unit tests for YCbCrConversionEffect. Mostly done by leveraging
2 // YCbCrInput and seeing that the right thing comes out at the
8 #include "effect_chain.h"
9 #include "gtest/gtest.h"
10 #include "image_format.h"
11 #include "test_util.h"
13 #include "ycbcr_input.h"
17 TEST(YCbCrConversionEffectTest, BasicInOut) {
21 // Pure-color test inputs, calculated with the formulas in Rec. 601
23 unsigned char y[width * height] = {
26 unsigned char cb[width * height] = {
27 128, 128, 90, 54, 240,
29 unsigned char cr[width * height] = {
30 128, 128, 240, 34, 110,
32 unsigned char expected_data[width * height * 4] = {
33 // The same data, just rearranged.
41 unsigned char out_data[width * height * 4];
43 EffectChainTester tester(NULL, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
46 format.color_space = COLORSPACE_sRGB;
47 format.gamma_curve = GAMMA_sRGB;
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;
60 tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format);
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);
68 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
69 expect_equal(expected_data, out_data, 4 * width, height);
72 TEST(YCbCrConversionEffectTest, ClampToValidRange) {
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
82 unsigned char cb[width * height] = {
83 0, 10, 16, 235, 240, 255,
85 unsigned char cr[width * height] = {
86 255, 240, 235, 16, 10, 0,
88 unsigned char expected_data[width * height * 4] = {
97 unsigned char out_data[width * height * 4];
99 EffectChainTester tester(NULL, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
102 format.color_space = COLORSPACE_sRGB;
103 format.gamma_curve = GAMMA_sRGB;
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;
116 tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format);
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);
124 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
125 expect_equal(expected_data, out_data, 4 * width, height);
128 TEST(YCbCrConversionEffectTest, LimitedRangeToFullRange) {
130 const int height = 5;
132 // Pure-color test inputs, calculated with the formulas in Rec. 601
134 unsigned char y[width * height] = {
135 16, 235, 81, 145, 41,
137 unsigned char cb[width * height] = {
138 128, 128, 90, 54, 240,
140 unsigned char cr[width * height] = {
141 128, 128, 240, 34, 110,
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.
153 unsigned char out_data[width * height * 4];
155 EffectChainTester tester(NULL, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
158 format.color_space = COLORSPACE_sRGB;
159 format.gamma_curve = GAMMA_sRGB;
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;
172 tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format);
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);
181 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
182 expect_equal(expected_data, out_data, 4 * width, height);
185 TEST(YCbCrConversionEffectTest, PlanarOutput) {
187 const int height = 5;
189 // Pure-color test inputs, calculated with the formulas in Rec. 601
191 unsigned char y[width * height] = {
192 16, 235, 81, 145, 41,
194 unsigned char cb[width * height] = {
195 128, 128, 90, 54, 240,
197 unsigned char cr[width * height] = {
198 128, 128, 240, 34, 110,
201 unsigned char out_y[width * height], out_cb[width * height], out_cr[width * height];
203 EffectChainTester tester(NULL, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
206 format.color_space = COLORSPACE_sRGB;
207 format.gamma_curve = GAMMA_sRGB;
209 YCbCrFormat ycbcr_format;
210 ycbcr_format.luma_coefficients = YCBCR_REC_601;
211 ycbcr_format.full_range = false;
212 ycbcr_format.num_levels = 256;
213 ycbcr_format.chroma_subsampling_x = 1;
214 ycbcr_format.chroma_subsampling_y = 1;
215 ycbcr_format.cb_x_position = 0.5f;
216 ycbcr_format.cb_y_position = 0.5f;
217 ycbcr_format.cr_x_position = 0.5f;
218 ycbcr_format.cr_y_position = 0.5f;
220 tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format, YCBCR_OUTPUT_PLANAR);
222 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
223 input->set_pixel_data(0, y);
224 input->set_pixel_data(1, cb);
225 input->set_pixel_data(2, cr);
226 tester.get_chain()->add_input(input);
228 tester.run(out_y, out_cb, out_cr, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
229 expect_equal(y, out_y, width, height);
230 expect_equal(cb, out_cb, width, height);
231 expect_equal(cr, out_cr, width, height);
234 TEST(YCbCrConversionEffectTest, SplitLumaAndChroma) {
236 const int height = 5;
238 // Pure-color test inputs, calculated with the formulas in Rec. 601
240 unsigned char y[width * height] = {
241 16, 235, 81, 145, 41,
243 unsigned char cb[width * height] = {
244 128, 128, 90, 54, 240,
246 unsigned char cr[width * height] = {
247 128, 128, 240, 34, 110,
250 // The R and A data, rearranged. Note: The G and B channels
251 // (the middle columns) are undefined. If we change the behavior,
252 // the test will need to be updated, but a failure is expected.
253 unsigned char expected_y[width * height * 4] = {
254 16, /*undefined:*/ 16, /*undefined:*/ 16, 255,
255 235, /*undefined:*/ 235, /*undefined:*/ 235, 255,
256 81, /*undefined:*/ 81, /*undefined:*/ 81, 255,
257 145, /*undefined:*/ 145, /*undefined:*/ 145, 255,
258 41, /*undefined:*/ 41, /*undefined:*/ 41, 255,
261 // Just the Cb and Cr data, rearranged. The B and A channels
262 // are undefined, as below.
263 unsigned char expected_cbcr[width * height * 4] = {
264 128, 128, /*undefined:*/ 128, /*undefined:*/ 255,
265 128, 128, /*undefined:*/ 128, /*undefined:*/ 255,
266 90, 240, /*undefined:*/ 240, /*undefined:*/ 255,
267 54, 34, /*undefined:*/ 34, /*undefined:*/ 255,
268 240, 110, /*undefined:*/ 110, /*undefined:*/ 255,
271 unsigned char out_y[width * height * 4], out_cbcr[width * height * 4];
273 EffectChainTester tester(NULL, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
276 format.color_space = COLORSPACE_sRGB;
277 format.gamma_curve = GAMMA_sRGB;
279 YCbCrFormat ycbcr_format;
280 ycbcr_format.luma_coefficients = YCBCR_REC_601;
281 ycbcr_format.full_range = false;
282 ycbcr_format.num_levels = 256;
283 ycbcr_format.chroma_subsampling_x = 1;
284 ycbcr_format.chroma_subsampling_y = 1;
285 ycbcr_format.cb_x_position = 0.5f;
286 ycbcr_format.cb_y_position = 0.5f;
287 ycbcr_format.cr_x_position = 0.5f;
288 ycbcr_format.cr_y_position = 0.5f;
290 tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format, YCBCR_OUTPUT_SPLIT_Y_AND_CBCR);
292 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
293 input->set_pixel_data(0, y);
294 input->set_pixel_data(1, cb);
295 input->set_pixel_data(2, cr);
296 tester.get_chain()->add_input(input);
298 tester.run(out_y, out_cbcr, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
299 expect_equal(expected_y, out_y, width * 4, height);
300 expect_equal(expected_cbcr, out_cbcr, width * 4, height);
303 TEST(YCbCrConversionEffectTest, OutputChunkyAndRGBA) {
305 const int height = 5;
307 // Pure-color test inputs, calculated with the formulas in Rec. 601
309 unsigned char y[width * height] = {
310 16, 235, 81, 145, 41,
312 unsigned char cb[width * height] = {
313 128, 128, 90, 54, 240,
315 unsigned char cr[width * height] = {
316 128, 128, 240, 34, 110,
318 unsigned char expected_ycbcr[width * height * 4] = {
319 // The same data, just rearranged.
326 unsigned char expected_rgba[width * height * 4] = {
334 unsigned char out_ycbcr[width * height * 4];
335 unsigned char out_rgba[width * height * 4];
337 EffectChainTester tester(NULL, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA8);
340 format.color_space = COLORSPACE_sRGB;
341 format.gamma_curve = GAMMA_sRGB;
343 YCbCrFormat ycbcr_format;
344 ycbcr_format.luma_coefficients = YCBCR_REC_601;
345 ycbcr_format.full_range = false;
346 ycbcr_format.num_levels = 256;
347 ycbcr_format.chroma_subsampling_x = 1;
348 ycbcr_format.chroma_subsampling_y = 1;
349 ycbcr_format.cb_x_position = 0.5f;
350 ycbcr_format.cb_y_position = 0.5f;
351 ycbcr_format.cr_x_position = 0.5f;
352 ycbcr_format.cr_y_position = 0.5f;
354 tester.add_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
355 tester.add_ycbcr_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format);
357 YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
358 input->set_pixel_data(0, y);
359 input->set_pixel_data(1, cb);
360 input->set_pixel_data(2, cr);
361 tester.get_chain()->add_input(input);
363 // Note: We don't test that the values actually get dithered,
364 // just that the shader compiles and doesn't mess up badly.
365 tester.get_chain()->set_dither_bits(8);
367 tester.run(out_ycbcr, out_rgba, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
368 expect_equal(expected_ycbcr, out_ycbcr, width * 4, height);
370 // Y'CbCr isn't 100% accurate (the input values are rounded),
371 // so we need some leeway.
372 expect_equal(expected_rgba, out_rgba, 4 * width, height, 7, 255 * 0.002);