1 /*
2 * Copyright 2015 The LibYuv Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include <stdlib.h>
12
13 #include "../unit_test/unit_test.h"
14 #include "libyuv/basic_types.h"
15 #include "libyuv/convert.h"
16 #include "libyuv/convert_argb.h"
17 #include "libyuv/convert_from.h"
18 #include "libyuv/convert_from_argb.h"
19 #include "libyuv/cpu_id.h"
20
21 namespace libyuv {
22
23 // TODO(fbarchard): clang x86 has a higher accuracy YUV to RGB.
24 // Port to Visual C and other CPUs
25 #if !defined(LIBYUV_BIT_EXACT) && !defined(LIBYUV_DISABLE_X86) && \
26 (defined(__x86_64__) || defined(__i386__))
27 #define ERROR_FULL 5
28 #define ERROR_J420 4
29 #else
30 #define ERROR_FULL 6
31 #define ERROR_J420 6
32 #endif
33 #define ERROR_R 1
34 #define ERROR_G 1
35 #ifdef LIBYUV_UNLIMITED_DATA
36 #define ERROR_B 1
37 #else
38 #define ERROR_B 18
39 #endif
40
41 #define TESTCS(TESTNAME, YUVTOARGB, ARGBTOYUV, HS1, HS, HN, DIFF) \
42 TEST_F(LibYUVColorTest, TESTNAME) { \
43 const int kPixels = benchmark_width_ * benchmark_height_; \
44 const int kHalfPixels = \
45 ((benchmark_width_ + 1) / 2) * ((benchmark_height_ + HS1) / HS); \
46 align_buffer_page_end(orig_y, kPixels); \
47 align_buffer_page_end(orig_u, kHalfPixels); \
48 align_buffer_page_end(orig_v, kHalfPixels); \
49 align_buffer_page_end(orig_pixels, kPixels * 4); \
50 align_buffer_page_end(temp_y, kPixels); \
51 align_buffer_page_end(temp_u, kHalfPixels); \
52 align_buffer_page_end(temp_v, kHalfPixels); \
53 align_buffer_page_end(dst_pixels_opt, kPixels * 4); \
54 align_buffer_page_end(dst_pixels_c, kPixels * 4); \
55 \
56 MemRandomize(orig_pixels, kPixels * 4); \
57 MemRandomize(orig_y, kPixels); \
58 MemRandomize(orig_u, kHalfPixels); \
59 MemRandomize(orig_v, kHalfPixels); \
60 MemRandomize(temp_y, kPixels); \
61 MemRandomize(temp_u, kHalfPixels); \
62 MemRandomize(temp_v, kHalfPixels); \
63 MemRandomize(dst_pixels_opt, kPixels * 4); \
64 MemRandomize(dst_pixels_c, kPixels * 4); \
65 \
66 /* The test is overall for color conversion matrix being reversible, so */ \
67 /* this initializes the pixel with 2x2 blocks to eliminate subsampling. */ \
68 uint8_t* p = orig_y; \
69 for (int y = 0; y < benchmark_height_ - HS1; y += HS) { \
70 for (int x = 0; x < benchmark_width_ - 1; x += 2) { \
71 uint8_t r = static_cast<uint8_t>(fastrand()); \
72 p[0] = r; \
73 p[1] = r; \
74 p[HN] = r; \
75 p[HN + 1] = r; \
76 p += 2; \
77 } \
78 if (benchmark_width_ & 1) { \
79 uint8_t r = static_cast<uint8_t>(fastrand()); \
80 p[0] = r; \
81 p[HN] = r; \
82 p += 1; \
83 } \
84 p += HN; \
85 } \
86 if ((benchmark_height_ & 1) && HS == 2) { \
87 for (int x = 0; x < benchmark_width_ - 1; x += 2) { \
88 uint8_t r = static_cast<uint8_t>(fastrand()); \
89 p[0] = r; \
90 p[1] = r; \
91 p += 2; \
92 } \
93 if (benchmark_width_ & 1) { \
94 uint8_t r = static_cast<uint8_t>(fastrand()); \
95 p[0] = r; \
96 p += 1; \
97 } \
98 } \
99 /* Start with YUV converted to ARGB. */ \
100 YUVTOARGB(orig_y, benchmark_width_, orig_u, (benchmark_width_ + 1) / 2, \
101 orig_v, (benchmark_width_ + 1) / 2, orig_pixels, \
102 benchmark_width_ * 4, benchmark_width_, benchmark_height_); \
103 \
104 ARGBTOYUV(orig_pixels, benchmark_width_ * 4, temp_y, benchmark_width_, \
105 temp_u, (benchmark_width_ + 1) / 2, temp_v, \
106 (benchmark_width_ + 1) / 2, benchmark_width_, \
107 benchmark_height_); \
108 \
109 MaskCpuFlags(disable_cpu_flags_); \
110 YUVTOARGB(temp_y, benchmark_width_, temp_u, (benchmark_width_ + 1) / 2, \
111 temp_v, (benchmark_width_ + 1) / 2, dst_pixels_c, \
112 benchmark_width_ * 4, benchmark_width_, benchmark_height_); \
113 MaskCpuFlags(benchmark_cpu_info_); \
114 \
115 for (int i = 0; i < benchmark_iterations_; ++i) { \
116 YUVTOARGB(temp_y, benchmark_width_, temp_u, (benchmark_width_ + 1) / 2, \
117 temp_v, (benchmark_width_ + 1) / 2, dst_pixels_opt, \
118 benchmark_width_ * 4, benchmark_width_, benchmark_height_); \
119 } \
120 /* Test C and SIMD match. */ \
121 for (int i = 0; i < kPixels * 4; ++i) { \
122 EXPECT_EQ(dst_pixels_c[i], dst_pixels_opt[i]); \
123 } \
124 /* Test SIMD is close to original. */ \
125 for (int i = 0; i < kPixels * 4; ++i) { \
126 EXPECT_NEAR(static_cast<int>(orig_pixels[i]), \
127 static_cast<int>(dst_pixels_opt[i]), DIFF); \
128 } \
129 \
130 free_aligned_buffer_page_end(orig_pixels); \
131 free_aligned_buffer_page_end(orig_y); \
132 free_aligned_buffer_page_end(orig_u); \
133 free_aligned_buffer_page_end(orig_v); \
134 free_aligned_buffer_page_end(temp_y); \
135 free_aligned_buffer_page_end(temp_u); \
136 free_aligned_buffer_page_end(temp_v); \
137 free_aligned_buffer_page_end(dst_pixels_opt); \
138 free_aligned_buffer_page_end(dst_pixels_c); \
139 }
140
141 TESTCS(TestI420, I420ToARGB, ARGBToI420, 1, 2, benchmark_width_, ERROR_FULL)
142 TESTCS(TestI422, I422ToARGB, ARGBToI422, 0, 1, 0, ERROR_FULL)
143 TESTCS(TestJ420, J420ToARGB, ARGBToJ420, 1, 2, benchmark_width_, ERROR_J420)
144 TESTCS(TestJ422, J422ToARGB, ARGBToJ422, 0, 1, 0, ERROR_J420)
145
YUVToRGB(int y,int u,int v,int * r,int * g,int * b)146 static void YUVToRGB(int y, int u, int v, int* r, int* g, int* b) {
147 const int kWidth = 16;
148 const int kHeight = 1;
149 const int kPixels = kWidth * kHeight;
150 const int kHalfPixels = ((kWidth + 1) / 2) * ((kHeight + 1) / 2);
151
152 SIMD_ALIGNED(uint8_t orig_y[16]);
153 SIMD_ALIGNED(uint8_t orig_u[8]);
154 SIMD_ALIGNED(uint8_t orig_v[8]);
155 SIMD_ALIGNED(uint8_t orig_pixels[16 * 4]);
156 memset(orig_y, y, kPixels);
157 memset(orig_u, u, kHalfPixels);
158 memset(orig_v, v, kHalfPixels);
159
160 /* YUV converted to ARGB. */
161 I422ToARGB(orig_y, kWidth, orig_u, (kWidth + 1) / 2, orig_v, (kWidth + 1) / 2,
162 orig_pixels, kWidth * 4, kWidth, kHeight);
163
164 *b = orig_pixels[0];
165 *g = orig_pixels[1];
166 *r = orig_pixels[2];
167 }
168
YUVJToRGB(int y,int u,int v,int * r,int * g,int * b)169 static void YUVJToRGB(int y, int u, int v, int* r, int* g, int* b) {
170 const int kWidth = 16;
171 const int kHeight = 1;
172 const int kPixels = kWidth * kHeight;
173 const int kHalfPixels = ((kWidth + 1) / 2) * ((kHeight + 1) / 2);
174
175 SIMD_ALIGNED(uint8_t orig_y[16]);
176 SIMD_ALIGNED(uint8_t orig_u[8]);
177 SIMD_ALIGNED(uint8_t orig_v[8]);
178 SIMD_ALIGNED(uint8_t orig_pixels[16 * 4]);
179 memset(orig_y, y, kPixels);
180 memset(orig_u, u, kHalfPixels);
181 memset(orig_v, v, kHalfPixels);
182
183 /* YUV converted to ARGB. */
184 J422ToARGB(orig_y, kWidth, orig_u, (kWidth + 1) / 2, orig_v, (kWidth + 1) / 2,
185 orig_pixels, kWidth * 4, kWidth, kHeight);
186
187 *b = orig_pixels[0];
188 *g = orig_pixels[1];
189 *r = orig_pixels[2];
190 }
191
YUVHToRGB(int y,int u,int v,int * r,int * g,int * b)192 static void YUVHToRGB(int y, int u, int v, int* r, int* g, int* b) {
193 const int kWidth = 16;
194 const int kHeight = 1;
195 const int kPixels = kWidth * kHeight;
196 const int kHalfPixels = ((kWidth + 1) / 2) * ((kHeight + 1) / 2);
197
198 SIMD_ALIGNED(uint8_t orig_y[16]);
199 SIMD_ALIGNED(uint8_t orig_u[8]);
200 SIMD_ALIGNED(uint8_t orig_v[8]);
201 SIMD_ALIGNED(uint8_t orig_pixels[16 * 4]);
202 memset(orig_y, y, kPixels);
203 memset(orig_u, u, kHalfPixels);
204 memset(orig_v, v, kHalfPixels);
205
206 /* YUV converted to ARGB. */
207 H422ToARGB(orig_y, kWidth, orig_u, (kWidth + 1) / 2, orig_v, (kWidth + 1) / 2,
208 orig_pixels, kWidth * 4, kWidth, kHeight);
209
210 *b = orig_pixels[0];
211 *g = orig_pixels[1];
212 *r = orig_pixels[2];
213 }
214
215 #define F422ToARGB(a, b, c, d, e, f, g, h, i, j) \
216 I422ToARGBMatrix(a, b, c, d, e, f, g, h, &kYuvF709Constants, i, j)
217
YUVFToRGB(int y,int u,int v,int * r,int * g,int * b)218 static void YUVFToRGB(int y, int u, int v, int* r, int* g, int* b) {
219 const int kWidth = 16;
220 const int kHeight = 1;
221 const int kPixels = kWidth * kHeight;
222 const int kHalfPixels = ((kWidth + 1) / 2) * ((kHeight + 1) / 2);
223
224 SIMD_ALIGNED(uint8_t orig_y[16]);
225 SIMD_ALIGNED(uint8_t orig_u[8]);
226 SIMD_ALIGNED(uint8_t orig_v[8]);
227 SIMD_ALIGNED(uint8_t orig_pixels[16 * 4]);
228 memset(orig_y, y, kPixels);
229 memset(orig_u, u, kHalfPixels);
230 memset(orig_v, v, kHalfPixels);
231
232 /* YUV converted to ARGB. */
233 F422ToARGB(orig_y, kWidth, orig_u, (kWidth + 1) / 2, orig_v, (kWidth + 1) / 2,
234 orig_pixels, kWidth * 4, kWidth, kHeight);
235
236 *b = orig_pixels[0];
237 *g = orig_pixels[1];
238 *r = orig_pixels[2];
239 }
240
YUVUToRGB(int y,int u,int v,int * r,int * g,int * b)241 static void YUVUToRGB(int y, int u, int v, int* r, int* g, int* b) {
242 const int kWidth = 16;
243 const int kHeight = 1;
244 const int kPixels = kWidth * kHeight;
245 const int kHalfPixels = ((kWidth + 1) / 2) * ((kHeight + 1) / 2);
246
247 SIMD_ALIGNED(uint8_t orig_y[16]);
248 SIMD_ALIGNED(uint8_t orig_u[8]);
249 SIMD_ALIGNED(uint8_t orig_v[8]);
250 SIMD_ALIGNED(uint8_t orig_pixels[16 * 4]);
251 memset(orig_y, y, kPixels);
252 memset(orig_u, u, kHalfPixels);
253 memset(orig_v, v, kHalfPixels);
254
255 /* YUV converted to ARGB. */
256 U422ToARGB(orig_y, kWidth, orig_u, (kWidth + 1) / 2, orig_v, (kWidth + 1) / 2,
257 orig_pixels, kWidth * 4, kWidth, kHeight);
258
259 *b = orig_pixels[0];
260 *g = orig_pixels[1];
261 *r = orig_pixels[2];
262 }
263
264 #define V422ToARGB(a, b, c, d, e, f, g, h, i, j) \
265 I422ToARGBMatrix(a, b, c, d, e, f, g, h, &kYuvV2020Constants, i, j)
266
YUVVToRGB(int y,int u,int v,int * r,int * g,int * b)267 static void YUVVToRGB(int y, int u, int v, int* r, int* g, int* b) {
268 const int kWidth = 16;
269 const int kHeight = 1;
270 const int kPixels = kWidth * kHeight;
271 const int kHalfPixels = ((kWidth + 1) / 2) * ((kHeight + 1) / 2);
272
273 SIMD_ALIGNED(uint8_t orig_y[16]);
274 SIMD_ALIGNED(uint8_t orig_u[8]);
275 SIMD_ALIGNED(uint8_t orig_v[8]);
276 SIMD_ALIGNED(uint8_t orig_pixels[16 * 4]);
277 memset(orig_y, y, kPixels);
278 memset(orig_u, u, kHalfPixels);
279 memset(orig_v, v, kHalfPixels);
280
281 /* YUV converted to ARGB. */
282 V422ToARGB(orig_y, kWidth, orig_u, (kWidth + 1) / 2, orig_v, (kWidth + 1) / 2,
283 orig_pixels, kWidth * 4, kWidth, kHeight);
284
285 *b = orig_pixels[0];
286 *g = orig_pixels[1];
287 *r = orig_pixels[2];
288 }
289
YToRGB(int y,int * r,int * g,int * b)290 static void YToRGB(int y, int* r, int* g, int* b) {
291 const int kWidth = 16;
292 const int kHeight = 1;
293 const int kPixels = kWidth * kHeight;
294
295 SIMD_ALIGNED(uint8_t orig_y[16]);
296 SIMD_ALIGNED(uint8_t orig_pixels[16 * 4]);
297 memset(orig_y, y, kPixels);
298
299 /* YUV converted to ARGB. */
300 I400ToARGB(orig_y, kWidth, orig_pixels, kWidth * 4, kWidth, kHeight);
301
302 *b = orig_pixels[0];
303 *g = orig_pixels[1];
304 *r = orig_pixels[2];
305 }
306
YJToRGB(int y,int * r,int * g,int * b)307 static void YJToRGB(int y, int* r, int* g, int* b) {
308 const int kWidth = 16;
309 const int kHeight = 1;
310 const int kPixels = kWidth * kHeight;
311
312 SIMD_ALIGNED(uint8_t orig_y[16]);
313 SIMD_ALIGNED(uint8_t orig_pixels[16 * 4]);
314 memset(orig_y, y, kPixels);
315
316 /* YUV converted to ARGB. */
317 J400ToARGB(orig_y, kWidth, orig_pixels, kWidth * 4, kWidth, kHeight);
318
319 *b = orig_pixels[0];
320 *g = orig_pixels[1];
321 *r = orig_pixels[2];
322 }
323
324 // Pick a method for clamping.
325 // #define CLAMPMETHOD_IF 1
326 // #define CLAMPMETHOD_TABLE 1
327 #define CLAMPMETHOD_TERNARY 1
328 // #define CLAMPMETHOD_MASK 1
329
330 // Pick a method for rounding.
331 #define ROUND(f) static_cast<int>(f + 0.5f)
332 // #define ROUND(f) lrintf(f)
333 // #define ROUND(f) static_cast<int>(round(f))
334 // #define ROUND(f) _mm_cvt_ss2si(_mm_load_ss(&f))
335
336 #if defined(CLAMPMETHOD_IF)
RoundToByte(float f)337 static int RoundToByte(float f) {
338 int i = ROUND(f);
339 if (i < 0) {
340 i = 0;
341 }
342 if (i > 255) {
343 i = 255;
344 }
345 return i;
346 }
347 #elif defined(CLAMPMETHOD_TABLE)
348 static const unsigned char clamptable[811] = {
349 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
350 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
351 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
352 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
353 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
354 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
355 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
356 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
358 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
359 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
360 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
361 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
362 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
363 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
364 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
365 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
366 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
367 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
368 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
369 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
370 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
371 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
372 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
373 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
374 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
375 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
376 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
377 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
378 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173,
379 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
380 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
381 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218,
382 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
383 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248,
384 249, 250, 251, 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255,
385 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
386 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
387 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
388 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
389 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
390 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
391 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
392 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
393 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
394 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
395 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
396 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
397 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
398 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
399 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
400 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
401 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
402 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
403 255};
404
RoundToByte(float f)405 static int RoundToByte(float f) {
406 return clamptable[ROUND(f) + 276];
407 }
408 #elif defined(CLAMPMETHOD_TERNARY)
RoundToByte(float f)409 static int RoundToByte(float f) {
410 int i = ROUND(f);
411 return (i < 0) ? 0 : ((i > 255) ? 255 : i);
412 }
413 #elif defined(CLAMPMETHOD_MASK)
RoundToByte(float f)414 static int RoundToByte(float f) {
415 int i = ROUND(f);
416 i = ((-(i) >> 31) & (i)); // clamp to 0.
417 return (((255 - (i)) >> 31) | (i)) & 255; // clamp to 255.
418 }
419 #endif
420
421 #define RANDOM256(s) ((s & 1) ? ((s >> 1) ^ 0xb8) : (s >> 1))
422
TEST_F(LibYUVColorTest,TestRoundToByte)423 TEST_F(LibYUVColorTest, TestRoundToByte) {
424 int allb = 0;
425 int count = benchmark_width_ * benchmark_height_;
426 for (int i = 0; i < benchmark_iterations_; ++i) {
427 float f = (fastrand() & 255) * 3.14f - 260.f;
428 for (int j = 0; j < count; ++j) {
429 int b = RoundToByte(f);
430 f += 0.91f;
431 allb |= b;
432 }
433 }
434 EXPECT_GE(allb, 0);
435 EXPECT_LE(allb, 255);
436 }
437
438 // BT.601 limited range YUV to RGB reference
YUVToRGBReference(int y,int u,int v,int * r,int * g,int * b)439 static void YUVToRGBReference(int y, int u, int v, int* r, int* g, int* b) {
440 *r = RoundToByte((y - 16) * 1.164 - (v - 128) * -1.596);
441 *g = RoundToByte((y - 16) * 1.164 - (u - 128) * 0.391 - (v - 128) * 0.813);
442 *b = RoundToByte((y - 16) * 1.164 - (u - 128) * -2.018);
443 }
444
445 // BT.601 full range YUV to RGB reference (aka JPEG)
YUVJToRGBReference(int y,int u,int v,int * r,int * g,int * b)446 static void YUVJToRGBReference(int y, int u, int v, int* r, int* g, int* b) {
447 *r = RoundToByte(y - (v - 128) * -1.40200);
448 *g = RoundToByte(y - (u - 128) * 0.34414 - (v - 128) * 0.71414);
449 *b = RoundToByte(y - (u - 128) * -1.77200);
450 }
451
452 // BT.709 limited range YUV to RGB reference
453 // See also http://www.equasys.de/colorconversion.html
YUVHToRGBReference(int y,int u,int v,int * r,int * g,int * b)454 static void YUVHToRGBReference(int y, int u, int v, int* r, int* g, int* b) {
455 *r = RoundToByte((y - 16) * 1.164 - (v - 128) * -1.793);
456 *g = RoundToByte((y - 16) * 1.164 - (u - 128) * 0.213 - (v - 128) * 0.533);
457 *b = RoundToByte((y - 16) * 1.164 - (u - 128) * -2.112);
458 }
459
460 // BT.709 full range YUV to RGB reference
YUVFToRGBReference(int y,int u,int v,int * r,int * g,int * b)461 static void YUVFToRGBReference(int y, int u, int v, int* r, int* g, int* b) {
462 *r = RoundToByte(y - (v - 128) * -1.5748);
463 *g = RoundToByte(y - (u - 128) * 0.18732 - (v - 128) * 0.46812);
464 *b = RoundToByte(y - (u - 128) * -1.8556);
465 }
466
467 // BT.2020 limited range YUV to RGB reference
YUVUToRGBReference(int y,int u,int v,int * r,int * g,int * b)468 static void YUVUToRGBReference(int y, int u, int v, int* r, int* g, int* b) {
469 *r = RoundToByte((y - 16) * 1.164384 - (v - 128) * -1.67867);
470 *g = RoundToByte((y - 16) * 1.164384 - (u - 128) * 0.187326 -
471 (v - 128) * 0.65042);
472 *b = RoundToByte((y - 16) * 1.164384 - (u - 128) * -2.14177);
473 }
474
475 // BT.2020 full range YUV to RGB reference
YUVVToRGBReference(int y,int u,int v,int * r,int * g,int * b)476 static void YUVVToRGBReference(int y, int u, int v, int* r, int* g, int* b) {
477 *r = RoundToByte(y + (v - 128) * 1.474600);
478 *g = RoundToByte(y - (u - 128) * 0.164553 - (v - 128) * 0.571353);
479 *b = RoundToByte(y + (u - 128) * 1.881400);
480 }
481
TEST_F(LibYUVColorTest,TestYUV)482 TEST_F(LibYUVColorTest, TestYUV) {
483 int r0, g0, b0, r1, g1, b1;
484
485 // cyan (less red)
486 YUVToRGBReference(240, 255, 0, &r0, &g0, &b0);
487 EXPECT_EQ(56, r0);
488 EXPECT_EQ(255, g0);
489 EXPECT_EQ(255, b0);
490
491 YUVToRGB(240, 255, 0, &r1, &g1, &b1);
492 EXPECT_EQ(57, r1);
493 EXPECT_EQ(255, g1);
494 EXPECT_EQ(255, b1);
495
496 // green (less red and blue)
497 YUVToRGBReference(240, 0, 0, &r0, &g0, &b0);
498 EXPECT_EQ(56, r0);
499 EXPECT_EQ(255, g0);
500 EXPECT_EQ(2, b0);
501
502 YUVToRGB(240, 0, 0, &r1, &g1, &b1);
503 EXPECT_EQ(57, r1);
504 EXPECT_EQ(255, g1);
505 #ifdef LIBYUV_UNLIMITED_DATA
506 EXPECT_EQ(3, b1);
507 #else
508 EXPECT_EQ(5, b1);
509 #endif
510
511 for (int i = 0; i < 256; ++i) {
512 YUVToRGBReference(i, 128, 128, &r0, &g0, &b0);
513 YUVToRGB(i, 128, 128, &r1, &g1, &b1);
514 EXPECT_NEAR(r0, r1, ERROR_R);
515 EXPECT_NEAR(g0, g1, ERROR_G);
516 EXPECT_NEAR(b0, b1, ERROR_B);
517
518 YUVToRGBReference(i, 0, 0, &r0, &g0, &b0);
519 YUVToRGB(i, 0, 0, &r1, &g1, &b1);
520 EXPECT_NEAR(r0, r1, ERROR_R);
521 EXPECT_NEAR(g0, g1, ERROR_G);
522 EXPECT_NEAR(b0, b1, ERROR_B);
523
524 YUVToRGBReference(i, 0, 255, &r0, &g0, &b0);
525 YUVToRGB(i, 0, 255, &r1, &g1, &b1);
526 EXPECT_NEAR(r0, r1, ERROR_R);
527 EXPECT_NEAR(g0, g1, ERROR_G);
528 EXPECT_NEAR(b0, b1, ERROR_B);
529 }
530 }
531
TEST_F(LibYUVColorTest,TestGreyYUV)532 TEST_F(LibYUVColorTest, TestGreyYUV) {
533 int r0, g0, b0, r1, g1, b1, r2, g2, b2;
534
535 // black
536 YUVToRGBReference(16, 128, 128, &r0, &g0, &b0);
537 EXPECT_EQ(0, r0);
538 EXPECT_EQ(0, g0);
539 EXPECT_EQ(0, b0);
540
541 YUVToRGB(16, 128, 128, &r1, &g1, &b1);
542 EXPECT_EQ(0, r1);
543 EXPECT_EQ(0, g1);
544 EXPECT_EQ(0, b1);
545
546 // white
547 YUVToRGBReference(240, 128, 128, &r0, &g0, &b0);
548 EXPECT_EQ(255, r0);
549 EXPECT_EQ(255, g0);
550 EXPECT_EQ(255, b0);
551
552 YUVToRGB(240, 128, 128, &r1, &g1, &b1);
553 EXPECT_EQ(255, r1);
554 EXPECT_EQ(255, g1);
555 EXPECT_EQ(255, b1);
556
557 // grey
558 YUVToRGBReference(128, 128, 128, &r0, &g0, &b0);
559 EXPECT_EQ(130, r0);
560 EXPECT_EQ(130, g0);
561 EXPECT_EQ(130, b0);
562
563 YUVToRGB(128, 128, 128, &r1, &g1, &b1);
564 EXPECT_EQ(130, r1);
565 EXPECT_EQ(130, g1);
566 EXPECT_EQ(130, b1);
567
568 for (int y = 0; y < 256; ++y) {
569 YUVToRGBReference(y, 128, 128, &r0, &g0, &b0);
570 YUVToRGB(y, 128, 128, &r1, &g1, &b1);
571 YToRGB(y, &r2, &g2, &b2);
572 EXPECT_EQ(r0, r1);
573 EXPECT_EQ(g0, g1);
574 EXPECT_EQ(b0, b1);
575 EXPECT_EQ(r0, r2);
576 EXPECT_EQ(g0, g2);
577 EXPECT_EQ(b0, b2);
578 }
579 }
580
PrintHistogram(int rh[256],int gh[256],int bh[256])581 static void PrintHistogram(int rh[256], int gh[256], int bh[256]) {
582 int i;
583 printf("hist ");
584 for (i = 0; i < 256; ++i) {
585 if (rh[i] || gh[i] || bh[i]) {
586 printf(" %8d", i - 128);
587 }
588 }
589 printf("\nred ");
590 for (i = 0; i < 256; ++i) {
591 if (rh[i] || gh[i] || bh[i]) {
592 printf(" %8d", rh[i]);
593 }
594 }
595 printf("\ngreen");
596 for (i = 0; i < 256; ++i) {
597 if (rh[i] || gh[i] || bh[i]) {
598 printf(" %8d", gh[i]);
599 }
600 }
601 printf("\nblue ");
602 for (i = 0; i < 256; ++i) {
603 if (rh[i] || gh[i] || bh[i]) {
604 printf(" %8d", bh[i]);
605 }
606 }
607 printf("\n");
608 }
609
610 // Step by 5 on inner loop goes from 0 to 255 inclusive.
611 // Set to 1 for better converage. 3, 5 or 17 for faster testing.
612 #ifdef DISABLE_SLOW_TESTS
613 #define FASTSTEP 5
614 #else
615 #define FASTSTEP 1
616 #endif
617
618 // BT.601 limited range.
TEST_F(LibYUVColorTest,TestFullYUV)619 TEST_F(LibYUVColorTest, TestFullYUV) {
620 int rh[256] = {
621 0,
622 };
623 int gh[256] = {
624 0,
625 };
626 int bh[256] = {
627 0,
628 };
629 for (int u = 0; u < 256; ++u) {
630 for (int v = 0; v < 256; ++v) {
631 for (int y2 = 0; y2 < 256; y2 += FASTSTEP) {
632 int r0, g0, b0, r1, g1, b1;
633 int y = RANDOM256(y2);
634 YUVToRGBReference(y, u, v, &r0, &g0, &b0);
635 YUVToRGB(y, u, v, &r1, &g1, &b1);
636 EXPECT_NEAR(r0, r1, ERROR_R);
637 EXPECT_NEAR(g0, g1, ERROR_G);
638 EXPECT_NEAR(b0, b1, ERROR_B);
639 ++rh[r1 - r0 + 128];
640 ++gh[g1 - g0 + 128];
641 ++bh[b1 - b0 + 128];
642 }
643 }
644 }
645 PrintHistogram(rh, gh, bh);
646 }
647
648 // BT.601 full range.
TEST_F(LibYUVColorTest,TestFullYUVJ)649 TEST_F(LibYUVColorTest, TestFullYUVJ) {
650 int rh[256] = {
651 0,
652 };
653 int gh[256] = {
654 0,
655 };
656 int bh[256] = {
657 0,
658 };
659 for (int u = 0; u < 256; ++u) {
660 for (int v = 0; v < 256; ++v) {
661 for (int y2 = 0; y2 < 256; y2 += FASTSTEP) {
662 int r0, g0, b0, r1, g1, b1;
663 int y = RANDOM256(y2);
664 YUVJToRGBReference(y, u, v, &r0, &g0, &b0);
665 YUVJToRGB(y, u, v, &r1, &g1, &b1);
666 EXPECT_NEAR(r0, r1, ERROR_R);
667 EXPECT_NEAR(g0, g1, ERROR_G);
668 EXPECT_NEAR(b0, b1, ERROR_B);
669 ++rh[r1 - r0 + 128];
670 ++gh[g1 - g0 + 128];
671 ++bh[b1 - b0 + 128];
672 }
673 }
674 }
675 PrintHistogram(rh, gh, bh);
676 }
677
678 // BT.709 limited range.
TEST_F(LibYUVColorTest,TestFullYUVH)679 TEST_F(LibYUVColorTest, TestFullYUVH) {
680 int rh[256] = {
681 0,
682 };
683 int gh[256] = {
684 0,
685 };
686 int bh[256] = {
687 0,
688 };
689 for (int u = 0; u < 256; ++u) {
690 for (int v = 0; v < 256; ++v) {
691 for (int y2 = 0; y2 < 256; y2 += FASTSTEP) {
692 int r0, g0, b0, r1, g1, b1;
693 int y = RANDOM256(y2);
694 YUVHToRGBReference(y, u, v, &r0, &g0, &b0);
695 YUVHToRGB(y, u, v, &r1, &g1, &b1);
696 EXPECT_NEAR(r0, r1, ERROR_R);
697 EXPECT_NEAR(g0, g1, ERROR_G);
698 EXPECT_NEAR(b0, b1, ERROR_B);
699 ++rh[r1 - r0 + 128];
700 ++gh[g1 - g0 + 128];
701 ++bh[b1 - b0 + 128];
702 }
703 }
704 }
705 PrintHistogram(rh, gh, bh);
706 }
707
708 // BT.709 full range.
TEST_F(LibYUVColorTest,TestFullYUVF)709 TEST_F(LibYUVColorTest, TestFullYUVF) {
710 int rh[256] = {
711 0,
712 };
713 int gh[256] = {
714 0,
715 };
716 int bh[256] = {
717 0,
718 };
719 for (int u = 0; u < 256; ++u) {
720 for (int v = 0; v < 256; ++v) {
721 for (int y2 = 0; y2 < 256; y2 += FASTSTEP) {
722 int r0, g0, b0, r1, g1, b1;
723 int y = RANDOM256(y2);
724 YUVFToRGBReference(y, u, v, &r0, &g0, &b0);
725 YUVFToRGB(y, u, v, &r1, &g1, &b1);
726 EXPECT_NEAR(r0, r1, ERROR_R);
727 EXPECT_NEAR(g0, g1, ERROR_G);
728 EXPECT_NEAR(b0, b1, ERROR_B);
729 ++rh[r1 - r0 + 128];
730 ++gh[g1 - g0 + 128];
731 ++bh[b1 - b0 + 128];
732 }
733 }
734 }
735 PrintHistogram(rh, gh, bh);
736 }
737
738 // BT.2020 limited range.
TEST_F(LibYUVColorTest,TestFullYUVU)739 TEST_F(LibYUVColorTest, TestFullYUVU) {
740 int rh[256] = {
741 0,
742 };
743 int gh[256] = {
744 0,
745 };
746 int bh[256] = {
747 0,
748 };
749 for (int u = 0; u < 256; ++u) {
750 for (int v = 0; v < 256; ++v) {
751 for (int y2 = 0; y2 < 256; y2 += FASTSTEP) {
752 int r0, g0, b0, r1, g1, b1;
753 int y = RANDOM256(y2);
754 YUVUToRGBReference(y, u, v, &r0, &g0, &b0);
755 YUVUToRGB(y, u, v, &r1, &g1, &b1);
756 EXPECT_NEAR(r0, r1, ERROR_R);
757 EXPECT_NEAR(g0, g1, ERROR_G);
758 EXPECT_NEAR(b0, b1, ERROR_B);
759 ++rh[r1 - r0 + 128];
760 ++gh[g1 - g0 + 128];
761 ++bh[b1 - b0 + 128];
762 }
763 }
764 }
765 PrintHistogram(rh, gh, bh);
766 }
767
768 // BT.2020 full range.
TEST_F(LibYUVColorTest,TestFullYUVV)769 TEST_F(LibYUVColorTest, TestFullYUVV) {
770 int rh[256] = {
771 0,
772 };
773 int gh[256] = {
774 0,
775 };
776 int bh[256] = {
777 0,
778 };
779 for (int u = 0; u < 256; ++u) {
780 for (int v = 0; v < 256; ++v) {
781 for (int y2 = 0; y2 < 256; y2 += FASTSTEP) {
782 int r0, g0, b0, r1, g1, b1;
783 int y = RANDOM256(y2);
784 YUVVToRGBReference(y, u, v, &r0, &g0, &b0);
785 YUVVToRGB(y, u, v, &r1, &g1, &b1);
786 EXPECT_NEAR(r0, r1, ERROR_R);
787 EXPECT_NEAR(g0, g1, 2);
788 EXPECT_NEAR(b0, b1, ERROR_B);
789 ++rh[r1 - r0 + 128];
790 ++gh[g1 - g0 + 128];
791 ++bh[b1 - b0 + 128];
792 }
793 }
794 }
795 PrintHistogram(rh, gh, bh);
796 }
797 #undef FASTSTEP
798
TEST_F(LibYUVColorTest,TestGreyYUVJ)799 TEST_F(LibYUVColorTest, TestGreyYUVJ) {
800 int r0, g0, b0, r1, g1, b1, r2, g2, b2;
801
802 // black
803 YUVJToRGBReference(0, 128, 128, &r0, &g0, &b0);
804 EXPECT_EQ(0, r0);
805 EXPECT_EQ(0, g0);
806 EXPECT_EQ(0, b0);
807
808 YUVJToRGB(0, 128, 128, &r1, &g1, &b1);
809 EXPECT_EQ(0, r1);
810 EXPECT_EQ(0, g1);
811 EXPECT_EQ(0, b1);
812
813 // white
814 YUVJToRGBReference(255, 128, 128, &r0, &g0, &b0);
815 EXPECT_EQ(255, r0);
816 EXPECT_EQ(255, g0);
817 EXPECT_EQ(255, b0);
818
819 YUVJToRGB(255, 128, 128, &r1, &g1, &b1);
820 EXPECT_EQ(255, r1);
821 EXPECT_EQ(255, g1);
822 EXPECT_EQ(255, b1);
823
824 // grey
825 YUVJToRGBReference(128, 128, 128, &r0, &g0, &b0);
826 EXPECT_EQ(128, r0);
827 EXPECT_EQ(128, g0);
828 EXPECT_EQ(128, b0);
829
830 YUVJToRGB(128, 128, 128, &r1, &g1, &b1);
831 EXPECT_EQ(128, r1);
832 EXPECT_EQ(128, g1);
833 EXPECT_EQ(128, b1);
834
835 for (int y = 0; y < 256; ++y) {
836 YUVJToRGBReference(y, 128, 128, &r0, &g0, &b0);
837 YUVJToRGB(y, 128, 128, &r1, &g1, &b1);
838 YJToRGB(y, &r2, &g2, &b2);
839 EXPECT_EQ(r0, r1);
840 EXPECT_EQ(g0, g1);
841 EXPECT_EQ(b0, b1);
842 EXPECT_EQ(r0, r2);
843 EXPECT_EQ(g0, g2);
844 EXPECT_EQ(b0, b2);
845 }
846 }
847
848 } // namespace libyuv
849