1 // Unit tests for YCbCr422InterleavedInput.
8 #include "effect_chain.h"
9 #include "gtest/gtest.h"
10 #include "test_util.h"
12 #include "resize_effect.h"
13 #include "ycbcr_422interleaved_input.h"
19 // Adapted from the Simple444 test from YCbCrInputTest.
20 TEST(YCbCr422InterleavedInputTest, Simple422) {
24 // Pure-color test inputs, calculated with the formulas in Rec. 601
26 unsigned char uyvy[width * height * 2] = {
27 /*U=*/128, /*Y=*/ 16, /*V=*/128, /*Y=*/ 16,
28 /*U=*/128, /*Y=*/235, /*V=*/128, /*Y=*/235,
29 /*U=*/ 90, /*Y=*/ 81, /*V=*/240, /*Y=*/ 81,
30 /*U=*/ 54, /*Y=*/145, /*V=*/ 34, /*Y=*/145,
31 /*U=*/240, /*Y=*/ 41, /*V=*/110, /*Y=*/ 41,
34 float expected_data[4 * width * height] = {
35 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0,
36 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
37 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
38 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0,
39 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
41 float out_data[4 * width * height];
43 EffectChainTester tester(nullptr, width, height);
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 = 2;
54 ycbcr_format.chroma_subsampling_y = 1;
55 ycbcr_format.cb_x_position = 0.0f; // Doesn't really matter here, since Y is constant.
56 ycbcr_format.cb_y_position = 0.5f;
57 ycbcr_format.cr_x_position = 0.0f;
58 ycbcr_format.cr_y_position = 0.5f;
60 YCbCr422InterleavedInput *input = new YCbCr422InterleavedInput(format, ycbcr_format, width, height);
61 input->set_pixel_data(uyvy);
62 tester.get_chain()->add_input(input);
64 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
66 // Y'CbCr isn't 100% accurate (the input values are rounded),
67 // so we need some leeway.
68 expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
71 // An effect that does nothing except changing its output sizes.
72 class VirtualResizeEffect : public Effect {
74 VirtualResizeEffect(int width, int height, int virtual_width, int virtual_height)
77 virtual_width(virtual_width),
78 virtual_height(virtual_height) {}
79 string effect_type_id() const override { return "VirtualResizeEffect"; }
80 string output_fragment_shader() override { return read_file("identity.frag"); }
82 bool changes_output_size() const override { return true; }
84 void get_output_size(unsigned *width, unsigned *height,
85 unsigned *virtual_width, unsigned *virtual_height) const override {
87 *height = this->height;
88 *virtual_width = this->virtual_width;
89 *virtual_height = this->virtual_height;
93 int width, height, virtual_width, virtual_height;
96 TEST(YCbCr422InterleavedInputTest, LumaLinearInterpolation) {
99 const int out_width = width * 3;
101 // Black, white, black and then gray.
102 unsigned char uyvy[width * height * 2] = {
103 /*U=*/128, /*Y=*/ 16,
104 /*V=*/128, /*Y=*/235,
105 /*U=*/128, /*Y=*/ 16,
106 /*V=*/128, /*Y=*/128,
109 float expected_data[out_width * height] = {
110 0.0, /**/0.0, 0.333, 0.667, /**/1.0, 0.667, 0.333, /**/0.0, 0.167, 0.333, /**/0.5, 0.5
112 float out_data[out_width * height];
114 EffectChainTester tester(nullptr, out_width, height);
117 format.color_space = COLORSPACE_sRGB;
118 format.gamma_curve = GAMMA_sRGB;
120 YCbCrFormat ycbcr_format;
121 ycbcr_format.luma_coefficients = YCBCR_REC_601;
122 ycbcr_format.full_range = false;
123 ycbcr_format.num_levels = 256;
124 ycbcr_format.chroma_subsampling_x = 2;
125 ycbcr_format.chroma_subsampling_y = 1;
126 ycbcr_format.cb_x_position = 0.0f; // Doesn't really matter here, since U/V are constant.
127 ycbcr_format.cb_y_position = 0.5f;
128 ycbcr_format.cr_x_position = 0.0f;
129 ycbcr_format.cr_y_position = 0.5f;
131 YCbCr422InterleavedInput *input = new YCbCr422InterleavedInput(format, ycbcr_format, width, height);
132 input->set_pixel_data(uyvy);
133 tester.get_chain()->add_input(input);
134 tester.get_chain()->add_effect(new VirtualResizeEffect(out_width, height, out_width, height));
136 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
138 // Y'CbCr isn't 100% accurate (the input values are rounded),
139 // so we need some leeway.
140 expect_equal(expected_data, out_data, out_width, height, 0.025, 0.002);
143 // Adapted from the YCbCrInput test of the same name.
144 TEST(YCbCr422InterleavedInputTest, DifferentCbAndCrPositioning) {
146 const int height = 4;
148 unsigned char uyvy[width * height * 2] = {
149 /*U=*/ 64, /*Y=*/126, /*V=*/ 48, /*Y=*/126, /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126,
150 /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126, /*U=*/192, /*Y=*/126, /*V=*/208, /*Y=*/126,
151 /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126, /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126,
152 /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126, /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126,
155 // Chroma samples in this case are always co-sited with a luma sample;
156 // their associated color values and position are marked off in comments.
157 float expected_data_blue[width * height] = {
158 0.000 /* 0.0 */, 0.250, 0.500 /* 0.5 */, 0.500,
159 0.500 /* 0.5 */, 0.750, 1.000 /* 1.0 */, 1.000,
160 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */, 0.500,
161 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */, 0.500,
163 float expected_data_red[width * height] = {
164 0.000, 0.000 /* 0.0 */, 0.250, 0.500 /* 0.5 */,
165 0.500, 0.500 /* 0.5 */, 0.750, 1.000 /* 1.0 */,
166 0.500, 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */,
167 0.500, 0.500 /* 0.5 */, 0.500, 0.500 /* 0.5 */,
169 float out_data[width * height];
171 EffectChainTester tester(nullptr, width, height);
174 format.color_space = COLORSPACE_sRGB;
175 format.gamma_curve = GAMMA_sRGB;
177 YCbCrFormat ycbcr_format;
178 ycbcr_format.luma_coefficients = YCBCR_REC_601;
179 ycbcr_format.full_range = false;
180 ycbcr_format.num_levels = 256;
181 ycbcr_format.chroma_subsampling_x = 2;
182 ycbcr_format.chroma_subsampling_y = 1;
183 ycbcr_format.cb_x_position = 0.0f;
184 ycbcr_format.cb_y_position = 0.5f;
185 ycbcr_format.cr_x_position = 1.0f;
186 ycbcr_format.cr_y_position = 0.5f;
188 YCbCr422InterleavedInput *input = new YCbCr422InterleavedInput(format, ycbcr_format, width, height);
189 input->set_pixel_data(uyvy);
190 tester.get_chain()->add_input(input);
192 // Y'CbCr isn't 100% accurate (the input values are rounded),
193 // so we need some leeway.
194 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
195 expect_equal(expected_data_red, out_data, width, height, 0.02, 0.002);
197 tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
198 expect_equal(expected_data_blue, out_data, width, height, 0.01, 0.001);
201 TEST(YCbCr422InterleavedInputTest, PBO) {
203 const int height = 5;
205 // Pure-color test inputs, calculated with the formulas in Rec. 601
207 unsigned char uyvy[width * height * 2] = {
208 /*U=*/128, /*Y=*/ 16, /*V=*/128, /*Y=*/ 16,
209 /*U=*/128, /*Y=*/235, /*V=*/128, /*Y=*/235,
210 /*U=*/ 90, /*Y=*/ 81, /*V=*/240, /*Y=*/ 81,
211 /*U=*/ 54, /*Y=*/145, /*V=*/ 34, /*Y=*/145,
212 /*U=*/240, /*Y=*/ 41, /*V=*/110, /*Y=*/ 41,
215 float expected_data[4 * width * height] = {
216 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0,
217 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
218 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
219 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0,
220 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
222 float out_data[4 * width * height];
225 glGenBuffers(1, &pbo);
226 glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo);
227 glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, width * height * 2, uyvy, GL_STREAM_DRAW);
228 glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
230 EffectChainTester tester(nullptr, width, height);
233 format.color_space = COLORSPACE_sRGB;
234 format.gamma_curve = GAMMA_sRGB;
236 YCbCrFormat ycbcr_format;
237 ycbcr_format.luma_coefficients = YCBCR_REC_601;
238 ycbcr_format.full_range = false;
239 ycbcr_format.num_levels = 256;
240 ycbcr_format.chroma_subsampling_x = 2;
241 ycbcr_format.chroma_subsampling_y = 1;
242 ycbcr_format.cb_x_position = 0.0f; // Doesn't really matter here, since Y is constant.
243 ycbcr_format.cb_y_position = 0.5f;
244 ycbcr_format.cr_x_position = 0.0f;
245 ycbcr_format.cr_y_position = 0.5f;
247 YCbCr422InterleavedInput *input = new YCbCr422InterleavedInput(format, ycbcr_format, width, height);
248 input->set_pixel_data((unsigned char *)BUFFER_OFFSET(0), pbo);
249 tester.get_chain()->add_input(input);
251 tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
253 // Y'CbCr isn't 100% accurate (the input values are rounded),
254 // so we need some leeway.
255 expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
257 glDeleteBuffers(1, &pbo);