go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

sharpyuv.c (21787B)


      1 // Copyright 2022 Google Inc. All Rights Reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style license
      4 // that can be found in the COPYING file in the root of the source
      5 // tree. An additional intellectual property rights grant can be found
      6 // in the file PATENTS. All contributing project authors may
      7 // be found in the AUTHORS file in the root of the source tree.
      8 // -----------------------------------------------------------------------------
      9 //
     10 // Sharp RGB to YUV conversion.
     11 //
     12 // Author: Skal (pascal.massimino@gmail.com)
     13 
     14 #include "sharpyuv/sharpyuv.h"
     15 
     16 #include <assert.h>
     17 #include <limits.h>
     18 #include <stddef.h>
     19 #include <stdlib.h>
     20 #include <string.h>
     21 
     22 #include "sharpyuv/sharpyuv_cpu.h"
     23 #include "sharpyuv/sharpyuv_dsp.h"
     24 #include "sharpyuv/sharpyuv_gamma.h"
     25 #include "src/webp/types.h"
     26 
     27 //------------------------------------------------------------------------------
     28 
     29 int SharpYuvGetVersion(void) {
     30   return SHARPYUV_VERSION;
     31 }
     32 
     33 //------------------------------------------------------------------------------
     34 // Sharp RGB->YUV conversion
     35 
     36 static const int kNumIterations = 4;
     37 
     38 #define YUV_FIX 16  // fixed-point precision for RGB->YUV
     39 static const int kYuvHalf = 1 << (YUV_FIX - 1);
     40 
     41 // Max bit depth so that intermediate calculations fit in 16 bits.
     42 static const int kMaxBitDepth = 14;
     43 
     44 // Returns the precision shift to use based on the input rgb_bit_depth.
     45 static int GetPrecisionShift(int rgb_bit_depth) {
     46   // Try to add 2 bits of precision if it fits in kMaxBitDepth. Otherwise remove
     47   // bits if needed.
     48   return ((rgb_bit_depth + 2) <= kMaxBitDepth) ? 2
     49                                                : (kMaxBitDepth - rgb_bit_depth);
     50 }
     51 
     52 typedef int16_t fixed_t;      // signed type with extra precision for UV
     53 typedef uint16_t fixed_y_t;   // unsigned type with extra precision for W
     54 
     55 //------------------------------------------------------------------------------
     56 
     57 static uint8_t clip_8b(fixed_t v) {
     58   return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u;
     59 }
     60 
     61 static uint16_t clip(fixed_t v, int max) {
     62   return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
     63 }
     64 
     65 static fixed_y_t clip_bit_depth(int y, int bit_depth) {
     66   const int max = (1 << bit_depth) - 1;
     67   return (!(y & ~max)) ? (fixed_y_t)y : (y < 0) ? 0 : max;
     68 }
     69 
     70 //------------------------------------------------------------------------------
     71 
     72 static int RGBToGray(int64_t r, int64_t g, int64_t b) {
     73   const int64_t luma = 13933 * r + 46871 * g + 4732 * b + kYuvHalf;
     74   return (int)(luma >> YUV_FIX);
     75 }
     76 
     77 static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
     78                           int rgb_bit_depth,
     79                           SharpYuvTransferFunctionType transfer_type) {
     80   const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
     81   const uint32_t A = SharpYuvGammaToLinear(a, bit_depth, transfer_type);
     82   const uint32_t B = SharpYuvGammaToLinear(b, bit_depth, transfer_type);
     83   const uint32_t C = SharpYuvGammaToLinear(c, bit_depth, transfer_type);
     84   const uint32_t D = SharpYuvGammaToLinear(d, bit_depth, transfer_type);
     85   return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth,
     86                                transfer_type);
     87 }
     88 
     89 static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w,
     90                                 int rgb_bit_depth,
     91                                 SharpYuvTransferFunctionType transfer_type) {
     92   const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
     93   int i = 0;
     94   do {
     95     const uint32_t R =
     96         SharpYuvGammaToLinear(src[0 * w + i], bit_depth, transfer_type);
     97     const uint32_t G =
     98         SharpYuvGammaToLinear(src[1 * w + i], bit_depth, transfer_type);
     99     const uint32_t B =
    100         SharpYuvGammaToLinear(src[2 * w + i], bit_depth, transfer_type);
    101     const uint32_t Y = RGBToGray(R, G, B);
    102     dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth, transfer_type);
    103   } while (++i < w);
    104 }
    105 
    106 static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
    107                          fixed_t* dst, int uv_w, int rgb_bit_depth,
    108                          SharpYuvTransferFunctionType transfer_type) {
    109   int i = 0;
    110   do {
    111     const int r =
    112         ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1], src2[0 * uv_w + 0],
    113                   src2[0 * uv_w + 1], rgb_bit_depth, transfer_type);
    114     const int g =
    115         ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], src2[2 * uv_w + 0],
    116                   src2[2 * uv_w + 1], rgb_bit_depth, transfer_type);
    117     const int b =
    118         ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], src2[4 * uv_w + 0],
    119                   src2[4 * uv_w + 1], rgb_bit_depth, transfer_type);
    120     const int W = RGBToGray(r, g, b);
    121     dst[0 * uv_w] = (fixed_t)(r - W);
    122     dst[1 * uv_w] = (fixed_t)(g - W);
    123     dst[2 * uv_w] = (fixed_t)(b - W);
    124     dst  += 1;
    125     src1 += 2;
    126     src2 += 2;
    127   } while (++i < uv_w);
    128 }
    129 
    130 static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) {
    131   int i = 0;
    132   assert(w > 0);
    133   do {
    134     y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
    135   } while (++i < w);
    136 }
    137 
    138 //------------------------------------------------------------------------------
    139 
    140 static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0, int bit_depth) {
    141   const int v0 = (A * 3 + B + 2) >> 2;
    142   return clip_bit_depth(v0 + W0, bit_depth);
    143 }
    144 
    145 //------------------------------------------------------------------------------
    146 
    147 static WEBP_INLINE int Shift(int v, int shift) {
    148   return (shift >= 0) ? (v << shift) : (v >> -shift);
    149 }
    150 
    151 static void ImportOneRow(const uint8_t* const r_ptr,
    152                          const uint8_t* const g_ptr,
    153                          const uint8_t* const b_ptr,
    154                          int rgb_step,
    155                          int rgb_bit_depth,
    156                          int pic_width,
    157                          fixed_y_t* const dst) {
    158   // Convert the rgb_step from a number of bytes to a number of uint8_t or
    159   // uint16_t values depending the bit depth.
    160   const int step = (rgb_bit_depth > 8) ? rgb_step / 2 : rgb_step;
    161   int i = 0;
    162   const int w = (pic_width + 1) & ~1;
    163   do {
    164     const int off = i * step;
    165     const int shift = GetPrecisionShift(rgb_bit_depth);
    166     if (rgb_bit_depth == 8) {
    167       dst[i + 0 * w] = Shift(r_ptr[off], shift);
    168       dst[i + 1 * w] = Shift(g_ptr[off], shift);
    169       dst[i + 2 * w] = Shift(b_ptr[off], shift);
    170     } else {
    171       dst[i + 0 * w] = Shift(((uint16_t*)r_ptr)[off], shift);
    172       dst[i + 1 * w] = Shift(((uint16_t*)g_ptr)[off], shift);
    173       dst[i + 2 * w] = Shift(((uint16_t*)b_ptr)[off], shift);
    174     }
    175   } while (++i < pic_width);
    176   if (pic_width & 1) {  // replicate rightmost pixel
    177     dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1];
    178     dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1];
    179     dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1];
    180   }
    181 }
    182 
    183 static void InterpolateTwoRows(const fixed_y_t* const best_y,
    184                                const fixed_t* prev_uv,
    185                                const fixed_t* cur_uv,
    186                                const fixed_t* next_uv,
    187                                int w,
    188                                fixed_y_t* out1,
    189                                fixed_y_t* out2,
    190                                int rgb_bit_depth) {
    191   const int uv_w = w >> 1;
    192   const int len = (w - 1) >> 1;   // length to filter
    193   int k = 3;
    194   const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
    195   while (k-- > 0) {   // process each R/G/B segments in turn
    196     // special boundary case for i==0
    197     out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0], bit_depth);
    198     out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w], bit_depth);
    199 
    200     SharpYuvFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1,
    201                       bit_depth);
    202     SharpYuvFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1,
    203                       bit_depth);
    204 
    205     // special boundary case for i == w - 1 when w is even
    206     if (!(w & 1)) {
    207       out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1],
    208                             best_y[w - 1 + 0], bit_depth);
    209       out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1],
    210                             best_y[w - 1 + w], bit_depth);
    211     }
    212     out1 += w;
    213     out2 += w;
    214     prev_uv += uv_w;
    215     cur_uv  += uv_w;
    216     next_uv += uv_w;
    217   }
    218 }
    219 
    220 static WEBP_INLINE int RGBToYUVComponent(int r, int g, int b,
    221                                          const int coeffs[4], int sfix) {
    222   const int srounder = 1 << (YUV_FIX + sfix - 1);
    223   const int luma = coeffs[0] * r + coeffs[1] * g + coeffs[2] * b +
    224                    coeffs[3] + srounder;
    225   return (luma >> (YUV_FIX + sfix));
    226 }
    227 
    228 static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
    229                             uint8_t* y_ptr, int y_stride, uint8_t* u_ptr,
    230                             int u_stride, uint8_t* v_ptr, int v_stride,
    231                             int rgb_bit_depth,
    232                             int yuv_bit_depth, int width, int height,
    233                             const SharpYuvConversionMatrix* yuv_matrix) {
    234   int i, j;
    235   const fixed_t* const best_uv_base = best_uv;
    236   const int w = (width + 1) & ~1;
    237   const int h = (height + 1) & ~1;
    238   const int uv_w = w >> 1;
    239   const int uv_h = h >> 1;
    240   const int sfix = GetPrecisionShift(rgb_bit_depth);
    241   const int yuv_max = (1 << yuv_bit_depth) - 1;
    242 
    243   best_uv = best_uv_base;
    244   j = 0;
    245   do {
    246     i = 0;
    247     do {
    248       const int off = (i >> 1);
    249       const int W = best_y[i];
    250       const int r = best_uv[off + 0 * uv_w] + W;
    251       const int g = best_uv[off + 1 * uv_w] + W;
    252       const int b = best_uv[off + 2 * uv_w] + W;
    253       const int y = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_y, sfix);
    254       if (yuv_bit_depth <= 8) {
    255         y_ptr[i] = clip_8b(y);
    256       } else {
    257         ((uint16_t*)y_ptr)[i] = clip(y, yuv_max);
    258       }
    259     } while (++i < width);
    260     best_y += w;
    261     best_uv += (j & 1) * 3 * uv_w;
    262     y_ptr += y_stride;
    263   } while (++j < height);
    264 
    265   best_uv = best_uv_base;
    266   j = 0;
    267   do {
    268     i = 0;
    269     do {
    270       // Note r, g and b values here are off by W, but a constant offset on all
    271       // 3 components doesn't change the value of u and v with a YCbCr matrix.
    272       const int r = best_uv[i + 0 * uv_w];
    273       const int g = best_uv[i + 1 * uv_w];
    274       const int b = best_uv[i + 2 * uv_w];
    275       const int u = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_u, sfix);
    276       const int v = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_v, sfix);
    277       if (yuv_bit_depth <= 8) {
    278         u_ptr[i] = clip_8b(u);
    279         v_ptr[i] = clip_8b(v);
    280       } else {
    281         ((uint16_t*)u_ptr)[i] = clip(u, yuv_max);
    282         ((uint16_t*)v_ptr)[i] = clip(v, yuv_max);
    283       }
    284     } while (++i < uv_w);
    285     best_uv += 3 * uv_w;
    286     u_ptr += u_stride;
    287     v_ptr += v_stride;
    288   } while (++j < uv_h);
    289   return 1;
    290 }
    291 
    292 //------------------------------------------------------------------------------
    293 // Main function
    294 
    295 static void* SafeMalloc(uint64_t nmemb, size_t size) {
    296   const uint64_t total_size = nmemb * (uint64_t)size;
    297   if (total_size != (size_t)total_size) return NULL;
    298   return malloc((size_t)total_size);
    299 }
    300 
    301 #define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((uint64_t)(W) * (H), sizeof(T)))
    302 
    303 static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
    304                             const uint8_t* b_ptr, int rgb_step, int rgb_stride,
    305                             int rgb_bit_depth, uint8_t* y_ptr, int y_stride,
    306                             uint8_t* u_ptr, int u_stride, uint8_t* v_ptr,
    307                             int v_stride, int yuv_bit_depth, int width,
    308                             int height,
    309                             const SharpYuvConversionMatrix* yuv_matrix,
    310                             SharpYuvTransferFunctionType transfer_type) {
    311   // we expand the right/bottom border if needed
    312   const int w = (width + 1) & ~1;
    313   const int h = (height + 1) & ~1;
    314   const int uv_w = w >> 1;
    315   const int uv_h = h >> 1;
    316   const int y_bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
    317   uint64_t prev_diff_y_sum = ~0;
    318   int j, iter;
    319 
    320   // TODO(skal): allocate one big memory chunk. But for now, it's easier
    321   // for valgrind debugging to have several chunks.
    322   fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t);   // scratch
    323   fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t);
    324   fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t);
    325   fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t);
    326   fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
    327   fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
    328   fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t);
    329   fixed_y_t* best_y = best_y_base;
    330   fixed_y_t* target_y = target_y_base;
    331   fixed_t* best_uv = best_uv_base;
    332   fixed_t* target_uv = target_uv_base;
    333   const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h);
    334   int ok;
    335   assert(w > 0);
    336   assert(h > 0);
    337 
    338   if (best_y_base == NULL || best_uv_base == NULL ||
    339       target_y_base == NULL || target_uv_base == NULL ||
    340       best_rgb_y == NULL || best_rgb_uv == NULL ||
    341       tmp_buffer == NULL) {
    342     ok = 0;
    343     goto End;
    344   }
    345 
    346   // Import RGB samples to W/RGB representation.
    347   for (j = 0; j < height; j += 2) {
    348     const int is_last_row = (j == height - 1);
    349     fixed_y_t* const src1 = tmp_buffer + 0 * w;
    350     fixed_y_t* const src2 = tmp_buffer + 3 * w;
    351 
    352     // prepare two rows of input
    353     ImportOneRow(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth, width,
    354                  src1);
    355     if (!is_last_row) {
    356       ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride,
    357                    rgb_step, rgb_bit_depth, width, src2);
    358     } else {
    359       memcpy(src2, src1, 3 * w * sizeof(*src2));
    360     }
    361     StoreGray(src1, best_y + 0, w);
    362     StoreGray(src2, best_y + w, w);
    363 
    364     UpdateW(src1, target_y, w, rgb_bit_depth, transfer_type);
    365     UpdateW(src2, target_y + w, w, rgb_bit_depth, transfer_type);
    366     UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth, transfer_type);
    367     memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
    368     best_y += 2 * w;
    369     best_uv += 3 * uv_w;
    370     target_y += 2 * w;
    371     target_uv += 3 * uv_w;
    372     r_ptr += 2 * rgb_stride;
    373     g_ptr += 2 * rgb_stride;
    374     b_ptr += 2 * rgb_stride;
    375   }
    376 
    377   // Iterate and resolve clipping conflicts.
    378   for (iter = 0; iter < kNumIterations; ++iter) {
    379     const fixed_t* cur_uv = best_uv_base;
    380     const fixed_t* prev_uv = best_uv_base;
    381     uint64_t diff_y_sum = 0;
    382 
    383     best_y = best_y_base;
    384     best_uv = best_uv_base;
    385     target_y = target_y_base;
    386     target_uv = target_uv_base;
    387     j = 0;
    388     do {
    389       fixed_y_t* const src1 = tmp_buffer + 0 * w;
    390       fixed_y_t* const src2 = tmp_buffer + 3 * w;
    391       {
    392         const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);
    393         InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w,
    394                            src1, src2, rgb_bit_depth);
    395         prev_uv = cur_uv;
    396         cur_uv = next_uv;
    397       }
    398 
    399       UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth, transfer_type);
    400       UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth, transfer_type);
    401       UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth, transfer_type);
    402 
    403       // update two rows of Y and one row of RGB
    404       diff_y_sum +=
    405           SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w, y_bit_depth);
    406       SharpYuvUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);
    407 
    408       best_y += 2 * w;
    409       best_uv += 3 * uv_w;
    410       target_y += 2 * w;
    411       target_uv += 3 * uv_w;
    412       j += 2;
    413     } while (j < h);
    414     // test exit condition
    415     if (iter > 0) {
    416       if (diff_y_sum < diff_y_threshold) break;
    417       if (diff_y_sum > prev_diff_y_sum) break;
    418     }
    419     prev_diff_y_sum = diff_y_sum;
    420   }
    421 
    422   // final reconstruction
    423   ok = ConvertWRGBToYUV(best_y_base, best_uv_base, y_ptr, y_stride, u_ptr,
    424                         u_stride, v_ptr, v_stride, rgb_bit_depth, yuv_bit_depth,
    425                         width, height, yuv_matrix);
    426 
    427  End:
    428   free(best_y_base);
    429   free(best_uv_base);
    430   free(target_y_base);
    431   free(target_uv_base);
    432   free(best_rgb_y);
    433   free(best_rgb_uv);
    434   free(tmp_buffer);
    435   return ok;
    436 }
    437 
    438 #undef SAFE_ALLOC
    439 
    440 #if defined(WEBP_USE_THREAD) && !defined(_WIN32)
    441 #include <pthread.h>  // NOLINT
    442 
    443 #define LOCK_ACCESS \
    444     static pthread_mutex_t sharpyuv_lock = PTHREAD_MUTEX_INITIALIZER; \
    445     if (pthread_mutex_lock(&sharpyuv_lock)) return
    446 #define UNLOCK_ACCESS_AND_RETURN                  \
    447     do {                                          \
    448       (void)pthread_mutex_unlock(&sharpyuv_lock); \
    449       return;                                     \
    450     } while (0)
    451 #else  // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
    452 #define LOCK_ACCESS do {} while (0)
    453 #define UNLOCK_ACCESS_AND_RETURN return
    454 #endif  // defined(WEBP_USE_THREAD) && !defined(_WIN32)
    455 
    456 // Hidden exported init function.
    457 // By default SharpYuvConvert calls it with SharpYuvGetCPUInfo. If needed,
    458 // users can declare it as extern and call it with an alternate VP8CPUInfo
    459 // function.
    460 extern VP8CPUInfo SharpYuvGetCPUInfo;
    461 SHARPYUV_EXTERN void SharpYuvInit(VP8CPUInfo cpu_info_func);
    462 void SharpYuvInit(VP8CPUInfo cpu_info_func) {
    463   static volatile VP8CPUInfo sharpyuv_last_cpuinfo_used =
    464       (VP8CPUInfo)&sharpyuv_last_cpuinfo_used;
    465   LOCK_ACCESS;
    466   // Only update SharpYuvGetCPUInfo when called from external code to avoid a
    467   // race on reading the value in SharpYuvConvert().
    468   if (cpu_info_func != (VP8CPUInfo)&SharpYuvGetCPUInfo) {
    469     SharpYuvGetCPUInfo = cpu_info_func;
    470   }
    471   if (sharpyuv_last_cpuinfo_used == SharpYuvGetCPUInfo) {
    472     UNLOCK_ACCESS_AND_RETURN;
    473   }
    474 
    475   SharpYuvInitDsp();
    476   SharpYuvInitGammaTables();
    477 
    478   sharpyuv_last_cpuinfo_used = SharpYuvGetCPUInfo;
    479   UNLOCK_ACCESS_AND_RETURN;
    480 }
    481 
    482 int SharpYuvConvert(const void* r_ptr, const void* g_ptr, const void* b_ptr,
    483                     int rgb_step, int rgb_stride, int rgb_bit_depth,
    484                     void* y_ptr, int y_stride, void* u_ptr, int u_stride,
    485                     void* v_ptr, int v_stride, int yuv_bit_depth, int width,
    486                     int height, const SharpYuvConversionMatrix* yuv_matrix) {
    487   SharpYuvOptions options;
    488   options.yuv_matrix = yuv_matrix;
    489   options.transfer_type = kSharpYuvTransferFunctionSrgb;
    490   return SharpYuvConvertWithOptions(
    491       r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride, rgb_bit_depth, y_ptr, y_stride,
    492       u_ptr, u_stride, v_ptr, v_stride, yuv_bit_depth, width, height, &options);
    493 }
    494 
    495 int SharpYuvOptionsInitInternal(const SharpYuvConversionMatrix* yuv_matrix,
    496                                 SharpYuvOptions* options, int version) {
    497   const int major = (version >> 24);
    498   const int minor = (version >> 16) & 0xff;
    499   if (options == NULL || yuv_matrix == NULL ||
    500       (major == SHARPYUV_VERSION_MAJOR && major == 0 &&
    501        minor != SHARPYUV_VERSION_MINOR) ||
    502       (major != SHARPYUV_VERSION_MAJOR)) {
    503     return 0;
    504   }
    505   options->yuv_matrix = yuv_matrix;
    506   options->transfer_type = kSharpYuvTransferFunctionSrgb;
    507   return 1;
    508 }
    509 
    510 int SharpYuvConvertWithOptions(const void* r_ptr, const void* g_ptr,
    511                                const void* b_ptr, int rgb_step, int rgb_stride,
    512                                int rgb_bit_depth, void* y_ptr, int y_stride,
    513                                void* u_ptr, int u_stride, void* v_ptr,
    514                                int v_stride, int yuv_bit_depth, int width,
    515                                int height, const SharpYuvOptions* options) {
    516   const SharpYuvConversionMatrix* yuv_matrix = options->yuv_matrix;
    517   SharpYuvTransferFunctionType transfer_type = options->transfer_type;
    518   SharpYuvConversionMatrix scaled_matrix;
    519   const int rgb_max = (1 << rgb_bit_depth) - 1;
    520   const int rgb_round = 1 << (rgb_bit_depth - 1);
    521   const int yuv_max = (1 << yuv_bit_depth) - 1;
    522   const int sfix = GetPrecisionShift(rgb_bit_depth);
    523 
    524   if (width < 1 || height < 1 || width == INT_MAX || height == INT_MAX ||
    525       r_ptr == NULL || g_ptr == NULL || b_ptr == NULL || y_ptr == NULL ||
    526       u_ptr == NULL || v_ptr == NULL) {
    527     return 0;
    528   }
    529   if (rgb_bit_depth != 8 && rgb_bit_depth != 10 && rgb_bit_depth != 12 &&
    530       rgb_bit_depth != 16) {
    531     return 0;
    532   }
    533   if (yuv_bit_depth != 8 && yuv_bit_depth != 10 && yuv_bit_depth != 12) {
    534     return 0;
    535   }
    536   if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride % 2 != 0)) {
    537     // Step/stride should be even for uint16_t buffers.
    538     return 0;
    539   }
    540   if (yuv_bit_depth > 8 &&
    541       (y_stride % 2 != 0 || u_stride % 2 != 0 || v_stride % 2 != 0)) {
    542     // Stride should be even for uint16_t buffers.
    543     return 0;
    544   }
    545   // The address of the function pointer is used to avoid a read race.
    546   SharpYuvInit((VP8CPUInfo)&SharpYuvGetCPUInfo);
    547 
    548   // Add scaling factor to go from rgb_bit_depth to yuv_bit_depth, to the
    549   // rgb->yuv conversion matrix.
    550   if (rgb_bit_depth == yuv_bit_depth) {
    551     memcpy(&scaled_matrix, yuv_matrix, sizeof(scaled_matrix));
    552   } else {
    553     int i;
    554     for (i = 0; i < 3; ++i) {
    555       scaled_matrix.rgb_to_y[i] =
    556           (yuv_matrix->rgb_to_y[i] * yuv_max + rgb_round) / rgb_max;
    557       scaled_matrix.rgb_to_u[i] =
    558           (yuv_matrix->rgb_to_u[i] * yuv_max + rgb_round) / rgb_max;
    559       scaled_matrix.rgb_to_v[i] =
    560           (yuv_matrix->rgb_to_v[i] * yuv_max + rgb_round) / rgb_max;
    561     }
    562   }
    563   // Also incorporate precision change scaling.
    564   scaled_matrix.rgb_to_y[3] = Shift(yuv_matrix->rgb_to_y[3], sfix);
    565   scaled_matrix.rgb_to_u[3] = Shift(yuv_matrix->rgb_to_u[3], sfix);
    566   scaled_matrix.rgb_to_v[3] = Shift(yuv_matrix->rgb_to_v[3], sfix);
    567 
    568   return DoSharpArgbToYuv(
    569       (const uint8_t*)r_ptr, (const uint8_t*)g_ptr, (const uint8_t*)b_ptr,
    570       rgb_step, rgb_stride, rgb_bit_depth, (uint8_t*)y_ptr, y_stride,
    571       (uint8_t*)u_ptr, u_stride, (uint8_t*)v_ptr, v_stride, yuv_bit_depth,
    572       width, height, &scaled_matrix, transfer_type);
    573 }
    574 
    575 //------------------------------------------------------------------------------