tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

scale_yuv_argb.cpp (39231B)


      1 /*
      2 *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
      3 *  Copyright 2016 Mozilla Foundation
      4 *
      5 *  Use of this source code is governed by a BSD-style license
      6 *  that can be found in the LICENSE file in the root of the source
      7 *  tree. An additional intellectual property rights grant can be found
      8 *  in the file PATENTS. All contributing project authors may
      9 *  be found in the AUTHORS file in the root of the source tree.
     10 */
     11 
     12 #include "libyuv/scale.h"
     13 
     14 #include <assert.h>
     15 #include <string.h>
     16 
     17 #include "libyuv/convert_argb.h"
     18 #include "libyuv/cpu_id.h"
     19 #include "libyuv/row.h"
     20 #include "libyuv/scale_row.h"
     21 #include "libyuv/video_common.h"
     22 
     23 #include "mozilla/gfx/Types.h"
     24 
     25 #ifdef __cplusplus
     26 namespace libyuv {
     27 extern "C" {
     28 #endif
     29 
     30 // YUV to RGB conversion and scaling functions were implemented by referencing
     31 // scale_argb.cc
     32 //
     33 // libyuv already has ScaleYUVToARGBBilinearUp(), but its implementation is not
     34 // completed yet. Implementations of the functions are based on it.
     35 // At first, ScaleYUVToARGBBilinearUp() was implemented by modifying the
     36 // libyuv's one. Then all another functions were implemented similarly.
     37 //
     38 // Function relationship between yuv_convert.cpp and scale_argb.cc are like
     39 // the followings
     40 //  - ScaleYUVToARGBDown2()      <-- ScaleARGBDown2()
     41 //  - ScaleYUVToARGBDownEven()   <-- ScaleARGBDownEven()
     42 //  - ScaleYUVToARGBBilinearDown() <-- ScaleARGBBilinearDown()
     43 //  - ScaleYUVToARGBBilinearUp() <-- ScaleARGBBilinearUp() and ScaleYUVToARGBBilinearUp() in libyuv
     44 //  - ScaleYUVToARGBSimple()     <-- ScaleARGBSimple()
     45 //  - ScaleYUVToARGB()           <-- ScaleARGB() // Removed some function calls for simplicity.
     46 //  - YUVToARGBScale()           <-- ARGBScale()
     47 //
     48 // Callings and selections of InterpolateRow() and ScaleARGBFilterCols() were
     49 // kept as same as possible.
     50 //
     51 // The followings changes were done to each scaling functions.
     52 //
     53 // -[1] Allocate YUV conversion buffer and use it as source buffer of scaling.
     54 //      Its usage is borrowed from the libyuv's ScaleYUVToARGBBilinearUp().
     55 // -[2] Conversion from YUV to RGB was abstracted as YUVBuferIter.
     56 //      It is for handling multiple yuv color formats.
     57 // -[3] Modified scaling functions as to handle YUV conversion buffer and
     58 //      use YUVBuferIter.
     59 // -[4] Color conversion function selections in YUVBuferIter were borrowed from
     60 //      I444ToARGBMatrix(), I422ToARGBMatrix() and I420ToARGBMatrix()
     61 
     62 typedef mozilla::gfx::YUVColorSpace YUVColorSpace;
     63 
     64 struct YUVBuferIter {
     65  int src_width;
     66  int src_height;
     67  int src_stride_y;
     68  int src_stride_u;
     69  int src_stride_v;
     70  const uint8_t* src_y;
     71  const uint8_t* src_u;
     72  const uint8_t* src_v;
     73 
     74  uint32_t src_fourcc;
     75  const struct YuvConstants* yuvconstants;
     76  int y_index;
     77  const uint8_t* src_row_y;
     78  const uint8_t* src_row_u;
     79  const uint8_t* src_row_v;
     80 
     81  void (*YUVToARGBRow)(const uint8_t* y_buf,
     82                       const uint8_t* u_buf,
     83                       const uint8_t* v_buf,
     84                       uint8_t* rgb_buf,
     85                       const struct YuvConstants* yuvconstants,
     86                       int width);
     87  void (*MoveTo)(YUVBuferIter& iter, int y_index);
     88  void (*MoveToNextRow)(YUVBuferIter& iter);
     89 };
     90 
     91 void YUVBuferIter_InitI422(YUVBuferIter& iter) {
     92  iter.YUVToARGBRow = I422ToARGBRow_C;
     93 #if defined(HAS_I422TOARGBROW_SSSE3)
     94  if (TestCpuFlag(kCpuHasSSSE3)) {
     95    iter.YUVToARGBRow = I422ToARGBRow_Any_SSSE3;
     96    if (IS_ALIGNED(iter.src_width, 8)) {
     97      iter.YUVToARGBRow = I422ToARGBRow_SSSE3;
     98    }
     99  }
    100 #endif
    101 #if defined(HAS_I422TOARGBROW_AVX2)
    102  if (TestCpuFlag(kCpuHasAVX2)) {
    103    iter.YUVToARGBRow = I422ToARGBRow_Any_AVX2;
    104    if (IS_ALIGNED(iter.src_width, 16)) {
    105      iter.YUVToARGBRow = I422ToARGBRow_AVX2;
    106    }
    107  }
    108 #endif
    109 #if defined(HAS_I422TOARGBROW_NEON)
    110  if (TestCpuFlag(kCpuHasNEON)) {
    111    iter.YUVToARGBRow = I422ToARGBRow_Any_NEON;
    112    if (IS_ALIGNED(iter.src_width, 8)) {
    113      iter.YUVToARGBRow = I422ToARGBRow_NEON;
    114    }
    115  }
    116 #endif
    117 #if defined(HAS_I422TOARGBROW_DSPR2)
    118  if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(iter.src_width, 4) &&
    119      IS_ALIGNED(iter.src_y, 4) && IS_ALIGNED(iter.src_stride_y, 4) &&
    120      IS_ALIGNED(iter.src_u, 2) && IS_ALIGNED(iter.src_stride_u, 2) &&
    121      IS_ALIGNED(iter.src_v, 2) && IS_ALIGNED(iter.src_stride_v, 2) {
    122    // Always satisfy IS_ALIGNED(argb_cnv_row, 4) && IS_ALIGNED(argb_cnv_rowstride, 4)
    123    iter.YUVToARGBRow = I422ToARGBRow_DSPR2;
    124  }
    125 #endif
    126 }
    127 
    128 void YUVBuferIter_InitI444(YUVBuferIter& iter) {
    129  iter.YUVToARGBRow = I444ToARGBRow_C;
    130 #if defined(HAS_I444TOARGBROW_SSSE3)
    131  if (TestCpuFlag(kCpuHasSSSE3)) {
    132    iter.YUVToARGBRow = I444ToARGBRow_Any_SSSE3;
    133    if (IS_ALIGNED(iter.src_width, 8)) {
    134      iter.YUVToARGBRow = I444ToARGBRow_SSSE3;
    135    }
    136  }
    137 #endif
    138 #if defined(HAS_I444TOARGBROW_AVX2)
    139  if (TestCpuFlag(kCpuHasAVX2)) {
    140    iter.YUVToARGBRow = I444ToARGBRow_Any_AVX2;
    141    if (IS_ALIGNED(iter.src_width, 16)) {
    142      iter.YUVToARGBRow = I444ToARGBRow_AVX2;
    143    }
    144  }
    145 #endif
    146 #if defined(HAS_I444TOARGBROW_NEON)
    147  if (TestCpuFlag(kCpuHasNEON)) {
    148    iter.YUVToARGBRow = I444ToARGBRow_Any_NEON;
    149    if (IS_ALIGNED(iter.src_width, 8)) {
    150      iter.YUVToARGBRow = I444ToARGBRow_NEON;
    151    }
    152  }
    153 #endif
    154 }
    155 
    156 
    157 static void YUVBuferIter_MoveToForI444(YUVBuferIter& iter, int y_index) {
    158  iter.y_index = y_index;
    159  iter.src_row_y = iter.src_y + y_index * iter.src_stride_y;
    160  iter.src_row_u = iter.src_u + y_index * iter.src_stride_u;
    161  iter.src_row_v = iter.src_v + y_index * iter.src_stride_v;
    162 }
    163 
    164 static void YUVBuferIter_MoveToNextRowForI444(YUVBuferIter& iter) {
    165  iter.src_row_y += iter.src_stride_y;
    166  iter.src_row_u += iter.src_stride_u;
    167  iter.src_row_v += iter.src_stride_v;
    168  iter.y_index++;
    169 }
    170 
    171 static void YUVBuferIter_MoveToForI422(YUVBuferIter& iter, int y_index) {
    172  iter.y_index = y_index;
    173  iter.src_row_y = iter.src_y + y_index * iter.src_stride_y;
    174  iter.src_row_u = iter.src_u + y_index * iter.src_stride_u;
    175  iter.src_row_v = iter.src_v + y_index * iter.src_stride_v;
    176 }
    177 
    178 static void YUVBuferIter_MoveToNextRowForI422(YUVBuferIter& iter) {
    179  iter.src_row_y += iter.src_stride_y;
    180  iter.src_row_u += iter.src_stride_u;
    181  iter.src_row_v += iter.src_stride_v;
    182  iter.y_index++;
    183 }
    184 
    185 static void YUVBuferIter_MoveToForI420(YUVBuferIter& iter, int y_index) {
    186  const int kYShift = 1;  // Shift Y by 1 to convert Y plane to UV coordinate.
    187  int uv_y_index = y_index >> kYShift;
    188 
    189  iter.y_index = y_index;
    190  iter.src_row_y = iter.src_y + y_index * iter.src_stride_y;
    191  iter.src_row_u = iter.src_u + uv_y_index * iter.src_stride_u;
    192  iter.src_row_v = iter.src_v + uv_y_index * iter.src_stride_v;
    193 }
    194 
    195 static void YUVBuferIter_MoveToNextRowForI420(YUVBuferIter& iter) {
    196  iter.src_row_y += iter.src_stride_y;
    197  if (iter.y_index & 1) {
    198    iter.src_row_u += iter.src_stride_u;
    199    iter.src_row_v += iter.src_stride_v;
    200  }
    201  iter.y_index++;
    202 }
    203 
    204 static __inline void YUVBuferIter_ConvertToARGBRow(YUVBuferIter& iter, uint8_t* argb_row) {
    205  iter.YUVToARGBRow(iter.src_row_y, iter.src_row_u, iter.src_row_v, argb_row, iter.yuvconstants, iter.src_width);
    206 }
    207 
    208 void YUVBuferIter_Init(YUVBuferIter& iter, uint32_t src_fourcc, YUVColorSpace yuv_color_space) {
    209  iter.src_fourcc = src_fourcc;
    210  iter.y_index = 0;
    211  iter.src_row_y = iter.src_y;
    212  iter.src_row_u = iter.src_u;
    213  iter.src_row_v = iter.src_v;
    214  switch (yuv_color_space) {
    215    case YUVColorSpace::BT2020:
    216      iter.yuvconstants = &kYuv2020Constants;
    217      break;
    218    case YUVColorSpace::BT709:
    219      iter.yuvconstants = &kYuvH709Constants;
    220      break;
    221    default:
    222      iter.yuvconstants = &kYuvI601Constants;
    223  }
    224 
    225  if (src_fourcc == FOURCC_I444) {
    226    YUVBuferIter_InitI444(iter);
    227    iter.MoveTo = YUVBuferIter_MoveToForI444;
    228    iter.MoveToNextRow = YUVBuferIter_MoveToNextRowForI444;
    229  } else if(src_fourcc == FOURCC_I422){
    230    YUVBuferIter_InitI422(iter);
    231    iter.MoveTo = YUVBuferIter_MoveToForI422;
    232    iter.MoveToNextRow = YUVBuferIter_MoveToNextRowForI422;
    233  } else {
    234    assert(src_fourcc == FOURCC_I420); // Should be FOURCC_I420
    235    YUVBuferIter_InitI422(iter);
    236    iter.MoveTo = YUVBuferIter_MoveToForI420;
    237    iter.MoveToNextRow = YUVBuferIter_MoveToNextRowForI420;
    238  }
    239 }
    240 
    241 // ScaleARGB ARGB, 1/2
    242 // This is an optimized version for scaling down a ARGB to 1/2 of
    243 // its original size.
    244 static void ScaleYUVToARGBDown2(int src_width, int src_height,
    245                                int dst_width, int dst_height,
    246                                int src_stride_y,
    247                                int src_stride_u,
    248                                int src_stride_v,
    249                                int dst_stride_argb,
    250                                const uint8_t* src_y,
    251                                const uint8_t* src_u,
    252                                const uint8_t* src_v,
    253                                uint8_t* dst_argb,
    254                                int x, int dx, int y, int dy,
    255                                enum FilterMode filtering,
    256                                uint32_t src_fourcc,
    257                                YUVColorSpace yuv_color_space) {
    258  int j;
    259 
    260  // Allocate 2 rows of ARGB for source conversion.
    261  const int kRowSize = (src_width * 4 + 15) & ~15;
    262  align_buffer_64(argb_cnv_row, kRowSize * 2);
    263  uint8_t* argb_cnv_rowptr = argb_cnv_row;
    264  int argb_cnv_rowstride = kRowSize;
    265 
    266  YUVBuferIter iter;
    267  iter.src_width = src_width;
    268  iter.src_height = src_height;
    269  iter.src_stride_y = src_stride_y;
    270  iter.src_stride_u = src_stride_u;
    271  iter.src_stride_v = src_stride_v;
    272  iter.src_y = src_y;
    273  iter.src_u = src_u;
    274  iter.src_v = src_v;
    275  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
    276 
    277  void (*ScaleARGBRowDown2)(const uint8_t* src_argb, ptrdiff_t src_stride,
    278                            uint8_t* dst_argb, int dst_width) =
    279    filtering == kFilterNone ? ScaleARGBRowDown2_C :
    280        (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_C :
    281        ScaleARGBRowDown2Box_C);
    282  assert(dx == 65536 * 2);  // Test scale factor of 2.
    283  assert((dy & 0x1ffff) == 0);  // Test vertical scale is multiple of 2.
    284  // Advance to odd row, even column.
    285  int yi = y >> 16;
    286  iter.MoveTo(iter, yi);
    287  ptrdiff_t x_offset;
    288  if (filtering == kFilterBilinear) {
    289    x_offset = (x >> 16) * 4;
    290  } else {
    291    x_offset = ((x >> 16) - 1) * 4;
    292  }
    293 #if defined(HAS_SCALEARGBROWDOWN2_SSE2)
    294  if (TestCpuFlag(kCpuHasSSE2)) {
    295    ScaleARGBRowDown2 = filtering == kFilterNone ? ScaleARGBRowDown2_Any_SSE2 :
    296        (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_Any_SSE2 :
    297        ScaleARGBRowDown2Box_Any_SSE2);
    298    if (IS_ALIGNED(dst_width, 4)) {
    299      ScaleARGBRowDown2 = filtering == kFilterNone ? ScaleARGBRowDown2_SSE2 :
    300          (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_SSE2 :
    301          ScaleARGBRowDown2Box_SSE2);
    302    }
    303  }
    304 
    305 #endif
    306 #if defined(HAS_SCALEARGBROWDOWN2_NEON)
    307  if (TestCpuFlag(kCpuHasNEON)) {
    308    ScaleARGBRowDown2 = filtering == kFilterNone ? ScaleARGBRowDown2_Any_NEON :
    309        (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_Any_NEON :
    310        ScaleARGBRowDown2Box_Any_NEON);
    311    if (IS_ALIGNED(dst_width, 8)) {
    312      ScaleARGBRowDown2 = filtering == kFilterNone ? ScaleARGBRowDown2_NEON :
    313          (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_NEON :
    314          ScaleARGBRowDown2Box_NEON);
    315    }
    316  }
    317 #endif
    318 
    319  const int dyi = dy >> 16;
    320  int lastyi = yi;
    321  YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
    322  // Prepare next row if necessary
    323  if (filtering != kFilterLinear) {
    324    if ((yi + dyi) < (src_height - 1)) {
    325      iter.MoveTo(iter, yi + dyi);
    326      YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr + argb_cnv_rowstride);
    327    } else {
    328      argb_cnv_rowstride = 0;
    329    }
    330  }
    331 
    332  if (filtering == kFilterLinear) {
    333    argb_cnv_rowstride = 0;
    334  }
    335  const int max_yi = src_height - 1;
    336  const int max_yi_minus_dyi = max_yi - dyi;
    337  for (j = 0; j < dst_height; ++j) {
    338    if (yi != lastyi) {
    339      if (yi > max_yi) {
    340        yi = max_yi;
    341      }
    342      if (yi != lastyi) {
    343        if (filtering == kFilterLinear) {
    344          iter.MoveTo(iter, yi);
    345          YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
    346          lastyi = yi;
    347        } else {
    348          // Prepare current row
    349          if (yi == iter.y_index) {
    350            argb_cnv_rowptr = argb_cnv_rowptr + argb_cnv_rowstride;
    351            argb_cnv_rowstride = - argb_cnv_rowstride;
    352          } else {
    353            iter.MoveTo(iter, yi);
    354            argb_cnv_rowptr = argb_cnv_row;
    355            argb_cnv_rowstride = kRowSize;
    356            YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
    357          }
    358          // Prepare next row if necessary
    359          if (iter.y_index  < max_yi) {
    360            int next_yi = yi < max_yi_minus_dyi ? yi + dyi : max_yi;
    361            iter.MoveTo(iter, next_yi);
    362            YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr + argb_cnv_rowstride);
    363          } else {
    364            argb_cnv_rowstride = 0;
    365          }
    366          lastyi = yi;
    367        }
    368      }
    369    }
    370    ScaleARGBRowDown2(argb_cnv_rowptr + x_offset, argb_cnv_rowstride, dst_argb, dst_width);
    371    dst_argb += dst_stride_argb;
    372    yi += dyi;
    373  }
    374 
    375  free_aligned_buffer_64(argb_cnv_row);
    376 }
    377 
    378 // ScaleARGB ARGB Even
    379 // This is an optimized version for scaling down a ARGB to even
    380 // multiple of its original size.
    381 static void ScaleYUVToARGBDownEven(int src_width, int src_height,
    382                                   int dst_width, int dst_height,
    383                                   int src_stride_y,
    384                                   int src_stride_u,
    385                                   int src_stride_v,
    386                                   int dst_stride_argb,
    387                                   const uint8_t* src_y,
    388                                   const uint8_t* src_u,
    389                                   const uint8_t* src_v,
    390                                   uint8_t* dst_argb,
    391                                   int x, int dx, int y, int dy,
    392                                   enum FilterMode filtering,
    393                                   uint32_t src_fourcc,
    394                                   YUVColorSpace yuv_color_space) {
    395  int j;
    396  // Allocate 2 rows of ARGB for source conversion.
    397  const int kRowSize = (src_width * 4 + 15) & ~15;
    398  align_buffer_64(argb_cnv_row, kRowSize * 2);
    399  uint8_t* argb_cnv_rowptr = argb_cnv_row;
    400  int argb_cnv_rowstride = kRowSize;
    401 
    402  int col_step = dx >> 16;
    403  void (*ScaleARGBRowDownEven)(const uint8_t* src_argb, ptrdiff_t src_stride,
    404                               int src_step, uint8_t* dst_argb, int dst_width) =
    405      filtering ? ScaleARGBRowDownEvenBox_C : ScaleARGBRowDownEven_C;
    406  assert(IS_ALIGNED(src_width, 2));
    407  assert(IS_ALIGNED(src_height, 2));
    408  int yi = y >> 16;
    409  const ptrdiff_t x_offset = (x >> 16) * 4;
    410 
    411 #if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2)
    412  if (TestCpuFlag(kCpuHasSSE2)) {
    413    ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_Any_SSE2 :
    414        ScaleARGBRowDownEven_Any_SSE2;
    415    if (IS_ALIGNED(dst_width, 4)) {
    416      ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_SSE2 :
    417          ScaleARGBRowDownEven_SSE2;
    418    }
    419  }
    420 #endif
    421 #if defined(HAS_SCALEARGBROWDOWNEVEN_NEON)
    422  if (TestCpuFlag(kCpuHasNEON)) {
    423    ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_Any_NEON :
    424        ScaleARGBRowDownEven_Any_NEON;
    425    if (IS_ALIGNED(dst_width, 4)) {
    426      ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_NEON :
    427          ScaleARGBRowDownEven_NEON;
    428    }
    429  }
    430 #endif
    431 
    432  YUVBuferIter iter;
    433  iter.src_width = src_width;
    434  iter.src_height = src_height;
    435  iter.src_stride_y = src_stride_y;
    436  iter.src_stride_u = src_stride_u;
    437  iter.src_stride_v = src_stride_v;
    438  iter.src_y = src_y;
    439  iter.src_u = src_u;
    440  iter.src_v = src_v;
    441  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
    442 
    443  const int dyi = dy >> 16;
    444  int lastyi = yi;
    445  YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
    446  // Prepare next row if necessary
    447  if (filtering != kFilterLinear) {
    448    if ((yi + dyi) < (src_height - 1)) {
    449      iter.MoveTo(iter, yi + dyi);
    450      YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr + argb_cnv_rowstride);
    451    } else {
    452      argb_cnv_rowstride = 0;
    453    }
    454  }
    455 
    456  if (filtering == kFilterLinear) {
    457    argb_cnv_rowstride = 0;
    458  }
    459  const int max_yi = src_height - 1;
    460  const int max_yi_minus_dyi = max_yi - dyi;
    461  for (j = 0; j < dst_height; ++j) {
    462    if (yi != lastyi) {
    463      if (yi > max_yi) {
    464        yi = max_yi;
    465      }
    466      if (yi != lastyi) {
    467        if (filtering == kFilterLinear) {
    468          iter.MoveTo(iter, yi);
    469          YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
    470          lastyi = yi;
    471        } else {
    472          // Prepare current row
    473          if (yi == iter.y_index) {
    474            argb_cnv_rowptr = argb_cnv_rowptr + argb_cnv_rowstride;
    475            argb_cnv_rowstride = - argb_cnv_rowstride;
    476          } else {
    477            iter.MoveTo(iter, yi);
    478            argb_cnv_rowptr = argb_cnv_row;
    479            argb_cnv_rowstride = kRowSize;
    480            YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
    481          }
    482          // Prepare next row if necessary
    483          if (iter.y_index  < max_yi) {
    484            int next_yi = yi < max_yi_minus_dyi ? yi + dyi : max_yi;
    485            iter.MoveTo(iter, next_yi);
    486            YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr + argb_cnv_rowstride);
    487          } else {
    488            argb_cnv_rowstride = 0;
    489          }
    490          lastyi = yi;
    491        }
    492      }
    493    }
    494    ScaleARGBRowDownEven(argb_cnv_rowptr + x_offset, argb_cnv_rowstride, col_step, dst_argb, dst_width);
    495    dst_argb += dst_stride_argb;
    496    yi += dyi;
    497  }
    498  free_aligned_buffer_64(argb_cnv_row);
    499 }
    500 
    501 // Scale YUV to ARGB down with bilinear interpolation.
    502 static void ScaleYUVToARGBBilinearDown(int src_width, int src_height,
    503                                       int dst_width, int dst_height,
    504                                       int src_stride_y,
    505                                       int src_stride_u,
    506                                       int src_stride_v,
    507                                       int dst_stride_argb,
    508                                       const uint8_t* src_y,
    509                                       const uint8_t* src_u,
    510                                       const uint8_t* src_v,
    511                                       uint8_t* dst_argb,
    512                                       int x, int dx, int y, int dy,
    513                                       enum FilterMode filtering,
    514                                       uint32_t src_fourcc,
    515                                       YUVColorSpace yuv_color_space) {
    516  int j;
    517  void (*InterpolateRow)(uint8_t* dst_argb, const uint8_t* src_argb,
    518      ptrdiff_t src_stride, int dst_width, int source_y_fraction) =
    519      InterpolateRow_C;
    520  void (*ScaleARGBFilterCols)(uint8_t* dst_argb, const uint8_t* src_argb,
    521      int dst_width, int x, int dx) =
    522      (src_width >= 32768) ? ScaleARGBFilterCols64_C : ScaleARGBFilterCols_C;
    523  int64_t xlast = x + (int64_t)(dst_width - 1) * dx;
    524  int64_t xl = (dx >= 0) ? x : xlast;
    525  int64_t xr = (dx >= 0) ? xlast : x;
    526  int clip_src_width;
    527  xl = (xl >> 16) & ~3;  // Left edge aligned.
    528  xr = (xr >> 16) + 1;  // Right most pixel used.  Bilinear uses 2 pixels.
    529  xr = (xr + 1 + 3) & ~3;  // 1 beyond 4 pixel aligned right most pixel.
    530  if (xr > src_width) {
    531    xr = src_width;
    532  }
    533  clip_src_width = (int)(xr - xl) * 4;  // Width aligned to 4.
    534  const ptrdiff_t xl_offset = xl * 4;
    535  x -= (int)(xl << 16);
    536 
    537  // Allocate 2 row of ARGB for source conversion.
    538  const int kRowSize = (src_width * 4 + 15) & ~15;
    539  align_buffer_64(argb_cnv_row, kRowSize * 2);
    540  uint8_t* argb_cnv_rowptr = argb_cnv_row;
    541  int argb_cnv_rowstride = kRowSize;
    542 
    543 #if defined(HAS_INTERPOLATEROW_SSSE3)
    544  if (TestCpuFlag(kCpuHasSSSE3)) {
    545    InterpolateRow = InterpolateRow_Any_SSSE3;
    546    if (IS_ALIGNED(clip_src_width, 16)) {
    547      InterpolateRow = InterpolateRow_SSSE3;
    548    }
    549  }
    550 #endif
    551 #if defined(HAS_INTERPOLATEROW_AVX2)
    552  if (TestCpuFlag(kCpuHasAVX2)) {
    553    InterpolateRow = InterpolateRow_Any_AVX2;
    554    if (IS_ALIGNED(clip_src_width, 32)) {
    555      InterpolateRow = InterpolateRow_AVX2;
    556    }
    557  }
    558 #endif
    559 #if defined(HAS_INTERPOLATEROW_NEON)
    560  if (TestCpuFlag(kCpuHasNEON)) {
    561    InterpolateRow = InterpolateRow_Any_NEON;
    562    if (IS_ALIGNED(clip_src_width, 16)) {
    563      InterpolateRow = InterpolateRow_NEON;
    564    }
    565  }
    566 #endif
    567 #if defined(HAS_INTERPOLATEROW_DSPR2)
    568  if (TestCpuFlag(kCpuHasDSPR2) &&
    569      IS_ALIGNED(src_argb, 4) && IS_ALIGNED(argb_cnv_rowstride, 4)) {
    570    InterpolateRow = InterpolateRow_Any_DSPR2;
    571    if (IS_ALIGNED(clip_src_width, 4)) {
    572      InterpolateRow = InterpolateRow_DSPR2;
    573    }
    574  }
    575 #endif
    576 #if defined(HAS_SCALEARGBFILTERCOLS_SSSE3)
    577  if (TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
    578    ScaleARGBFilterCols = ScaleARGBFilterCols_SSSE3;
    579  }
    580 #endif
    581 #if defined(HAS_SCALEARGBFILTERCOLS_NEON)
    582  if (TestCpuFlag(kCpuHasNEON)) {
    583    ScaleARGBFilterCols = ScaleARGBFilterCols_Any_NEON;
    584    if (IS_ALIGNED(dst_width, 4)) {
    585      ScaleARGBFilterCols = ScaleARGBFilterCols_NEON;
    586    }
    587  }
    588 #endif
    589 
    590  int yi = y >> 16;
    591 
    592  YUVBuferIter iter;
    593  iter.src_width = src_width;
    594  iter.src_height = src_height;
    595  iter.src_stride_y = src_stride_y;
    596  iter.src_stride_u = src_stride_u;
    597  iter.src_stride_v = src_stride_v;
    598  iter.src_y = src_y;
    599  iter.src_u = src_u;
    600  iter.src_v = src_v;
    601  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
    602  iter.MoveTo(iter, yi);
    603 
    604  // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear.
    605  // Allocate a row of ARGB.
    606  align_buffer_64(row, clip_src_width * 4);
    607 
    608  int lastyi = yi;
    609  YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
    610  // Prepare next row if necessary
    611  if (filtering != kFilterLinear) {
    612    if ((yi + 1) < src_height) {
    613      iter.MoveToNextRow(iter);
    614      YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr + argb_cnv_rowstride);
    615    } else {
    616      argb_cnv_rowstride = 0;
    617    }
    618  }
    619 
    620  const int max_y = (src_height - 1) << 16;
    621  const int max_yi = src_height - 1;
    622  for (j = 0; j < dst_height; ++j) {
    623    yi = y >> 16;
    624    if (yi != lastyi) {
    625      if (y > max_y) {
    626        y = max_y;
    627        yi = y >> 16;
    628      }
    629      if (yi != lastyi) {
    630        if (filtering == kFilterLinear) {
    631          iter.MoveTo(iter, yi);
    632          YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
    633          lastyi = yi;
    634        } else {
    635          // Prepare current row
    636          if (yi == iter.y_index) {
    637            argb_cnv_rowptr = argb_cnv_rowptr + argb_cnv_rowstride;
    638            argb_cnv_rowstride = - argb_cnv_rowstride;
    639          } else {
    640            iter.MoveTo(iter, yi);
    641            argb_cnv_rowptr = argb_cnv_row;
    642            argb_cnv_rowstride = kRowSize;
    643            YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
    644          }
    645          // Prepare next row if necessary
    646          if (iter.y_index < max_yi) {
    647            iter.MoveToNextRow(iter);
    648            YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr + argb_cnv_rowstride);
    649          } else {
    650            argb_cnv_rowstride = 0;
    651          }
    652          lastyi = yi;
    653        }
    654      }
    655    }
    656    if (filtering == kFilterLinear) {
    657      ScaleARGBFilterCols(dst_argb, argb_cnv_rowptr + xl_offset, dst_width, x, dx);
    658    } else {
    659      int yf = (y >> 8) & 255;
    660      InterpolateRow(row, argb_cnv_rowptr + xl_offset, argb_cnv_rowstride, clip_src_width, yf);
    661      ScaleARGBFilterCols(dst_argb, row, dst_width, x, dx);
    662    }
    663    dst_argb += dst_stride_argb;
    664    y += dy;
    665  }
    666  free_aligned_buffer_64(row);
    667  free_aligned_buffer_64(argb_cnv_row);
    668 }
    669 
    670 // Scale YUV to ARGB up with bilinear interpolation.
    671 static void ScaleYUVToARGBBilinearUp(int src_width, int src_height,
    672                                     int dst_width, int dst_height,
    673                                     int src_stride_y,
    674                                     int src_stride_u,
    675                                     int src_stride_v,
    676                                     int dst_stride_argb,
    677                                     const uint8_t* src_y,
    678                                     const uint8_t* src_u,
    679                                     const uint8_t* src_v,
    680                                     uint8_t* dst_argb,
    681                                     int x, int dx, int y, int dy,
    682                                     enum FilterMode filtering,
    683                                     uint32_t src_fourcc,
    684                                     YUVColorSpace yuv_color_space) {
    685  int j;
    686  void (*InterpolateRow)(uint8_t* dst_argb, const uint8_t* src_argb,
    687      ptrdiff_t src_stride, int dst_width, int source_y_fraction) =
    688      InterpolateRow_C;
    689  void (*ScaleARGBFilterCols)(uint8_t* dst_argb, const uint8_t* src_argb,
    690      int dst_width, int x, int dx) =
    691      filtering ? ScaleARGBFilterCols_C : ScaleARGBCols_C;
    692  const int max_y = (src_height - 1) << 16;
    693 
    694  // Allocate 1 row of ARGB for source conversion.
    695  align_buffer_64(argb_cnv_row, src_width * 4);
    696 
    697 #if defined(HAS_INTERPOLATEROW_SSSE3)
    698  if (TestCpuFlag(kCpuHasSSSE3)) {
    699    InterpolateRow = InterpolateRow_Any_SSSE3;
    700    if (IS_ALIGNED(dst_width, 4)) {
    701      InterpolateRow = InterpolateRow_SSSE3;
    702    }
    703  }
    704 #endif
    705 #if defined(HAS_INTERPOLATEROW_AVX2)
    706  if (TestCpuFlag(kCpuHasAVX2)) {
    707    InterpolateRow = InterpolateRow_Any_AVX2;
    708    if (IS_ALIGNED(dst_width, 8)) {
    709      InterpolateRow = InterpolateRow_AVX2;
    710    }
    711  }
    712 #endif
    713 #if defined(HAS_INTERPOLATEROW_NEON)
    714  if (TestCpuFlag(kCpuHasNEON)) {
    715    InterpolateRow = InterpolateRow_Any_NEON;
    716    if (IS_ALIGNED(dst_width, 4)) {
    717      InterpolateRow = InterpolateRow_NEON;
    718    }
    719  }
    720 #endif
    721 #if defined(HAS_INTERPOLATEROW_DSPR2)
    722  if (TestCpuFlag(kCpuHasDSPR2) &&
    723      IS_ALIGNED(dst_argb, 4) && IS_ALIGNED(dst_stride_argb, 4)) {
    724    InterpolateRow = InterpolateRow_DSPR2;
    725  }
    726 #endif
    727  if (src_width >= 32768) {
    728    ScaleARGBFilterCols = filtering ?
    729        ScaleARGBFilterCols64_C : ScaleARGBCols64_C;
    730  }
    731 #if defined(HAS_SCALEARGBFILTERCOLS_SSSE3)
    732  if (filtering && TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
    733    ScaleARGBFilterCols = ScaleARGBFilterCols_SSSE3;
    734  }
    735 #endif
    736 #if defined(HAS_SCALEARGBFILTERCOLS_NEON)
    737  if (filtering && TestCpuFlag(kCpuHasNEON)) {
    738    ScaleARGBFilterCols = ScaleARGBFilterCols_Any_NEON;
    739    if (IS_ALIGNED(dst_width, 4)) {
    740      ScaleARGBFilterCols = ScaleARGBFilterCols_NEON;
    741    }
    742  }
    743 #endif
    744 #if defined(HAS_SCALEARGBCOLS_SSE2)
    745  if (!filtering && TestCpuFlag(kCpuHasSSE2) && src_width < 32768) {
    746    ScaleARGBFilterCols = ScaleARGBCols_SSE2;
    747  }
    748 #endif
    749 #if defined(HAS_SCALEARGBCOLS_NEON)
    750  if (!filtering && TestCpuFlag(kCpuHasNEON)) {
    751    ScaleARGBFilterCols = ScaleARGBCols_Any_NEON;
    752    if (IS_ALIGNED(dst_width, 8)) {
    753      ScaleARGBFilterCols = ScaleARGBCols_NEON;
    754    }
    755  }
    756 #endif
    757  if (!filtering && src_width * 2 == dst_width && x < 0x8000) {
    758    ScaleARGBFilterCols = ScaleARGBColsUp2_C;
    759 #if defined(HAS_SCALEARGBCOLSUP2_SSE2)
    760    if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
    761      ScaleARGBFilterCols = ScaleARGBColsUp2_SSE2;
    762    }
    763 #endif
    764  }
    765 
    766  if (y > max_y) {
    767    y = max_y;
    768  }
    769 
    770  int yi = y >> 16;
    771 
    772  YUVBuferIter iter;
    773  iter.src_width = src_width;
    774  iter.src_height = src_height;
    775  iter.src_stride_y = src_stride_y;
    776  iter.src_stride_u = src_stride_u;
    777  iter.src_stride_v = src_stride_v;
    778  iter.src_y = src_y;
    779  iter.src_u = src_u;
    780  iter.src_v = src_v;
    781  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
    782  iter.MoveTo(iter, yi);
    783 
    784  // Allocate 2 rows of ARGB.
    785  const int kRowSize = (dst_width * 4 + 15) & ~15;
    786  align_buffer_64(row, kRowSize * 2);
    787 
    788  uint8_t* rowptr = row;
    789  int rowstride = kRowSize;
    790  int lastyi = yi;
    791 
    792  YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_row);
    793  ScaleARGBFilterCols(rowptr, argb_cnv_row, dst_width, x, dx);
    794 
    795  if (filtering == kFilterLinear) {
    796    rowstride = 0;
    797  }
    798  // Prepare next row if necessary
    799  if (filtering != kFilterLinear) {
    800    if ((yi + 1) < src_height) {
    801      iter.MoveToNextRow(iter);
    802      YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_row);
    803      ScaleARGBFilterCols(rowptr + rowstride, argb_cnv_row, dst_width, x, dx);
    804    }else {
    805      rowstride = 0;
    806    }
    807  }
    808 
    809  const int max_yi = src_height - 1;
    810  for (j = 0; j < dst_height; ++j) {
    811    yi = y >> 16;
    812    if (yi != lastyi) {
    813      if (y > max_y) {
    814        y = max_y;
    815        yi = y >> 16;
    816      }
    817      if (yi != lastyi) {
    818        if (filtering == kFilterLinear) {
    819            iter.MoveToNextRow(iter);
    820            YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_row);
    821            ScaleARGBFilterCols(rowptr, argb_cnv_row, dst_width, x, dx);
    822        } else {
    823          // Prepare next row if necessary
    824          if (yi < max_yi) {
    825            iter.MoveToNextRow(iter);
    826            rowptr += rowstride;
    827            rowstride = -rowstride;
    828            // TODO(fbarchard): Convert the clipped region of row.
    829            YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_row);
    830            ScaleARGBFilterCols(rowptr + rowstride, argb_cnv_row, dst_width, x, dx);
    831          } else {
    832            rowstride = 0;
    833          }
    834        }
    835        lastyi = yi;
    836      }
    837    }
    838    if (filtering == kFilterLinear) {
    839      InterpolateRow(dst_argb, rowptr, 0, dst_width * 4, 0);
    840    } else {
    841      int yf = (y >> 8) & 255;
    842      InterpolateRow(dst_argb, rowptr, rowstride, dst_width * 4, yf);
    843    }
    844    dst_argb += dst_stride_argb;
    845    y += dy;
    846  }
    847  free_aligned_buffer_64(row);
    848  free_aligned_buffer_64(argb_cnv_row);
    849 }
    850 
    851 // Scale ARGB to/from any dimensions, without interpolation.
    852 // Fixed point math is used for performance: The upper 16 bits
    853 // of x and dx is the integer part of the source position and
    854 // the lower 16 bits are the fixed decimal part.
    855 
    856 static void ScaleYUVToARGBSimple(int src_width, int src_height,
    857                                 int dst_width, int dst_height,
    858                                 int src_stride_y,
    859                                 int src_stride_u,
    860                                 int src_stride_v,
    861                                 int dst_stride_argb,
    862                                 const uint8_t* src_y,
    863                                 const uint8_t* src_u,
    864                                 const uint8_t* src_v,
    865                                 uint8_t* dst_argb,
    866                                 int x, int dx, int y, int dy,
    867                                 uint32_t src_fourcc,
    868                                 YUVColorSpace yuv_color_space) {
    869  int j;
    870  void (*ScaleARGBCols)(uint8_t* dst_argb, const uint8_t* src_argb,
    871      int dst_width, int x, int dx) =
    872      (src_width >= 32768) ? ScaleARGBCols64_C : ScaleARGBCols_C;
    873 
    874  // Allocate 1 row of ARGB for source conversion.
    875  align_buffer_64(argb_cnv_row, src_width * 4);
    876 
    877 #if defined(HAS_SCALEARGBCOLS_SSE2)
    878  if (TestCpuFlag(kCpuHasSSE2) && src_width < 32768) {
    879    ScaleARGBCols = ScaleARGBCols_SSE2;
    880  }
    881 #endif
    882 #if defined(HAS_SCALEARGBCOLS_NEON)
    883  if (TestCpuFlag(kCpuHasNEON)) {
    884    ScaleARGBCols = ScaleARGBCols_Any_NEON;
    885    if (IS_ALIGNED(dst_width, 8)) {
    886      ScaleARGBCols = ScaleARGBCols_NEON;
    887    }
    888  }
    889 #endif
    890  if (src_width * 2 == dst_width && x < 0x8000) {
    891    ScaleARGBCols = ScaleARGBColsUp2_C;
    892 #if defined(HAS_SCALEARGBCOLSUP2_SSE2)
    893    if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
    894      ScaleARGBCols = ScaleARGBColsUp2_SSE2;
    895    }
    896 #endif
    897  }
    898 
    899  int yi = y >> 16;
    900 
    901  YUVBuferIter iter;
    902  iter.src_width = src_width;
    903  iter.src_height = src_height;
    904  iter.src_stride_y = src_stride_y;
    905  iter.src_stride_u = src_stride_u;
    906  iter.src_stride_v = src_stride_v;
    907  iter.src_y = src_y;
    908  iter.src_u = src_u;
    909  iter.src_v = src_v;
    910  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
    911  iter.MoveTo(iter, yi);
    912 
    913  int lasty = yi;
    914  YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_row);
    915 
    916  for (j = 0; j < dst_height; ++j) {
    917    yi = y >> 16;
    918    if (yi != lasty) {
    919      iter.MoveTo(iter, yi);
    920      YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_row);
    921      lasty = yi;
    922    }
    923    ScaleARGBCols(dst_argb, argb_cnv_row, dst_width, x, dx);
    924    dst_argb += dst_stride_argb;
    925    y += dy;
    926  }
    927  free_aligned_buffer_64(argb_cnv_row);
    928 }
    929 
    930 static void YUVToARGBCopy(const uint8_t* src_y, int src_stride_y,
    931                          const uint8_t* src_u, int src_stride_u,
    932                          const uint8_t* src_v, int src_stride_v,
    933                          int src_width, int src_height,
    934                          uint8_t* dst_argb, int dst_stride_argb,
    935                          int dst_width, int dst_height,
    936                          uint32_t src_fourcc,
    937                          YUVColorSpace yuv_color_space)
    938 {
    939  YUVBuferIter iter;
    940  iter.src_width = src_width;
    941  iter.src_height = src_height;
    942  iter.src_stride_y = src_stride_y;
    943  iter.src_stride_u = src_stride_u;
    944  iter.src_stride_v = src_stride_v;
    945  iter.src_y = src_y;
    946  iter.src_u = src_u;
    947  iter.src_v = src_v;
    948  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
    949 
    950  for (int j = 0; j < dst_height; ++j) {
    951    YUVBuferIter_ConvertToARGBRow(iter, dst_argb);
    952    iter.MoveToNextRow(iter);
    953    dst_argb += dst_stride_argb;
    954  }
    955 }
    956 
    957 static void ScaleYUVToARGB(const uint8_t* src_y, int src_stride_y,
    958                           const uint8_t* src_u, int src_stride_u,
    959                           const uint8_t* src_v, int src_stride_v,
    960                           int src_width, int src_height,
    961                           uint8_t* dst_argb, int dst_stride_argb,
    962                           int dst_width, int dst_height,
    963                           enum FilterMode filtering,
    964                           uint32_t src_fourcc,
    965                           YUVColorSpace yuv_color_space)
    966 {
    967  // Initial source x/y coordinate and step values as 16.16 fixed point.
    968  int x = 0;
    969  int y = 0;
    970  int dx = 0;
    971  int dy = 0;
    972  // ARGB does not support box filter yet, but allow the user to pass it.
    973  // Simplify filtering when possible.
    974  filtering = ScaleFilterReduce(src_width, src_height,
    975                                dst_width, dst_height,
    976                                filtering);
    977  ScaleSlope(src_width, src_height, dst_width, dst_height, filtering,
    978             &x, &y, &dx, &dy);
    979 
    980  // Special case for integer step values.
    981  if (((dx | dy) & 0xffff) == 0) {
    982    if (!dx || !dy) {  // 1 pixel wide and/or tall.
    983      filtering = kFilterNone;
    984    } else {
    985      // Optimized even scale down. ie 2, 4, 6, 8, 10x.
    986      if (!(dx & 0x10000) && !(dy & 0x10000)) {
    987        if (dx == 0x20000) {
    988          // Optimized 1/2 downsample.
    989          ScaleYUVToARGBDown2(src_width, src_height,
    990                              dst_width, dst_height,
    991                              src_stride_y,
    992                              src_stride_u,
    993                              src_stride_v,
    994                              dst_stride_argb,
    995                              src_y,
    996                              src_u,
    997                              src_v,
    998                              dst_argb,
    999                              x, dx, y, dy,
   1000                              filtering,
   1001                              src_fourcc,
   1002                              yuv_color_space);
   1003          return;
   1004        }
   1005        ScaleYUVToARGBDownEven(src_width, src_height,
   1006                               dst_width, dst_height,
   1007                               src_stride_y,
   1008                               src_stride_u,
   1009                               src_stride_v,
   1010                               dst_stride_argb,
   1011                               src_y,
   1012                               src_u,
   1013                               src_v,
   1014                               dst_argb,
   1015                               x, dx, y, dy,
   1016                               filtering,
   1017                               src_fourcc,
   1018                               yuv_color_space);
   1019        return;
   1020      }
   1021      // Optimized odd scale down. ie 3, 5, 7, 9x.
   1022      if ((dx & 0x10000) && (dy & 0x10000)) {
   1023        filtering = kFilterNone;
   1024        if (dx == 0x10000 && dy == 0x10000) {
   1025          // Straight conversion and copy.
   1026          YUVToARGBCopy(src_y, src_stride_y,
   1027                        src_u, src_stride_u,
   1028                        src_v, src_stride_v,
   1029                        src_width, src_height,
   1030                        dst_argb, dst_stride_argb,
   1031                        dst_width, dst_height,
   1032                        src_fourcc,
   1033                        yuv_color_space);
   1034          return;
   1035        }
   1036      }
   1037    }
   1038  }
   1039  if (filtering && dy < 65536) {
   1040    ScaleYUVToARGBBilinearUp(src_width, src_height,
   1041                             dst_width, dst_height,
   1042                             src_stride_y,
   1043                             src_stride_u,
   1044                             src_stride_v,
   1045                             dst_stride_argb,
   1046                             src_y,
   1047                             src_u,
   1048                             src_v,
   1049                             dst_argb,
   1050                             x, dx, y, dy,
   1051                             filtering,
   1052                             src_fourcc,
   1053                             yuv_color_space);
   1054    return;
   1055  }
   1056  if (filtering) {
   1057    ScaleYUVToARGBBilinearDown(src_width, src_height,
   1058                               dst_width, dst_height,
   1059                               src_stride_y,
   1060                               src_stride_u,
   1061                               src_stride_v,
   1062                               dst_stride_argb,
   1063                               src_y,
   1064                               src_u,
   1065                               src_v,
   1066                               dst_argb,
   1067                               x, dx, y, dy,
   1068                               filtering,
   1069                               src_fourcc,
   1070                               yuv_color_space);
   1071    return;
   1072  }
   1073  ScaleYUVToARGBSimple(src_width, src_height,
   1074                       dst_width, dst_height,
   1075                       src_stride_y,
   1076                       src_stride_u,
   1077                       src_stride_v,
   1078                       dst_stride_argb,
   1079                       src_y,
   1080                       src_u,
   1081                       src_v,
   1082                       dst_argb,
   1083                       x, dx, y, dy,
   1084                       src_fourcc,
   1085                       yuv_color_space);
   1086 }
   1087 
   1088 bool IsConvertSupported(uint32_t src_fourcc)
   1089 {
   1090  if (src_fourcc == FOURCC_I444 ||
   1091      src_fourcc == FOURCC_I422 ||
   1092      src_fourcc == FOURCC_I420) {
   1093    return true;
   1094  }
   1095  return false;
   1096 }
   1097 
   1098 LIBYUV_API
   1099 int YUVToARGBScale(const uint8_t* src_y, int src_stride_y,
   1100                   const uint8_t* src_u, int src_stride_u,
   1101                   const uint8_t* src_v, int src_stride_v,
   1102                   uint32_t src_fourcc,
   1103                   YUVColorSpace yuv_color_space,
   1104                   int src_width, int src_height,
   1105                   uint8_t* dst_argb, int dst_stride_argb,
   1106                   int dst_width, int dst_height,
   1107                   enum FilterMode filtering)
   1108 {
   1109  if (!src_y || !src_u || !src_v ||
   1110      src_width == 0 || src_height == 0 ||
   1111      !dst_argb || dst_width <= 0 || dst_height <= 0) {
   1112    return -1;
   1113  }
   1114  if (!IsConvertSupported(src_fourcc)) {
   1115    return -1;
   1116  }
   1117  ScaleYUVToARGB(src_y, src_stride_y,
   1118                 src_u, src_stride_u,
   1119                 src_v, src_stride_v,
   1120                 src_width, src_height,
   1121                 dst_argb, dst_stride_argb,
   1122                 dst_width, dst_height,
   1123                 filtering,
   1124                 src_fourcc,
   1125                 yuv_color_space);
   1126  return 0;
   1127 }
   1128 
   1129 #ifdef __cplusplus
   1130 }  // extern "C"
   1131 }  // namespace libyuv
   1132 #endif