]> git.sesse.net Git - movit/blob - ycbcr_input_test.cpp
Make the demo program run with core contexts.
[movit] / ycbcr_input_test.cpp
1 // Unit tests for YCbCrInput.
2
3 #include <epoxy/gl.h>
4 #include <stddef.h>
5
6 #include "effect_chain.h"
7 #include "gtest/gtest.h"
8 #include "test_util.h"
9 #include "util.h"
10 #include "resource_pool.h"
11 #include "ycbcr_input.h"
12
13 namespace movit {
14
15 TEST(YCbCrInputTest, Simple444) {
16         const int width = 1;
17         const int height = 5;
18
19         // Pure-color test inputs, calculated with the formulas in Rec. 601
20         // section 2.5.4.
21         unsigned char y[width * height] = {
22                 16, 235, 81, 145, 41,
23         };
24         unsigned char cb[width * height] = {
25                 128, 128, 90, 54, 240,
26         };
27         unsigned char cr[width * height] = {
28                 128, 128, 240, 34, 110,
29         };
30         float expected_data[4 * width * height] = {
31                 0.0, 0.0, 0.0, 1.0,
32                 1.0, 1.0, 1.0, 1.0,
33                 1.0, 0.0, 0.0, 1.0,
34                 0.0, 1.0, 0.0, 1.0,
35                 0.0, 0.0, 1.0, 1.0,
36         };
37         float out_data[4 * width * height];
38
39         EffectChainTester tester(NULL, width, height);
40
41         ImageFormat format;
42         format.color_space = COLORSPACE_sRGB;
43         format.gamma_curve = GAMMA_sRGB;
44
45         YCbCrFormat ycbcr_format;
46         ycbcr_format.luma_coefficients = YCBCR_REC_601;
47         ycbcr_format.full_range = false;
48         ycbcr_format.num_levels = 256;
49         ycbcr_format.chroma_subsampling_x = 1;
50         ycbcr_format.chroma_subsampling_y = 1;
51         ycbcr_format.cb_x_position = 0.5f;
52         ycbcr_format.cb_y_position = 0.5f;
53         ycbcr_format.cr_x_position = 0.5f;
54         ycbcr_format.cr_y_position = 0.5f;
55
56         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
57         input->set_pixel_data(0, y);
58         input->set_pixel_data(1, cb);
59         input->set_pixel_data(2, cr);
60         tester.get_chain()->add_input(input);
61
62         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
63
64         // Y'CbCr isn't 100% accurate (the input values are rounded),
65         // so we need some leeway.
66         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
67 }
68
69 TEST(YCbCrInputTest, FullRangeRec601) {
70         const int width = 1;
71         const int height = 5;
72
73         // Pure-color test inputs, calculated with the formulas in Rec. 601
74         // section 2.5.4 but without the scaling factors applied
75         // (so both R, G, B, Y, Cb and R vary from 0 to 255).
76         unsigned char y[width * height] = {
77                 0, 255, 76, 150, 29,
78         };
79         unsigned char cb[width * height] = {
80                 128, 128, 85, 44, 255,
81         };
82         unsigned char cr[width * height] = {
83                 128, 128, 255, 21, 107,
84         };
85         float expected_data[4 * width * height] = {
86                 0.0, 0.0, 0.0, 1.0,
87                 1.0, 1.0, 1.0, 1.0,
88                 1.0, 0.0, 0.0, 1.0,
89                 0.0, 1.0, 0.0, 1.0,
90                 0.0, 0.0, 1.0, 1.0,
91         };
92         float out_data[4 * width * height];
93
94         EffectChainTester tester(NULL, width, height);
95
96         ImageFormat format;
97         format.color_space = COLORSPACE_sRGB;
98         format.gamma_curve = GAMMA_sRGB;
99
100         YCbCrFormat ycbcr_format;
101         ycbcr_format.luma_coefficients = YCBCR_REC_601;
102         ycbcr_format.full_range = true;
103         ycbcr_format.num_levels = 256;
104         ycbcr_format.chroma_subsampling_x = 1;
105         ycbcr_format.chroma_subsampling_y = 1;
106         ycbcr_format.cb_x_position = 0.5f;
107         ycbcr_format.cb_y_position = 0.5f;
108         ycbcr_format.cr_x_position = 0.5f;
109         ycbcr_format.cr_y_position = 0.5f;
110
111         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
112         input->set_pixel_data(0, y);
113         input->set_pixel_data(1, cb);
114         input->set_pixel_data(2, cr);
115         tester.get_chain()->add_input(input);
116
117         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
118
119         // Y'CbCr isn't 100% accurate (the input values are rounded),
120         // so we need some leeway.
121         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
122 }
123
124 TEST(YCbCrInputTest, Rec709) {
125         const int width = 1;
126         const int height = 5;
127
128         // Pure-color test inputs, calculated with the formulas in Rec. 709
129         // page 19, items 3.4 and 3.5.
130         unsigned char y[width * height] = {
131                 16, 235, 63, 173, 32, 
132         };
133         unsigned char cb[width * height] = {
134                 128, 128, 102, 42, 240,
135         };
136         unsigned char cr[width * height] = {
137                 128, 128, 240, 26, 118,
138         };
139         float expected_data[4 * width * height] = {
140                 0.0, 0.0, 0.0, 1.0,
141                 1.0, 1.0, 1.0, 1.0,
142                 1.0, 0.0, 0.0, 1.0,
143                 0.0, 1.0, 0.0, 1.0,
144                 0.0, 0.0, 1.0, 1.0,
145         };
146         float out_data[4 * width * height];
147
148         EffectChainTester tester(NULL, width, height);
149
150         ImageFormat format;
151         format.color_space = COLORSPACE_sRGB;
152         format.gamma_curve = GAMMA_sRGB;
153
154         YCbCrFormat ycbcr_format;
155         ycbcr_format.luma_coefficients = YCBCR_REC_709;
156         ycbcr_format.full_range = false;
157         ycbcr_format.num_levels = 256;
158         ycbcr_format.chroma_subsampling_x = 1;
159         ycbcr_format.chroma_subsampling_y = 1;
160         ycbcr_format.cb_x_position = 0.5f;
161         ycbcr_format.cb_y_position = 0.5f;
162         ycbcr_format.cr_x_position = 0.5f;
163         ycbcr_format.cr_y_position = 0.5f;
164
165         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
166         input->set_pixel_data(0, y);
167         input->set_pixel_data(1, cb);
168         input->set_pixel_data(2, cr);
169         tester.get_chain()->add_input(input);
170
171         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
172
173         // Y'CbCr isn't 100% accurate (the input values are rounded),
174         // so we need some leeway.
175         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
176 }
177
178 TEST(YCbCrInputTest, Rec2020) {
179         const int width = 1;
180         const int height = 5;
181
182         // Pure-color test inputs, calculated with the formulas in Rec. 2020
183         // page 4, tables 4 and 5 (for conventional non-constant luminance).
184         // Note that we still use 8-bit inputs, even though Rec. 2020 is only
185         // defined for 10- and 12-bit.
186         unsigned char y[width * height] = {
187                 16, 235, 74, 164, 29,
188         };
189         unsigned char cb[width * height] = {
190                 128, 128, 97, 47, 240,
191         };
192         unsigned char cr[width * height] = {
193                 128, 128, 240, 25, 119,
194         };
195         float expected_data[4 * width * height] = {
196                 0.0, 0.0, 0.0, 1.0,
197                 1.0, 1.0, 1.0, 1.0,
198                 1.0, 0.0, 0.0, 1.0,
199                 0.0, 1.0, 0.0, 1.0,
200                 0.0, 0.0, 1.0, 1.0,
201         };
202         float out_data[4 * width * height];
203
204         EffectChainTester tester(NULL, width, height);
205
206         ImageFormat format;
207         format.color_space = COLORSPACE_sRGB;
208         format.gamma_curve = GAMMA_sRGB;
209
210         YCbCrFormat ycbcr_format;
211         ycbcr_format.luma_coefficients = YCBCR_REC_2020;
212         ycbcr_format.full_range = false;
213         ycbcr_format.num_levels = 256;
214         ycbcr_format.chroma_subsampling_x = 1;
215         ycbcr_format.chroma_subsampling_y = 1;
216         ycbcr_format.cb_x_position = 0.5f;
217         ycbcr_format.cb_y_position = 0.5f;
218         ycbcr_format.cr_x_position = 0.5f;
219         ycbcr_format.cr_y_position = 0.5f;
220
221         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
222         input->set_pixel_data(0, y);
223         input->set_pixel_data(1, cb);
224         input->set_pixel_data(2, cr);
225         tester.get_chain()->add_input(input);
226
227         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
228
229         // Y'CbCr isn't 100% accurate (the input values are rounded),
230         // so we need some leeway.
231         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
232 }
233
234 TEST(YCbCrInputTest, Subsampling420) {
235         const int width = 4;
236         const int height = 4;
237
238         unsigned char y[width * height] = {
239                 126, 126, 126, 126,
240                 126, 126, 126, 126,
241                 126, 126, 126, 126,
242                 126, 126, 126, 126,
243         };
244         unsigned char cb[(width/2) * (height/2)] = {
245                 64, 128,
246                 128, 192,
247         };
248         unsigned char cr[(width/2) * (height/2)] = {
249                 128, 128,
250                 128, 128,
251         };
252
253         // Note: This is only the blue channel. The chroma samples (with associated
254         // values for blue) are marked off in comments.
255         float expected_data[width * height] = {
256                 0.000, 0.125, 0.375, 0.500, 
257                  /* 0.0 */      /* 0.5 */
258                 0.125, 0.250, 0.500, 0.625,
259
260                 0.375, 0.500, 0.750, 0.875,
261                  /* 0.5 */      /* 1.0 */
262                 0.500, 0.625, 0.875, 1.000,
263         };
264         float out_data[width * height];
265
266         EffectChainTester tester(NULL, width, height);
267
268         ImageFormat format;
269         format.color_space = COLORSPACE_sRGB;
270         format.gamma_curve = GAMMA_sRGB;
271
272         YCbCrFormat ycbcr_format;
273         ycbcr_format.luma_coefficients = YCBCR_REC_601;
274         ycbcr_format.full_range = false;
275         ycbcr_format.num_levels = 256;
276         ycbcr_format.chroma_subsampling_x = 2;
277         ycbcr_format.chroma_subsampling_y = 2;
278         ycbcr_format.cb_x_position = 0.5f;
279         ycbcr_format.cb_y_position = 0.5f;
280         ycbcr_format.cr_x_position = 0.5f;
281         ycbcr_format.cr_y_position = 0.5f;
282
283         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
284         input->set_pixel_data(0, y);
285         input->set_pixel_data(1, cb);
286         input->set_pixel_data(2, cr);
287         tester.get_chain()->add_input(input);
288
289         tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
290
291         // Y'CbCr isn't 100% accurate (the input values are rounded),
292         // so we need some leeway.
293         expect_equal(expected_data, out_data, width, height, 0.01, 0.001);
294 }
295
296 TEST(YCbCrInputTest, Subsampling420WithNonCenteredSamples) {
297         const int width = 4;
298         const int height = 4;
299
300         unsigned char y[width * height] = {
301                 126, 126, 126, 126,
302                 126, 126, 126, 126,
303                 126, 126, 126, 126,
304                 126, 126, 126, 126,
305         };
306         unsigned char cb[(width/2) * (height/2)] = {
307                 64, 128,
308                 128, 192,
309         };
310         unsigned char cr[(width/2) * (height/2)] = {
311                 128, 128,
312                 128, 128,
313         };
314
315         // Note: This is only the blue channel. The chroma samples (with associated
316         // values for blue) are marked off in comments.
317         float expected_data[width * height] = {
318                    0.000, 0.250, 0.500, 0.500, 
319                 /* 0.0 */     /* 0.5 */
320                    0.125, 0.375, 0.625, 0.625,
321
322                    0.375, 0.625, 0.875, 0.875,
323                 /* 0.5 */     /* 1.0 */
324                    0.500, 0.750, 1.000, 1.000,
325         };
326         float out_data[width * height];
327
328         EffectChainTester tester(NULL, width, height);
329
330         ImageFormat format;
331         format.color_space = COLORSPACE_sRGB;
332         format.gamma_curve = GAMMA_sRGB;
333
334         YCbCrFormat ycbcr_format;
335         ycbcr_format.luma_coefficients = YCBCR_REC_601;
336         ycbcr_format.full_range = false;
337         ycbcr_format.num_levels = 256;
338         ycbcr_format.chroma_subsampling_x = 2;
339         ycbcr_format.chroma_subsampling_y = 2;
340         ycbcr_format.cb_x_position = 0.0f;
341         ycbcr_format.cb_y_position = 0.5f;
342         ycbcr_format.cr_x_position = 0.0f;
343         ycbcr_format.cr_y_position = 0.5f;
344
345         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
346         input->set_pixel_data(0, y);
347         input->set_pixel_data(1, cb);
348         input->set_pixel_data(2, cr);
349         tester.get_chain()->add_input(input);
350
351         tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
352
353         // Y'CbCr isn't 100% accurate (the input values are rounded),
354         // so we need some leeway.
355         expect_equal(expected_data, out_data, width, height, 0.01, 0.001);
356 }
357
358 // Yes, some 4:2:2 formats actually have this craziness.
359 TEST(YCbCrInputTest, DifferentCbAndCrPositioning) {
360         const int width = 4;
361         const int height = 4;
362
363         unsigned char y[width * height] = {
364                 126, 126, 126, 126,
365                 126, 126, 126, 126,
366                 126, 126, 126, 126,
367                 126, 126, 126, 126,
368         };
369         unsigned char cb[(width/2) * height] = {
370                 64, 128,
371                 128, 192,
372                 128, 128,
373                 128, 128,
374         };
375         unsigned char cr[(width/2) * height] = {
376                 48, 128,
377                 128, 208,
378                 128, 128,
379                 128, 128,
380         };
381
382         // Chroma samples in this csae are always co-sited with a luma sample;
383         // their associated color values and position are marked off in comments.
384         float expected_data_blue[width * height] = {
385                    0.000 /* 0.0 */, 0.250,           0.500 /* 0.5 */, 0.500, 
386                    0.500 /* 0.5 */, 0.750,           1.000 /* 1.0 */, 1.000, 
387                    0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 0.500, 
388                    0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 0.500, 
389         };
390         float expected_data_red[width * height] = {
391                    0.000,           0.000 /* 0.0 */, 0.250,           0.500 /* 0.5 */, 
392                    0.500,           0.500 /* 0.5 */, 0.750,           1.000 /* 1.0 */, 
393                    0.500,           0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 
394                    0.500,           0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 
395         };
396         float out_data[width * height];
397
398         EffectChainTester tester(NULL, width, height);
399
400         ImageFormat format;
401         format.color_space = COLORSPACE_sRGB;
402         format.gamma_curve = GAMMA_sRGB;
403
404         YCbCrFormat ycbcr_format;
405         ycbcr_format.luma_coefficients = YCBCR_REC_601;
406         ycbcr_format.full_range = false;
407         ycbcr_format.num_levels = 256;
408         ycbcr_format.chroma_subsampling_x = 2;
409         ycbcr_format.chroma_subsampling_y = 1;
410         ycbcr_format.cb_x_position = 0.0f;
411         ycbcr_format.cb_y_position = 0.5f;
412         ycbcr_format.cr_x_position = 1.0f;
413         ycbcr_format.cr_y_position = 0.5f;
414
415         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
416         input->set_pixel_data(0, y);
417         input->set_pixel_data(1, cb);
418         input->set_pixel_data(2, cr);
419         tester.get_chain()->add_input(input);
420
421         // Y'CbCr isn't 100% accurate (the input values are rounded),
422         // so we need some leeway.
423         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
424         expect_equal(expected_data_red, out_data, width, height, 0.02, 0.002);
425
426         tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
427         expect_equal(expected_data_blue, out_data, width, height, 0.01, 0.001);
428 }
429
430 TEST(YCbCrInputTest, PBO) {
431         const int width = 1;
432         const int height = 5;
433
434         // Pure-color test inputs, calculated with the formulas in Rec. 601
435         // section 2.5.4.
436         unsigned char data[width * height * 3] = {
437                 16, 235, 81, 145, 41,
438                 128, 128, 90, 54, 240,
439                 128, 128, 240, 34, 110,
440         };
441         float expected_data[4 * width * height] = {
442                 0.0, 0.0, 0.0, 1.0,
443                 1.0, 1.0, 1.0, 1.0,
444                 1.0, 0.0, 0.0, 1.0,
445                 0.0, 1.0, 0.0, 1.0,
446                 0.0, 0.0, 1.0, 1.0,
447         };
448         float out_data[4 * width * height];
449
450         GLuint pbo;
451         glGenBuffers(1, &pbo);
452         glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo);
453         glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, width * height * 3, data, GL_STREAM_DRAW);
454         glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
455
456         EffectChainTester tester(NULL, width, height);
457
458         ImageFormat format;
459         format.color_space = COLORSPACE_sRGB;
460         format.gamma_curve = GAMMA_sRGB;
461
462         YCbCrFormat ycbcr_format;
463         ycbcr_format.luma_coefficients = YCBCR_REC_601;
464         ycbcr_format.full_range = false;
465         ycbcr_format.num_levels = 256;
466         ycbcr_format.chroma_subsampling_x = 1;
467         ycbcr_format.chroma_subsampling_y = 1;
468         ycbcr_format.cb_x_position = 0.5f;
469         ycbcr_format.cb_y_position = 0.5f;
470         ycbcr_format.cr_x_position = 0.5f;
471         ycbcr_format.cr_y_position = 0.5f;
472
473         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
474         input->set_pixel_data(0, (unsigned char *)BUFFER_OFFSET(0), pbo);
475         input->set_pixel_data(1, (unsigned char *)BUFFER_OFFSET(width * height), pbo);
476         input->set_pixel_data(2, (unsigned char *)BUFFER_OFFSET(width * height * 2), pbo);
477         tester.get_chain()->add_input(input);
478
479         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
480
481         // Y'CbCr isn't 100% accurate (the input values are rounded),
482         // so we need some leeway.
483         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
484
485         glDeleteBuffers(1, &pbo);
486 }
487
488 TEST(YCbCrInputTest, CombinedCbAndCr) {
489         const int width = 1;
490         const int height = 5;
491
492         // Pure-color test inputs, calculated with the formulas in Rec. 601
493         // section 2.5.4.
494         unsigned char y[width * height] = {
495                 16, 235, 81, 145, 41,
496         };
497         unsigned char cb_cr[width * height * 2] = {
498                 128, 128,
499                 128, 128,
500                  90, 240,
501                  54,  34,
502                 240, 110,
503         };
504         float expected_data[4 * width * height] = {
505                 0.0, 0.0, 0.0, 1.0,
506                 1.0, 1.0, 1.0, 1.0,
507                 1.0, 0.0, 0.0, 1.0,
508                 0.0, 1.0, 0.0, 1.0,
509                 0.0, 0.0, 1.0, 1.0,
510         };
511         float out_data[4 * width * height];
512
513         EffectChainTester tester(NULL, width, height);
514
515         ImageFormat format;
516         format.color_space = COLORSPACE_sRGB;
517         format.gamma_curve = GAMMA_sRGB;
518
519         YCbCrFormat ycbcr_format;
520         ycbcr_format.luma_coefficients = YCBCR_REC_601;
521         ycbcr_format.full_range = false;
522         ycbcr_format.num_levels = 256;
523         ycbcr_format.chroma_subsampling_x = 1;
524         ycbcr_format.chroma_subsampling_y = 1;
525         ycbcr_format.cb_x_position = 0.5f;
526         ycbcr_format.cb_y_position = 0.5f;
527         ycbcr_format.cr_x_position = 0.5f;
528         ycbcr_format.cr_y_position = 0.5f;
529
530         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height, YCBCR_INPUT_SPLIT_Y_AND_CBCR);
531         input->set_pixel_data(0, y);
532         input->set_pixel_data(1, cb_cr);
533         tester.get_chain()->add_input(input);
534
535         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
536
537         // Y'CbCr isn't 100% accurate (the input values are rounded),
538         // so we need some leeway.
539         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
540 }
541
542 TEST(YCbCrInputTest, ExternalTexture) {
543         const int width = 1;
544         const int height = 5;
545
546         // Pure-color test inputs, calculated with the formulas in Rec. 601
547         // section 2.5.4.
548         unsigned char y[width * height] = {
549                 16, 235, 81, 145, 41,
550         };
551         unsigned char cb[width * height] = {
552                 128, 128, 90, 54, 240,
553         };
554         unsigned char cr[width * height] = {
555                 128, 128, 240, 34, 110,
556         };
557         float expected_data[4 * width * height] = {
558                 0.0, 0.0, 0.0, 1.0,
559                 1.0, 1.0, 1.0, 1.0,
560                 1.0, 0.0, 0.0, 1.0,
561                 0.0, 1.0, 0.0, 1.0,
562                 0.0, 0.0, 1.0, 1.0,
563         };
564         float out_data[4 * width * height];
565
566         EffectChainTester tester(NULL, width, height);
567
568         ImageFormat format;
569         format.color_space = COLORSPACE_sRGB;
570         format.gamma_curve = GAMMA_sRGB;
571
572         YCbCrFormat ycbcr_format;
573         ycbcr_format.luma_coefficients = YCBCR_REC_601;
574         ycbcr_format.full_range = false;
575         ycbcr_format.num_levels = 256;
576         ycbcr_format.chroma_subsampling_x = 1;
577         ycbcr_format.chroma_subsampling_y = 1;
578         ycbcr_format.cb_x_position = 0.5f;
579         ycbcr_format.cb_y_position = 0.5f;
580         ycbcr_format.cr_x_position = 0.5f;
581         ycbcr_format.cr_y_position = 0.5f;
582
583         // Make a texture for the Cb data; keep the others as regular uploads.
584         ResourcePool pool;
585         GLuint cb_tex = pool.create_2d_texture(GL_R8, width, height);
586         check_error();
587         glBindTexture(GL_TEXTURE_2D, cb_tex);
588         check_error();
589         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
590         check_error();
591         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
592         check_error();
593         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, cb);
594         check_error();
595         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
596         check_error();
597         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
598         check_error();
599
600         YCbCrInput *input = new YCbCrInput(format, ycbcr_format, width, height);
601         input->set_pixel_data(0, y);
602         input->set_texture_num(1, cb_tex);
603         input->set_pixel_data(2, cr);
604         tester.get_chain()->add_input(input);
605
606         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
607
608         pool.release_2d_texture(cb_tex);
609
610         // Y'CbCr isn't 100% accurate (the input values are rounded),
611         // so we need some leeway.
612         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
613 }
614
615 }  // namespace movit