xref: /aosp_15_r20/external/libyuv/unit_test/color_test.cc (revision 4e366538070a3a6c5c163c31b791eab742e1657a)
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