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/planar_functions.h"
     16 #include "libyuv/rotate.h"
     17 #include "libyuv/scale.h"  // For ScalePlane()
     18 #include "libyuv/row.h"
     19 
     20 #ifdef __cplusplus
     21 namespace libyuv {
     22 extern "C" {
     23 #endif
     24 
     25 #define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s)
     26 static __inline int Abs(int v) {
     27   return v >= 0 ? v : -v;
     28 }
     29 
     30 // Any I4xx To I420 format with mirroring.
     31 static int I4xxToI420(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 src_y_width, int src_y_height,
     38                       int src_uv_width, int src_uv_height) {
     39   const int dst_y_width = Abs(src_y_width);
     40   const int dst_y_height = Abs(src_y_height);
     41   const int dst_uv_width = SUBSAMPLE(dst_y_width, 1, 1);
     42   const int dst_uv_height = SUBSAMPLE(dst_y_height, 1, 1);
     43   if (src_y_width == 0 || src_y_height == 0 ||
     44       src_uv_width == 0 || src_uv_height == 0) {
     45     return -1;
     46   }
     47   ScalePlane(src_y, src_stride_y, src_y_width, src_y_height,
     48              dst_y, dst_stride_y, dst_y_width, dst_y_height,
     49              kFilterBilinear);
     50   ScalePlane(src_u, src_stride_u, src_uv_width, src_uv_height,
     51              dst_u, dst_stride_u, dst_uv_width, dst_uv_height,
     52              kFilterBilinear);
     53   ScalePlane(src_v, src_stride_v, src_uv_width, src_uv_height,
     54              dst_v, dst_stride_v, dst_uv_width, dst_uv_height,
     55              kFilterBilinear);
     56   return 0;
     57 }
     58 
     59 // Copy I420 with optional flipping
     60 // TODO(fbarchard): Use Scale plane which supports mirroring, but ensure
     61 // is does row coalescing.
     62 LIBYUV_API
     63 int I420Copy(const uint8* src_y, int src_stride_y,
     64              const uint8* src_u, int src_stride_u,
     65              const uint8* src_v, int src_stride_v,
     66              uint8* dst_y, int dst_stride_y,
     67              uint8* dst_u, int dst_stride_u,
     68              uint8* dst_v, int dst_stride_v,
     69              int width, int height) {
     70   int halfwidth = (width + 1) >> 1;
     71   int halfheight = (height + 1) >> 1;
     72   if (!src_y || !src_u || !src_v ||
     73       !dst_y || !dst_u || !dst_v ||
     74       width <= 0 || height == 0) {
     75     return -1;
     76   }
     77   // Negative height means invert the image.
     78   if (height < 0) {
     79     height = -height;
     80     halfheight = (height + 1) >> 1;
     81     src_y = src_y + (height - 1) * src_stride_y;
     82     src_u = src_u + (halfheight - 1) * src_stride_u;
     83     src_v = src_v + (halfheight - 1) * src_stride_v;
     84     src_stride_y = -src_stride_y;
     85     src_stride_u = -src_stride_u;
     86     src_stride_v = -src_stride_v;
     87   }
     88 
     89   if (dst_y) {
     90     CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
     91   }
     92   // Copy UV planes.
     93   CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
     94   CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
     95   return 0;
     96 }
     97 
     98 // 422 chroma is 1/2 width, 1x height
     99 // 420 chroma is 1/2 width, 1/2 height
    100 LIBYUV_API
    101 int I422ToI420(const uint8* src_y, int src_stride_y,
    102                const uint8* src_u, int src_stride_u,
    103                const uint8* src_v, int src_stride_v,
    104                uint8* dst_y, int dst_stride_y,
    105                uint8* dst_u, int dst_stride_u,
    106                uint8* dst_v, int dst_stride_v,
    107                int width, int height) {
    108   const int src_uv_width = SUBSAMPLE(width, 1, 1);
    109   return I4xxToI420(src_y, src_stride_y,
    110                     src_u, src_stride_u,
    111                     src_v, src_stride_v,
    112                     dst_y, dst_stride_y,
    113                     dst_u, dst_stride_u,
    114                     dst_v, dst_stride_v,
    115                     width, height,
    116                     src_uv_width, height);
    117 }
    118 
    119 // 444 chroma is 1x width, 1x height
    120 // 420 chroma is 1/2 width, 1/2 height
    121 LIBYUV_API
    122 int I444ToI420(const uint8* src_y, int src_stride_y,
    123                const uint8* src_u, int src_stride_u,
    124                const uint8* src_v, int src_stride_v,
    125                uint8* dst_y, int dst_stride_y,
    126                uint8* dst_u, int dst_stride_u,
    127                uint8* dst_v, int dst_stride_v,
    128                int width, int height) {
    129   return I4xxToI420(src_y, src_stride_y,
    130                     src_u, src_stride_u,
    131                     src_v, src_stride_v,
    132                     dst_y, dst_stride_y,
    133                     dst_u, dst_stride_u,
    134                     dst_v, dst_stride_v,
    135                     width, height,
    136                     width, height);
    137 }
    138 
    139 // 411 chroma is 1/4 width, 1x height
    140 // 420 chroma is 1/2 width, 1/2 height
    141 LIBYUV_API
    142 int I411ToI420(const uint8* src_y, int src_stride_y,
    143                const uint8* src_u, int src_stride_u,
    144                const uint8* src_v, int src_stride_v,
    145                uint8* dst_y, int dst_stride_y,
    146                uint8* dst_u, int dst_stride_u,
    147                uint8* dst_v, int dst_stride_v,
    148                int width, int height) {
    149   const int src_uv_width = SUBSAMPLE(width, 3, 2);
    150   return I4xxToI420(src_y, src_stride_y,
    151                     src_u, src_stride_u,
    152                     src_v, src_stride_v,
    153                     dst_y, dst_stride_y,
    154                     dst_u, dst_stride_u,
    155                     dst_v, dst_stride_v,
    156                     width, height,
    157                     src_uv_width, height);
    158 }
    159 
    160 // I400 is greyscale typically used in MJPG
    161 LIBYUV_API
    162 int I400ToI420(const uint8* src_y, int src_stride_y,
    163                uint8* dst_y, int dst_stride_y,
    164                uint8* dst_u, int dst_stride_u,
    165                uint8* dst_v, int dst_stride_v,
    166                int width, int height) {
    167   int halfwidth = (width + 1) >> 1;
    168   int halfheight = (height + 1) >> 1;
    169   if (!src_y || !dst_y || !dst_u || !dst_v ||
    170       width <= 0 || height == 0) {
    171     return -1;
    172   }
    173   // Negative height means invert the image.
    174   if (height < 0) {
    175     height = -height;
    176     halfheight = (height + 1) >> 1;
    177     src_y = src_y + (height - 1) * src_stride_y;
    178     src_stride_y = -src_stride_y;
    179   }
    180   CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
    181   SetPlane(dst_u, dst_stride_u, halfwidth, halfheight, 128);
    182   SetPlane(dst_v, dst_stride_v, halfwidth, halfheight, 128);
    183   return 0;
    184 }
    185 
    186 static void CopyPlane2(const uint8* src, int src_stride_0, int src_stride_1,
    187                        uint8* dst, int dst_stride,
    188                        int width, int height) {
    189   int y;
    190   void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
    191 #if defined(HAS_COPYROW_X86)
    192   if (TestCpuFlag(kCpuHasX86) && IS_ALIGNED(width, 4)) {
    193     CopyRow = CopyRow_X86;
    194   }
    195 #endif
    196 #if defined(HAS_COPYROW_SSE2)
    197   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 32) &&
    198       IS_ALIGNED(src, 16) &&
    199       IS_ALIGNED(src_stride_0, 16) && IS_ALIGNED(src_stride_1, 16) &&
    200       IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) {
    201     CopyRow = CopyRow_SSE2;
    202   }
    203 #endif
    204 #if defined(HAS_COPYROW_ERMS)
    205   if (TestCpuFlag(kCpuHasERMS)) {
    206     CopyRow = CopyRow_ERMS;
    207   }
    208 #endif
    209 #if defined(HAS_COPYROW_NEON)
    210   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 32)) {
    211     CopyRow = CopyRow_NEON;
    212   }
    213 #endif
    214 #if defined(HAS_COPYROW_MIPS)
    215   if (TestCpuFlag(kCpuHasMIPS)) {
    216     CopyRow = CopyRow_MIPS;
    217   }
    218 #endif
    219 
    220   // Copy plane
    221   for (y = 0; y < height - 1; y += 2) {
    222     CopyRow(src, dst, width);
    223     CopyRow(src + src_stride_0, dst + dst_stride, width);
    224     src += src_stride_0 + src_stride_1;
    225     dst += dst_stride * 2;
    226   }
    227   if (height & 1) {
    228     CopyRow(src, dst, width);
    229   }
    230 }
    231 
    232 // Support converting from FOURCC_M420
    233 // Useful for bandwidth constrained transports like USB 1.0 and 2.0 and for
    234 // easy conversion to I420.
    235 // M420 format description:
    236 // M420 is row biplanar 420: 2 rows of Y and 1 row of UV.
    237 // Chroma is half width / half height. (420)
    238 // src_stride_m420 is row planar. Normally this will be the width in pixels.
    239 //   The UV plane is half width, but 2 values, so src_stride_m420 applies to
    240 //   this as well as the two Y planes.
    241 static int X420ToI420(const uint8* src_y,
    242                       int src_stride_y0, int src_stride_y1,
    243                       const uint8* src_uv, int src_stride_uv,
    244                       uint8* dst_y, int dst_stride_y,
    245                       uint8* dst_u, int dst_stride_u,
    246                       uint8* dst_v, int dst_stride_v,
    247                       int width, int height) {
    248   int y;
    249   int halfwidth = (width + 1) >> 1;
    250   int halfheight = (height + 1) >> 1;
    251   void (*SplitUVRow)(const uint8* src_uv, uint8* dst_u, uint8* dst_v, int pix) =
    252       SplitUVRow_C;
    253   if (!src_y || !src_uv ||
    254       !dst_y || !dst_u || !dst_v ||
    255       width <= 0 || height == 0) {
    256     return -1;
    257   }
    258   // Negative height means invert the image.
    259   if (height < 0) {
    260     height = -height;
    261     halfheight = (height + 1) >> 1;
    262     dst_y = dst_y + (height - 1) * dst_stride_y;
    263     dst_u = dst_u + (halfheight - 1) * dst_stride_u;
    264     dst_v = dst_v + (halfheight - 1) * dst_stride_v;
    265     dst_stride_y = -dst_stride_y;
    266     dst_stride_u = -dst_stride_u;
    267     dst_stride_v = -dst_stride_v;
    268   }
    269   // Coalesce rows.
    270   if (src_stride_y0 == width &&
    271       src_stride_y1 == width &&
    272       dst_stride_y == width) {
    273     width *= height;
    274     height = 1;
    275     src_stride_y0 = src_stride_y1 = dst_stride_y = 0;
    276   }
    277   // Coalesce rows.
    278   if (src_stride_uv == halfwidth * 2 &&
    279       dst_stride_u == halfwidth &&
    280       dst_stride_v == halfwidth) {
    281     halfwidth *= halfheight;
    282     halfheight = 1;
    283     src_stride_uv = dst_stride_u = dst_stride_v = 0;
    284   }
    285 #if defined(HAS_SPLITUVROW_SSE2)
    286   if (TestCpuFlag(kCpuHasSSE2) && halfwidth >= 16) {
    287     SplitUVRow = SplitUVRow_Any_SSE2;
    288     if (IS_ALIGNED(halfwidth, 16)) {
    289       SplitUVRow = SplitUVRow_Unaligned_SSE2;
    290       if (IS_ALIGNED(src_uv, 16) && IS_ALIGNED(src_stride_uv, 16) &&
    291           IS_ALIGNED(dst_u, 16) && IS_ALIGNED(dst_stride_u, 16) &&
    292           IS_ALIGNED(dst_v, 16) && IS_ALIGNED(dst_stride_v, 16)) {
    293         SplitUVRow = SplitUVRow_SSE2;
    294       }
    295     }
    296   }
    297 #endif
    298 #if defined(HAS_SPLITUVROW_AVX2)
    299   if (TestCpuFlag(kCpuHasAVX2) && halfwidth >= 32) {
    300     SplitUVRow = SplitUVRow_Any_AVX2;
    301     if (IS_ALIGNED(halfwidth, 32)) {
    302       SplitUVRow = SplitUVRow_AVX2;
    303     }
    304   }
    305 #endif
    306 #if defined(HAS_SPLITUVROW_NEON)
    307   if (TestCpuFlag(kCpuHasNEON) && halfwidth >= 16) {
    308     SplitUVRow = SplitUVRow_Any_NEON;
    309     if (IS_ALIGNED(halfwidth, 16)) {
    310       SplitUVRow = SplitUVRow_NEON;
    311     }
    312   }
    313 #endif
    314 #if defined(HAS_SPLITUVROW_MIPS_DSPR2)
    315   if (TestCpuFlag(kCpuHasMIPS_DSPR2) && halfwidth >= 16) {
    316     SplitUVRow = SplitUVRow_Any_MIPS_DSPR2;
    317     if (IS_ALIGNED(halfwidth, 16)) {
    318       SplitUVRow = SplitUVRow_Unaligned_MIPS_DSPR2;
    319       if (IS_ALIGNED(src_uv, 4) && IS_ALIGNED(src_stride_uv, 4) &&
    320           IS_ALIGNED(dst_u, 4) && IS_ALIGNED(dst_stride_u, 4) &&
    321           IS_ALIGNED(dst_v, 4) && IS_ALIGNED(dst_stride_v, 4)) {
    322         SplitUVRow = SplitUVRow_MIPS_DSPR2;
    323       }
    324     }
    325   }
    326 #endif
    327 
    328   if (dst_y) {
    329     if (src_stride_y0 == src_stride_y1) {
    330       CopyPlane(src_y, src_stride_y0, dst_y, dst_stride_y, width, height);
    331     } else {
    332       CopyPlane2(src_y, src_stride_y0, src_stride_y1, dst_y, dst_stride_y,
    333                  width, height);
    334     }
    335   }
    336 
    337   for (y = 0; y < halfheight; ++y) {
    338     // Copy a row of UV.
    339     SplitUVRow(src_uv, dst_u, dst_v, halfwidth);
    340     dst_u += dst_stride_u;
    341     dst_v += dst_stride_v;
    342     src_uv += src_stride_uv;
    343   }
    344   return 0;
    345 }
    346 
    347 // Convert NV12 to I420.
    348 LIBYUV_API
    349 int NV12ToI420(const uint8* src_y, int src_stride_y,
    350                const uint8* src_uv, int src_stride_uv,
    351                uint8* dst_y, int dst_stride_y,
    352                uint8* dst_u, int dst_stride_u,
    353                uint8* dst_v, int dst_stride_v,
    354                int width, int height) {
    355   return X420ToI420(src_y, src_stride_y, src_stride_y,
    356                     src_uv, src_stride_uv,
    357                     dst_y, dst_stride_y,
    358                     dst_u, dst_stride_u,
    359                     dst_v, dst_stride_v,
    360                     width, height);
    361 }
    362 
    363 // Convert NV21 to I420.  Same as NV12 but u and v pointers swapped.
    364 LIBYUV_API
    365 int NV21ToI420(const uint8* src_y, int src_stride_y,
    366                const uint8* src_vu, int src_stride_vu,
    367                uint8* dst_y, int dst_stride_y,
    368                uint8* dst_u, int dst_stride_u,
    369                uint8* dst_v, int dst_stride_v,
    370                int width, int height) {
    371   return X420ToI420(src_y, src_stride_y, src_stride_y,
    372                     src_vu, src_stride_vu,
    373                     dst_y, dst_stride_y,
    374                     dst_v, dst_stride_v,
    375                     dst_u, dst_stride_u,
    376                     width, height);
    377 }
    378 
    379 // Convert M420 to I420.
    380 LIBYUV_API
    381 int M420ToI420(const uint8* src_m420, int src_stride_m420,
    382                uint8* dst_y, int dst_stride_y,
    383                uint8* dst_u, int dst_stride_u,
    384                uint8* dst_v, int dst_stride_v,
    385                int width, int height) {
    386   return X420ToI420(src_m420, src_stride_m420, src_stride_m420 * 2,
    387                     src_m420 + src_stride_m420 * 2, src_stride_m420 * 3,
    388                     dst_y, dst_stride_y,
    389                     dst_u, dst_stride_u,
    390                     dst_v, dst_stride_v,
    391                     width, height);
    392 }
    393 
    394 // Convert Q420 to I420.
    395 // Format is rows of YY/YUYV
    396 LIBYUV_API
    397 int Q420ToI420(const uint8* src_y, int src_stride_y,
    398                const uint8* src_yuy2, int src_stride_yuy2,
    399                uint8* dst_y, int dst_stride_y,
    400                uint8* dst_u, int dst_stride_u,
    401                uint8* dst_v, int dst_stride_v,
    402                int width, int height) {
    403   int y;
    404   int halfheight = (height + 1) >> 1;
    405   void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
    406   void (*YUY2ToUV422Row)(const uint8* src_yuy2, uint8* dst_u, uint8* dst_v,
    407       int pix) = YUY2ToUV422Row_C;
    408   void (*YUY2ToYRow)(const uint8* src_yuy2, uint8* dst_y, int pix) =
    409       YUY2ToYRow_C;
    410   if (!src_y || !src_yuy2 ||
    411       !dst_y || !dst_u || !dst_v ||
    412       width <= 0 || height == 0) {
    413     return -1;
    414   }
    415   // Negative height means invert the image.
    416   if (height < 0) {
    417     height = -height;
    418     halfheight = (height + 1) >> 1;
    419     dst_y = dst_y + (height - 1) * dst_stride_y;
    420     dst_u = dst_u + (halfheight - 1) * dst_stride_u;
    421     dst_v = dst_v + (halfheight - 1) * dst_stride_v;
    422     dst_stride_y = -dst_stride_y;
    423     dst_stride_u = -dst_stride_u;
    424     dst_stride_v = -dst_stride_v;
    425   }
    426   // CopyRow for rows of just Y in Q420 copied to Y plane of I420.
    427 #if defined(HAS_COPYROW_NEON)
    428   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 32)) {
    429     CopyRow = CopyRow_NEON;
    430   }
    431 #endif
    432 #if defined(HAS_COPYROW_X86)
    433   if (IS_ALIGNED(width, 4)) {
    434     CopyRow = CopyRow_X86;
    435   }
    436 #endif
    437 #if defined(HAS_COPYROW_SSE2)
    438   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 32) &&
    439       IS_ALIGNED(src_y, 16) && IS_ALIGNED(src_stride_y, 16) &&
    440       IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
    441     CopyRow = CopyRow_SSE2;
    442   }
    443 #endif
    444 #if defined(HAS_COPYROW_ERMS)
    445   if (TestCpuFlag(kCpuHasERMS)) {
    446     CopyRow = CopyRow_ERMS;
    447   }
    448 #endif
    449 #if defined(HAS_COPYROW_MIPS)
    450   if (TestCpuFlag(kCpuHasMIPS)) {
    451     CopyRow = CopyRow_MIPS;
    452   }
    453 #endif
    454 
    455 #if defined(HAS_YUY2TOYROW_SSE2)
    456   if (TestCpuFlag(kCpuHasSSE2) && width >= 16) {
    457     YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2;
    458     YUY2ToYRow = YUY2ToYRow_Any_SSE2;
    459     if (IS_ALIGNED(width, 16)) {
    460       YUY2ToUV422Row = YUY2ToUV422Row_Unaligned_SSE2;
    461       YUY2ToYRow = YUY2ToYRow_Unaligned_SSE2;
    462       if (IS_ALIGNED(src_yuy2, 16) && IS_ALIGNED(src_stride_yuy2, 16)) {
    463         YUY2ToUV422Row = YUY2ToUV422Row_SSE2;
    464         if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
    465           YUY2ToYRow = YUY2ToYRow_SSE2;
    466         }
    467       }
    468     }
    469   }
    470 #endif
    471 #if defined(HAS_YUY2TOYROW_AVX2)
    472   if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    473     YUY2ToUV422Row = YUY2ToUV422Row_Any_AVX2;
    474     YUY2ToYRow = YUY2ToYRow_Any_AVX2;
    475     if (IS_ALIGNED(width, 32)) {
    476       YUY2ToUV422Row = YUY2ToUV422Row_AVX2;
    477       YUY2ToYRow = YUY2ToYRow_AVX2;
    478     }
    479   }
    480 #endif
    481 #if defined(HAS_YUY2TOYROW_NEON)
    482   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    483     YUY2ToYRow = YUY2ToYRow_Any_NEON;
    484     if (width >= 16) {
    485       YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON;
    486     }
    487     if (IS_ALIGNED(width, 16)) {
    488       YUY2ToYRow = YUY2ToYRow_NEON;
    489       YUY2ToUV422Row = YUY2ToUV422Row_NEON;
    490     }
    491   }
    492 #endif
    493 
    494   for (y = 0; y < height - 1; y += 2) {
    495     CopyRow(src_y, dst_y, width);
    496     src_y += src_stride_y;
    497     dst_y += dst_stride_y;
    498 
    499     YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
    500     YUY2ToYRow(src_yuy2, dst_y, width);
    501     src_yuy2 += src_stride_yuy2;
    502     dst_y += dst_stride_y;
    503     dst_u += dst_stride_u;
    504     dst_v += dst_stride_v;
    505   }
    506   if (height & 1) {
    507     CopyRow(src_y, dst_y, width);
    508     YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
    509   }
    510   return 0;
    511 }
    512 
    513 // Convert YUY2 to I420.
    514 LIBYUV_API
    515 int YUY2ToI420(const uint8* src_yuy2, int src_stride_yuy2,
    516                uint8* dst_y, int dst_stride_y,
    517                uint8* dst_u, int dst_stride_u,
    518                uint8* dst_v, int dst_stride_v,
    519                int width, int height) {
    520   int y;
    521   void (*YUY2ToUVRow)(const uint8* src_yuy2, int src_stride_yuy2,
    522       uint8* dst_u, uint8* dst_v, int pix) = YUY2ToUVRow_C;
    523   void (*YUY2ToYRow)(const uint8* src_yuy2,
    524       uint8* dst_y, int pix) = YUY2ToYRow_C;
    525   // Negative height means invert the image.
    526   if (height < 0) {
    527     height = -height;
    528     src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
    529     src_stride_yuy2 = -src_stride_yuy2;
    530   }
    531 #if defined(HAS_YUY2TOYROW_SSE2)
    532   if (TestCpuFlag(kCpuHasSSE2) && width >= 16) {
    533     YUY2ToUVRow = YUY2ToUVRow_Any_SSE2;
    534     YUY2ToYRow = YUY2ToYRow_Any_SSE2;
    535     if (IS_ALIGNED(width, 16)) {
    536       YUY2ToUVRow = YUY2ToUVRow_Unaligned_SSE2;
    537       YUY2ToYRow = YUY2ToYRow_Unaligned_SSE2;
    538       if (IS_ALIGNED(src_yuy2, 16) && IS_ALIGNED(src_stride_yuy2, 16)) {
    539         YUY2ToUVRow = YUY2ToUVRow_SSE2;
    540         if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
    541           YUY2ToYRow = YUY2ToYRow_SSE2;
    542         }
    543       }
    544     }
    545   }
    546 #endif
    547 #if defined(HAS_YUY2TOYROW_AVX2)
    548   if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    549     YUY2ToUVRow = YUY2ToUVRow_Any_AVX2;
    550     YUY2ToYRow = YUY2ToYRow_Any_AVX2;
    551     if (IS_ALIGNED(width, 32)) {
    552       YUY2ToUVRow = YUY2ToUVRow_AVX2;
    553       YUY2ToYRow = YUY2ToYRow_AVX2;
    554     }
    555   }
    556 #endif
    557 #if defined(HAS_YUY2TOYROW_NEON)
    558   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    559     YUY2ToYRow = YUY2ToYRow_Any_NEON;
    560     if (width >= 16) {
    561       YUY2ToUVRow = YUY2ToUVRow_Any_NEON;
    562     }
    563     if (IS_ALIGNED(width, 16)) {
    564       YUY2ToYRow = YUY2ToYRow_NEON;
    565       YUY2ToUVRow = YUY2ToUVRow_NEON;
    566     }
    567   }
    568 #endif
    569 
    570   for (y = 0; y < height - 1; y += 2) {
    571     YUY2ToUVRow(src_yuy2, src_stride_yuy2, dst_u, dst_v, width);
    572     YUY2ToYRow(src_yuy2, dst_y, width);
    573     YUY2ToYRow(src_yuy2 + src_stride_yuy2, dst_y + dst_stride_y, width);
    574     src_yuy2 += src_stride_yuy2 * 2;
    575     dst_y += dst_stride_y * 2;
    576     dst_u += dst_stride_u;
    577     dst_v += dst_stride_v;
    578   }
    579   if (height & 1) {
    580     YUY2ToUVRow(src_yuy2, 0, dst_u, dst_v, width);
    581     YUY2ToYRow(src_yuy2, dst_y, width);
    582   }
    583   return 0;
    584 }
    585 
    586 // Convert UYVY to I420.
    587 LIBYUV_API
    588 int UYVYToI420(const uint8* src_uyvy, int src_stride_uyvy,
    589                uint8* dst_y, int dst_stride_y,
    590                uint8* dst_u, int dst_stride_u,
    591                uint8* dst_v, int dst_stride_v,
    592                int width, int height) {
    593   int y;
    594   void (*UYVYToUVRow)(const uint8* src_uyvy, int src_stride_uyvy,
    595       uint8* dst_u, uint8* dst_v, int pix) = UYVYToUVRow_C;
    596   void (*UYVYToYRow)(const uint8* src_uyvy,
    597       uint8* dst_y, int pix) = UYVYToYRow_C;
    598   // Negative height means invert the image.
    599   if (height < 0) {
    600     height = -height;
    601     src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
    602     src_stride_uyvy = -src_stride_uyvy;
    603   }
    604 #if defined(HAS_UYVYTOYROW_SSE2)
    605   if (TestCpuFlag(kCpuHasSSE2) && width >= 16) {
    606     UYVYToUVRow = UYVYToUVRow_Any_SSE2;
    607     UYVYToYRow = UYVYToYRow_Any_SSE2;
    608     if (IS_ALIGNED(width, 16)) {
    609       UYVYToUVRow = UYVYToUVRow_Unaligned_SSE2;
    610       UYVYToYRow = UYVYToYRow_Unaligned_SSE2;
    611       if (IS_ALIGNED(src_uyvy, 16) && IS_ALIGNED(src_stride_uyvy, 16)) {
    612         UYVYToUVRow = UYVYToUVRow_SSE2;
    613         if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
    614           UYVYToYRow = UYVYToYRow_SSE2;
    615         }
    616       }
    617     }
    618   }
    619 #endif
    620 #if defined(HAS_UYVYTOYROW_AVX2)
    621   if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    622     UYVYToUVRow = UYVYToUVRow_Any_AVX2;
    623     UYVYToYRow = UYVYToYRow_Any_AVX2;
    624     if (IS_ALIGNED(width, 32)) {
    625       UYVYToUVRow = UYVYToUVRow_AVX2;
    626       UYVYToYRow = UYVYToYRow_AVX2;
    627     }
    628   }
    629 #endif
    630 #if defined(HAS_UYVYTOYROW_NEON)
    631   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    632     UYVYToYRow = UYVYToYRow_Any_NEON;
    633     if (width >= 16) {
    634       UYVYToUVRow = UYVYToUVRow_Any_NEON;
    635     }
    636     if (IS_ALIGNED(width, 16)) {
    637       UYVYToYRow = UYVYToYRow_NEON;
    638       UYVYToUVRow = UYVYToUVRow_NEON;
    639     }
    640   }
    641 #endif
    642 
    643   for (y = 0; y < height - 1; y += 2) {
    644     UYVYToUVRow(src_uyvy, src_stride_uyvy, dst_u, dst_v, width);
    645     UYVYToYRow(src_uyvy, dst_y, width);
    646     UYVYToYRow(src_uyvy + src_stride_uyvy, dst_y + dst_stride_y, width);
    647     src_uyvy += src_stride_uyvy * 2;
    648     dst_y += dst_stride_y * 2;
    649     dst_u += dst_stride_u;
    650     dst_v += dst_stride_v;
    651   }
    652   if (height & 1) {
    653     UYVYToUVRow(src_uyvy, 0, dst_u, dst_v, width);
    654     UYVYToYRow(src_uyvy, dst_y, width);
    655   }
    656   return 0;
    657 }
    658 
    659 // Convert ARGB to I420.
    660 LIBYUV_API
    661 int ARGBToI420(const uint8* src_argb, int src_stride_argb,
    662                uint8* dst_y, int dst_stride_y,
    663                uint8* dst_u, int dst_stride_u,
    664                uint8* dst_v, int dst_stride_v,
    665                int width, int height) {
    666   int y;
    667   void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
    668       uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
    669   void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
    670       ARGBToYRow_C;
    671   if (!src_argb ||
    672       !dst_y || !dst_u || !dst_v ||
    673       width <= 0 || height == 0) {
    674     return -1;
    675   }
    676   // Negative height means invert the image.
    677   if (height < 0) {
    678     height = -height;
    679     src_argb = src_argb + (height - 1) * src_stride_argb;
    680     src_stride_argb = -src_stride_argb;
    681   }
    682 #if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
    683   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    684     ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
    685     ARGBToYRow = ARGBToYRow_Any_SSSE3;
    686     if (IS_ALIGNED(width, 16)) {
    687       ARGBToUVRow = ARGBToUVRow_Unaligned_SSSE3;
    688       ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
    689       if (IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) {
    690         ARGBToUVRow = ARGBToUVRow_SSSE3;
    691         if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
    692           ARGBToYRow = ARGBToYRow_SSSE3;
    693         }
    694       }
    695     }
    696   }
    697 #endif
    698 #if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
    699   if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    700     ARGBToUVRow = ARGBToUVRow_Any_AVX2;
    701     ARGBToYRow = ARGBToYRow_Any_AVX2;
    702     if (IS_ALIGNED(width, 32)) {
    703       ARGBToUVRow = ARGBToUVRow_AVX2;
    704       ARGBToYRow = ARGBToYRow_AVX2;
    705     }
    706   }
    707 #endif
    708 #if defined(HAS_ARGBTOYROW_NEON)
    709   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    710     ARGBToYRow = ARGBToYRow_Any_NEON;
    711     if (IS_ALIGNED(width, 8)) {
    712       ARGBToYRow = ARGBToYRow_NEON;
    713     }
    714     if (width >= 16) {
    715       ARGBToUVRow = ARGBToUVRow_Any_NEON;
    716       if (IS_ALIGNED(width, 16)) {
    717         ARGBToUVRow = ARGBToUVRow_NEON;
    718       }
    719     }
    720   }
    721 #endif
    722 
    723   for (y = 0; y < height - 1; y += 2) {
    724     ARGBToUVRow(src_argb, src_stride_argb, dst_u, dst_v, width);
    725     ARGBToYRow(src_argb, dst_y, width);
    726     ARGBToYRow(src_argb + src_stride_argb, dst_y + dst_stride_y, width);
    727     src_argb += src_stride_argb * 2;
    728     dst_y += dst_stride_y * 2;
    729     dst_u += dst_stride_u;
    730     dst_v += dst_stride_v;
    731   }
    732   if (height & 1) {
    733     ARGBToUVRow(src_argb, 0, dst_u, dst_v, width);
    734     ARGBToYRow(src_argb, dst_y, width);
    735   }
    736   return 0;
    737 }
    738 
    739 // Convert BGRA to I420.
    740 LIBYUV_API
    741 int BGRAToI420(const uint8* src_bgra, int src_stride_bgra,
    742                uint8* dst_y, int dst_stride_y,
    743                uint8* dst_u, int dst_stride_u,
    744                uint8* dst_v, int dst_stride_v,
    745                int width, int height) {
    746   int y;
    747   void (*BGRAToUVRow)(const uint8* src_bgra0, int src_stride_bgra,
    748       uint8* dst_u, uint8* dst_v, int width) = BGRAToUVRow_C;
    749   void (*BGRAToYRow)(const uint8* src_bgra, uint8* dst_y, int pix) =
    750       BGRAToYRow_C;
    751   if (!src_bgra ||
    752       !dst_y || !dst_u || !dst_v ||
    753       width <= 0 || height == 0) {
    754     return -1;
    755   }
    756   // Negative height means invert the image.
    757   if (height < 0) {
    758     height = -height;
    759     src_bgra = src_bgra + (height - 1) * src_stride_bgra;
    760     src_stride_bgra = -src_stride_bgra;
    761   }
    762 #if defined(HAS_BGRATOYROW_SSSE3)
    763   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    764     BGRAToUVRow = BGRAToUVRow_Any_SSSE3;
    765     BGRAToYRow = BGRAToYRow_Any_SSSE3;
    766     if (IS_ALIGNED(width, 16)) {
    767       BGRAToUVRow = BGRAToUVRow_Unaligned_SSSE3;
    768       BGRAToYRow = BGRAToYRow_Unaligned_SSSE3;
    769       if (IS_ALIGNED(src_bgra, 16) && IS_ALIGNED(src_stride_bgra, 16)) {
    770         BGRAToUVRow = BGRAToUVRow_SSSE3;
    771         if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
    772           BGRAToYRow = BGRAToYRow_SSSE3;
    773         }
    774       }
    775     }
    776   }
    777 #elif defined(HAS_BGRATOYROW_NEON)
    778   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    779     BGRAToYRow = BGRAToYRow_Any_NEON;
    780     if (IS_ALIGNED(width, 8)) {
    781       BGRAToYRow = BGRAToYRow_NEON;
    782     }
    783     if (width >= 16) {
    784       BGRAToUVRow = BGRAToUVRow_Any_NEON;
    785       if (IS_ALIGNED(width, 16)) {
    786         BGRAToUVRow = BGRAToUVRow_NEON;
    787       }
    788     }
    789   }
    790 #endif
    791 
    792   for (y = 0; y < height - 1; y += 2) {
    793     BGRAToUVRow(src_bgra, src_stride_bgra, dst_u, dst_v, width);
    794     BGRAToYRow(src_bgra, dst_y, width);
    795     BGRAToYRow(src_bgra + src_stride_bgra, dst_y + dst_stride_y, width);
    796     src_bgra += src_stride_bgra * 2;
    797     dst_y += dst_stride_y * 2;
    798     dst_u += dst_stride_u;
    799     dst_v += dst_stride_v;
    800   }
    801   if (height & 1) {
    802     BGRAToUVRow(src_bgra, 0, dst_u, dst_v, width);
    803     BGRAToYRow(src_bgra, dst_y, width);
    804   }
    805   return 0;
    806 }
    807 
    808 // Convert ABGR to I420.
    809 LIBYUV_API
    810 int ABGRToI420(const uint8* src_abgr, int src_stride_abgr,
    811                uint8* dst_y, int dst_stride_y,
    812                uint8* dst_u, int dst_stride_u,
    813                uint8* dst_v, int dst_stride_v,
    814                int width, int height) {
    815   int y;
    816   void (*ABGRToUVRow)(const uint8* src_abgr0, int src_stride_abgr,
    817       uint8* dst_u, uint8* dst_v, int width) = ABGRToUVRow_C;
    818   void (*ABGRToYRow)(const uint8* src_abgr, uint8* dst_y, int pix) =
    819       ABGRToYRow_C;
    820   if (!src_abgr ||
    821       !dst_y || !dst_u || !dst_v ||
    822       width <= 0 || height == 0) {
    823     return -1;
    824   }
    825   // Negative height means invert the image.
    826   if (height < 0) {
    827     height = -height;
    828     src_abgr = src_abgr + (height - 1) * src_stride_abgr;
    829     src_stride_abgr = -src_stride_abgr;
    830   }
    831 #if defined(HAS_ABGRTOYROW_SSSE3)
    832   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    833     ABGRToUVRow = ABGRToUVRow_Any_SSSE3;
    834     ABGRToYRow = ABGRToYRow_Any_SSSE3;
    835     if (IS_ALIGNED(width, 16)) {
    836       ABGRToUVRow = ABGRToUVRow_Unaligned_SSSE3;
    837       ABGRToYRow = ABGRToYRow_Unaligned_SSSE3;
    838       if (IS_ALIGNED(src_abgr, 16) && IS_ALIGNED(src_stride_abgr, 16)) {
    839         ABGRToUVRow = ABGRToUVRow_SSSE3;
    840         if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
    841           ABGRToYRow = ABGRToYRow_SSSE3;
    842         }
    843       }
    844     }
    845   }
    846 #elif defined(HAS_ABGRTOYROW_NEON)
    847   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    848     ABGRToYRow = ABGRToYRow_Any_NEON;
    849     if (IS_ALIGNED(width, 8)) {
    850       ABGRToYRow = ABGRToYRow_NEON;
    851     }
    852     if (width >= 16) {
    853       ABGRToUVRow = ABGRToUVRow_Any_NEON;
    854       if (IS_ALIGNED(width, 16)) {
    855         ABGRToUVRow = ABGRToUVRow_NEON;
    856       }
    857     }
    858   }
    859 #endif
    860 
    861   for (y = 0; y < height - 1; y += 2) {
    862     ABGRToUVRow(src_abgr, src_stride_abgr, dst_u, dst_v, width);
    863     ABGRToYRow(src_abgr, dst_y, width);
    864     ABGRToYRow(src_abgr + src_stride_abgr, dst_y + dst_stride_y, width);
    865     src_abgr += src_stride_abgr * 2;
    866     dst_y += dst_stride_y * 2;
    867     dst_u += dst_stride_u;
    868     dst_v += dst_stride_v;
    869   }
    870   if (height & 1) {
    871     ABGRToUVRow(src_abgr, 0, dst_u, dst_v, width);
    872     ABGRToYRow(src_abgr, dst_y, width);
    873   }
    874   return 0;
    875 }
    876 
    877 // Convert RGBA to I420.
    878 LIBYUV_API
    879 int RGBAToI420(const uint8* src_rgba, int src_stride_rgba,
    880                uint8* dst_y, int dst_stride_y,
    881                uint8* dst_u, int dst_stride_u,
    882                uint8* dst_v, int dst_stride_v,
    883                int width, int height) {
    884   int y;
    885   void (*RGBAToUVRow)(const uint8* src_rgba0, int src_stride_rgba,
    886       uint8* dst_u, uint8* dst_v, int width) = RGBAToUVRow_C;
    887   void (*RGBAToYRow)(const uint8* src_rgba, uint8* dst_y, int pix) =
    888       RGBAToYRow_C;
    889   if (!src_rgba ||
    890       !dst_y || !dst_u || !dst_v ||
    891       width <= 0 || height == 0) {
    892     return -1;
    893   }
    894   // Negative height means invert the image.
    895   if (height < 0) {
    896     height = -height;
    897     src_rgba = src_rgba + (height - 1) * src_stride_rgba;
    898     src_stride_rgba = -src_stride_rgba;
    899   }
    900 #if defined(HAS_RGBATOYROW_SSSE3)
    901   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    902     RGBAToUVRow = RGBAToUVRow_Any_SSSE3;
    903     RGBAToYRow = RGBAToYRow_Any_SSSE3;
    904     if (IS_ALIGNED(width, 16)) {
    905       RGBAToUVRow = RGBAToUVRow_Unaligned_SSSE3;
    906       RGBAToYRow = RGBAToYRow_Unaligned_SSSE3;
    907       if (IS_ALIGNED(src_rgba, 16) && IS_ALIGNED(src_stride_rgba, 16)) {
    908         RGBAToUVRow = RGBAToUVRow_SSSE3;
    909         if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
    910           RGBAToYRow = RGBAToYRow_SSSE3;
    911         }
    912       }
    913     }
    914   }
    915 #elif defined(HAS_RGBATOYROW_NEON)
    916   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    917     RGBAToYRow = RGBAToYRow_Any_NEON;
    918     if (IS_ALIGNED(width, 8)) {
    919       RGBAToYRow = RGBAToYRow_NEON;
    920     }
    921     if (width >= 16) {
    922       RGBAToUVRow = RGBAToUVRow_Any_NEON;
    923       if (IS_ALIGNED(width, 16)) {
    924         RGBAToUVRow = RGBAToUVRow_NEON;
    925       }
    926     }
    927   }
    928 #endif
    929 
    930   for (y = 0; y < height - 1; y += 2) {
    931     RGBAToUVRow(src_rgba, src_stride_rgba, dst_u, dst_v, width);
    932     RGBAToYRow(src_rgba, dst_y, width);
    933     RGBAToYRow(src_rgba + src_stride_rgba, dst_y + dst_stride_y, width);
    934     src_rgba += src_stride_rgba * 2;
    935     dst_y += dst_stride_y * 2;
    936     dst_u += dst_stride_u;
    937     dst_v += dst_stride_v;
    938   }
    939   if (height & 1) {
    940     RGBAToUVRow(src_rgba, 0, dst_u, dst_v, width);
    941     RGBAToYRow(src_rgba, dst_y, width);
    942   }
    943   return 0;
    944 }
    945 
    946 // Convert RGB24 to I420.
    947 LIBYUV_API
    948 int RGB24ToI420(const uint8* src_rgb24, int src_stride_rgb24,
    949                 uint8* dst_y, int dst_stride_y,
    950                 uint8* dst_u, int dst_stride_u,
    951                 uint8* dst_v, int dst_stride_v,
    952                 int width, int height) {
    953   int y;
    954 #if defined(HAS_RGB24TOYROW_NEON)
    955   void (*RGB24ToUVRow)(const uint8* src_rgb24, int src_stride_rgb24,
    956       uint8* dst_u, uint8* dst_v, int width) = RGB24ToUVRow_C;
    957   void (*RGB24ToYRow)(const uint8* src_rgb24, uint8* dst_y, int pix) =
    958       RGB24ToYRow_C;
    959 #else
    960   void (*RGB24ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
    961       RGB24ToARGBRow_C;
    962   void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
    963       uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
    964   void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
    965       ARGBToYRow_C;
    966   // Allocate 2 rows of ARGB.
    967   const int kRowSize = (width * 4 + 15) & ~15;
    968   align_buffer_64(row, kRowSize * 2);
    969 #endif
    970   if (!src_rgb24 || !dst_y || !dst_u || !dst_v ||
    971       width <= 0 || height == 0) {
    972     return -1;
    973   }
    974   // Negative height means invert the image.
    975   if (height < 0) {
    976     height = -height;
    977     src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24;
    978     src_stride_rgb24 = -src_stride_rgb24;
    979   }
    980 
    981 #if defined(HAS_RGB24TOYROW_NEON)
    982   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    983     RGB24ToYRow = RGB24ToYRow_Any_NEON;
    984     if (IS_ALIGNED(width, 8)) {
    985       RGB24ToYRow = RGB24ToYRow_NEON;
    986     }
    987     if (width >= 16) {
    988       RGB24ToUVRow = RGB24ToUVRow_Any_NEON;
    989       if (IS_ALIGNED(width, 16)) {
    990         RGB24ToUVRow = RGB24ToUVRow_NEON;
    991       }
    992     }
    993   }
    994 #else  // HAS_RGB24TOYROW_NEON
    995 
    996 #if defined(HAS_RGB24TOARGBROW_SSSE3)
    997   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    998     RGB24ToARGBRow = RGB24ToARGBRow_Any_SSSE3;
    999     if (IS_ALIGNED(width, 16)) {
   1000       RGB24ToARGBRow = RGB24ToARGBRow_SSSE3;
   1001     }
   1002   }
   1003 #endif
   1004 #if defined(HAS_ARGBTOUVROW_SSSE3)
   1005   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1006     ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
   1007     if (IS_ALIGNED(width, 16)) {
   1008       ARGBToUVRow = ARGBToUVRow_SSSE3;
   1009     }
   1010   }
   1011 #endif
   1012 #if defined(HAS_ARGBTOUVROW_SSSE3)
   1013   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1014     ARGBToYRow = ARGBToYRow_Any_SSSE3;
   1015     if (IS_ALIGNED(width, 16)) {
   1016       ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
   1017       if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
   1018         ARGBToYRow = ARGBToYRow_SSSE3;
   1019       }
   1020     }
   1021   }
   1022 #endif  // HAS_ARGBTOUVROW_SSSE3
   1023 #endif  // HAS_RGB24TOYROW_NEON
   1024 
   1025   for (y = 0; y < height - 1; y += 2) {
   1026 #if defined(HAS_RGB24TOYROW_NEON)
   1027     RGB24ToUVRow(src_rgb24, src_stride_rgb24, dst_u, dst_v, width);
   1028     RGB24ToYRow(src_rgb24, dst_y, width);
   1029     RGB24ToYRow(src_rgb24 + src_stride_rgb24, dst_y + dst_stride_y, width);
   1030 #else
   1031     RGB24ToARGBRow(src_rgb24, row, width);
   1032     RGB24ToARGBRow(src_rgb24 + src_stride_rgb24, row + kRowSize, width);
   1033     ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
   1034     ARGBToYRow(row, dst_y, width);
   1035     ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
   1036 #endif
   1037     src_rgb24 += src_stride_rgb24 * 2;
   1038     dst_y += dst_stride_y * 2;
   1039     dst_u += dst_stride_u;
   1040     dst_v += dst_stride_v;
   1041   }
   1042   if (height & 1) {
   1043 #if defined(HAS_RGB24TOYROW_NEON)
   1044     RGB24ToUVRow(src_rgb24, 0, dst_u, dst_v, width);
   1045     RGB24ToYRow(src_rgb24, dst_y, width);
   1046 #else
   1047     RGB24ToARGBRow(src_rgb24, row, width);
   1048     ARGBToUVRow(row, 0, dst_u, dst_v, width);
   1049     ARGBToYRow(row, dst_y, width);
   1050 #endif
   1051   }
   1052 #if !defined(HAS_RGB24TOYROW_NEON)
   1053   free_aligned_buffer_64(row);
   1054 #endif
   1055   return 0;
   1056 }
   1057 
   1058 // Convert RAW to I420.
   1059 LIBYUV_API
   1060 int RAWToI420(const uint8* src_raw, int src_stride_raw,
   1061               uint8* dst_y, int dst_stride_y,
   1062               uint8* dst_u, int dst_stride_u,
   1063               uint8* dst_v, int dst_stride_v,
   1064               int width, int height) {
   1065   int y;
   1066 #if defined(HAS_RAWTOYROW_NEON)
   1067   void (*RAWToUVRow)(const uint8* src_raw, int src_stride_raw,
   1068       uint8* dst_u, uint8* dst_v, int width) = RAWToUVRow_C;
   1069   void (*RAWToYRow)(const uint8* src_raw, uint8* dst_y, int pix) =
   1070       RAWToYRow_C;
   1071 #else
   1072   void (*RAWToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
   1073       RAWToARGBRow_C;
   1074   void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
   1075       uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
   1076   void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
   1077       ARGBToYRow_C;
   1078   // Allocate 2 rows of ARGB.
   1079   const int kRowSize = (width * 4 + 15) & ~15;
   1080   align_buffer_64(row, kRowSize * 2);
   1081 #endif
   1082   if (!src_raw || !dst_y || !dst_u || !dst_v ||
   1083       width <= 0 || height == 0) {
   1084     return -1;
   1085   }
   1086   // Negative height means invert the image.
   1087   if (height < 0) {
   1088     height = -height;
   1089     src_raw = src_raw + (height - 1) * src_stride_raw;
   1090     src_stride_raw = -src_stride_raw;
   1091   }
   1092 
   1093 #if defined(HAS_RAWTOYROW_NEON)
   1094   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
   1095     RAWToYRow = RAWToYRow_Any_NEON;
   1096     if (IS_ALIGNED(width, 8)) {
   1097       RAWToYRow = RAWToYRow_NEON;
   1098     }
   1099     if (width >= 16) {
   1100       RAWToUVRow = RAWToUVRow_Any_NEON;
   1101       if (IS_ALIGNED(width, 16)) {
   1102         RAWToUVRow = RAWToUVRow_NEON;
   1103       }
   1104     }
   1105   }
   1106 #else  // HAS_RAWTOYROW_NEON
   1107 
   1108 #if defined(HAS_RAWTOARGBROW_SSSE3)
   1109   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1110     RAWToARGBRow = RAWToARGBRow_Any_SSSE3;
   1111     if (IS_ALIGNED(width, 16)) {
   1112       RAWToARGBRow = RAWToARGBRow_SSSE3;
   1113     }
   1114   }
   1115 #endif
   1116 #if defined(HAS_ARGBTOUVROW_SSSE3)
   1117   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1118     ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
   1119     if (IS_ALIGNED(width, 16)) {
   1120       ARGBToUVRow = ARGBToUVRow_SSSE3;
   1121     }
   1122   }
   1123 #endif
   1124 #if defined(HAS_ARGBTOUVROW_SSSE3)
   1125   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1126     ARGBToYRow = ARGBToYRow_Any_SSSE3;
   1127     if (IS_ALIGNED(width, 16)) {
   1128       ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
   1129       if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
   1130         ARGBToYRow = ARGBToYRow_SSSE3;
   1131       }
   1132     }
   1133   }
   1134 #endif  // HAS_ARGBTOUVROW_SSSE3
   1135 #endif  // HAS_RAWTOYROW_NEON
   1136 
   1137   for (y = 0; y < height - 1; y += 2) {
   1138 #if defined(HAS_RAWTOYROW_NEON)
   1139     RAWToUVRow(src_raw, src_stride_raw, dst_u, dst_v, width);
   1140     RAWToYRow(src_raw, dst_y, width);
   1141     RAWToYRow(src_raw + src_stride_raw, dst_y + dst_stride_y, width);
   1142 #else
   1143     RAWToARGBRow(src_raw, row, width);
   1144     RAWToARGBRow(src_raw + src_stride_raw, row + kRowSize, width);
   1145     ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
   1146     ARGBToYRow(row, dst_y, width);
   1147     ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
   1148 #endif
   1149     src_raw += src_stride_raw * 2;
   1150     dst_y += dst_stride_y * 2;
   1151     dst_u += dst_stride_u;
   1152     dst_v += dst_stride_v;
   1153   }
   1154   if (height & 1) {
   1155 #if defined(HAS_RAWTOYROW_NEON)
   1156     RAWToUVRow(src_raw, 0, dst_u, dst_v, width);
   1157     RAWToYRow(src_raw, dst_y, width);
   1158 #else
   1159     RAWToARGBRow(src_raw, row, width);
   1160     ARGBToUVRow(row, 0, dst_u, dst_v, width);
   1161     ARGBToYRow(row, dst_y, width);
   1162 #endif
   1163   }
   1164 #if !defined(HAS_RAWTOYROW_NEON)
   1165   free_aligned_buffer_64(row);
   1166 #endif
   1167   return 0;
   1168 }
   1169 
   1170 // Convert RGB565 to I420.
   1171 LIBYUV_API
   1172 int RGB565ToI420(const uint8* src_rgb565, int src_stride_rgb565,
   1173                  uint8* dst_y, int dst_stride_y,
   1174                  uint8* dst_u, int dst_stride_u,
   1175                  uint8* dst_v, int dst_stride_v,
   1176                  int width, int height) {
   1177   int y;
   1178 #if defined(HAS_RGB565TOYROW_NEON)
   1179   void (*RGB565ToUVRow)(const uint8* src_rgb565, int src_stride_rgb565,
   1180       uint8* dst_u, uint8* dst_v, int width) = RGB565ToUVRow_C;
   1181   void (*RGB565ToYRow)(const uint8* src_rgb565, uint8* dst_y, int pix) =
   1182       RGB565ToYRow_C;
   1183 #else
   1184   void (*RGB565ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
   1185       RGB565ToARGBRow_C;
   1186   void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
   1187       uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
   1188   void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
   1189       ARGBToYRow_C;
   1190   // Allocate 2 rows of ARGB.
   1191   const int kRowSize = (width * 4 + 15) & ~15;
   1192   align_buffer_64(row, kRowSize * 2);
   1193 #endif
   1194   if (!src_rgb565 || !dst_y || !dst_u || !dst_v ||
   1195       width <= 0 || height == 0) {
   1196     return -1;
   1197   }
   1198   // Negative height means invert the image.
   1199   if (height < 0) {
   1200     height = -height;
   1201     src_rgb565 = src_rgb565 + (height - 1) * src_stride_rgb565;
   1202     src_stride_rgb565 = -src_stride_rgb565;
   1203   }
   1204 
   1205 #if defined(HAS_RGB565TOYROW_NEON)
   1206   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
   1207     RGB565ToYRow = RGB565ToYRow_Any_NEON;
   1208     if (IS_ALIGNED(width, 8)) {
   1209       RGB565ToYRow = RGB565ToYRow_NEON;
   1210     }
   1211     if (width >= 16) {
   1212       RGB565ToUVRow = RGB565ToUVRow_Any_NEON;
   1213       if (IS_ALIGNED(width, 16)) {
   1214         RGB565ToUVRow = RGB565ToUVRow_NEON;
   1215       }
   1216     }
   1217   }
   1218 #else  // HAS_RGB565TOYROW_NEON
   1219 
   1220 #if defined(HAS_RGB565TOARGBROW_SSE2)
   1221   if (TestCpuFlag(kCpuHasSSE2) && width >= 8) {
   1222     RGB565ToARGBRow = RGB565ToARGBRow_Any_SSE2;
   1223     if (IS_ALIGNED(width, 8)) {
   1224       RGB565ToARGBRow = RGB565ToARGBRow_SSE2;
   1225     }
   1226   }
   1227 #endif
   1228 #if defined(HAS_ARGBTOUVROW_SSSE3)
   1229   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1230     ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
   1231     if (IS_ALIGNED(width, 16)) {
   1232       ARGBToUVRow = ARGBToUVRow_SSSE3;
   1233     }
   1234   }
   1235 #endif
   1236 #if defined(HAS_ARGBTOUVROW_SSSE3)
   1237   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1238     ARGBToYRow = ARGBToYRow_Any_SSSE3;
   1239     if (IS_ALIGNED(width, 16)) {
   1240       ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
   1241       if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
   1242         ARGBToYRow = ARGBToYRow_SSSE3;
   1243       }
   1244     }
   1245   }
   1246 #endif  // HAS_ARGBTOUVROW_SSSE3
   1247 #endif  // HAS_RGB565TOYROW_NEON
   1248 
   1249   for (y = 0; y < height - 1; y += 2) {
   1250 #if defined(HAS_RGB565TOYROW_NEON)
   1251     RGB565ToUVRow(src_rgb565, src_stride_rgb565, dst_u, dst_v, width);
   1252     RGB565ToYRow(src_rgb565, dst_y, width);
   1253     RGB565ToYRow(src_rgb565 + src_stride_rgb565, dst_y + dst_stride_y, width);
   1254 #else
   1255     RGB565ToARGBRow(src_rgb565, row, width);
   1256     RGB565ToARGBRow(src_rgb565 + src_stride_rgb565, row + kRowSize, width);
   1257     ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
   1258     ARGBToYRow(row, dst_y, width);
   1259     ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
   1260 #endif
   1261     src_rgb565 += src_stride_rgb565 * 2;
   1262     dst_y += dst_stride_y * 2;
   1263     dst_u += dst_stride_u;
   1264     dst_v += dst_stride_v;
   1265   }
   1266   if (height & 1) {
   1267 #if defined(HAS_RGB565TOYROW_NEON)
   1268     RGB565ToUVRow(src_rgb565, 0, dst_u, dst_v, width);
   1269     RGB565ToYRow(src_rgb565, dst_y, width);
   1270 #else
   1271     RGB565ToARGBRow(src_rgb565, row, width);
   1272     ARGBToUVRow(row, 0, dst_u, dst_v, width);
   1273     ARGBToYRow(row, dst_y, width);
   1274 #endif
   1275   }
   1276 #if !defined(HAS_RGB565TOYROW_NEON)
   1277   free_aligned_buffer_64(row);
   1278 #endif
   1279   return 0;
   1280 }
   1281 
   1282 // Convert ARGB1555 to I420.
   1283 LIBYUV_API
   1284 int ARGB1555ToI420(const uint8* src_argb1555, int src_stride_argb1555,
   1285                    uint8* dst_y, int dst_stride_y,
   1286                    uint8* dst_u, int dst_stride_u,
   1287                    uint8* dst_v, int dst_stride_v,
   1288                    int width, int height) {
   1289   int y;
   1290 #if defined(HAS_ARGB1555TOYROW_NEON)
   1291   void (*ARGB1555ToUVRow)(const uint8* src_argb1555, int src_stride_argb1555,
   1292       uint8* dst_u, uint8* dst_v, int width) = ARGB1555ToUVRow_C;
   1293   void (*ARGB1555ToYRow)(const uint8* src_argb1555, uint8* dst_y, int pix) =
   1294       ARGB1555ToYRow_C;
   1295 #else
   1296   void (*ARGB1555ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
   1297       ARGB1555ToARGBRow_C;
   1298   void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
   1299       uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
   1300   void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
   1301       ARGBToYRow_C;
   1302   // Allocate 2 rows of ARGB.
   1303   const int kRowSize = (width * 4 + 15) & ~15;
   1304   align_buffer_64(row, kRowSize * 2);
   1305 #endif
   1306   if (!src_argb1555 || !dst_y || !dst_u || !dst_v ||
   1307       width <= 0 || height == 0) {
   1308     return -1;
   1309   }
   1310   // Negative height means invert the image.
   1311   if (height < 0) {
   1312     height = -height;
   1313     src_argb1555 = src_argb1555 + (height - 1) * src_stride_argb1555;
   1314     src_stride_argb1555 = -src_stride_argb1555;
   1315   }
   1316 
   1317 #if defined(HAS_ARGB1555TOYROW_NEON)
   1318   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
   1319     ARGB1555ToYRow = ARGB1555ToYRow_Any_NEON;
   1320     if (IS_ALIGNED(width, 8)) {
   1321       ARGB1555ToYRow = ARGB1555ToYRow_NEON;
   1322     }
   1323     if (width >= 16) {
   1324       ARGB1555ToUVRow = ARGB1555ToUVRow_Any_NEON;
   1325       if (IS_ALIGNED(width, 16)) {
   1326         ARGB1555ToUVRow = ARGB1555ToUVRow_NEON;
   1327       }
   1328     }
   1329   }
   1330 #else  // HAS_ARGB1555TOYROW_NEON
   1331 
   1332 #if defined(HAS_ARGB1555TOARGBROW_SSE2)
   1333   if (TestCpuFlag(kCpuHasSSE2) && width >= 8) {
   1334     ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_SSE2;
   1335     if (IS_ALIGNED(width, 8)) {
   1336       ARGB1555ToARGBRow = ARGB1555ToARGBRow_SSE2;
   1337     }
   1338   }
   1339 #endif
   1340 #if defined(HAS_ARGBTOUVROW_SSSE3)
   1341   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1342     ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
   1343     if (IS_ALIGNED(width, 16)) {
   1344       ARGBToUVRow = ARGBToUVRow_SSSE3;
   1345     }
   1346   }
   1347 #endif
   1348 #if defined(HAS_ARGBTOUVROW_SSSE3)
   1349   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1350     ARGBToYRow = ARGBToYRow_Any_SSSE3;
   1351     if (IS_ALIGNED(width, 16)) {
   1352       ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
   1353       if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
   1354         ARGBToYRow = ARGBToYRow_SSSE3;
   1355       }
   1356     }
   1357   }
   1358 #endif  // HAS_ARGBTOUVROW_SSSE3
   1359 #endif  // HAS_ARGB1555TOYROW_NEON
   1360 
   1361   for (y = 0; y < height - 1; y += 2) {
   1362 #if defined(HAS_ARGB1555TOYROW_NEON)
   1363     ARGB1555ToUVRow(src_argb1555, src_stride_argb1555, dst_u, dst_v, width);
   1364     ARGB1555ToYRow(src_argb1555, dst_y, width);
   1365     ARGB1555ToYRow(src_argb1555 + src_stride_argb1555, dst_y + dst_stride_y,
   1366                    width);
   1367 #else
   1368     ARGB1555ToARGBRow(src_argb1555, row, width);
   1369     ARGB1555ToARGBRow(src_argb1555 + src_stride_argb1555, row + kRowSize,
   1370                       width);
   1371     ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
   1372     ARGBToYRow(row, dst_y, width);
   1373     ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
   1374 #endif
   1375     src_argb1555 += src_stride_argb1555 * 2;
   1376     dst_y += dst_stride_y * 2;
   1377     dst_u += dst_stride_u;
   1378     dst_v += dst_stride_v;
   1379   }
   1380   if (height & 1) {
   1381 #if defined(HAS_ARGB1555TOYROW_NEON)
   1382     ARGB1555ToUVRow(src_argb1555, 0, dst_u, dst_v, width);
   1383     ARGB1555ToYRow(src_argb1555, dst_y, width);
   1384 #else
   1385     ARGB1555ToARGBRow(src_argb1555, row, width);
   1386     ARGBToUVRow(row, 0, dst_u, dst_v, width);
   1387     ARGBToYRow(row, dst_y, width);
   1388 #endif
   1389   }
   1390 #if !defined(HAS_ARGB1555TOYROW_NEON)
   1391   free_aligned_buffer_64(row);
   1392 #endif
   1393   return 0;
   1394 }
   1395 
   1396 // Convert ARGB4444 to I420.
   1397 LIBYUV_API
   1398 int ARGB4444ToI420(const uint8* src_argb4444, int src_stride_argb4444,
   1399                    uint8* dst_y, int dst_stride_y,
   1400                    uint8* dst_u, int dst_stride_u,
   1401                    uint8* dst_v, int dst_stride_v,
   1402                    int width, int height) {
   1403   int y;
   1404 #if defined(HAS_ARGB4444TOYROW_NEON)
   1405   void (*ARGB4444ToUVRow)(const uint8* src_argb4444, int src_stride_argb4444,
   1406       uint8* dst_u, uint8* dst_v, int width) = ARGB4444ToUVRow_C;
   1407   void (*ARGB4444ToYRow)(const uint8* src_argb4444, uint8* dst_y, int pix) =
   1408       ARGB4444ToYRow_C;
   1409 #else
   1410   void (*ARGB4444ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
   1411       ARGB4444ToARGBRow_C;
   1412   void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
   1413       uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
   1414   void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
   1415       ARGBToYRow_C;
   1416   // Allocate 2 rows of ARGB.
   1417   const int kRowSize = (width * 4 + 15) & ~15;
   1418   align_buffer_64(row, kRowSize * 2);
   1419 #endif
   1420   if (!src_argb4444 || !dst_y || !dst_u || !dst_v ||
   1421       width <= 0 || height == 0) {
   1422     return -1;
   1423   }
   1424   // Negative height means invert the image.
   1425   if (height < 0) {
   1426     height = -height;
   1427     src_argb4444 = src_argb4444 + (height - 1) * src_stride_argb4444;
   1428     src_stride_argb4444 = -src_stride_argb4444;
   1429   }
   1430 
   1431 #if defined(HAS_ARGB4444TOYROW_NEON)
   1432   if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
   1433     ARGB4444ToYRow = ARGB4444ToYRow_Any_NEON;
   1434     if (IS_ALIGNED(width, 8)) {
   1435       ARGB4444ToYRow = ARGB4444ToYRow_NEON;
   1436     }
   1437     if (width >= 16) {
   1438       ARGB4444ToUVRow = ARGB4444ToUVRow_Any_NEON;
   1439       if (IS_ALIGNED(width, 16)) {
   1440         ARGB4444ToUVRow = ARGB4444ToUVRow_NEON;
   1441       }
   1442     }
   1443   }
   1444 #else  // HAS_ARGB4444TOYROW_NEON
   1445 
   1446 #if defined(HAS_ARGB4444TOARGBROW_SSE2)
   1447   if (TestCpuFlag(kCpuHasSSE2) && width >= 8) {
   1448     ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_SSE2;
   1449     if (IS_ALIGNED(width, 8)) {
   1450       ARGB4444ToARGBRow = ARGB4444ToARGBRow_SSE2;
   1451     }
   1452   }
   1453 #endif
   1454 #if defined(HAS_ARGBTOUVROW_SSSE3)
   1455   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1456     ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
   1457     if (IS_ALIGNED(width, 16)) {
   1458       ARGBToUVRow = ARGBToUVRow_SSSE3;
   1459     }
   1460   }
   1461 #endif
   1462 #if defined(HAS_ARGBTOUVROW_SSSE3)
   1463   if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
   1464     ARGBToYRow = ARGBToYRow_Any_SSSE3;
   1465     if (IS_ALIGNED(width, 16)) {
   1466       ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
   1467       if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
   1468         ARGBToYRow = ARGBToYRow_SSSE3;
   1469       }
   1470     }
   1471   }
   1472 #endif  // HAS_ARGBTOUVROW_SSSE3
   1473 #endif  // HAS_ARGB4444TOYROW_NEON
   1474 
   1475   for (y = 0; y < height - 1; y += 2) {
   1476 #if defined(HAS_ARGB4444TOYROW_NEON)
   1477     ARGB4444ToUVRow(src_argb4444, src_stride_argb4444, dst_u, dst_v, width);
   1478     ARGB4444ToYRow(src_argb4444, dst_y, width);
   1479     ARGB4444ToYRow(src_argb4444 + src_stride_argb4444, dst_y + dst_stride_y,
   1480                    width);
   1481 #else
   1482     ARGB4444ToARGBRow(src_argb4444, row, width);
   1483     ARGB4444ToARGBRow(src_argb4444 + src_stride_argb4444, row + kRowSize,
   1484                       width);
   1485     ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
   1486     ARGBToYRow(row, dst_y, width);
   1487     ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
   1488 #endif
   1489     src_argb4444 += src_stride_argb4444 * 2;
   1490     dst_y += dst_stride_y * 2;
   1491     dst_u += dst_stride_u;
   1492     dst_v += dst_stride_v;
   1493   }
   1494   if (height & 1) {
   1495 #if defined(HAS_ARGB4444TOYROW_NEON)
   1496     ARGB4444ToUVRow(src_argb4444, 0, dst_u, dst_v, width);
   1497     ARGB4444ToYRow(src_argb4444, dst_y, width);
   1498 #else
   1499     ARGB4444ToARGBRow(src_argb4444, row, width);
   1500     ARGBToUVRow(row, 0, dst_u, dst_v, width);
   1501     ARGBToYRow(row, dst_y, width);
   1502 #endif
   1503   }
   1504 #if !defined(HAS_ARGB4444TOYROW_NEON)
   1505   free_aligned_buffer_64(row);
   1506 #endif
   1507   return 0;
   1508 }
   1509 
   1510 #ifdef __cplusplus
   1511 }  // extern "C"
   1512 }  // namespace libyuv
   1513 #endif
   1514