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