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/convert.h" 12 13 #include "libyuv/basic_types.h" 14 #include "libyuv/cpu_id.h" 15 #include "libyuv/format_conversion.h" 16 #ifdef HAVE_JPEG 17 #include "libyuv/mjpeg_decoder.h" 18 #endif 19 #include "libyuv/planar_functions.h" 20 #include "libyuv/rotate.h" 21 #include "libyuv/video_common.h" 22 #include "libyuv/row.h" 23 24 #ifdef __cplusplus 25 namespace libyuv { 26 extern "C" { 27 #endif 28 29 // Copy I420 with optional flipping 30 LIBYUV_API 31 int I420Copy(const uint8* src_y, int src_stride_y, 32 const uint8* src_u, int src_stride_u, 33 const uint8* src_v, int src_stride_v, 34 uint8* dst_y, int dst_stride_y, 35 uint8* dst_u, int dst_stride_u, 36 uint8* dst_v, int dst_stride_v, 37 int width, int height) { 38 if (!src_y || !src_u || !src_v || 39 !dst_y || !dst_u || !dst_v || 40 width <= 0 || height == 0) { 41 return -1; 42 } 43 // Negative height means invert the image. 44 if (height < 0) { 45 height = -height; 46 int halfheight = (height + 1) >> 1; 47 src_y = src_y + (height - 1) * src_stride_y; 48 src_u = src_u + (halfheight - 1) * src_stride_u; 49 src_v = src_v + (halfheight - 1) * src_stride_v; 50 src_stride_y = -src_stride_y; 51 src_stride_u = -src_stride_u; 52 src_stride_v = -src_stride_v; 53 } 54 55 int halfwidth = (width + 1) >> 1; 56 int halfheight = (height + 1) >> 1; 57 if (dst_y) { 58 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height); 59 } 60 CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight); 61 CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight); 62 return 0; 63 } 64 65 // Move to row_win etc. 66 #if !defined(YUV_DISABLE_ASM) && defined(_M_IX86) 67 #define HAS_HALFROW_SSE2 68 __declspec(naked) __declspec(align(16)) 69 static void HalfRow_SSE2(const uint8* src_uv, int src_uv_stride, 70 uint8* dst_uv, int pix) { 71 __asm { 72 push edi 73 mov eax, [esp + 4 + 4] // src_uv 74 mov edx, [esp + 4 + 8] // src_uv_stride 75 mov edi, [esp + 4 + 12] // dst_v 76 mov ecx, [esp + 4 + 16] // pix 77 sub edi, eax 78 79 align 16 80 convertloop: 81 movdqa xmm0, [eax] 82 pavgb xmm0, [eax + edx] 83 sub ecx, 16 84 movdqa [eax + edi], xmm0 85 lea eax, [eax + 16] 86 jg convertloop 87 pop edi 88 ret 89 } 90 } 91 92 #elif !defined(YUV_DISABLE_ASM) && (defined(__x86_64__) || defined(__i386__)) 93 #define HAS_HALFROW_SSE2 94 static void HalfRow_SSE2(const uint8* src_uv, int src_uv_stride, 95 uint8* dst_uv, int pix) { 96 asm volatile ( 97 "sub %0,%1 \n" 98 ".p2align 4 \n" 99 "1: \n" 100 "movdqa (%0),%%xmm0 \n" 101 "pavgb (%0,%3),%%xmm0 \n" 102 "sub $0x10,%2 \n" 103 "movdqa %%xmm0,(%0,%1) \n" 104 "lea 0x10(%0),%0 \n" 105 "jg 1b \n" 106 : "+r"(src_uv), // %0 107 "+r"(dst_uv), // %1 108 "+r"(pix) // %2 109 : "r"(static_cast<intptr_t>(src_uv_stride)) // %3 110 : "memory", "cc" 111 #if defined(__SSE2__) 112 , "xmm0" 113 #endif 114 ); 115 } 116 #endif 117 118 static void HalfRow_C(const uint8* src_uv, int src_uv_stride, 119 uint8* dst_uv, int pix) { 120 for (int x = 0; x < pix; ++x) { 121 dst_uv[x] = (src_uv[x] + src_uv[src_uv_stride + x] + 1) >> 1; 122 } 123 } 124 125 LIBYUV_API 126 int I422ToI420(const uint8* src_y, int src_stride_y, 127 const uint8* src_u, int src_stride_u, 128 const uint8* src_v, int src_stride_v, 129 uint8* dst_y, int dst_stride_y, 130 uint8* dst_u, int dst_stride_u, 131 uint8* dst_v, int dst_stride_v, 132 int width, int height) { 133 if (!src_y || !src_u || !src_v || 134 !dst_y || !dst_u || !dst_v || 135 width <= 0 || height == 0) { 136 return -1; 137 } 138 // Negative height means invert the image. 139 if (height < 0) { 140 height = -height; 141 src_y = src_y + (height - 1) * src_stride_y; 142 src_u = src_u + (height - 1) * src_stride_u; 143 src_v = src_v + (height - 1) * src_stride_v; 144 src_stride_y = -src_stride_y; 145 src_stride_u = -src_stride_u; 146 src_stride_v = -src_stride_v; 147 } 148 int halfwidth = (width + 1) >> 1; 149 void (*HalfRow)(const uint8* src_uv, int src_uv_stride, 150 uint8* dst_uv, int pix) = HalfRow_C; 151 #if defined(HAS_HALFROW_SSE2) 152 if (TestCpuFlag(kCpuHasSSE2) && 153 IS_ALIGNED(halfwidth, 16) && 154 IS_ALIGNED(src_u, 16) && IS_ALIGNED(src_stride_u, 16) && 155 IS_ALIGNED(src_v, 16) && IS_ALIGNED(src_stride_v, 16) && 156 IS_ALIGNED(dst_u, 16) && IS_ALIGNED(dst_stride_u, 16) && 157 IS_ALIGNED(dst_v, 16) && IS_ALIGNED(dst_stride_v, 16)) { 158 HalfRow = HalfRow_SSE2; 159 } 160 #endif 161 162 // Copy Y plane 163 if (dst_y) { 164 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height); 165 } 166 167 // SubSample U plane. 168 int y; 169 for (y = 0; y < height - 1; y += 2) { 170 HalfRow(src_u, src_stride_u, dst_u, halfwidth); 171 src_u += src_stride_u * 2; 172 dst_u += dst_stride_u; 173 } 174 if (height & 1) { 175 HalfRow(src_u, 0, dst_u, halfwidth); 176 } 177 178 // SubSample V plane. 179 for (y = 0; y < height - 1; y += 2) { 180 HalfRow(src_v, src_stride_v, dst_v, halfwidth); 181 src_v += src_stride_v * 2; 182 dst_v += dst_stride_v; 183 } 184 if (height & 1) { 185 HalfRow(src_v, 0, dst_v, halfwidth); 186 } 187 return 0; 188 } 189 190 // Blends 32x2 pixels to 16x1 191 // source in scale.cc 192 #if !defined(YUV_DISABLE_ASM) && (defined(__ARM_NEON__) || defined(LIBYUV_NEON)) 193 #define HAS_SCALEROWDOWN2_NEON 194 void ScaleRowDown2Int_NEON(const uint8* src_ptr, ptrdiff_t src_stride, 195 uint8* dst, int dst_width); 196 #elif !defined(YUV_DISABLE_ASM) && \ 197 (defined(_M_IX86) || defined(__x86_64__) || defined(__i386__)) 198 199 void ScaleRowDown2Int_SSE2(const uint8* src_ptr, ptrdiff_t src_stride, 200 uint8* dst_ptr, int dst_width); 201 #endif 202 void ScaleRowDown2Int_C(const uint8* src_ptr, ptrdiff_t src_stride, 203 uint8* dst_ptr, int dst_width); 204 205 LIBYUV_API 206 int I444ToI420(const uint8* src_y, int src_stride_y, 207 const uint8* src_u, int src_stride_u, 208 const uint8* src_v, int src_stride_v, 209 uint8* dst_y, int dst_stride_y, 210 uint8* dst_u, int dst_stride_u, 211 uint8* dst_v, int dst_stride_v, 212 int width, int height) { 213 if (!src_y || !src_u || !src_v || 214 !dst_y || !dst_u || !dst_v || 215 width <= 0 || height == 0) { 216 return -1; 217 } 218 // Negative height means invert the image. 219 if (height < 0) { 220 height = -height; 221 src_y = src_y + (height - 1) * src_stride_y; 222 src_u = src_u + (height - 1) * src_stride_u; 223 src_v = src_v + (height - 1) * src_stride_v; 224 src_stride_y = -src_stride_y; 225 src_stride_u = -src_stride_u; 226 src_stride_v = -src_stride_v; 227 } 228 int halfwidth = (width + 1) >> 1; 229 void (*ScaleRowDown2)(const uint8* src_ptr, ptrdiff_t src_stride, 230 uint8* dst_ptr, int dst_width) = ScaleRowDown2Int_C; 231 #if defined(HAS_SCALEROWDOWN2_NEON) 232 if (TestCpuFlag(kCpuHasNEON) && 233 IS_ALIGNED(halfwidth, 16)) { 234 ScaleRowDown2 = ScaleRowDown2Int_NEON; 235 } 236 #elif defined(HAS_SCALEROWDOWN2_SSE2) 237 if (TestCpuFlag(kCpuHasSSE2) && 238 IS_ALIGNED(halfwidth, 16) && 239 IS_ALIGNED(src_u, 16) && IS_ALIGNED(src_stride_u, 16) && 240 IS_ALIGNED(src_v, 16) && IS_ALIGNED(src_stride_v, 16) && 241 IS_ALIGNED(dst_u, 16) && IS_ALIGNED(dst_stride_u, 16) && 242 IS_ALIGNED(dst_v, 16) && IS_ALIGNED(dst_stride_v, 16)) { 243 ScaleRowDown2 = ScaleRowDown2Int_SSE2; 244 } 245 #endif 246 247 // Copy Y plane 248 if (dst_y) { 249 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height); 250 } 251 252 // SubSample U plane. 253 int y; 254 for (y = 0; y < height - 1; y += 2) { 255 ScaleRowDown2(src_u, src_stride_u, dst_u, halfwidth); 256 src_u += src_stride_u * 2; 257 dst_u += dst_stride_u; 258 } 259 if (height & 1) { 260 ScaleRowDown2(src_u, 0, dst_u, halfwidth); 261 } 262 263 // SubSample V plane. 264 for (y = 0; y < height - 1; y += 2) { 265 ScaleRowDown2(src_v, src_stride_v, dst_v, halfwidth); 266 src_v += src_stride_v * 2; 267 dst_v += dst_stride_v; 268 } 269 if (height & 1) { 270 ScaleRowDown2(src_v, 0, dst_v, halfwidth); 271 } 272 return 0; 273 } 274 275 // use Bilinear for upsampling chroma 276 void ScalePlaneBilinear(int src_width, int src_height, 277 int dst_width, int dst_height, 278 int src_stride, int dst_stride, 279 const uint8* src_ptr, uint8* dst_ptr); 280 281 // 411 chroma is 1/4 width, 1x height 282 // 420 chroma is 1/2 width, 1/2 height 283 LIBYUV_API 284 int I411ToI420(const uint8* src_y, int src_stride_y, 285 const uint8* src_u, int src_stride_u, 286 const uint8* src_v, int src_stride_v, 287 uint8* dst_y, int dst_stride_y, 288 uint8* dst_u, int dst_stride_u, 289 uint8* dst_v, int dst_stride_v, 290 int width, int height) { 291 if (!src_y || !src_u || !src_v || 292 !dst_y || !dst_u || !dst_v || 293 width <= 0 || height == 0) { 294 return -1; 295 } 296 // Negative height means invert the image. 297 if (height < 0) { 298 height = -height; 299 dst_y = dst_y + (height - 1) * dst_stride_y; 300 dst_u = dst_u + (height - 1) * dst_stride_u; 301 dst_v = dst_v + (height - 1) * dst_stride_v; 302 dst_stride_y = -dst_stride_y; 303 dst_stride_u = -dst_stride_u; 304 dst_stride_v = -dst_stride_v; 305 } 306 307 // Copy Y plane 308 if (dst_y) { 309 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height); 310 } 311 312 int halfwidth = (width + 1) >> 1; 313 int halfheight = (height + 1) >> 1; 314 int quarterwidth = (width + 3) >> 2; 315 316 // Resample U plane. 317 ScalePlaneBilinear(quarterwidth, height, // from 1/4 width, 1x height 318 halfwidth, halfheight, // to 1/2 width, 1/2 height 319 src_stride_u, 320 dst_stride_u, 321 src_u, dst_u); 322 323 // Resample V plane. 324 ScalePlaneBilinear(quarterwidth, height, // from 1/4 width, 1x height 325 halfwidth, halfheight, // to 1/2 width, 1/2 height 326 src_stride_v, 327 dst_stride_v, 328 src_v, dst_v); 329 return 0; 330 } 331 332 // I400 is greyscale typically used in MJPG 333 LIBYUV_API 334 int I400ToI420(const uint8* src_y, int src_stride_y, 335 uint8* dst_y, int dst_stride_y, 336 uint8* dst_u, int dst_stride_u, 337 uint8* dst_v, int dst_stride_v, 338 int width, int height) { 339 if (!src_y || !dst_y || !dst_u || !dst_v || 340 width <= 0 || height == 0) { 341 return -1; 342 } 343 // Negative height means invert the image. 344 if (height < 0) { 345 height = -height; 346 src_y = src_y + (height - 1) * src_stride_y; 347 src_stride_y = -src_stride_y; 348 } 349 int halfwidth = (width + 1) >> 1; 350 int halfheight = (height + 1) >> 1; 351 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height); 352 SetPlane(dst_u, dst_stride_u, halfwidth, halfheight, 128); 353 SetPlane(dst_v, dst_stride_v, halfwidth, halfheight, 128); 354 return 0; 355 } 356 357 static void CopyPlane2(const uint8* src, int src_stride_0, int src_stride_1, 358 uint8* dst, int dst_stride_frame, 359 int width, int height) { 360 void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C; 361 #if defined(HAS_COPYROW_NEON) 362 if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 64)) { 363 CopyRow = CopyRow_NEON; 364 } 365 #elif defined(HAS_COPYROW_X86) 366 if (IS_ALIGNED(width, 4)) { 367 CopyRow = CopyRow_X86; 368 #if defined(HAS_COPYROW_SSE2) 369 if (TestCpuFlag(kCpuHasSSE2) && 370 IS_ALIGNED(width, 32) && IS_ALIGNED(src, 16) && 371 IS_ALIGNED(src_stride_0, 16) && IS_ALIGNED(src_stride_1, 16) && 372 IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride_frame, 16)) { 373 CopyRow = CopyRow_SSE2; 374 } 375 #endif 376 } 377 #endif 378 379 // Copy plane 380 for (int y = 0; y < height - 1; y += 2) { 381 CopyRow(src, dst, width); 382 CopyRow(src + src_stride_0, dst + dst_stride_frame, width); 383 src += src_stride_0 + src_stride_1; 384 dst += dst_stride_frame * 2; 385 } 386 if (height & 1) { 387 CopyRow(src, dst, width); 388 } 389 } 390 391 // Support converting from FOURCC_M420 392 // Useful for bandwidth constrained transports like USB 1.0 and 2.0 and for 393 // easy conversion to I420. 394 // M420 format description: 395 // M420 is row biplanar 420: 2 rows of Y and 1 row of UV. 396 // Chroma is half width / half height. (420) 397 // src_stride_m420 is row planar. Normally this will be the width in pixels. 398 // The UV plane is half width, but 2 values, so src_stride_m420 applies to 399 // this as well as the two Y planes. 400 static int X420ToI420(const uint8* src_y, 401 int src_stride_y0, int src_stride_y1, 402 const uint8* src_uv, int src_stride_uv, 403 uint8* dst_y, int dst_stride_y, 404 uint8* dst_u, int dst_stride_u, 405 uint8* dst_v, int dst_stride_v, 406 int width, int height) { 407 if (!src_y || !src_uv || 408 !dst_y || !dst_u || !dst_v || 409 width <= 0 || height == 0) { 410 return -1; 411 } 412 // Negative height means invert the image. 413 if (height < 0) { 414 height = -height; 415 int halfheight = (height + 1) >> 1; 416 dst_y = dst_y + (height - 1) * dst_stride_y; 417 dst_u = dst_u + (halfheight - 1) * dst_stride_u; 418 dst_v = dst_v + (halfheight - 1) * dst_stride_v; 419 dst_stride_y = -dst_stride_y; 420 dst_stride_u = -dst_stride_u; 421 dst_stride_v = -dst_stride_v; 422 } 423 424 int halfwidth = (width + 1) >> 1; 425 void (*SplitUV)(const uint8* src_uv, uint8* dst_u, uint8* dst_v, int pix) = 426 SplitUV_C; 427 #if defined(HAS_SPLITUV_NEON) 428 if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(halfwidth, 16)) { 429 SplitUV = SplitUV_NEON; 430 } 431 #elif defined(HAS_SPLITUV_SSE2) 432 if (TestCpuFlag(kCpuHasSSE2) && 433 IS_ALIGNED(halfwidth, 16) && 434 IS_ALIGNED(src_uv, 16) && IS_ALIGNED(src_stride_uv, 16) && 435 IS_ALIGNED(dst_u, 16) && IS_ALIGNED(dst_stride_u, 16) && 436 IS_ALIGNED(dst_v, 16) && IS_ALIGNED(dst_stride_v, 16)) { 437 SplitUV = SplitUV_SSE2; 438 } 439 #endif 440 441 if (dst_y) { 442 CopyPlane2(src_y, src_stride_y0, src_stride_y1, dst_y, dst_stride_y, 443 width, height); 444 } 445 446 int halfheight = (height + 1) >> 1; 447 for (int y = 0; y < halfheight; ++y) { 448 // Copy a row of UV. 449 SplitUV(src_uv, dst_u, dst_v, halfwidth); 450 dst_u += dst_stride_u; 451 dst_v += dst_stride_v; 452 src_uv += src_stride_uv; 453 } 454 return 0; 455 } 456 457 // Convert NV12 to I420. 458 LIBYUV_API 459 int NV12ToI420(const uint8* src_y, int src_stride_y, 460 const uint8* src_uv, int src_stride_uv, 461 uint8* dst_y, int dst_stride_y, 462 uint8* dst_u, int dst_stride_u, 463 uint8* dst_v, int dst_stride_v, 464 int width, int height) { 465 return X420ToI420(src_y, src_stride_y, src_stride_y, 466 src_uv, src_stride_uv, 467 dst_y, dst_stride_y, 468 dst_u, dst_stride_u, 469 dst_v, dst_stride_v, 470 width, height); 471 } 472 473 // Convert M420 to I420. 474 LIBYUV_API 475 int M420ToI420(const uint8* src_m420, int src_stride_m420, 476 uint8* dst_y, int dst_stride_y, 477 uint8* dst_u, int dst_stride_u, 478 uint8* dst_v, int dst_stride_v, 479 int width, int height) { 480 return X420ToI420(src_m420, src_stride_m420, src_stride_m420 * 2, 481 src_m420 + src_stride_m420 * 2, src_stride_m420 * 3, 482 dst_y, dst_stride_y, 483 dst_u, dst_stride_u, 484 dst_v, dst_stride_v, 485 width, height); 486 } 487 488 // Convert Q420 to I420. 489 // Format is rows of YY/YUYV 490 LIBYUV_API 491 int Q420ToI420(const uint8* src_y, int src_stride_y, 492 const uint8* src_yuy2, int src_stride_yuy2, 493 uint8* dst_y, int dst_stride_y, 494 uint8* dst_u, int dst_stride_u, 495 uint8* dst_v, int dst_stride_v, 496 int width, int height) { 497 if (!src_y || !src_yuy2 || 498 !dst_y || !dst_u || !dst_v || 499 width <= 0 || height == 0) { 500 return -1; 501 } 502 // Negative height means invert the image. 503 if (height < 0) { 504 height = -height; 505 int halfheight = (height + 1) >> 1; 506 dst_y = dst_y + (height - 1) * dst_stride_y; 507 dst_u = dst_u + (halfheight - 1) * dst_stride_u; 508 dst_v = dst_v + (halfheight - 1) * dst_stride_v; 509 dst_stride_y = -dst_stride_y; 510 dst_stride_u = -dst_stride_u; 511 dst_stride_v = -dst_stride_v; 512 } 513 // CopyRow for rows of just Y in Q420 copied to Y plane of I420. 514 void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C; 515 #if defined(HAS_COPYROW_NEON) 516 if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 64)) { 517 CopyRow = CopyRow_NEON; 518 } 519 #endif 520 #if defined(HAS_COPYROW_X86) 521 if (IS_ALIGNED(width, 4)) { 522 CopyRow = CopyRow_X86; 523 } 524 #endif 525 #if defined(HAS_COPYROW_SSE2) 526 if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 32) && 527 IS_ALIGNED(src_y, 16) && IS_ALIGNED(src_stride_y, 16) && 528 IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 529 CopyRow = CopyRow_SSE2; 530 } 531 #endif 532 533 void (*YUY2ToUV422Row)(const uint8* src_yuy2, uint8* dst_u, uint8* dst_v, 534 int pix) = YUY2ToUV422Row_C; 535 void (*YUY2ToYRow)(const uint8* src_yuy2, uint8* dst_y, int pix) = 536 YUY2ToYRow_C; 537 #if defined(HAS_YUY2TOYROW_SSE2) 538 if (TestCpuFlag(kCpuHasSSE2)) { 539 if (width > 16) { 540 YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2; 541 YUY2ToYRow = YUY2ToYRow_Any_SSE2; 542 } 543 if (IS_ALIGNED(width, 16)) { 544 YUY2ToUV422Row = YUY2ToUV422Row_Unaligned_SSE2; 545 YUY2ToYRow = YUY2ToYRow_Unaligned_SSE2; 546 if (IS_ALIGNED(src_yuy2, 16) && IS_ALIGNED(src_stride_yuy2, 16)) { 547 YUY2ToUV422Row = YUY2ToUV422Row_SSE2; 548 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 549 YUY2ToYRow = YUY2ToYRow_SSE2; 550 } 551 } 552 } 553 } 554 #elif defined(HAS_YUY2TOYROW_NEON) 555 if (TestCpuFlag(kCpuHasNEON)) { 556 if (width > 8) { 557 YUY2ToYRow = YUY2ToYRow_Any_NEON; 558 if (width > 16) { 559 YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON; 560 } 561 } 562 if (IS_ALIGNED(width, 16)) { 563 YUY2ToYRow = YUY2ToYRow_NEON; 564 YUY2ToUV422Row = YUY2ToUV422Row_NEON; 565 } 566 } 567 #endif 568 569 for (int y = 0; y < height - 1; y += 2) { 570 CopyRow(src_y, dst_y, width); 571 src_y += src_stride_y; 572 dst_y += dst_stride_y; 573 574 YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width); 575 YUY2ToYRow(src_yuy2, dst_y, width); 576 src_yuy2 += src_stride_yuy2; 577 dst_y += dst_stride_y; 578 dst_u += dst_stride_u; 579 dst_v += dst_stride_v; 580 } 581 if (height & 1) { 582 CopyRow(src_y, dst_y, width); 583 YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width); 584 } 585 return 0; 586 } 587 588 // Test if over reading on source is safe. 589 // TODO(fbarchard): Find more efficient solution to safely do odd sizes. 590 // Macros to control read policy, from slowest to fastest: 591 // READSAFE_NEVER - disables read ahead on systems with strict memory reads 592 // READSAFE_ODDHEIGHT - last row of odd height done with C. 593 // This policy assumes that the caller handles the last row of an odd height 594 // image using C. 595 // READSAFE_PAGE - enable read ahead within same page. 596 // A page is 4096 bytes. When reading ahead, if the last pixel is near the 597 // end the page, and a read spans the page into the next page, a memory 598 // exception can occur if that page has not been allocated, or is a guard 599 // page. This setting ensures the overread is within the same page. 600 // READSAFE_ALWAYS - enables read ahead on systems without memory exceptions 601 // or where buffers are padded by 64 bytes. 602 603 #if defined(HAS_RGB24TOARGBROW_SSSE3) || \ 604 defined(HAS_RGB24TOARGBROW_SSSE3) || \ 605 defined(HAS_RAWTOARGBROW_SSSE3) || \ 606 defined(HAS_RGB565TOARGBROW_SSE2) || \ 607 defined(HAS_ARGB1555TOARGBROW_SSE2) || \ 608 defined(HAS_ARGB4444TOARGBROW_SSE2) 609 610 #define READSAFE_ODDHEIGHT 611 612 static bool TestReadSafe(const uint8* src_yuy2, int src_stride_yuy2, 613 int width, int height, int bpp, int overread) { 614 if (width > kMaxStride) { 615 return false; 616 } 617 #if defined(READSAFE_ALWAYS) 618 return true; 619 #elif defined(READSAFE_NEVER) 620 return false; 621 #elif defined(READSAFE_ODDHEIGHT) 622 if (!(width & 15) || 623 (src_stride_yuy2 >= 0 && (height & 1) && width * bpp >= overread)) { 624 return true; 625 } 626 return false; 627 #elif defined(READSAFE_PAGE) 628 if (src_stride_yuy2 >= 0) { 629 src_yuy2 += (height - 1) * src_stride_yuy2; 630 } 631 uintptr_t last_adr = (uintptr_t)(src_yuy2) + width * bpp - 1; 632 uintptr_t last_read_adr = last_adr + overread - 1; 633 if (((last_adr ^ last_read_adr) & ~4095) == 0) { 634 return true; 635 } 636 return false; 637 #endif 638 } 639 #endif 640 641 // Convert YUY2 to I420. 642 LIBYUV_API 643 int YUY2ToI420(const uint8* src_yuy2, int src_stride_yuy2, 644 uint8* dst_y, int dst_stride_y, 645 uint8* dst_u, int dst_stride_u, 646 uint8* dst_v, int dst_stride_v, 647 int width, int height) { 648 // Negative height means invert the image. 649 if (height < 0) { 650 height = -height; 651 src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2; 652 src_stride_yuy2 = -src_stride_yuy2; 653 } 654 void (*YUY2ToUVRow)(const uint8* src_yuy2, int src_stride_yuy2, 655 uint8* dst_u, uint8* dst_v, int pix); 656 void (*YUY2ToYRow)(const uint8* src_yuy2, 657 uint8* dst_y, int pix); 658 YUY2ToYRow = YUY2ToYRow_C; 659 YUY2ToUVRow = YUY2ToUVRow_C; 660 #if defined(HAS_YUY2TOYROW_SSE2) 661 if (TestCpuFlag(kCpuHasSSE2)) { 662 if (width > 16) { 663 YUY2ToUVRow = YUY2ToUVRow_Any_SSE2; 664 YUY2ToYRow = YUY2ToYRow_Any_SSE2; 665 } 666 if (IS_ALIGNED(width, 16)) { 667 YUY2ToUVRow = YUY2ToUVRow_Unaligned_SSE2; 668 YUY2ToYRow = YUY2ToYRow_Unaligned_SSE2; 669 if (IS_ALIGNED(src_yuy2, 16) && IS_ALIGNED(src_stride_yuy2, 16)) { 670 YUY2ToUVRow = YUY2ToUVRow_SSE2; 671 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 672 YUY2ToYRow = YUY2ToYRow_SSE2; 673 } 674 } 675 } 676 } 677 #elif defined(HAS_YUY2TOYROW_NEON) 678 if (TestCpuFlag(kCpuHasNEON)) { 679 if (width > 8) { 680 YUY2ToYRow = YUY2ToYRow_Any_NEON; 681 if (width > 16) { 682 YUY2ToUVRow = YUY2ToUVRow_Any_NEON; 683 } 684 } 685 if (IS_ALIGNED(width, 16)) { 686 YUY2ToYRow = YUY2ToYRow_NEON; 687 YUY2ToUVRow = YUY2ToUVRow_NEON; 688 } 689 } 690 #endif 691 692 for (int y = 0; y < height - 1; y += 2) { 693 YUY2ToUVRow(src_yuy2, src_stride_yuy2, dst_u, dst_v, width); 694 YUY2ToYRow(src_yuy2, dst_y, width); 695 YUY2ToYRow(src_yuy2 + src_stride_yuy2, dst_y + dst_stride_y, width); 696 src_yuy2 += src_stride_yuy2 * 2; 697 dst_y += dst_stride_y * 2; 698 dst_u += dst_stride_u; 699 dst_v += dst_stride_v; 700 } 701 if (height & 1) { 702 YUY2ToUVRow(src_yuy2, 0, dst_u, dst_v, width); 703 YUY2ToYRow(src_yuy2, dst_y, width); 704 } 705 return 0; 706 } 707 708 // Convert UYVY to I420. 709 LIBYUV_API 710 int UYVYToI420(const uint8* src_uyvy, int src_stride_uyvy, 711 uint8* dst_y, int dst_stride_y, 712 uint8* dst_u, int dst_stride_u, 713 uint8* dst_v, int dst_stride_v, 714 int width, int height) { 715 // Negative height means invert the image. 716 if (height < 0) { 717 height = -height; 718 src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy; 719 src_stride_uyvy = -src_stride_uyvy; 720 } 721 void (*UYVYToUVRow)(const uint8* src_uyvy, int src_stride_uyvy, 722 uint8* dst_u, uint8* dst_v, int pix); 723 void (*UYVYToYRow)(const uint8* src_uyvy, 724 uint8* dst_y, int pix); 725 UYVYToYRow = UYVYToYRow_C; 726 UYVYToUVRow = UYVYToUVRow_C; 727 #if defined(HAS_UYVYTOYROW_SSE2) 728 if (TestCpuFlag(kCpuHasSSE2)) { 729 if (width > 16) { 730 UYVYToUVRow = UYVYToUVRow_Any_SSE2; 731 UYVYToYRow = UYVYToYRow_Any_SSE2; 732 } 733 if (IS_ALIGNED(width, 16)) { 734 UYVYToUVRow = UYVYToUVRow_Unaligned_SSE2; 735 UYVYToYRow = UYVYToYRow_Unaligned_SSE2; 736 if (IS_ALIGNED(src_uyvy, 16) && IS_ALIGNED(src_stride_uyvy, 16)) { 737 UYVYToUVRow = UYVYToUVRow_SSE2; 738 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 739 UYVYToYRow = UYVYToYRow_SSE2; 740 } 741 } 742 } 743 } 744 #elif defined(HAS_UYVYTOYROW_NEON) 745 if (TestCpuFlag(kCpuHasNEON)) { 746 if (width > 8) { 747 UYVYToYRow = UYVYToYRow_Any_NEON; 748 if (width > 16) { 749 UYVYToUVRow = UYVYToUVRow_Any_NEON; 750 } 751 } 752 if (IS_ALIGNED(width, 16)) { 753 UYVYToYRow = UYVYToYRow_NEON; 754 UYVYToUVRow = UYVYToUVRow_NEON; 755 } 756 } 757 #endif 758 759 for (int y = 0; y < height - 1; y += 2) { 760 UYVYToUVRow(src_uyvy, src_stride_uyvy, dst_u, dst_v, width); 761 UYVYToYRow(src_uyvy, dst_y, width); 762 UYVYToYRow(src_uyvy + src_stride_uyvy, dst_y + dst_stride_y, width); 763 src_uyvy += src_stride_uyvy * 2; 764 dst_y += dst_stride_y * 2; 765 dst_u += dst_stride_u; 766 dst_v += dst_stride_v; 767 } 768 if (height & 1) { 769 UYVYToUVRow(src_uyvy, 0, dst_u, dst_v, width); 770 UYVYToYRow(src_uyvy, dst_y, width); 771 } 772 return 0; 773 } 774 775 // Visual C x86 or GCC little endian. 776 #if defined(__x86_64__) || defined(_M_X64) || \ 777 defined(__i386__) || defined(_M_IX86) || \ 778 defined(__arm__) || defined(_M_ARM) || \ 779 (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) 780 #define LIBYUV_LITTLE_ENDIAN 781 #endif 782 783 #ifdef LIBYUV_LITTLE_ENDIAN 784 #define READWORD(p) (*reinterpret_cast<const uint32*>(p)) 785 #else 786 static inline uint32 READWORD(const uint8* p) { 787 return static_cast<uint32>(p[0]) | 788 (static_cast<uint32>(p[1]) << 8) | 789 (static_cast<uint32>(p[2]) << 16) | 790 (static_cast<uint32>(p[3]) << 24); 791 } 792 #endif 793 794 // Must be multiple of 6 pixels. Will over convert to handle remainder. 795 // https://developer.apple.com/quicktime/icefloe/dispatch019.html#v210 796 static void V210ToUYVYRow_C(const uint8* src_v210, uint8* dst_uyvy, int width) { 797 for (int x = 0; x < width; x += 6) { 798 uint32 w = READWORD(src_v210 + 0); 799 dst_uyvy[0] = (w >> 2) & 0xff; 800 dst_uyvy[1] = (w >> 12) & 0xff; 801 dst_uyvy[2] = (w >> 22) & 0xff; 802 803 w = READWORD(src_v210 + 4); 804 dst_uyvy[3] = (w >> 2) & 0xff; 805 dst_uyvy[4] = (w >> 12) & 0xff; 806 dst_uyvy[5] = (w >> 22) & 0xff; 807 808 w = READWORD(src_v210 + 8); 809 dst_uyvy[6] = (w >> 2) & 0xff; 810 dst_uyvy[7] = (w >> 12) & 0xff; 811 dst_uyvy[8] = (w >> 22) & 0xff; 812 813 w = READWORD(src_v210 + 12); 814 dst_uyvy[9] = (w >> 2) & 0xff; 815 dst_uyvy[10] = (w >> 12) & 0xff; 816 dst_uyvy[11] = (w >> 22) & 0xff; 817 818 src_v210 += 16; 819 dst_uyvy += 12; 820 } 821 } 822 823 // Convert V210 to I420. 824 // V210 is 10 bit version of UYVY. 16 bytes to store 6 pixels. 825 // With is multiple of 48. 826 LIBYUV_API 827 int V210ToI420(const uint8* src_v210, int src_stride_v210, 828 uint8* dst_y, int dst_stride_y, 829 uint8* dst_u, int dst_stride_u, 830 uint8* dst_v, int dst_stride_v, 831 int width, int height) { 832 if (width * 2 * 2 > kMaxStride) { // 2 rows of UYVY are required. 833 return -1; 834 } else if (!src_v210 || !dst_y || !dst_u || !dst_v || 835 width <= 0 || height == 0) { 836 return -1; 837 } 838 // Negative height means invert the image. 839 if (height < 0) { 840 height = -height; 841 src_v210 = src_v210 + (height - 1) * src_stride_v210; 842 src_stride_v210 = -src_stride_v210; 843 } 844 SIMD_ALIGNED(uint8 row[kMaxStride * 2]); 845 void (*V210ToUYVYRow)(const uint8* src_v210, uint8* dst_uyvy, int pix); 846 V210ToUYVYRow = V210ToUYVYRow_C; 847 848 void (*UYVYToUVRow)(const uint8* src_uyvy, int src_stride_uyvy, 849 uint8* dst_u, uint8* dst_v, int pix); 850 void (*UYVYToYRow)(const uint8* src_uyvy, 851 uint8* dst_y, int pix); 852 UYVYToYRow = UYVYToYRow_C; 853 UYVYToUVRow = UYVYToUVRow_C; 854 #if defined(HAS_UYVYTOYROW_SSE2) 855 if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 16)) { 856 UYVYToUVRow = UYVYToUVRow_SSE2; 857 UYVYToYRow = UYVYToYRow_Unaligned_SSE2; 858 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 859 UYVYToYRow = UYVYToYRow_SSE2; 860 } 861 } 862 #elif defined(HAS_UYVYTOYROW_NEON) 863 if (TestCpuFlag(kCpuHasNEON)) { 864 if (width > 8) { 865 UYVYToYRow = UYVYToYRow_Any_NEON; 866 if (width > 16) { 867 UYVYToUVRow = UYVYToUVRow_Any_NEON; 868 } 869 } 870 if (IS_ALIGNED(width, 16)) { 871 UYVYToYRow = UYVYToYRow_NEON; 872 UYVYToUVRow = UYVYToUVRow_NEON; 873 } 874 } 875 #endif 876 877 #if defined(HAS_UYVYTOYROW_SSE2) 878 if (TestCpuFlag(kCpuHasSSE2)) { 879 if (width > 16) { 880 UYVYToUVRow = UYVYToUVRow_Any_SSE2; 881 UYVYToYRow = UYVYToYRow_Any_SSE2; 882 } 883 if (IS_ALIGNED(width, 16)) { 884 UYVYToYRow = UYVYToYRow_Unaligned_SSE2; 885 UYVYToUVRow = UYVYToUVRow_SSE2; 886 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 887 UYVYToYRow = UYVYToYRow_SSE2; 888 } 889 } 890 } 891 #elif defined(HAS_UYVYTOYROW_NEON) 892 if (TestCpuFlag(kCpuHasNEON)) { 893 if (width > 8) { 894 UYVYToYRow = UYVYToYRow_Any_NEON; 895 if (width > 16) { 896 UYVYToUVRow = UYVYToUVRow_Any_NEON; 897 } 898 } 899 if (IS_ALIGNED(width, 16)) { 900 UYVYToYRow = UYVYToYRow_NEON; 901 UYVYToUVRow = UYVYToUVRow_NEON; 902 } 903 } 904 #endif 905 906 for (int y = 0; y < height - 1; y += 2) { 907 V210ToUYVYRow(src_v210, row, width); 908 V210ToUYVYRow(src_v210 + src_stride_v210, row + kMaxStride, width); 909 UYVYToUVRow(row, kMaxStride, dst_u, dst_v, width); 910 UYVYToYRow(row, dst_y, width); 911 UYVYToYRow(row + kMaxStride, dst_y + dst_stride_y, width); 912 src_v210 += src_stride_v210 * 2; 913 dst_y += dst_stride_y * 2; 914 dst_u += dst_stride_u; 915 dst_v += dst_stride_v; 916 } 917 if (height & 1) { 918 V210ToUYVYRow(src_v210, row, width); 919 UYVYToUVRow(row, 0, dst_u, dst_v, width); 920 UYVYToYRow(row, dst_y, width); 921 } 922 return 0; 923 } 924 925 LIBYUV_API 926 int ARGBToI420(const uint8* src_argb, int src_stride_argb, 927 uint8* dst_y, int dst_stride_y, 928 uint8* dst_u, int dst_stride_u, 929 uint8* dst_v, int dst_stride_v, 930 int width, int height) { 931 if (!src_argb || 932 !dst_y || !dst_u || !dst_v || 933 width <= 0 || height == 0) { 934 return -1; 935 } 936 // Negative height means invert the image. 937 if (height < 0) { 938 height = -height; 939 src_argb = src_argb + (height - 1) * src_stride_argb; 940 src_stride_argb = -src_stride_argb; 941 } 942 void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix); 943 void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb, 944 uint8* dst_u, uint8* dst_v, int width); 945 946 ARGBToYRow = ARGBToYRow_C; 947 ARGBToUVRow = ARGBToUVRow_C; 948 #if defined(HAS_ARGBTOYROW_SSSE3) 949 if (TestCpuFlag(kCpuHasSSSE3)) { 950 if (width > 16) { 951 ARGBToUVRow = ARGBToUVRow_Any_SSSE3; 952 ARGBToYRow = ARGBToYRow_Any_SSSE3; 953 } 954 if (IS_ALIGNED(width, 16)) { 955 ARGBToUVRow = ARGBToUVRow_Unaligned_SSSE3; 956 ARGBToYRow = ARGBToYRow_Unaligned_SSSE3; 957 if (IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) { 958 ARGBToUVRow = ARGBToUVRow_SSSE3; 959 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 960 ARGBToYRow = ARGBToYRow_SSSE3; 961 } 962 } 963 } 964 } 965 #endif 966 967 for (int y = 0; y < height - 1; y += 2) { 968 ARGBToUVRow(src_argb, src_stride_argb, dst_u, dst_v, width); 969 ARGBToYRow(src_argb, dst_y, width); 970 ARGBToYRow(src_argb + src_stride_argb, dst_y + dst_stride_y, width); 971 src_argb += src_stride_argb * 2; 972 dst_y += dst_stride_y * 2; 973 dst_u += dst_stride_u; 974 dst_v += dst_stride_v; 975 } 976 if (height & 1) { 977 ARGBToUVRow(src_argb, 0, dst_u, dst_v, width); 978 ARGBToYRow(src_argb, dst_y, width); 979 } 980 return 0; 981 } 982 983 LIBYUV_API 984 int BGRAToI420(const uint8* src_bgra, int src_stride_bgra, 985 uint8* dst_y, int dst_stride_y, 986 uint8* dst_u, int dst_stride_u, 987 uint8* dst_v, int dst_stride_v, 988 int width, int height) { 989 if (!src_bgra || 990 !dst_y || !dst_u || !dst_v || 991 width <= 0 || height == 0) { 992 return -1; 993 } 994 // Negative height means invert the image. 995 if (height < 0) { 996 height = -height; 997 src_bgra = src_bgra + (height - 1) * src_stride_bgra; 998 src_stride_bgra = -src_stride_bgra; 999 } 1000 void (*BGRAToYRow)(const uint8* src_bgra, uint8* dst_y, int pix); 1001 void (*BGRAToUVRow)(const uint8* src_bgra0, int src_stride_bgra, 1002 uint8* dst_u, uint8* dst_v, int width); 1003 1004 BGRAToYRow = BGRAToYRow_C; 1005 BGRAToUVRow = BGRAToUVRow_C; 1006 #if defined(HAS_BGRATOYROW_SSSE3) 1007 if (TestCpuFlag(kCpuHasSSSE3)) { 1008 if (width > 16) { 1009 BGRAToUVRow = BGRAToUVRow_Any_SSSE3; 1010 BGRAToYRow = BGRAToYRow_Any_SSSE3; 1011 } 1012 if (IS_ALIGNED(width, 16)) { 1013 BGRAToUVRow = BGRAToUVRow_Unaligned_SSSE3; 1014 BGRAToYRow = BGRAToYRow_Unaligned_SSSE3; 1015 if (IS_ALIGNED(src_bgra, 16) && IS_ALIGNED(src_stride_bgra, 16)) { 1016 BGRAToUVRow = BGRAToUVRow_SSSE3; 1017 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 1018 BGRAToYRow = BGRAToYRow_SSSE3; 1019 } 1020 } 1021 } 1022 } 1023 #endif 1024 1025 for (int y = 0; y < height - 1; y += 2) { 1026 BGRAToUVRow(src_bgra, src_stride_bgra, dst_u, dst_v, width); 1027 BGRAToYRow(src_bgra, dst_y, width); 1028 BGRAToYRow(src_bgra + src_stride_bgra, dst_y + dst_stride_y, width); 1029 src_bgra += src_stride_bgra * 2; 1030 dst_y += dst_stride_y * 2; 1031 dst_u += dst_stride_u; 1032 dst_v += dst_stride_v; 1033 } 1034 if (height & 1) { 1035 BGRAToUVRow(src_bgra, 0, dst_u, dst_v, width); 1036 BGRAToYRow(src_bgra, dst_y, width); 1037 } 1038 return 0; 1039 } 1040 1041 LIBYUV_API 1042 int ABGRToI420(const uint8* src_abgr, int src_stride_abgr, 1043 uint8* dst_y, int dst_stride_y, 1044 uint8* dst_u, int dst_stride_u, 1045 uint8* dst_v, int dst_stride_v, 1046 int width, int height) { 1047 if (!src_abgr || 1048 !dst_y || !dst_u || !dst_v || 1049 width <= 0 || height == 0) { 1050 return -1; 1051 } 1052 // Negative height means invert the image. 1053 if (height < 0) { 1054 height = -height; 1055 src_abgr = src_abgr + (height - 1) * src_stride_abgr; 1056 src_stride_abgr = -src_stride_abgr; 1057 } 1058 void (*ABGRToYRow)(const uint8* src_abgr, uint8* dst_y, int pix); 1059 void (*ABGRToUVRow)(const uint8* src_abgr0, int src_stride_abgr, 1060 uint8* dst_u, uint8* dst_v, int width); 1061 1062 ABGRToYRow = ABGRToYRow_C; 1063 ABGRToUVRow = ABGRToUVRow_C; 1064 #if defined(HAS_ABGRTOYROW_SSSE3) 1065 if (TestCpuFlag(kCpuHasSSSE3)) { 1066 if (width > 16) { 1067 ABGRToUVRow = ABGRToUVRow_Any_SSSE3; 1068 ABGRToYRow = ABGRToYRow_Any_SSSE3; 1069 } 1070 if (IS_ALIGNED(width, 16)) { 1071 ABGRToUVRow = ABGRToUVRow_Unaligned_SSSE3; 1072 ABGRToYRow = ABGRToYRow_Unaligned_SSSE3; 1073 if (IS_ALIGNED(src_abgr, 16) && IS_ALIGNED(src_stride_abgr, 16)) { 1074 ABGRToUVRow = ABGRToUVRow_SSSE3; 1075 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 1076 ABGRToYRow = ABGRToYRow_SSSE3; 1077 } 1078 } 1079 } 1080 } 1081 #endif 1082 1083 for (int y = 0; y < height - 1; y += 2) { 1084 ABGRToUVRow(src_abgr, src_stride_abgr, dst_u, dst_v, width); 1085 ABGRToYRow(src_abgr, dst_y, width); 1086 ABGRToYRow(src_abgr + src_stride_abgr, dst_y + dst_stride_y, width); 1087 src_abgr += src_stride_abgr * 2; 1088 dst_y += dst_stride_y * 2; 1089 dst_u += dst_stride_u; 1090 dst_v += dst_stride_v; 1091 } 1092 if (height & 1) { 1093 ABGRToUVRow(src_abgr, 0, dst_u, dst_v, width); 1094 ABGRToYRow(src_abgr, dst_y, width); 1095 } 1096 return 0; 1097 } 1098 1099 LIBYUV_API 1100 int RGBAToI420(const uint8* src_rgba, int src_stride_rgba, 1101 uint8* dst_y, int dst_stride_y, 1102 uint8* dst_u, int dst_stride_u, 1103 uint8* dst_v, int dst_stride_v, 1104 int width, int height) { 1105 if (!src_rgba || 1106 !dst_y || !dst_u || !dst_v || 1107 width <= 0 || height == 0) { 1108 return -1; 1109 } 1110 // Negative height means invert the image. 1111 if (height < 0) { 1112 height = -height; 1113 src_rgba = src_rgba + (height - 1) * src_stride_rgba; 1114 src_stride_rgba = -src_stride_rgba; 1115 } 1116 void (*RGBAToYRow)(const uint8* src_rgba, uint8* dst_y, int pix); 1117 void (*RGBAToUVRow)(const uint8* src_rgba0, int src_stride_rgba, 1118 uint8* dst_u, uint8* dst_v, int width); 1119 1120 RGBAToYRow = RGBAToYRow_C; 1121 RGBAToUVRow = RGBAToUVRow_C; 1122 #if defined(HAS_RGBATOYROW_SSSE3) 1123 if (TestCpuFlag(kCpuHasSSSE3)) { 1124 if (width > 16) { 1125 RGBAToUVRow = RGBAToUVRow_Any_SSSE3; 1126 RGBAToYRow = RGBAToYRow_Any_SSSE3; 1127 } 1128 if (IS_ALIGNED(width, 16)) { 1129 RGBAToUVRow = RGBAToUVRow_Unaligned_SSSE3; 1130 RGBAToYRow = RGBAToYRow_Unaligned_SSSE3; 1131 if (IS_ALIGNED(src_rgba, 16) && IS_ALIGNED(src_stride_rgba, 16)) { 1132 RGBAToUVRow = RGBAToUVRow_SSSE3; 1133 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 1134 RGBAToYRow = RGBAToYRow_SSSE3; 1135 } 1136 } 1137 } 1138 } 1139 #endif 1140 1141 for (int y = 0; y < height - 1; y += 2) { 1142 RGBAToUVRow(src_rgba, src_stride_rgba, dst_u, dst_v, width); 1143 RGBAToYRow(src_rgba, dst_y, width); 1144 RGBAToYRow(src_rgba + src_stride_rgba, dst_y + dst_stride_y, width); 1145 src_rgba += src_stride_rgba * 2; 1146 dst_y += dst_stride_y * 2; 1147 dst_u += dst_stride_u; 1148 dst_v += dst_stride_v; 1149 } 1150 if (height & 1) { 1151 RGBAToUVRow(src_rgba, 0, dst_u, dst_v, width); 1152 RGBAToYRow(src_rgba, dst_y, width); 1153 } 1154 return 0; 1155 } 1156 1157 LIBYUV_API 1158 int RGB24ToI420(const uint8* src_rgb24, int src_stride_rgb24, 1159 uint8* dst_y, int dst_stride_y, 1160 uint8* dst_u, int dst_stride_u, 1161 uint8* dst_v, int dst_stride_v, 1162 int width, int height) { 1163 if (width * 4 > kMaxStride) { // Row buffer is required. 1164 return -1; 1165 } else if (!src_rgb24 || 1166 !dst_y || !dst_u || !dst_v || 1167 width <= 0 || height == 0) { 1168 return -1; 1169 } 1170 // Negative height means invert the image. 1171 if (height < 0) { 1172 height = -height; 1173 src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24; 1174 src_stride_rgb24 = -src_stride_rgb24; 1175 } 1176 SIMD_ALIGNED(uint8 row[kMaxStride * 2]); 1177 void (*RGB24ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix); 1178 1179 RGB24ToARGBRow = RGB24ToARGBRow_C; 1180 #if defined(HAS_RGB24TOARGBROW_SSSE3) 1181 if (TestCpuFlag(kCpuHasSSSE3) && 1182 TestReadSafe(src_rgb24, src_stride_rgb24, width, height, 3, 48)) { 1183 RGB24ToARGBRow = RGB24ToARGBRow_SSSE3; 1184 } 1185 #endif 1186 1187 void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix); 1188 void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb, 1189 uint8* dst_u, uint8* dst_v, int width); 1190 1191 ARGBToYRow = ARGBToYRow_C; 1192 ARGBToUVRow = ARGBToUVRow_C; 1193 #if defined(HAS_ARGBTOYROW_SSSE3) 1194 if (TestCpuFlag(kCpuHasSSSE3)) { 1195 if (width > 16) { 1196 ARGBToUVRow = ARGBToUVRow_Any_SSSE3; 1197 } 1198 ARGBToYRow = ARGBToYRow_Any_SSSE3; 1199 if (IS_ALIGNED(width, 16)) { 1200 ARGBToUVRow = ARGBToUVRow_SSSE3; 1201 ARGBToYRow = ARGBToYRow_Unaligned_SSSE3; 1202 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 1203 ARGBToYRow = ARGBToYRow_SSSE3; 1204 } 1205 } 1206 } 1207 #endif 1208 1209 for (int y = 0; y < height - 1; y += 2) { 1210 RGB24ToARGBRow(src_rgb24, row, width); 1211 RGB24ToARGBRow(src_rgb24 + src_stride_rgb24, row + kMaxStride, width); 1212 ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width); 1213 ARGBToYRow(row, dst_y, width); 1214 ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width); 1215 src_rgb24 += src_stride_rgb24 * 2; 1216 dst_y += dst_stride_y * 2; 1217 dst_u += dst_stride_u; 1218 dst_v += dst_stride_v; 1219 } 1220 if (height & 1) { 1221 RGB24ToARGBRow_C(src_rgb24, row, width); 1222 ARGBToUVRow(row, 0, dst_u, dst_v, width); 1223 ARGBToYRow(row, dst_y, width); 1224 } 1225 return 0; 1226 } 1227 1228 LIBYUV_API 1229 int RAWToI420(const uint8* src_raw, int src_stride_raw, 1230 uint8* dst_y, int dst_stride_y, 1231 uint8* dst_u, int dst_stride_u, 1232 uint8* dst_v, int dst_stride_v, 1233 int width, int height) { 1234 if (width * 4 > kMaxStride) { // Row buffer is required. 1235 return -1; 1236 } else if (!src_raw || 1237 !dst_y || !dst_u || !dst_v || 1238 width <= 0 || height == 0) { 1239 return -1; 1240 } 1241 // Negative height means invert the image. 1242 if (height < 0) { 1243 height = -height; 1244 src_raw = src_raw + (height - 1) * src_stride_raw; 1245 src_stride_raw = -src_stride_raw; 1246 } 1247 SIMD_ALIGNED(uint8 row[kMaxStride * 2]); 1248 void (*RAWToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix); 1249 1250 RAWToARGBRow = RAWToARGBRow_C; 1251 #if defined(HAS_RAWTOARGBROW_SSSE3) 1252 if (TestCpuFlag(kCpuHasSSSE3) && 1253 TestReadSafe(src_raw, src_stride_raw, width, height, 3, 48)) { 1254 RAWToARGBRow = RAWToARGBRow_SSSE3; 1255 } 1256 #endif 1257 1258 void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix); 1259 void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb, 1260 uint8* dst_u, uint8* dst_v, int width); 1261 1262 ARGBToYRow = ARGBToYRow_C; 1263 ARGBToUVRow = ARGBToUVRow_C; 1264 #if defined(HAS_ARGBTOYROW_SSSE3) 1265 if (TestCpuFlag(kCpuHasSSSE3)) { 1266 if (width > 16) { 1267 ARGBToUVRow = ARGBToUVRow_Any_SSSE3; 1268 } 1269 ARGBToYRow = ARGBToYRow_Any_SSSE3; 1270 if (IS_ALIGNED(width, 16)) { 1271 ARGBToUVRow = ARGBToUVRow_SSSE3; 1272 ARGBToYRow = ARGBToYRow_Unaligned_SSSE3; 1273 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 1274 ARGBToYRow = ARGBToYRow_SSSE3; 1275 } 1276 } 1277 } 1278 #endif 1279 1280 for (int y = 0; y < height - 1; y += 2) { 1281 RAWToARGBRow(src_raw, row, width); 1282 RAWToARGBRow(src_raw + src_stride_raw, row + kMaxStride, width); 1283 ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width); 1284 ARGBToYRow(row, dst_y, width); 1285 ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width); 1286 src_raw += src_stride_raw * 2; 1287 dst_y += dst_stride_y * 2; 1288 dst_u += dst_stride_u; 1289 dst_v += dst_stride_v; 1290 } 1291 if (height & 1) { 1292 RAWToARGBRow_C(src_raw, row, width); 1293 ARGBToUVRow(row, 0, dst_u, dst_v, width); 1294 ARGBToYRow(row, dst_y, width); 1295 } 1296 return 0; 1297 } 1298 1299 LIBYUV_API 1300 int RGB565ToI420(const uint8* src_rgb565, int src_stride_rgb565, 1301 uint8* dst_y, int dst_stride_y, 1302 uint8* dst_u, int dst_stride_u, 1303 uint8* dst_v, int dst_stride_v, 1304 int width, int height) { 1305 if (width * 4 > kMaxStride) { // Row buffer is required. 1306 return -1; 1307 } else if (!src_rgb565 || 1308 !dst_y || !dst_u || !dst_v || 1309 width <= 0 || height == 0) { 1310 return -1; 1311 } 1312 // Negative height means invert the image. 1313 if (height < 0) { 1314 height = -height; 1315 src_rgb565 = src_rgb565 + (height - 1) * src_stride_rgb565; 1316 src_stride_rgb565 = -src_stride_rgb565; 1317 } 1318 SIMD_ALIGNED(uint8 row[kMaxStride * 2]); 1319 void (*RGB565ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix); 1320 1321 RGB565ToARGBRow = RGB565ToARGBRow_C; 1322 #if defined(HAS_RGB565TOARGBROW_SSE2) 1323 if (TestCpuFlag(kCpuHasSSE2) && 1324 TestReadSafe(src_rgb565, src_stride_rgb565, width, height, 2, 16)) { 1325 RGB565ToARGBRow = RGB565ToARGBRow_SSE2; 1326 } 1327 #endif 1328 1329 void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix); 1330 void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb, 1331 uint8* dst_u, uint8* dst_v, int width); 1332 1333 ARGBToYRow = ARGBToYRow_C; 1334 ARGBToUVRow = ARGBToUVRow_C; 1335 #if defined(HAS_ARGBTOYROW_SSSE3) 1336 if (TestCpuFlag(kCpuHasSSSE3)) { 1337 if (width > 16) { 1338 ARGBToUVRow = ARGBToUVRow_Any_SSSE3; 1339 } 1340 ARGBToYRow = ARGBToYRow_Any_SSSE3; 1341 if (IS_ALIGNED(width, 16)) { 1342 ARGBToUVRow = ARGBToUVRow_SSSE3; 1343 ARGBToYRow = ARGBToYRow_Unaligned_SSSE3; 1344 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 1345 ARGBToYRow = ARGBToYRow_SSSE3; 1346 } 1347 } 1348 } 1349 #endif 1350 1351 for (int y = 0; y < height - 1; y += 2) { 1352 RGB565ToARGBRow(src_rgb565, row, width); 1353 RGB565ToARGBRow(src_rgb565 + src_stride_rgb565, row + kMaxStride, width); 1354 ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width); 1355 ARGBToYRow(row, dst_y, width); 1356 ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width); 1357 src_rgb565 += src_stride_rgb565 * 2; 1358 dst_y += dst_stride_y * 2; 1359 dst_u += dst_stride_u; 1360 dst_v += dst_stride_v; 1361 } 1362 if (height & 1) { 1363 RGB565ToARGBRow_C(src_rgb565, row, width); 1364 ARGBToUVRow(row, 0, dst_u, dst_v, width); 1365 ARGBToYRow(row, dst_y, width); 1366 } 1367 return 0; 1368 } 1369 1370 LIBYUV_API 1371 int ARGB1555ToI420(const uint8* src_argb1555, int src_stride_argb1555, 1372 uint8* dst_y, int dst_stride_y, 1373 uint8* dst_u, int dst_stride_u, 1374 uint8* dst_v, int dst_stride_v, 1375 int width, int height) { 1376 if (width * 4 > kMaxStride) { // Row buffer is required. 1377 return -1; 1378 } else if (!src_argb1555 || 1379 !dst_y || !dst_u || !dst_v || 1380 width <= 0 || height == 0) { 1381 return -1; 1382 } 1383 // Negative height means invert the image. 1384 if (height < 0) { 1385 height = -height; 1386 src_argb1555 = src_argb1555 + (height - 1) * src_stride_argb1555; 1387 src_stride_argb1555 = -src_stride_argb1555; 1388 } 1389 SIMD_ALIGNED(uint8 row[kMaxStride * 2]); 1390 void (*ARGB1555ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix); 1391 1392 ARGB1555ToARGBRow = ARGB1555ToARGBRow_C; 1393 #if defined(HAS_ARGB1555TOARGBROW_SSE2) 1394 if (TestCpuFlag(kCpuHasSSE2) && 1395 TestReadSafe(src_argb1555, src_stride_argb1555, width, height, 2, 16)) { 1396 ARGB1555ToARGBRow = ARGB1555ToARGBRow_SSE2; 1397 } 1398 #endif 1399 1400 void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix); 1401 void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb, 1402 uint8* dst_u, uint8* dst_v, int width); 1403 1404 ARGBToYRow = ARGBToYRow_C; 1405 ARGBToUVRow = ARGBToUVRow_C; 1406 #if defined(HAS_ARGBTOYROW_SSSE3) 1407 if (TestCpuFlag(kCpuHasSSSE3)) { 1408 if (width > 16) { 1409 ARGBToUVRow = ARGBToUVRow_Any_SSSE3; 1410 } 1411 ARGBToYRow = ARGBToYRow_Any_SSSE3; 1412 if (IS_ALIGNED(width, 16)) { 1413 ARGBToUVRow = ARGBToUVRow_SSSE3; 1414 ARGBToYRow = ARGBToYRow_Unaligned_SSSE3; 1415 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 1416 ARGBToYRow = ARGBToYRow_SSSE3; 1417 } 1418 } 1419 } 1420 #endif 1421 1422 for (int y = 0; y < height - 1; y += 2) { 1423 ARGB1555ToARGBRow(src_argb1555, row, width); 1424 ARGB1555ToARGBRow(src_argb1555 + src_stride_argb1555, 1425 row + kMaxStride, width); 1426 ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width); 1427 ARGBToYRow(row, dst_y, width); 1428 ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width); 1429 src_argb1555 += src_stride_argb1555 * 2; 1430 dst_y += dst_stride_y * 2; 1431 dst_u += dst_stride_u; 1432 dst_v += dst_stride_v; 1433 } 1434 if (height & 1) { 1435 ARGB1555ToARGBRow_C(src_argb1555, row, width); 1436 ARGBToUVRow(row, 0, dst_u, dst_v, width); 1437 ARGBToYRow(row, dst_y, width); 1438 } 1439 return 0; 1440 } 1441 1442 LIBYUV_API 1443 int ARGB4444ToI420(const uint8* src_argb4444, int src_stride_argb4444, 1444 uint8* dst_y, int dst_stride_y, 1445 uint8* dst_u, int dst_stride_u, 1446 uint8* dst_v, int dst_stride_v, 1447 int width, int height) { 1448 if (width * 4 > kMaxStride) { // Row buffer is required. 1449 return -1; 1450 } else if (!src_argb4444 || 1451 !dst_y || !dst_u || !dst_v || 1452 width <= 0 || height == 0) { 1453 return -1; 1454 } 1455 // Negative height means invert the image. 1456 if (height < 0) { 1457 height = -height; 1458 src_argb4444 = src_argb4444 + (height - 1) * src_stride_argb4444; 1459 src_stride_argb4444 = -src_stride_argb4444; 1460 } 1461 SIMD_ALIGNED(uint8 row[kMaxStride * 2]); 1462 void (*ARGB4444ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix); 1463 1464 ARGB4444ToARGBRow = ARGB4444ToARGBRow_C; 1465 #if defined(HAS_ARGB4444TOARGBROW_SSE2) 1466 if (TestCpuFlag(kCpuHasSSE2) && 1467 TestReadSafe(src_argb4444, src_stride_argb4444, width, height, 2, 16)) { 1468 ARGB4444ToARGBRow = ARGB4444ToARGBRow_SSE2; 1469 } 1470 #endif 1471 1472 void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix); 1473 void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb, 1474 uint8* dst_u, uint8* dst_v, int width); 1475 1476 ARGBToYRow = ARGBToYRow_C; 1477 ARGBToUVRow = ARGBToUVRow_C; 1478 #if defined(HAS_ARGBTOYROW_SSSE3) 1479 if (TestCpuFlag(kCpuHasSSSE3)) { 1480 if (width > 16) { 1481 ARGBToUVRow = ARGBToUVRow_Any_SSSE3; 1482 } 1483 ARGBToYRow = ARGBToYRow_Any_SSSE3; 1484 if (IS_ALIGNED(width, 16)) { 1485 ARGBToUVRow = ARGBToUVRow_SSSE3; 1486 ARGBToYRow = ARGBToYRow_Unaligned_SSSE3; 1487 if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) { 1488 ARGBToYRow = ARGBToYRow_SSSE3; 1489 } 1490 } 1491 } 1492 #endif 1493 1494 for (int y = 0; y < height - 1; y += 2) { 1495 ARGB4444ToARGBRow(src_argb4444, row, width); 1496 ARGB4444ToARGBRow(src_argb4444 + src_stride_argb4444, 1497 row + kMaxStride, width); 1498 ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width); 1499 ARGBToYRow(row, dst_y, width); 1500 ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width); 1501 src_argb4444 += src_stride_argb4444 * 2; 1502 dst_y += dst_stride_y * 2; 1503 dst_u += dst_stride_u; 1504 dst_v += dst_stride_v; 1505 } 1506 if (height & 1) { 1507 ARGB4444ToARGBRow_C(src_argb4444, row, width); 1508 ARGBToUVRow(row, 0, dst_u, dst_v, width); 1509 ARGBToYRow(row, dst_y, width); 1510 } 1511 return 0; 1512 } 1513 1514 #ifdef HAVE_JPEG 1515 struct I420Buffers { 1516 uint8* y; 1517 int y_stride; 1518 uint8* u; 1519 int u_stride; 1520 uint8* v; 1521 int v_stride; 1522 int w; 1523 int h; 1524 }; 1525 1526 static void JpegCopyI420(void* opaque, 1527 const uint8* const* data, 1528 const int* strides, 1529 int rows) { 1530 I420Buffers* dest = static_cast<I420Buffers*>(opaque); 1531 I420Copy(data[0], strides[0], 1532 data[1], strides[1], 1533 data[2], strides[2], 1534 dest->y, dest->y_stride, 1535 dest->u, dest->u_stride, 1536 dest->v, dest->v_stride, 1537 dest->w, rows); 1538 dest->y += rows * dest->y_stride; 1539 dest->u += ((rows + 1) >> 1) * dest->u_stride; 1540 dest->v += ((rows + 1) >> 1) * dest->v_stride; 1541 dest->h -= rows; 1542 } 1543 1544 static void JpegI422ToI420(void* opaque, 1545 const uint8* const* data, 1546 const int* strides, 1547 int rows) { 1548 I420Buffers* dest = static_cast<I420Buffers*>(opaque); 1549 I422ToI420(data[0], strides[0], 1550 data[1], strides[1], 1551 data[2], strides[2], 1552 dest->y, dest->y_stride, 1553 dest->u, dest->u_stride, 1554 dest->v, dest->v_stride, 1555 dest->w, rows); 1556 dest->y += rows * dest->y_stride; 1557 dest->u += ((rows + 1) >> 1) * dest->u_stride; 1558 dest->v += ((rows + 1) >> 1) * dest->v_stride; 1559 dest->h -= rows; 1560 } 1561 1562 static void JpegI444ToI420(void* opaque, 1563 const uint8* const* data, 1564 const int* strides, 1565 int rows) { 1566 I420Buffers* dest = static_cast<I420Buffers*>(opaque); 1567 I444ToI420(data[0], strides[0], 1568 data[1], strides[1], 1569 data[2], strides[2], 1570 dest->y, dest->y_stride, 1571 dest->u, dest->u_stride, 1572 dest->v, dest->v_stride, 1573 dest->w, rows); 1574 dest->y += rows * dest->y_stride; 1575 dest->u += ((rows + 1) >> 1) * dest->u_stride; 1576 dest->v += ((rows + 1) >> 1) * dest->v_stride; 1577 dest->h -= rows; 1578 } 1579 1580 static void JpegI411ToI420(void* opaque, 1581 const uint8* const* data, 1582 const int* strides, 1583 int rows) { 1584 I420Buffers* dest = static_cast<I420Buffers*>(opaque); 1585 I411ToI420(data[0], strides[0], 1586 data[1], strides[1], 1587 data[2], strides[2], 1588 dest->y, dest->y_stride, 1589 dest->u, dest->u_stride, 1590 dest->v, dest->v_stride, 1591 dest->w, rows); 1592 dest->y += rows * dest->y_stride; 1593 dest->u += ((rows + 1) >> 1) * dest->u_stride; 1594 dest->v += ((rows + 1) >> 1) * dest->v_stride; 1595 dest->h -= rows; 1596 } 1597 1598 static void JpegI400ToI420(void* opaque, 1599 const uint8* const* data, 1600 const int* strides, 1601 int rows) { 1602 I420Buffers* dest = static_cast<I420Buffers*>(opaque); 1603 I400ToI420(data[0], strides[0], 1604 dest->y, dest->y_stride, 1605 dest->u, dest->u_stride, 1606 dest->v, dest->v_stride, 1607 dest->w, rows); 1608 dest->y += rows * dest->y_stride; 1609 dest->u += ((rows + 1) >> 1) * dest->u_stride; 1610 dest->v += ((rows + 1) >> 1) * dest->v_stride; 1611 dest->h -= rows; 1612 } 1613 1614 // MJPG (Motion JPeg) to I420 1615 // TODO(fbarchard): review w and h requirement. dw and dh may be enough. 1616 LIBYUV_API 1617 int MJPGToI420(const uint8* sample, 1618 size_t sample_size, 1619 uint8* y, int y_stride, 1620 uint8* u, int u_stride, 1621 uint8* v, int v_stride, 1622 int w, int h, 1623 int dw, int dh) { 1624 if (sample_size == kUnknownDataSize) { 1625 // ERROR: MJPEG frame size unknown 1626 return -1; 1627 } 1628 1629 // TODO(fbarchard): Port to C 1630 MJpegDecoder mjpeg_decoder; 1631 bool ret = mjpeg_decoder.LoadFrame(sample, sample_size); 1632 if (ret && (mjpeg_decoder.GetWidth() != w || 1633 mjpeg_decoder.GetHeight() != h)) { 1634 // ERROR: MJPEG frame has unexpected dimensions 1635 mjpeg_decoder.UnloadFrame(); 1636 return 1; // runtime failure 1637 } 1638 if (ret) { 1639 I420Buffers bufs = { y, y_stride, u, u_stride, v, v_stride, dw, dh }; 1640 // YUV420 1641 if (mjpeg_decoder.GetColorSpace() == 1642 MJpegDecoder::kColorSpaceYCbCr && 1643 mjpeg_decoder.GetNumComponents() == 3 && 1644 mjpeg_decoder.GetVertSampFactor(0) == 2 && 1645 mjpeg_decoder.GetHorizSampFactor(0) == 2 && 1646 mjpeg_decoder.GetVertSampFactor(1) == 1 && 1647 mjpeg_decoder.GetHorizSampFactor(1) == 1 && 1648 mjpeg_decoder.GetVertSampFactor(2) == 1 && 1649 mjpeg_decoder.GetHorizSampFactor(2) == 1) { 1650 ret = mjpeg_decoder.DecodeToCallback(&JpegCopyI420, &bufs, dw, dh); 1651 // YUV422 1652 } else if (mjpeg_decoder.GetColorSpace() == 1653 MJpegDecoder::kColorSpaceYCbCr && 1654 mjpeg_decoder.GetNumComponents() == 3 && 1655 mjpeg_decoder.GetVertSampFactor(0) == 1 && 1656 mjpeg_decoder.GetHorizSampFactor(0) == 2 && 1657 mjpeg_decoder.GetVertSampFactor(1) == 1 && 1658 mjpeg_decoder.GetHorizSampFactor(1) == 1 && 1659 mjpeg_decoder.GetVertSampFactor(2) == 1 && 1660 mjpeg_decoder.GetHorizSampFactor(2) == 1) { 1661 ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToI420, &bufs, dw, dh); 1662 // YUV444 1663 } else if (mjpeg_decoder.GetColorSpace() == 1664 MJpegDecoder::kColorSpaceYCbCr && 1665 mjpeg_decoder.GetNumComponents() == 3 && 1666 mjpeg_decoder.GetVertSampFactor(0) == 1 && 1667 mjpeg_decoder.GetHorizSampFactor(0) == 1 && 1668 mjpeg_decoder.GetVertSampFactor(1) == 1 && 1669 mjpeg_decoder.GetHorizSampFactor(1) == 1 && 1670 mjpeg_decoder.GetVertSampFactor(2) == 1 && 1671 mjpeg_decoder.GetHorizSampFactor(2) == 1) { 1672 ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToI420, &bufs, dw, dh); 1673 // YUV411 1674 } else if (mjpeg_decoder.GetColorSpace() == 1675 MJpegDecoder::kColorSpaceYCbCr && 1676 mjpeg_decoder.GetNumComponents() == 3 && 1677 mjpeg_decoder.GetVertSampFactor(0) == 1 && 1678 mjpeg_decoder.GetHorizSampFactor(0) == 4 && 1679 mjpeg_decoder.GetVertSampFactor(1) == 1 && 1680 mjpeg_decoder.GetHorizSampFactor(1) == 1 && 1681 mjpeg_decoder.GetVertSampFactor(2) == 1 && 1682 mjpeg_decoder.GetHorizSampFactor(2) == 1) { 1683 ret = mjpeg_decoder.DecodeToCallback(&JpegI411ToI420, &bufs, dw, dh); 1684 // YUV400 1685 } else if (mjpeg_decoder.GetColorSpace() == 1686 MJpegDecoder::kColorSpaceGrayscale && 1687 mjpeg_decoder.GetNumComponents() == 1 && 1688 mjpeg_decoder.GetVertSampFactor(0) == 1 && 1689 mjpeg_decoder.GetHorizSampFactor(0) == 1) { 1690 ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToI420, &bufs, dw, dh); 1691 } else { 1692 // TODO(fbarchard): Implement conversion for any other colorspace/sample 1693 // factors that occur in practice. 411 is supported by libjpeg 1694 // ERROR: Unable to convert MJPEG frame because format is not supported 1695 mjpeg_decoder.UnloadFrame(); 1696 return 1; 1697 } 1698 } 1699 return 0; 1700 } 1701 #endif 1702 1703 // Convert camera sample to I420 with cropping, rotation and vertical flip. 1704 // src_width is used for source stride computation 1705 // src_height is used to compute location of planes, and indicate inversion 1706 // sample_size is measured in bytes and is the size of the frame. 1707 // With MJPEG it is the compressed size of the frame. 1708 LIBYUV_API 1709 int ConvertToI420(const uint8* sample, 1710 #ifdef HAVE_JPEG 1711 size_t sample_size, 1712 #else 1713 size_t /* sample_size */, 1714 #endif 1715 uint8* y, int y_stride, 1716 uint8* u, int u_stride, 1717 uint8* v, int v_stride, 1718 int crop_x, int crop_y, 1719 int src_width, int src_height, 1720 int dst_width, int dst_height, 1721 RotationMode rotation, 1722 uint32 format) { 1723 if (!y || !u || !v || !sample || 1724 src_width <= 0 || dst_width <= 0 || 1725 src_height == 0 || dst_height == 0) { 1726 return -1; 1727 } 1728 int aligned_src_width = (src_width + 1) & ~1; 1729 const uint8* src; 1730 const uint8* src_uv; 1731 int abs_src_height = (src_height < 0) ? -src_height : src_height; 1732 int inv_dst_height = (dst_height < 0) ? -dst_height : dst_height; 1733 if (src_height < 0) { 1734 inv_dst_height = -inv_dst_height; 1735 } 1736 int r = 0; 1737 1738 // One pass rotation is available for some formats. For the rest, convert 1739 // to I420 (with optional vertical flipping) into a temporary I420 buffer, 1740 // and then rotate the I420 to the final destination buffer. 1741 // For in-place conversion, if destination y is same as source sample, 1742 // also enable temporary buffer. 1743 bool need_buf = (rotation && format != FOURCC_I420 && 1744 format != FOURCC_NV12 && format != FOURCC_NV21 && 1745 format != FOURCC_YU12 && format != FOURCC_YV12) || y == sample; 1746 uint8* tmp_y = y; 1747 uint8* tmp_u = u; 1748 uint8* tmp_v = v; 1749 int tmp_y_stride = y_stride; 1750 int tmp_u_stride = u_stride; 1751 int tmp_v_stride = v_stride; 1752 uint8* buf = NULL; 1753 int abs_dst_height = (dst_height < 0) ? -dst_height : dst_height; 1754 if (need_buf) { 1755 int y_size = dst_width * abs_dst_height; 1756 int uv_size = ((dst_width + 1) / 2) * ((abs_dst_height + 1) / 2); 1757 buf = new uint8[y_size + uv_size * 2]; 1758 if (!buf) { 1759 return 1; // Out of memory runtime error. 1760 } 1761 y = buf; 1762 u = y + y_size; 1763 v = u + uv_size; 1764 y_stride = dst_width; 1765 u_stride = v_stride = ((dst_width + 1) / 2); 1766 } 1767 1768 switch (format) { 1769 // Single plane formats 1770 case FOURCC_YUY2: 1771 src = sample + (aligned_src_width * crop_y + crop_x) * 2; 1772 r = YUY2ToI420(src, aligned_src_width * 2, 1773 y, y_stride, 1774 u, u_stride, 1775 v, v_stride, 1776 dst_width, inv_dst_height); 1777 break; 1778 case FOURCC_UYVY: 1779 src = sample + (aligned_src_width * crop_y + crop_x) * 2; 1780 r = UYVYToI420(src, aligned_src_width * 2, 1781 y, y_stride, 1782 u, u_stride, 1783 v, v_stride, 1784 dst_width, inv_dst_height); 1785 break; 1786 case FOURCC_V210: 1787 // stride is multiple of 48 pixels (128 bytes). 1788 // pixels come in groups of 6 = 16 bytes 1789 src = sample + (aligned_src_width + 47) / 48 * 128 * crop_y + 1790 crop_x / 6 * 16; 1791 r = V210ToI420(src, (aligned_src_width + 47) / 48 * 128, 1792 y, y_stride, 1793 u, u_stride, 1794 v, v_stride, 1795 dst_width, inv_dst_height); 1796 break; 1797 case FOURCC_24BG: 1798 src = sample + (src_width * crop_y + crop_x) * 3; 1799 r = RGB24ToI420(src, src_width * 3, 1800 y, y_stride, 1801 u, u_stride, 1802 v, v_stride, 1803 dst_width, inv_dst_height); 1804 break; 1805 case FOURCC_RAW: 1806 src = sample + (src_width * crop_y + crop_x) * 3; 1807 r = RAWToI420(src, src_width * 3, 1808 y, y_stride, 1809 u, u_stride, 1810 v, v_stride, 1811 dst_width, inv_dst_height); 1812 break; 1813 case FOURCC_ARGB: 1814 src = sample + (src_width * crop_y + crop_x) * 4; 1815 r = ARGBToI420(src, src_width * 4, 1816 y, y_stride, 1817 u, u_stride, 1818 v, v_stride, 1819 dst_width, inv_dst_height); 1820 break; 1821 case FOURCC_BGRA: 1822 src = sample + (src_width * crop_y + crop_x) * 4; 1823 r = BGRAToI420(src, src_width * 4, 1824 y, y_stride, 1825 u, u_stride, 1826 v, v_stride, 1827 dst_width, inv_dst_height); 1828 break; 1829 case FOURCC_ABGR: 1830 src = sample + (src_width * crop_y + crop_x) * 4; 1831 r = ABGRToI420(src, src_width * 4, 1832 y, y_stride, 1833 u, u_stride, 1834 v, v_stride, 1835 dst_width, inv_dst_height); 1836 break; 1837 case FOURCC_RGBA: 1838 src = sample + (src_width * crop_y + crop_x) * 4; 1839 r = RGBAToI420(src, src_width * 4, 1840 y, y_stride, 1841 u, u_stride, 1842 v, v_stride, 1843 dst_width, inv_dst_height); 1844 break; 1845 case FOURCC_RGBP: 1846 src = sample + (src_width * crop_y + crop_x) * 2; 1847 r = RGB565ToI420(src, src_width * 2, 1848 y, y_stride, 1849 u, u_stride, 1850 v, v_stride, 1851 dst_width, inv_dst_height); 1852 break; 1853 case FOURCC_RGBO: 1854 src = sample + (src_width * crop_y + crop_x) * 2; 1855 r = ARGB1555ToI420(src, src_width * 2, 1856 y, y_stride, 1857 u, u_stride, 1858 v, v_stride, 1859 dst_width, inv_dst_height); 1860 break; 1861 case FOURCC_R444: 1862 src = sample + (src_width * crop_y + crop_x) * 2; 1863 r = ARGB4444ToI420(src, src_width * 2, 1864 y, y_stride, 1865 u, u_stride, 1866 v, v_stride, 1867 dst_width, inv_dst_height); 1868 break; 1869 // TODO(fbarchard): Support cropping Bayer by odd numbers 1870 // by adjusting fourcc. 1871 case FOURCC_BGGR: 1872 src = sample + (src_width * crop_y + crop_x); 1873 r = BayerBGGRToI420(src, src_width, 1874 y, y_stride, 1875 u, u_stride, 1876 v, v_stride, 1877 dst_width, inv_dst_height); 1878 break; 1879 1880 case FOURCC_GBRG: 1881 src = sample + (src_width * crop_y + crop_x); 1882 r = BayerGBRGToI420(src, src_width, 1883 y, y_stride, 1884 u, u_stride, 1885 v, v_stride, 1886 dst_width, inv_dst_height); 1887 break; 1888 1889 case FOURCC_GRBG: 1890 src = sample + (src_width * crop_y + crop_x); 1891 r = BayerGRBGToI420(src, src_width, 1892 y, y_stride, 1893 u, u_stride, 1894 v, v_stride, 1895 dst_width, inv_dst_height); 1896 break; 1897 1898 case FOURCC_RGGB: 1899 src = sample + (src_width * crop_y + crop_x); 1900 r = BayerRGGBToI420(src, src_width, 1901 y, y_stride, 1902 u, u_stride, 1903 v, v_stride, 1904 dst_width, inv_dst_height); 1905 break; 1906 1907 case FOURCC_I400: 1908 src = sample + src_width * crop_y + crop_x; 1909 r = I400ToI420(src, src_width, 1910 y, y_stride, 1911 u, u_stride, 1912 v, v_stride, 1913 dst_width, inv_dst_height); 1914 break; 1915 1916 // Biplanar formats 1917 case FOURCC_NV12: 1918 src = sample + (src_width * crop_y + crop_x); 1919 src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x; 1920 r = NV12ToI420Rotate(src, src_width, 1921 src_uv, aligned_src_width, 1922 y, y_stride, 1923 u, u_stride, 1924 v, v_stride, 1925 dst_width, inv_dst_height, rotation); 1926 break; 1927 case FOURCC_NV21: 1928 src = sample + (src_width * crop_y + crop_x); 1929 src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x; 1930 // Call NV12 but with u and v parameters swapped. 1931 r = NV12ToI420Rotate(src, src_width, 1932 src_uv, aligned_src_width, 1933 y, y_stride, 1934 v, v_stride, 1935 u, u_stride, 1936 dst_width, inv_dst_height, rotation); 1937 break; 1938 case FOURCC_M420: 1939 src = sample + (src_width * crop_y) * 12 / 8 + crop_x; 1940 r = M420ToI420(src, src_width, 1941 y, y_stride, 1942 u, u_stride, 1943 v, v_stride, 1944 dst_width, inv_dst_height); 1945 break; 1946 case FOURCC_Q420: 1947 src = sample + (src_width + aligned_src_width * 2) * crop_y + crop_x; 1948 src_uv = sample + (src_width + aligned_src_width * 2) * crop_y + 1949 src_width + crop_x * 2; 1950 r = Q420ToI420(src, src_width * 3, 1951 src_uv, src_width * 3, 1952 y, y_stride, 1953 u, u_stride, 1954 v, v_stride, 1955 dst_width, inv_dst_height); 1956 break; 1957 // Triplanar formats 1958 case FOURCC_I420: 1959 case FOURCC_YU12: 1960 case FOURCC_YV12: { 1961 const uint8* src_y = sample + (src_width * crop_y + crop_x); 1962 const uint8* src_u; 1963 const uint8* src_v; 1964 int halfwidth = (src_width + 1) / 2; 1965 int halfheight = (abs_src_height + 1) / 2; 1966 if (format == FOURCC_YV12) { 1967 src_v = sample + src_width * abs_src_height + 1968 (halfwidth * crop_y + crop_x) / 2; 1969 src_u = sample + src_width * abs_src_height + 1970 halfwidth * (halfheight + crop_y / 2) + crop_x / 2; 1971 } else { 1972 src_u = sample + src_width * abs_src_height + 1973 (halfwidth * crop_y + crop_x) / 2; 1974 src_v = sample + src_width * abs_src_height + 1975 halfwidth * (halfheight + crop_y / 2) + crop_x / 2; 1976 } 1977 r = I420Rotate(src_y, src_width, 1978 src_u, halfwidth, 1979 src_v, halfwidth, 1980 y, y_stride, 1981 u, u_stride, 1982 v, v_stride, 1983 dst_width, inv_dst_height, rotation); 1984 break; 1985 } 1986 case FOURCC_I422: 1987 case FOURCC_YV16: { 1988 const uint8* src_y = sample + src_width * crop_y + crop_x; 1989 const uint8* src_u; 1990 const uint8* src_v; 1991 int halfwidth = (src_width + 1) / 2; 1992 if (format == FOURCC_YV16) { 1993 src_v = sample + src_width * abs_src_height + 1994 halfwidth * crop_y + crop_x / 2; 1995 src_u = sample + src_width * abs_src_height + 1996 halfwidth * (abs_src_height + crop_y) + crop_x / 2; 1997 } else { 1998 src_u = sample + src_width * abs_src_height + 1999 halfwidth * crop_y + crop_x / 2; 2000 src_v = sample + src_width * abs_src_height + 2001 halfwidth * (abs_src_height + crop_y) + crop_x / 2; 2002 } 2003 r = I422ToI420(src_y, src_width, 2004 src_u, halfwidth, 2005 src_v, halfwidth, 2006 y, y_stride, 2007 u, u_stride, 2008 v, v_stride, 2009 dst_width, inv_dst_height); 2010 break; 2011 } 2012 case FOURCC_I444: 2013 case FOURCC_YV24: { 2014 const uint8* src_y = sample + src_width * crop_y + crop_x; 2015 const uint8* src_u; 2016 const uint8* src_v; 2017 if (format == FOURCC_YV24) { 2018 src_v = sample + src_width * (abs_src_height + crop_y) + crop_x; 2019 src_u = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x; 2020 } else { 2021 src_u = sample + src_width * (abs_src_height + crop_y) + crop_x; 2022 src_v = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x; 2023 } 2024 r = I444ToI420(src_y, src_width, 2025 src_u, src_width, 2026 src_v, src_width, 2027 y, y_stride, 2028 u, u_stride, 2029 v, v_stride, 2030 dst_width, inv_dst_height); 2031 break; 2032 } 2033 case FOURCC_I411: { 2034 int quarterwidth = (src_width + 3) / 4; 2035 const uint8* src_y = sample + src_width * crop_y + crop_x; 2036 const uint8* src_u = sample + src_width * abs_src_height + 2037 quarterwidth * crop_y + crop_x / 4; 2038 const uint8* src_v = sample + src_width * abs_src_height + 2039 quarterwidth * (abs_src_height + crop_y) + crop_x / 4; 2040 r = I411ToI420(src_y, src_width, 2041 src_u, quarterwidth, 2042 src_v, quarterwidth, 2043 y, y_stride, 2044 u, u_stride, 2045 v, v_stride, 2046 dst_width, inv_dst_height); 2047 break; 2048 } 2049 #ifdef HAVE_JPEG 2050 case FOURCC_MJPG: 2051 r = MJPGToI420(sample, sample_size, 2052 y, y_stride, 2053 u, u_stride, 2054 v, v_stride, 2055 src_width, abs_src_height, dst_width, inv_dst_height); 2056 break; 2057 #endif 2058 default: 2059 r = -1; // unknown fourcc - return failure code. 2060 } 2061 2062 if (need_buf) { 2063 if (!r) { 2064 r = I420Rotate(y, y_stride, 2065 u, u_stride, 2066 v, v_stride, 2067 tmp_y, tmp_y_stride, 2068 tmp_u, tmp_u_stride, 2069 tmp_v, tmp_v_stride, 2070 dst_width, abs_dst_height, rotation); 2071 } 2072 delete buf; 2073 } 2074 2075 return r; 2076 } 2077 2078 #ifdef __cplusplus 2079 } // extern "C" 2080 } // namespace libyuv 2081 #endif 2082