]> git.sesse.net Git - movit/blob - ycbcr_422interleaved_input_test.cpp
59cc5305282c8a51c616c5d151b1c2adbe9fa223
[movit] / ycbcr_422interleaved_input_test.cpp
1 // Unit tests for YCbCr422InterleavedInput.
2
3 #include <epoxy/gl.h>
4 #include <stddef.h>
5
6 #include <string>
7
8 #include "effect_chain.h"
9 #include "gtest/gtest.h"
10 #include "test_util.h"
11 #include "util.h"
12 #include "resize_effect.h"
13 #include "ycbcr_422interleaved_input.h"
14
15 using namespace std;
16
17 namespace movit {
18
19 // Adapted from the Simple444 test from YCbCrInputTest.
20 TEST(YCbCr422InterleavedInputTest, Simple422) {
21         const int width = 2;
22         const int height = 5;
23
24         // Pure-color test inputs, calculated with the formulas in Rec. 601
25         // section 2.5.4.
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,
32         };
33
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,
40         };
41         float out_data[4 * width * height];
42
43         EffectChainTester tester(nullptr, width, height);
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 = 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;
59
60         YCbCr422InterleavedInput *input = new YCbCr422InterleavedInput(format, ycbcr_format, width, height);
61         input->set_pixel_data(uyvy);
62         tester.get_chain()->add_input(input);
63
64         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
65
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);
69 }
70
71 // An effect that does nothing except changing its output sizes.
72 class VirtualResizeEffect : public Effect {
73 public:
74         VirtualResizeEffect(int width, int height, int virtual_width, int virtual_height)
75                 : width(width),
76                   height(height),
77                   virtual_width(virtual_width),
78                   virtual_height(virtual_height) {}
79         virtual string effect_type_id() const { return "VirtualResizeEffect"; }
80         string output_fragment_shader() { return read_file("identity.frag"); }
81
82         virtual bool changes_output_size() const { return true; }
83
84         virtual void get_output_size(unsigned *width, unsigned *height,
85                                      unsigned *virtual_width, unsigned *virtual_height) const {
86                 *width = this->width;
87                 *height = this->height;
88                 *virtual_width = this->virtual_width;
89                 *virtual_height = this->virtual_height;
90         }
91
92 private:
93         int width, height, virtual_width, virtual_height;
94 };
95
96 TEST(YCbCr422InterleavedInputTest, LumaLinearInterpolation) {
97         const int width = 4;
98         const int height = 1;
99         const int out_width = width * 3;
100
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,
107         };
108
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
111         };
112         float out_data[out_width * height];
113
114         EffectChainTester tester(nullptr, out_width, height);
115
116         ImageFormat format;
117         format.color_space = COLORSPACE_sRGB;
118         format.gamma_curve = GAMMA_sRGB;
119
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;
130
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));
135
136         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
137
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);
141 }
142
143 // Adapted from the YCbCrInput test of the same name.
144 TEST(YCbCr422InterleavedInputTest, DifferentCbAndCrPositioning) {
145         const int width = 4;
146         const int height = 4;
147
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,
153         };
154
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, 
162         };
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 */, 
168         };
169         float out_data[width * height];
170
171         EffectChainTester tester(nullptr, width, height);
172
173         ImageFormat format;
174         format.color_space = COLORSPACE_sRGB;
175         format.gamma_curve = GAMMA_sRGB;
176
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;
187
188         YCbCr422InterleavedInput *input = new YCbCr422InterleavedInput(format, ycbcr_format, width, height);
189         input->set_pixel_data(uyvy);
190         tester.get_chain()->add_input(input);
191
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);
196
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);
199 }
200
201 TEST(YCbCr422InterleavedInputTest, PBO) {
202         const int width = 2;
203         const int height = 5;
204
205         // Pure-color test inputs, calculated with the formulas in Rec. 601
206         // section 2.5.4.
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,
213         };
214
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,
221         };
222         float out_data[4 * width * height];
223
224         GLuint pbo;
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);
229
230         EffectChainTester tester(nullptr, width, height);
231
232         ImageFormat format;
233         format.color_space = COLORSPACE_sRGB;
234         format.gamma_curve = GAMMA_sRGB;
235
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;
246
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);
250
251         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
252
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);
256
257         glDeleteBuffers(1, &pbo);
258 }
259
260 }  // namespace movit