Home | History | Annotate | Download | only in source
      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