tor-browser

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

ycbcr_to_rgb565.cpp (23332B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include <stdlib.h>
      7 #include <limits.h>
      8 #include "nsDebug.h"
      9 #include "ycbcr_to_rgb565.h"
     10 
     11 
     12 
     13 #ifdef HAVE_YCBCR_TO_RGB565
     14 
     15 namespace mozilla {
     16 
     17 namespace gfx {
     18 
     19 /*This contains all of the parameters that are needed to convert a row.
     20  Passing them in a struct instead of as individual parameters saves the need
     21   to continually push onto the stack the ones that are fixed for every row.*/
     22 struct yuv2rgb565_row_scale_bilinear_ctx{
     23  uint16_t *rgb_row;
     24  const uint8_t *y_row;
     25  const uint8_t *u_row;
     26  const uint8_t *v_row;
     27  int y_yweight;
     28  int y_pitch;
     29  int width;
     30  int source_x0_q16;
     31  int source_dx_q16;
     32  /*Not used for 4:4:4, except with chroma-nearest.*/
     33  int source_uv_xoffs_q16;
     34  /*Not used for 4:4:4 or chroma-nearest.*/
     35  int uv_pitch;
     36  /*Not used for 4:2:2, 4:4:4, or chroma-nearest.*/
     37  int uv_yweight;
     38 };
     39 
     40 
     41 
     42 /*This contains all of the parameters that are needed to convert a row.
     43  Passing them in a struct instead of as individual parameters saves the need
     44   to continually push onto the stack the ones that are fixed for every row.*/
     45 struct yuv2rgb565_row_scale_nearest_ctx{
     46  uint16_t *rgb_row;
     47  const uint8_t *y_row;
     48  const uint8_t *u_row;
     49  const uint8_t *v_row;
     50  int width;
     51  int source_x0_q16;
     52  int source_dx_q16;
     53  /*Not used for 4:4:4.*/
     54  int source_uv_xoffs_q16;
     55 };
     56 
     57 
     58 
     59 typedef void (*yuv2rgb565_row_scale_bilinear_func)(
     60 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither);
     61 
     62 typedef void (*yuv2rgb565_row_scale_nearest_func)(
     63 const yuv2rgb565_row_scale_nearest_ctx *ctx, int dither);
     64 
     65 
     66 
     67 //TODO: fix NEON asm for iOS
     68 # if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__)
     69 
     70 extern "C" void ScaleYCbCr42xToRGB565_BilinearY_Row_NEON(
     71 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither);
     72 
     73 void __attribute((noinline)) yuv42x_to_rgb565_row_neon(uint16_t *dst,
     74                                                       const uint8_t *y,
     75                                                       const uint8_t *u,
     76                                                       const uint8_t *v,
     77                                                       int n,
     78                                                       int oddflag);
     79 
     80 #endif
     81 
     82 
     83 
     84 /*Bilinear interpolation of a single value.
     85  This uses the exact same formulas as the asm, even though it adds some extra
     86   shifts that do nothing but reduce accuracy.*/
     87 static int bislerp(const uint8_t *row,
     88                   int pitch,
     89                   int source_x,
     90                   int xweight,
     91                   int yweight) {
     92  int a;
     93  int b;
     94  int c;
     95  int d;
     96  a = row[source_x];
     97  b = row[source_x+1];
     98  c = row[source_x+pitch];
     99  d = row[source_x+pitch+1];
    100  a = ((a<<8)+(c-a)*yweight+128)>>8;
    101  b = ((b<<8)+(d-b)*yweight+128)>>8;
    102  return ((a<<8)+(b-a)*xweight+128)>>8;
    103 }
    104 
    105 /*Convert a single pixel from Y'CbCr to RGB565.
    106  This uses the exact same formulas as the asm, even though we could make the
    107   constants a lot more accurate with 32-bit wide registers.*/
    108 static uint16_t yu2rgb565(int y, int u, int v, int dither) {
    109  /*This combines the constant offset that needs to be added during the Y'CbCr
    110     conversion with a rounding offset that depends on the dither parameter.*/
    111  static const int DITHER_BIAS[4][3]={
    112    {-14240,    8704,    -17696},
    113    {-14240+128,8704+64, -17696+128},
    114    {-14240+256,8704+128,-17696+256},
    115    {-14240+384,8704+192,-17696+384}
    116  };
    117  int r;
    118  int g;
    119  int b;
    120  r = std::clamp((74*y+102*v+DITHER_BIAS[dither][0])>>9, 0, 31);
    121  g = std::clamp((74*y-25*u-52*v+DITHER_BIAS[dither][1])>>8, 0, 63);
    122  b = std::clamp((74*y+129*u+DITHER_BIAS[dither][2])>>9, 0, 31);
    123  return (uint16_t)(r<<11 | g<<5 | b);
    124 }
    125 
    126 static void ScaleYCbCr420ToRGB565_Bilinear_Row_C(
    127 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither){
    128  int x;
    129  int source_x_q16;
    130  source_x_q16 = ctx->source_x0_q16;
    131  for (x = 0; x < ctx->width; x++) {
    132    int source_x;
    133    int xweight;
    134    int y;
    135    int u;
    136    int v;
    137    xweight = ((source_x_q16&0xFFFF)+128)>>8;
    138    source_x = source_x_q16>>16;
    139    y = bislerp(ctx->y_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
    140    xweight = (((source_x_q16+ctx->source_uv_xoffs_q16)&0x1FFFF)+256)>>9;
    141    source_x = (source_x_q16+ctx->source_uv_xoffs_q16)>>17;
    142    source_x_q16 += ctx->source_dx_q16;
    143    u = bislerp(ctx->u_row, ctx->uv_pitch, source_x, xweight, ctx->uv_yweight);
    144    v = bislerp(ctx->v_row, ctx->uv_pitch, source_x, xweight, ctx->uv_yweight);
    145    ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
    146    dither ^= 3;
    147  }
    148 }
    149 
    150 static void ScaleYCbCr422ToRGB565_Bilinear_Row_C(
    151 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither){
    152  int x;
    153  int source_x_q16;
    154  source_x_q16 = ctx->source_x0_q16;
    155  for (x = 0; x < ctx->width; x++) {
    156    int source_x;
    157    int xweight;
    158    int y;
    159    int u;
    160    int v;
    161    xweight = ((source_x_q16&0xFFFF)+128)>>8;
    162    source_x = source_x_q16>>16;
    163    y = bislerp(ctx->y_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
    164    xweight = (((source_x_q16+ctx->source_uv_xoffs_q16)&0x1FFFF)+256)>>9;
    165    source_x = (source_x_q16+ctx->source_uv_xoffs_q16)>>17;
    166    source_x_q16 += ctx->source_dx_q16;
    167    u = bislerp(ctx->u_row, ctx->uv_pitch, source_x, xweight, ctx->y_yweight);
    168    v = bislerp(ctx->v_row, ctx->uv_pitch, source_x, xweight, ctx->y_yweight);
    169    ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
    170    dither ^= 3;
    171  }
    172 }
    173 
    174 static void ScaleYCbCr444ToRGB565_Bilinear_Row_C(
    175 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither){
    176  int x;
    177  int source_x_q16;
    178  source_x_q16 = ctx->source_x0_q16;
    179  for (x = 0; x < ctx->width; x++) {
    180    int source_x;
    181    int xweight;
    182    int y;
    183    int u;
    184    int v;
    185    xweight = ((source_x_q16&0xFFFF)+128)>>8;
    186    source_x = source_x_q16>>16;
    187    source_x_q16 += ctx->source_dx_q16;
    188    y = bislerp(ctx->y_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
    189    u = bislerp(ctx->u_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
    190    v = bislerp(ctx->v_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
    191    ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
    192    dither ^= 3;
    193  }
    194 }
    195 
    196 static void ScaleYCbCr42xToRGB565_BilinearY_Row_C(
    197 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither){
    198  int x;
    199  int source_x_q16;
    200  source_x_q16 = ctx->source_x0_q16;
    201  for (x = 0; x < ctx->width; x++) {
    202    int source_x;
    203    int xweight;
    204    int y;
    205    int u;
    206    int v;
    207    xweight = ((source_x_q16&0xFFFF)+128)>>8;
    208    source_x = source_x_q16>>16;
    209    y = bislerp(ctx->y_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
    210    source_x = (source_x_q16+ctx->source_uv_xoffs_q16)>>17;
    211    source_x_q16 += ctx->source_dx_q16;
    212    u = ctx->u_row[source_x];
    213    v = ctx->v_row[source_x];
    214    ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
    215    dither ^= 3;
    216  }
    217 }
    218 
    219 static void ScaleYCbCr444ToRGB565_BilinearY_Row_C(
    220 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither){
    221  int x;
    222  int source_x_q16;
    223  source_x_q16 = ctx->source_x0_q16;
    224  for (x = 0; x < ctx->width; x++) {
    225    int source_x;
    226    int xweight;
    227    int y;
    228    int u;
    229    int v;
    230    xweight = ((source_x_q16&0xFFFF)+128)>>8;
    231    source_x = source_x_q16>>16;
    232    y = bislerp(ctx->y_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
    233    source_x = (source_x_q16+ctx->source_uv_xoffs_q16)>>16;
    234    source_x_q16 += ctx->source_dx_q16;
    235    u = ctx->u_row[source_x];
    236    v = ctx->v_row[source_x];
    237    ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
    238    dither ^= 3;
    239  }
    240 }
    241 
    242 static void ScaleYCbCr42xToRGB565_Nearest_Row_C(
    243 const yuv2rgb565_row_scale_nearest_ctx *ctx, int dither){
    244  int y;
    245  int u;
    246  int v;
    247  int x;
    248  int source_x_q16;
    249  int source_x;
    250  source_x_q16 = ctx->source_x0_q16;
    251  for (x = 0; x < ctx->width; x++) {
    252    source_x = source_x_q16>>16;
    253    y = ctx->y_row[source_x];
    254    source_x = (source_x_q16+ctx->source_uv_xoffs_q16)>>17;
    255    source_x_q16 += ctx->source_dx_q16;
    256    u = ctx->u_row[source_x];
    257    v = ctx->v_row[source_x];
    258    ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
    259    dither ^= 3;
    260  }
    261 }
    262 
    263 static void ScaleYCbCr444ToRGB565_Nearest_Row_C(
    264 const yuv2rgb565_row_scale_nearest_ctx *ctx, int dither){
    265  int y;
    266  int u;
    267  int v;
    268  int x;
    269  int source_x_q16;
    270  int source_x;
    271  source_x_q16 = ctx->source_x0_q16;
    272  for (x = 0; x < ctx->width; x++) {
    273    source_x = source_x_q16>>16;
    274    source_x_q16 += ctx->source_dx_q16;
    275    y = ctx->y_row[source_x];
    276    u = ctx->u_row[source_x];
    277    v = ctx->v_row[source_x];
    278    ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
    279    dither ^= 3;
    280  }
    281 }
    282 
    283 void ScaleYCbCrToRGB565(const uint8_t *y_buf,
    284                                 const uint8_t *u_buf,
    285                                 const uint8_t *v_buf,
    286                                 uint8_t *rgb_buf,
    287                                 int source_x0,
    288                                 int source_y0,
    289                                 int source_width,
    290                                 int source_height,
    291                                 int width,
    292                                 int height,
    293                                 int y_pitch,
    294                                 int uv_pitch,
    295                                 int rgb_pitch,
    296                                 YUVType yuv_type,
    297                                 ScaleFilter filter) {
    298  int source_x0_q16;
    299  int source_y0_q16;
    300  int source_dx_q16;
    301  int source_dy_q16;
    302  int source_uv_xoffs_q16;
    303  int source_uv_yoffs_q16;
    304  int x_shift;
    305  int y_shift;
    306  int ymin;
    307  int ymax;
    308  int uvmin;
    309  int uvmax;
    310  int dither;
    311  /*We don't support negative destination rectangles (just flip the source
    312     instead), and for empty ones there's nothing to do.*/
    313  if (width <= 0 || height <= 0)
    314    return;
    315  /*These bounds are required to avoid 16.16 fixed-point overflow.*/
    316  NS_ASSERTION(source_x0 > (INT_MIN>>16) && source_x0 < (INT_MAX>>16),
    317    "ScaleYCbCrToRGB565 source X offset out of bounds.");
    318  NS_ASSERTION(source_x0+source_width > (INT_MIN>>16)
    319            && source_x0+source_width < (INT_MAX>>16),
    320    "ScaleYCbCrToRGB565 source width out of bounds.");
    321  NS_ASSERTION(source_y0 > (INT_MIN>>16) && source_y0 < (INT_MAX>>16),
    322    "ScaleYCbCrToRGB565 source Y offset out of bounds.");
    323  NS_ASSERTION(source_y0+source_height > (INT_MIN>>16)
    324            && source_y0+source_height < (INT_MAX>>16),
    325    "ScaleYCbCrToRGB565 source height out of bounds.");
    326  /*We require the same stride for Y' and Cb and Cr for 4:4:4 content.*/
    327  NS_ASSERTION(yuv_type != YV24 || y_pitch == uv_pitch,
    328    "ScaleYCbCrToRGB565 luma stride differs from chroma for 4:4:4 content.");
    329  /*We assume we can read outside the bounds of the input, because it makes
    330     the code much simpler (and in practice is true: both Theora and VP8 return
    331     padded reference frames).
    332    In practice, we do not even _have_ the actual bounds of the source, as
    333     we are passed a crop rectangle from it, and not the dimensions of the full
    334     image.
    335    This assertion will not guarantee our out-of-bounds reads are safe, but it
    336     should at least catch the simple case of passing in an unpadded buffer.*/
    337  NS_ASSERTION(abs(y_pitch) >= abs(source_width)+16,
    338    "ScaleYCbCrToRGB565 source image unpadded?");
    339  /*The NEON code requires the pointers to be aligned to a 16-byte boundary at
    340     the start of each row.
    341    This should be true for all of our sources.
    342    We could try to fix this up if it's not true by adjusting source_x0, but
    343     that would require the mis-alignment to be the same for the U and V
    344     planes.*/
    345  NS_ASSERTION((y_pitch&15) == 0 && (uv_pitch&15) == 0 &&
    346   ((y_buf-(uint8_t *)nullptr)&15) == 0 &&
    347   ((u_buf-(uint8_t *)nullptr)&15) == 0 &&
    348   ((v_buf-(uint8_t *)nullptr)&15) == 0,
    349   "ScaleYCbCrToRGB565 source image unaligned");
    350  /*We take an area-based approach to pixel coverage to avoid shifting by small
    351     amounts (or not so small, when up-scaling or down-scaling by a large
    352     factor).
    353 
    354    An illustrative example: scaling 4:2:0 up by 2, using JPEG chroma cositing^.
    355 
    356    + = RGB destination locations
    357    * = Y' source locations
    358    - = Cb, Cr source locations
    359 
    360    +   +   +   +  +   +   +   +
    361      *       *      *       *
    362    +   +   +   +  +   +   +   +
    363          -              -
    364    +   +   +   +  +   +   +   +
    365      *       *      *       *
    366    +   +   +   +  +   +   +   +
    367 
    368    +   +   +   +  +   +   +   +
    369      *       *      *       *
    370    +   +   +   +  +   +   +   +
    371          -              -
    372    +   +   +   +  +   +   +   +
    373      *       *      *       *
    374    +   +   +   +  +   +   +   +
    375 
    376    So, the coordinates of the upper-left + (first destination site) should
    377     be (-0.25,-0.25) in the source Y' coordinate system.
    378    Similarly, the coordinates should be (-0.375,-0.375) in the source Cb, Cr
    379     coordinate system.
    380    Note that the origin and scale of these two coordinate systems is not the
    381     same!
    382 
    383    ^JPEG cositing is required for Theora; VP8 doesn't specify cositing rules,
    384     but nearly all software converters in existence (at least those that are
    385     open source, and many that are not) use JPEG cositing instead of MPEG.*/
    386  source_dx_q16 = (source_width<<16) / width;
    387  source_x0_q16 = (source_x0<<16)+(source_dx_q16>>1)-0x8000;
    388  source_dy_q16 = (source_height<<16) / height;
    389  source_y0_q16 = (source_y0<<16)+(source_dy_q16>>1)-0x8000;
    390  x_shift = (yuv_type != YV24);
    391  y_shift = (yuv_type == YV12);
    392  /*These two variables hold the difference between the origins of the Y' and
    393     the Cb, Cr coordinate systems, using the scale of the Y' coordinate
    394     system.*/
    395  source_uv_xoffs_q16 = -(x_shift<<15);
    396  source_uv_yoffs_q16 = -(y_shift<<15);
    397  /*Compute the range of source rows we'll actually use.
    398    This doesn't guarantee we won't read outside this range.*/
    399  ymin = source_height >= 0 ? source_y0 : source_y0+source_height-1;
    400  ymax = source_height >= 0 ? source_y0+source_height-1 : source_y0;
    401  uvmin = ymin>>y_shift;
    402  uvmax = ((ymax+1+y_shift)>>y_shift)-1;
    403  /*Pick a dithering pattern.
    404    The "&3" at the end is just in case RAND_MAX is lying.*/
    405  dither = (rand()/(RAND_MAX>>2))&3;
    406  /*Nearest-neighbor scaling.*/
    407  if (filter == FILTER_NONE) {
    408    yuv2rgb565_row_scale_nearest_ctx ctx;
    409    yuv2rgb565_row_scale_nearest_func scale_row;
    410    int y;
    411    /*Add rounding offsets once, in advance.*/
    412    source_x0_q16 += 0x8000;
    413    source_y0_q16 += 0x8000;
    414    source_uv_xoffs_q16 += (x_shift<<15);
    415    source_uv_yoffs_q16 += (y_shift<<15);
    416    if (yuv_type == YV12)
    417      scale_row = ScaleYCbCr42xToRGB565_Nearest_Row_C;
    418    else
    419      scale_row = ScaleYCbCr444ToRGB565_Nearest_Row_C;
    420    ctx.width = width;
    421    ctx.source_x0_q16 = source_x0_q16;
    422    ctx.source_dx_q16 = source_dx_q16;
    423    ctx.source_uv_xoffs_q16 = source_uv_xoffs_q16;
    424    for (y=0; y<height; y++) {
    425      int source_y;
    426      ctx.rgb_row = (uint16_t *)(rgb_buf + y*rgb_pitch);
    427      source_y = source_y0_q16>>16;
    428      source_y = std::clamp(source_y, ymin, ymax);
    429      ctx.y_row = y_buf + source_y*y_pitch;
    430      source_y = (source_y0_q16+source_uv_yoffs_q16)>>(16+y_shift);
    431      source_y = std::clamp(source_y, uvmin, uvmax);
    432      source_y0_q16 += source_dy_q16;
    433      ctx.u_row = u_buf + source_y*uv_pitch;
    434      ctx.v_row = v_buf + source_y*uv_pitch;
    435      (*scale_row)(&ctx, dither);
    436      dither ^= 2;
    437    }
    438  }
    439  /*Bilinear scaling.*/
    440  else {
    441    yuv2rgb565_row_scale_bilinear_ctx ctx;
    442    yuv2rgb565_row_scale_bilinear_func scale_row;
    443    int uvxscale_min;
    444    int uvxscale_max;
    445    int uvyscale_min;
    446    int uvyscale_max;
    447    int y;
    448    /*Check how close the chroma scaling is to unity.
    449      If it's close enough, we can get away with nearest-neighbor chroma
    450       sub-sampling, and only doing bilinear on luma.
    451      If a given axis is subsampled, we use bounds on the luma step of
    452       [0.67...2], which is equivalent to scaling chroma by [1...3].
    453      If it's not subsampled, we use bounds of [0.5...1.33], which is
    454       equivalent to scaling chroma by [0.75...2].
    455      The lower bound is chosen as a trade-off between speed and how terrible
    456       nearest neighbor looks when upscaling.*/
    457 # define CHROMA_NEAREST_SUBSAMP_STEP_MIN  0xAAAA
    458 # define CHROMA_NEAREST_NORMAL_STEP_MIN   0x8000
    459 # define CHROMA_NEAREST_SUBSAMP_STEP_MAX 0x20000
    460 # define CHROMA_NEAREST_NORMAL_STEP_MAX  0x15555
    461    uvxscale_min = yuv_type != YV24 ?
    462     CHROMA_NEAREST_SUBSAMP_STEP_MIN : CHROMA_NEAREST_NORMAL_STEP_MIN;
    463    uvxscale_max = yuv_type != YV24 ?
    464     CHROMA_NEAREST_SUBSAMP_STEP_MAX : CHROMA_NEAREST_NORMAL_STEP_MAX;
    465    uvyscale_min = yuv_type == YV12 ?
    466     CHROMA_NEAREST_SUBSAMP_STEP_MIN : CHROMA_NEAREST_NORMAL_STEP_MIN;
    467    uvyscale_max = yuv_type == YV12 ?
    468     CHROMA_NEAREST_SUBSAMP_STEP_MAX : CHROMA_NEAREST_NORMAL_STEP_MAX;
    469    if (uvxscale_min <= abs(source_dx_q16)
    470     && abs(source_dx_q16) <= uvxscale_max
    471     && uvyscale_min <= abs(source_dy_q16)
    472     && abs(source_dy_q16) <= uvyscale_max) {
    473      /*Add the rounding offsets now.*/
    474      source_uv_xoffs_q16 += 1<<(15+x_shift);
    475      source_uv_yoffs_q16 += 1<<(15+y_shift);
    476      if (yuv_type != YV24) {
    477        scale_row =
    478 //TODO: fix NEON asm for iOS
    479 #  if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__)
    480         supports_neon() ? ScaleYCbCr42xToRGB565_BilinearY_Row_NEON :
    481 #  endif
    482         ScaleYCbCr42xToRGB565_BilinearY_Row_C;
    483      }
    484      else
    485        scale_row = ScaleYCbCr444ToRGB565_BilinearY_Row_C;
    486    }
    487    else {
    488      if (yuv_type == YV12)
    489        scale_row = ScaleYCbCr420ToRGB565_Bilinear_Row_C;
    490      else if (yuv_type == YV16)
    491        scale_row = ScaleYCbCr422ToRGB565_Bilinear_Row_C;
    492      else
    493        scale_row = ScaleYCbCr444ToRGB565_Bilinear_Row_C;
    494    }
    495    ctx.width = width;
    496    ctx.y_pitch = y_pitch;
    497    ctx.source_x0_q16 = source_x0_q16;
    498    ctx.source_dx_q16 = source_dx_q16;
    499    ctx.source_uv_xoffs_q16 = source_uv_xoffs_q16;
    500    ctx.uv_pitch = uv_pitch;
    501    for (y=0; y<height; y++) {
    502      int source_y;
    503      int yweight;
    504      int uvweight;
    505      ctx.rgb_row = (uint16_t *)(rgb_buf + y*rgb_pitch);
    506      source_y = (source_y0_q16+128)>>16;
    507      yweight = ((source_y0_q16+128)>>8)&0xFF;
    508      if (source_y < ymin) {
    509        source_y = ymin;
    510        yweight = 0;
    511      }
    512      if (source_y > ymax) {
    513        source_y = ymax;
    514        yweight = 0;
    515      }
    516      ctx.y_row = y_buf + source_y*y_pitch;
    517      source_y = source_y0_q16+source_uv_yoffs_q16+(128<<y_shift);
    518      source_y0_q16 += source_dy_q16;
    519      uvweight = source_y>>(8+y_shift)&0xFF;
    520      source_y >>= 16+y_shift;
    521      if (source_y < uvmin) {
    522        source_y = uvmin;
    523        uvweight = 0;
    524      }
    525      if (source_y > uvmax) {
    526        source_y = uvmax;
    527        uvweight = 0;
    528      }
    529      ctx.u_row = u_buf + source_y*uv_pitch;
    530      ctx.v_row = v_buf + source_y*uv_pitch;
    531      ctx.y_yweight = yweight;
    532      ctx.uv_yweight = uvweight;
    533      (*scale_row)(&ctx, dither);
    534      dither ^= 2;
    535    }
    536  }
    537 }
    538 
    539 bool IsScaleYCbCrToRGB565Fast(int source_x0,
    540                                       int source_y0,
    541                                       int source_width,
    542                                       int source_height,
    543                                       int width,
    544                                       int height,
    545                                       YUVType yuv_type,
    546                                       ScaleFilter filter)
    547 {
    548  // Very fast.
    549  if (width <= 0 || height <= 0)
    550    return true;
    551 #  if defined(MOZILLA_MAY_SUPPORT_NEON)
    552  if (filter != FILTER_NONE) {
    553    int source_dx_q16;
    554    int source_dy_q16;
    555    int uvxscale_min;
    556    int uvxscale_max;
    557    int uvyscale_min;
    558    int uvyscale_max;
    559    source_dx_q16 = (source_width<<16) / width;
    560    source_dy_q16 = (source_height<<16) / height;
    561    uvxscale_min = yuv_type != YV24 ?
    562     CHROMA_NEAREST_SUBSAMP_STEP_MIN : CHROMA_NEAREST_NORMAL_STEP_MIN;
    563    uvxscale_max = yuv_type != YV24 ?
    564     CHROMA_NEAREST_SUBSAMP_STEP_MAX : CHROMA_NEAREST_NORMAL_STEP_MAX;
    565    uvyscale_min = yuv_type == YV12 ?
    566     CHROMA_NEAREST_SUBSAMP_STEP_MIN : CHROMA_NEAREST_NORMAL_STEP_MIN;
    567    uvyscale_max = yuv_type == YV12 ?
    568     CHROMA_NEAREST_SUBSAMP_STEP_MAX : CHROMA_NEAREST_NORMAL_STEP_MAX;
    569    if (uvxscale_min <= abs(source_dx_q16)
    570     && abs(source_dx_q16) <= uvxscale_max
    571     && uvyscale_min <= abs(source_dy_q16)
    572     && abs(source_dy_q16) <= uvyscale_max) {
    573      if (yuv_type != YV24)
    574        return supports_neon();
    575    }
    576  }
    577 #  endif
    578  return false;
    579 }
    580 
    581 
    582 
    583 void yuv_to_rgb565_row_c(uint16_t *dst,
    584                         const uint8_t *y,
    585                         const uint8_t *u,
    586                         const uint8_t *v,
    587                         int x_shift,
    588                         int pic_x,
    589                         int pic_width)
    590 {
    591  int x;
    592  for (x = 0; x < pic_width; x++)
    593  {
    594    dst[x] = yu2rgb565(y[pic_x+x],
    595                       u[(pic_x+x)>>x_shift],
    596                       v[(pic_x+x)>>x_shift],
    597                       2); // Disable dithering for now.
    598  }
    599 }
    600 
    601 void ConvertYCbCrToRGB565(const uint8_t* y_buf,
    602                                   const uint8_t* u_buf,
    603                                   const uint8_t* v_buf,
    604                                   uint8_t* rgb_buf,
    605                                   int pic_x,
    606                                   int pic_y,
    607                                   int pic_width,
    608                                   int pic_height,
    609                                   int y_pitch,
    610                                   int uv_pitch,
    611                                   int rgb_pitch,
    612                                   YUVType yuv_type)
    613 {
    614  int x_shift;
    615  int y_shift;
    616  x_shift = yuv_type != YV24;
    617  y_shift = yuv_type == YV12;
    618 //TODO: fix NEON asm for iOS
    619 #  if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__)
    620  if (yuv_type != YV24 && supports_neon())
    621  {
    622    for (int i = 0; i < pic_height; i++) {
    623      int yoffs;
    624      int uvoffs;
    625      yoffs = y_pitch * (pic_y+i) + pic_x;
    626      uvoffs = uv_pitch * ((pic_y+i)>>y_shift) + (pic_x>>x_shift);
    627      yuv42x_to_rgb565_row_neon((uint16_t*)(rgb_buf + rgb_pitch * i),
    628                                y_buf + yoffs,
    629                                u_buf + uvoffs,
    630                                v_buf + uvoffs,
    631                                pic_width,
    632                                pic_x&x_shift);
    633    }
    634  }
    635  else
    636 #  endif
    637  {
    638    for (int i = 0; i < pic_height; i++) {
    639      int yoffs;
    640      int uvoffs;
    641      yoffs = y_pitch * (pic_y+i);
    642      uvoffs = uv_pitch * ((pic_y+i)>>y_shift);
    643      yuv_to_rgb565_row_c((uint16_t*)(rgb_buf + rgb_pitch * i),
    644                          y_buf + yoffs,
    645                          u_buf + uvoffs,
    646                          v_buf + uvoffs,
    647                          x_shift,
    648                          pic_x,
    649                          pic_width);
    650    }
    651  }
    652 }
    653 
    654 bool IsConvertYCbCrToRGB565Fast(int pic_x,
    655                                         int pic_y,
    656                                         int pic_width,
    657                                         int pic_height,
    658                                         YUVType yuv_type)
    659 {
    660 #  if defined(MOZILLA_MAY_SUPPORT_NEON)
    661  return (yuv_type != YV24 && supports_neon());
    662 #  else
    663  return false;
    664 #  endif
    665 }
    666 
    667 } // namespace gfx
    668 
    669 } // namespace mozilla
    670 
    671 #endif // HAVE_YCBCR_TO_RGB565