1 /* 2 * Copyright 2011 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 "libyuv/format_conversion.h" 12 13 #include "libyuv/basic_types.h" 14 #include "libyuv/cpu_id.h" 15 #include "libyuv/video_common.h" 16 #include "libyuv/row.h" 17 18 #ifdef __cplusplus 19 namespace libyuv { 20 extern "C" { 21 #endif 22 23 // Note: to do this with Neon vld4.8 would load ARGB values into 4 registers 24 // and vst would select which 2 components to write. The low level would need 25 // to be ARGBToBG, ARGBToGB, ARGBToRG, ARGBToGR 26 27 #if !defined(YUV_DISABLE_ASM) && defined(_M_IX86) 28 #define HAS_ARGBTOBAYERROW_SSSE3 29 __declspec(naked) __declspec(align(16)) 30 static void ARGBToBayerRow_SSSE3(const uint8* src_argb, 31 uint8* dst_bayer, uint32 selector, int pix) { 32 __asm { 33 mov eax, [esp + 4] // src_argb 34 mov edx, [esp + 8] // dst_bayer 35 movd xmm5, [esp + 12] // selector 36 mov ecx, [esp + 16] // pix 37 pshufd xmm5, xmm5, 0 38 39 align 16 40 wloop: 41 movdqa xmm0, [eax] 42 lea eax, [eax + 16] 43 pshufb xmm0, xmm5 44 sub ecx, 4 45 movd [edx], xmm0 46 lea edx, [edx + 4] 47 jg wloop 48 ret 49 } 50 } 51 52 #elif !defined(YUV_DISABLE_ASM) && (defined(__x86_64__) || defined(__i386__)) 53 54 #define HAS_ARGBTOBAYERROW_SSSE3 55 static void ARGBToBayerRow_SSSE3(const uint8* src_argb, uint8* dst_bayer, 56 uint32 selector, int pix) { 57 asm volatile ( 58 "movd %3,%%xmm5 \n" 59 "pshufd $0x0,%%xmm5,%%xmm5 \n" 60 ".p2align 4 \n" 61 "1: \n" 62 "movdqa (%0),%%xmm0 \n" 63 "lea 0x10(%0),%0 \n" 64 "pshufb %%xmm5,%%xmm0 \n" 65 "sub $0x4,%2 \n" 66 "movd %%xmm0,(%1) \n" 67 "lea 0x4(%1),%1 \n" 68 "jg 1b \n" 69 : "+r"(src_argb), // %0 70 "+r"(dst_bayer), // %1 71 "+r"(pix) // %2 72 : "g"(selector) // %3 73 : "memory", "cc" 74 #if defined(__SSE2__) 75 , "xmm0", "xmm5" 76 #endif 77 78 ); 79 } 80 #endif 81 82 static void ARGBToBayerRow_C(const uint8* src_argb, 83 uint8* dst_bayer, uint32 selector, int pix) { 84 int index0 = selector & 0xff; 85 int index1 = (selector >> 8) & 0xff; 86 // Copy a row of Bayer. 87 for (int x = 0; x < pix - 1; x += 2) { 88 dst_bayer[0] = src_argb[index0]; 89 dst_bayer[1] = src_argb[index1]; 90 src_argb += 8; 91 dst_bayer += 2; 92 } 93 if (pix & 1) { 94 dst_bayer[0] = src_argb[index0]; 95 } 96 } 97 98 // generate a selector mask useful for pshufb 99 static uint32 GenerateSelector(int select0, int select1) { 100 return static_cast<uint32>(select0) | 101 static_cast<uint32>((select1 + 4) << 8) | 102 static_cast<uint32>((select0 + 8) << 16) | 103 static_cast<uint32>((select1 + 12) << 24); 104 } 105 106 static int MakeSelectors(const int blue_index, 107 const int green_index, 108 const int red_index, 109 uint32 dst_fourcc_bayer, 110 uint32 *index_map) { 111 // Now build a lookup table containing the indices for the four pixels in each 112 // 2x2 Bayer grid. 113 switch (dst_fourcc_bayer) { 114 case FOURCC_BGGR: 115 index_map[0] = GenerateSelector(blue_index, green_index); 116 index_map[1] = GenerateSelector(green_index, red_index); 117 break; 118 case FOURCC_GBRG: 119 index_map[0] = GenerateSelector(green_index, blue_index); 120 index_map[1] = GenerateSelector(red_index, green_index); 121 break; 122 case FOURCC_RGGB: 123 index_map[0] = GenerateSelector(red_index, green_index); 124 index_map[1] = GenerateSelector(green_index, blue_index); 125 break; 126 case FOURCC_GRBG: 127 index_map[0] = GenerateSelector(green_index, red_index); 128 index_map[1] = GenerateSelector(blue_index, green_index); 129 break; 130 default: 131 return -1; // Bad FourCC 132 } 133 return 0; 134 } 135 136 // Converts 32 bit ARGB to Bayer RGB formats. 137 LIBYUV_API 138 int ARGBToBayer(const uint8* src_argb, int src_stride_argb, 139 uint8* dst_bayer, int dst_stride_bayer, 140 int width, int height, 141 uint32 dst_fourcc_bayer) { 142 if (height < 0) { 143 height = -height; 144 src_argb = src_argb + (height - 1) * src_stride_argb; 145 src_stride_argb = -src_stride_argb; 146 } 147 void (*ARGBToBayerRow)(const uint8* src_argb, uint8* dst_bayer, 148 uint32 selector, int pix) = ARGBToBayerRow_C; 149 #if defined(HAS_ARGBTOBAYERROW_SSSE3) 150 if (TestCpuFlag(kCpuHasSSSE3) && 151 IS_ALIGNED(width, 4) && 152 IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) { 153 ARGBToBayerRow = ARGBToBayerRow_SSSE3; 154 } 155 #endif 156 const int blue_index = 0; // Offsets for ARGB format 157 const int green_index = 1; 158 const int red_index = 2; 159 uint32 index_map[2]; 160 if (MakeSelectors(blue_index, green_index, red_index, 161 dst_fourcc_bayer, index_map)) { 162 return -1; // Bad FourCC 163 } 164 165 for (int y = 0; y < height; ++y) { 166 ARGBToBayerRow(src_argb, dst_bayer, index_map[y & 1], width); 167 src_argb += src_stride_argb; 168 dst_bayer += dst_stride_bayer; 169 } 170 return 0; 171 } 172 173 #define AVG(a, b) (((a) + (b)) >> 1) 174 175 static void BayerRowBG(const uint8* src_bayer0, int src_stride_bayer, 176 uint8* dst_argb, int pix) { 177 const uint8* src_bayer1 = src_bayer0 + src_stride_bayer; 178 uint8 g = src_bayer0[1]; 179 uint8 r = src_bayer1[1]; 180 for (int x = 0; x < pix - 2; x += 2) { 181 dst_argb[0] = src_bayer0[0]; 182 dst_argb[1] = AVG(g, src_bayer0[1]); 183 dst_argb[2] = AVG(r, src_bayer1[1]); 184 dst_argb[3] = 255U; 185 dst_argb[4] = AVG(src_bayer0[0], src_bayer0[2]); 186 dst_argb[5] = src_bayer0[1]; 187 dst_argb[6] = src_bayer1[1]; 188 dst_argb[7] = 255U; 189 g = src_bayer0[1]; 190 r = src_bayer1[1]; 191 src_bayer0 += 2; 192 src_bayer1 += 2; 193 dst_argb += 8; 194 } 195 dst_argb[0] = src_bayer0[0]; 196 dst_argb[1] = AVG(g, src_bayer0[1]); 197 dst_argb[2] = AVG(r, src_bayer1[1]); 198 dst_argb[3] = 255U; 199 if (!(pix & 1)) { 200 dst_argb[4] = src_bayer0[0]; 201 dst_argb[5] = src_bayer0[1]; 202 dst_argb[6] = src_bayer1[1]; 203 dst_argb[7] = 255U; 204 } 205 } 206 207 static void BayerRowRG(const uint8* src_bayer0, int src_stride_bayer, 208 uint8* dst_argb, int pix) { 209 const uint8* src_bayer1 = src_bayer0 + src_stride_bayer; 210 uint8 g = src_bayer0[1]; 211 uint8 b = src_bayer1[1]; 212 for (int x = 0; x < pix - 2; x += 2) { 213 dst_argb[0] = AVG(b, src_bayer1[1]); 214 dst_argb[1] = AVG(g, src_bayer0[1]); 215 dst_argb[2] = src_bayer0[0]; 216 dst_argb[3] = 255U; 217 dst_argb[4] = src_bayer1[1]; 218 dst_argb[5] = src_bayer0[1]; 219 dst_argb[6] = AVG(src_bayer0[0], src_bayer0[2]); 220 dst_argb[7] = 255U; 221 g = src_bayer0[1]; 222 b = src_bayer1[1]; 223 src_bayer0 += 2; 224 src_bayer1 += 2; 225 dst_argb += 8; 226 } 227 dst_argb[0] = AVG(b, src_bayer1[1]); 228 dst_argb[1] = AVG(g, src_bayer0[1]); 229 dst_argb[2] = src_bayer0[0]; 230 dst_argb[3] = 255U; 231 if (!(pix & 1)) { 232 dst_argb[4] = src_bayer1[1]; 233 dst_argb[5] = src_bayer0[1]; 234 dst_argb[6] = src_bayer0[0]; 235 dst_argb[7] = 255U; 236 } 237 } 238 239 static void BayerRowGB(const uint8* src_bayer0, int src_stride_bayer, 240 uint8* dst_argb, int pix) { 241 const uint8* src_bayer1 = src_bayer0 + src_stride_bayer; 242 uint8 b = src_bayer0[1]; 243 for (int x = 0; x < pix - 2; x += 2) { 244 dst_argb[0] = AVG(b, src_bayer0[1]); 245 dst_argb[1] = src_bayer0[0]; 246 dst_argb[2] = src_bayer1[0]; 247 dst_argb[3] = 255U; 248 dst_argb[4] = src_bayer0[1]; 249 dst_argb[5] = AVG(src_bayer0[0], src_bayer0[2]); 250 dst_argb[6] = AVG(src_bayer1[0], src_bayer1[2]); 251 dst_argb[7] = 255U; 252 b = src_bayer0[1]; 253 src_bayer0 += 2; 254 src_bayer1 += 2; 255 dst_argb += 8; 256 } 257 dst_argb[0] = AVG(b, src_bayer0[1]); 258 dst_argb[1] = src_bayer0[0]; 259 dst_argb[2] = src_bayer1[0]; 260 dst_argb[3] = 255U; 261 if (!(pix & 1)) { 262 dst_argb[4] = src_bayer0[1]; 263 dst_argb[5] = src_bayer0[0]; 264 dst_argb[6] = src_bayer1[0]; 265 dst_argb[7] = 255U; 266 } 267 } 268 269 static void BayerRowGR(const uint8* src_bayer0, int src_stride_bayer, 270 uint8* dst_argb, int pix) { 271 const uint8* src_bayer1 = src_bayer0 + src_stride_bayer; 272 uint8 r = src_bayer0[1]; 273 for (int x = 0; x < pix - 2; x += 2) { 274 dst_argb[0] = src_bayer1[0]; 275 dst_argb[1] = src_bayer0[0]; 276 dst_argb[2] = AVG(r, src_bayer0[1]); 277 dst_argb[3] = 255U; 278 dst_argb[4] = AVG(src_bayer1[0], src_bayer1[2]); 279 dst_argb[5] = AVG(src_bayer0[0], src_bayer0[2]); 280 dst_argb[6] = src_bayer0[1]; 281 dst_argb[7] = 255U; 282 r = src_bayer0[1]; 283 src_bayer0 += 2; 284 src_bayer1 += 2; 285 dst_argb += 8; 286 } 287 dst_argb[0] = src_bayer1[0]; 288 dst_argb[1] = src_bayer0[0]; 289 dst_argb[2] = AVG(r, src_bayer0[1]); 290 dst_argb[3] = 255U; 291 if (!(pix & 1)) { 292 dst_argb[4] = src_bayer1[0]; 293 dst_argb[5] = src_bayer0[0]; 294 dst_argb[6] = src_bayer0[1]; 295 dst_argb[7] = 255U; 296 } 297 } 298 299 // Converts any Bayer RGB format to ARGB. 300 LIBYUV_API 301 int BayerToARGB(const uint8* src_bayer, int src_stride_bayer, 302 uint8* dst_argb, int dst_stride_argb, 303 int width, int height, 304 uint32 src_fourcc_bayer) { 305 if (height < 0) { 306 height = -height; 307 dst_argb = dst_argb + (height - 1) * dst_stride_argb; 308 dst_stride_argb = -dst_stride_argb; 309 } 310 void (*BayerRow0)(const uint8* src_bayer, int src_stride_bayer, 311 uint8* dst_argb, int pix); 312 void (*BayerRow1)(const uint8* src_bayer, int src_stride_bayer, 313 uint8* dst_argb, int pix); 314 switch (src_fourcc_bayer) { 315 case FOURCC_BGGR: 316 BayerRow0 = BayerRowBG; 317 BayerRow1 = BayerRowGR; 318 break; 319 case FOURCC_GBRG: 320 BayerRow0 = BayerRowGB; 321 BayerRow1 = BayerRowRG; 322 break; 323 case FOURCC_GRBG: 324 BayerRow0 = BayerRowGR; 325 BayerRow1 = BayerRowBG; 326 break; 327 case FOURCC_RGGB: 328 BayerRow0 = BayerRowRG; 329 BayerRow1 = BayerRowGB; 330 break; 331 default: 332 return -1; // Bad FourCC 333 } 334 335 for (int y = 0; y < height - 1; y += 2) { 336 BayerRow0(src_bayer, src_stride_bayer, dst_argb, width); 337 BayerRow1(src_bayer + src_stride_bayer, -src_stride_bayer, 338 dst_argb + dst_stride_argb, width); 339 src_bayer += src_stride_bayer * 2; 340 dst_argb += dst_stride_argb * 2; 341 } 342 if (height & 1) { 343 BayerRow0(src_bayer, -src_stride_bayer, dst_argb, width); 344 } 345 return 0; 346 } 347 348 // Converts any Bayer RGB format to ARGB. 349 LIBYUV_API 350 int BayerToI420(const uint8* src_bayer, int src_stride_bayer, 351 uint8* dst_y, int dst_stride_y, 352 uint8* dst_u, int dst_stride_u, 353 uint8* dst_v, int dst_stride_v, 354 int width, int height, 355 uint32 src_fourcc_bayer) { 356 if (width * 4 > kMaxStride) { 357 return -1; // Size too large for row buffer 358 } 359 // Negative height means invert the image. 360 if (height < 0) { 361 height = -height; 362 int halfheight = (height + 1) >> 1; 363 dst_y = dst_y + (height - 1) * dst_stride_y; 364 dst_u = dst_u + (halfheight - 1) * dst_stride_u; 365 dst_v = dst_v + (halfheight - 1) * dst_stride_v; 366 dst_stride_y = -dst_stride_y; 367 dst_stride_u = -dst_stride_u; 368 dst_stride_v = -dst_stride_v; 369 } 370 void (*BayerRow0)(const uint8* src_bayer, int src_stride_bayer, 371 uint8* dst_argb, int pix); 372 void (*BayerRow1)(const uint8* src_bayer, int src_stride_bayer, 373 uint8* dst_argb, int pix); 374 void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) = 375 ARGBToYRow_C; 376 void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb, 377 uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C; 378 SIMD_ALIGNED(uint8 row[kMaxStride * 2]); 379 380 #if defined(HAS_ARGBTOYROW_SSSE3) 381 if (TestCpuFlag(kCpuHasSSSE3) && 382 IS_ALIGNED(width, 16) && 383 IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 384 ARGBToYRow = ARGBToYRow_SSSE3; 385 } 386 #endif 387 #if defined(HAS_ARGBTOUVROW_SSSE3) 388 if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 16)) { 389 ARGBToUVRow = ARGBToUVRow_SSSE3; 390 } 391 #endif 392 393 switch (src_fourcc_bayer) { 394 case FOURCC_BGGR: 395 BayerRow0 = BayerRowBG; 396 BayerRow1 = BayerRowGR; 397 break; 398 case FOURCC_GBRG: 399 BayerRow0 = BayerRowGB; 400 BayerRow1 = BayerRowRG; 401 break; 402 case FOURCC_GRBG: 403 BayerRow0 = BayerRowGR; 404 BayerRow1 = BayerRowBG; 405 break; 406 case FOURCC_RGGB: 407 BayerRow0 = BayerRowRG; 408 BayerRow1 = BayerRowGB; 409 break; 410 default: 411 return -1; // Bad FourCC 412 } 413 414 for (int y = 0; y < height - 1; y += 2) { 415 BayerRow0(src_bayer, src_stride_bayer, row, width); 416 BayerRow1(src_bayer + src_stride_bayer, -src_stride_bayer, 417 row + kMaxStride, width); 418 ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width); 419 ARGBToYRow(row, dst_y, width); 420 ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width); 421 src_bayer += src_stride_bayer * 2; 422 dst_y += dst_stride_y * 2; 423 dst_u += dst_stride_u; 424 dst_v += dst_stride_v; 425 } 426 if (height & 1) { 427 BayerRow0(src_bayer, src_stride_bayer, row, width); 428 ARGBToUVRow(row, 0, dst_u, dst_v, width); 429 ARGBToYRow(row, dst_y, width); 430 } 431 return 0; 432 } 433 434 // Convert I420 to Bayer. 435 LIBYUV_API 436 int I420ToBayer(const uint8* src_y, int src_stride_y, 437 const uint8* src_u, int src_stride_u, 438 const uint8* src_v, int src_stride_v, 439 uint8* dst_bayer, int dst_stride_bayer, 440 int width, int height, 441 uint32 dst_fourcc_bayer) { 442 // Negative height means invert the image. 443 if (height < 0) { 444 height = -height; 445 int halfheight = (height + 1) >> 1; 446 src_y = src_y + (height - 1) * src_stride_y; 447 src_u = src_u + (halfheight - 1) * src_stride_u; 448 src_v = src_v + (halfheight - 1) * src_stride_v; 449 src_stride_y = -src_stride_y; 450 src_stride_u = -src_stride_u; 451 src_stride_v = -src_stride_v; 452 } 453 void (*I422ToARGBRow)(const uint8* y_buf, 454 const uint8* u_buf, 455 const uint8* v_buf, 456 uint8* rgb_buf, 457 int width) = I422ToARGBRow_C; 458 #if defined(HAS_I422TOARGBROW_NEON) 459 if (TestCpuFlag(kCpuHasNEON)) { 460 I422ToARGBRow = I422ToARGBRow_NEON; 461 } 462 #elif defined(HAS_I422TOARGBROW_SSSE3) 463 if (TestCpuFlag(kCpuHasSSSE3)) { 464 I422ToARGBRow = I422ToARGBRow_SSSE3; 465 } 466 #endif 467 SIMD_ALIGNED(uint8 row[kMaxStride]); 468 void (*ARGBToBayerRow)(const uint8* src_argb, uint8* dst_bayer, 469 uint32 selector, int pix) = ARGBToBayerRow_C; 470 #if defined(HAS_ARGBTOBAYERROW_SSSE3) 471 if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4)) { 472 ARGBToBayerRow = ARGBToBayerRow_SSSE3; 473 } 474 #endif 475 const int blue_index = 0; // Offsets for ARGB format 476 const int green_index = 1; 477 const int red_index = 2; 478 uint32 index_map[2]; 479 if (MakeSelectors(blue_index, green_index, red_index, 480 dst_fourcc_bayer, index_map)) { 481 return -1; // Bad FourCC 482 } 483 484 for (int y = 0; y < height; ++y) { 485 I422ToARGBRow(src_y, src_u, src_v, row, width); 486 ARGBToBayerRow(row, dst_bayer, index_map[y & 1], width); 487 dst_bayer += dst_stride_bayer; 488 src_y += src_stride_y; 489 if (y & 1) { 490 src_u += src_stride_u; 491 src_v += src_stride_v; 492 } 493 } 494 return 0; 495 } 496 497 #define MAKEBAYERFOURCC(BAYER) \ 498 LIBYUV_API \ 499 int Bayer##BAYER##ToI420(const uint8* src_bayer, int src_stride_bayer, \ 500 uint8* dst_y, int dst_stride_y, \ 501 uint8* dst_u, int dst_stride_u, \ 502 uint8* dst_v, int dst_stride_v, \ 503 int width, int height) { \ 504 return BayerToI420(src_bayer, src_stride_bayer, \ 505 dst_y, dst_stride_y, \ 506 dst_u, dst_stride_u, \ 507 dst_v, dst_stride_v, \ 508 width, height, \ 509 FOURCC_##BAYER); \ 510 } \ 511 \ 512 LIBYUV_API \ 513 int I420ToBayer##BAYER(const uint8* src_y, int src_stride_y, \ 514 const uint8* src_u, int src_stride_u, \ 515 const uint8* src_v, int src_stride_v, \ 516 uint8* dst_bayer, int dst_stride_bayer, \ 517 int width, int height) { \ 518 return I420ToBayer(src_y, src_stride_y, \ 519 src_u, src_stride_u, \ 520 src_v, src_stride_v, \ 521 dst_bayer, dst_stride_bayer, \ 522 width, height, \ 523 FOURCC_##BAYER); \ 524 } \ 525 \ 526 LIBYUV_API \ 527 int ARGBToBayer##BAYER(const uint8* src_argb, int src_stride_argb, \ 528 uint8* dst_bayer, int dst_stride_bayer, \ 529 int width, int height) { \ 530 return ARGBToBayer(src_argb, src_stride_argb, \ 531 dst_bayer, dst_stride_bayer, \ 532 width, height, \ 533 FOURCC_##BAYER); \ 534 } \ 535 \ 536 LIBYUV_API \ 537 int Bayer##BAYER##ToARGB(const uint8* src_bayer, int src_stride_bayer, \ 538 uint8* dst_argb, int dst_stride_argb, \ 539 int width, int height) { \ 540 return BayerToARGB(src_bayer, src_stride_bayer, \ 541 dst_argb, dst_stride_argb, \ 542 width, height, \ 543 FOURCC_##BAYER); \ 544 } 545 546 MAKEBAYERFOURCC(BGGR) 547 MAKEBAYERFOURCC(GBRG) 548 MAKEBAYERFOURCC(GRBG) 549 MAKEBAYERFOURCC(RGGB) 550 551 #ifdef __cplusplus 552 } // extern "C" 553 } // namespace libyuv 554 #endif 555