Home | History | Annotate | Download | only in enc
      1 // Copyright 2014 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 // WebPPicture utils for colorspace conversion
     11 //
     12 // Author: Skal (pascal.massimino (at) gmail.com)
     13 
     14 #include <assert.h>
     15 #include <stdlib.h>
     16 #include <math.h>
     17 
     18 #include "./vp8enci.h"
     19 #include "../utils/random.h"
     20 #include "../utils/utils.h"
     21 #include "../dsp/yuv.h"
     22 
     23 // Uncomment to disable gamma-compression during RGB->U/V averaging
     24 #define USE_GAMMA_COMPRESSION
     25 
     26 // If defined, use table to compute x / alpha.
     27 #define USE_INVERSE_ALPHA_TABLE
     28 
     29 static const union {
     30   uint32_t argb;
     31   uint8_t  bytes[4];
     32 } test_endian = { 0xff000000u };
     33 #define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff)
     34 
     35 static WEBP_INLINE uint32_t MakeARGB32(int a, int r, int g, int b) {
     36   return (((uint32_t)a << 24) | (r << 16) | (g << 8) | b);
     37 }
     38 
     39 //------------------------------------------------------------------------------
     40 // Detection of non-trivial transparency
     41 
     42 // Returns true if alpha[] has non-0xff values.
     43 static int CheckNonOpaque(const uint8_t* alpha, int width, int height,
     44                           int x_step, int y_step) {
     45   if (alpha == NULL) return 0;
     46   while (height-- > 0) {
     47     int x;
     48     for (x = 0; x < width * x_step; x += x_step) {
     49       if (alpha[x] != 0xff) return 1;  // TODO(skal): check 4/8 bytes at a time.
     50     }
     51     alpha += y_step;
     52   }
     53   return 0;
     54 }
     55 
     56 // Checking for the presence of non-opaque alpha.
     57 int WebPPictureHasTransparency(const WebPPicture* picture) {
     58   if (picture == NULL) return 0;
     59   if (!picture->use_argb) {
     60     return CheckNonOpaque(picture->a, picture->width, picture->height,
     61                           1, picture->a_stride);
     62   } else {
     63     int x, y;
     64     const uint32_t* argb = picture->argb;
     65     if (argb == NULL) return 0;
     66     for (y = 0; y < picture->height; ++y) {
     67       for (x = 0; x < picture->width; ++x) {
     68         if (argb[x] < 0xff000000u) return 1;   // test any alpha values != 0xff
     69       }
     70       argb += picture->argb_stride;
     71     }
     72   }
     73   return 0;
     74 }
     75 
     76 //------------------------------------------------------------------------------
     77 // Code for gamma correction
     78 
     79 #if defined(USE_GAMMA_COMPRESSION)
     80 
     81 // gamma-compensates loss of resolution during chroma subsampling
     82 #define kGamma 0.80      // for now we use a different gamma value than kGammaF
     83 #define kGammaFix 12     // fixed-point precision for linear values
     84 #define kGammaScale ((1 << kGammaFix) - 1)
     85 #define kGammaTabFix 7   // fixed-point fractional bits precision
     86 #define kGammaTabScale (1 << kGammaTabFix)
     87 #define kGammaTabRounder (kGammaTabScale >> 1)
     88 #define kGammaTabSize (1 << (kGammaFix - kGammaTabFix))
     89 
     90 static int kLinearToGammaTab[kGammaTabSize + 1];
     91 static uint16_t kGammaToLinearTab[256];
     92 static int kGammaTablesOk = 0;
     93 
     94 static void InitGammaTables(void) {
     95   if (!kGammaTablesOk) {
     96     int v;
     97     const double scale = (double)(1 << kGammaTabFix) / kGammaScale;
     98     const double norm = 1. / 255.;
     99     for (v = 0; v <= 255; ++v) {
    100       kGammaToLinearTab[v] =
    101           (uint16_t)(pow(norm * v, kGamma) * kGammaScale + .5);
    102     }
    103     for (v = 0; v <= kGammaTabSize; ++v) {
    104       kLinearToGammaTab[v] = (int)(255. * pow(scale * v, 1. / kGamma) + .5);
    105     }
    106     kGammaTablesOk = 1;
    107   }
    108 }
    109 
    110 static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) {
    111   return kGammaToLinearTab[v];
    112 }
    113 
    114 static WEBP_INLINE int Interpolate(int v) {
    115   const int tab_pos = v >> (kGammaTabFix + 2);    // integer part
    116   const int x = v & ((kGammaTabScale << 2) - 1);  // fractional part
    117   const int v0 = kLinearToGammaTab[tab_pos];
    118   const int v1 = kLinearToGammaTab[tab_pos + 1];
    119   const int y = v1 * x + v0 * ((kGammaTabScale << 2) - x);   // interpolate
    120   assert(tab_pos + 1 < kGammaTabSize + 1);
    121   return y;
    122 }
    123 
    124 // Convert a linear value 'v' to YUV_FIX+2 fixed-point precision
    125 // U/V value, suitable for RGBToU/V calls.
    126 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
    127   const int y = Interpolate(base_value << shift);   // final uplifted value
    128   return (y + kGammaTabRounder) >> kGammaTabFix;    // descale
    129 }
    130 
    131 #else
    132 
    133 static void InitGammaTables(void) {}
    134 static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; }
    135 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
    136   return (int)(base_value << shift);
    137 }
    138 
    139 #endif    // USE_GAMMA_COMPRESSION
    140 
    141 //------------------------------------------------------------------------------
    142 // RGB -> YUV conversion
    143 
    144 static int RGBToY(int r, int g, int b, VP8Random* const rg) {
    145   return (rg == NULL) ? VP8RGBToY(r, g, b, YUV_HALF)
    146                       : VP8RGBToY(r, g, b, VP8RandomBits(rg, YUV_FIX));
    147 }
    148 
    149 static int RGBToU(int r, int g, int b, VP8Random* const rg) {
    150   return (rg == NULL) ? VP8RGBToU(r, g, b, YUV_HALF << 2)
    151                       : VP8RGBToU(r, g, b, VP8RandomBits(rg, YUV_FIX + 2));
    152 }
    153 
    154 static int RGBToV(int r, int g, int b, VP8Random* const rg) {
    155   return (rg == NULL) ? VP8RGBToV(r, g, b, YUV_HALF << 2)
    156                       : VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2));
    157 }
    158 
    159 //------------------------------------------------------------------------------
    160 // Smart RGB->YUV conversion
    161 
    162 static const int kNumIterations = 6;
    163 static const int kMinDimensionIterativeConversion = 4;
    164 
    165 // We use a-priori a different precision for storing RGB and Y/W components
    166 // We could use YFIX=0 and only uint8_t for fixed_y_t, but it produces some
    167 // banding sometimes. Better use extra precision.
    168 // TODO(skal): cleanup once TFIX/YFIX values are fixed.
    169 
    170 typedef int16_t fixed_t;      // signed type with extra TFIX precision for UV
    171 typedef uint16_t fixed_y_t;   // unsigned type with extra YFIX precision for W
    172 #define TFIX 6   // fixed-point precision of RGB
    173 #define YFIX 2   // fixed point precision for Y/W
    174 
    175 #define THALF ((1 << TFIX) >> 1)
    176 #define MAX_Y_T ((256 << YFIX) - 1)
    177 #define TROUNDER (1 << (YUV_FIX + TFIX - 1))
    178 
    179 #if defined(USE_GAMMA_COMPRESSION)
    180 
    181 // float variant of gamma-correction
    182 // We use tables of different size and precision, along with a 'real-world'
    183 // Gamma value close to ~2.
    184 #define kGammaF 2.2
    185 static float kGammaToLinearTabF[MAX_Y_T + 1];   // size scales with Y_FIX
    186 static float kLinearToGammaTabF[kGammaTabSize + 2];
    187 static int kGammaTablesFOk = 0;
    188 
    189 static void InitGammaTablesF(void) {
    190   if (!kGammaTablesFOk) {
    191     int v;
    192     const double norm = 1. / MAX_Y_T;
    193     const double scale = 1. / kGammaTabSize;
    194     for (v = 0; v <= MAX_Y_T; ++v) {
    195       kGammaToLinearTabF[v] = (float)pow(norm * v, kGammaF);
    196     }
    197     for (v = 0; v <= kGammaTabSize; ++v) {
    198       kLinearToGammaTabF[v] = (float)(MAX_Y_T * pow(scale * v, 1. / kGammaF));
    199     }
    200     // to prevent small rounding errors to cause read-overflow:
    201     kLinearToGammaTabF[kGammaTabSize + 1] = kLinearToGammaTabF[kGammaTabSize];
    202     kGammaTablesFOk = 1;
    203   }
    204 }
    205 
    206 static WEBP_INLINE float GammaToLinearF(int v) {
    207   return kGammaToLinearTabF[v];
    208 }
    209 
    210 static WEBP_INLINE float LinearToGammaF(float value) {
    211   const float v = value * kGammaTabSize;
    212   const int tab_pos = (int)v;
    213   const float x = v - (float)tab_pos;      // fractional part
    214   const float v0 = kLinearToGammaTabF[tab_pos + 0];
    215   const float v1 = kLinearToGammaTabF[tab_pos + 1];
    216   const float y = v1 * x + v0 * (1.f - x);  // interpolate
    217   return y;
    218 }
    219 
    220 #else
    221 
    222 static void InitGammaTablesF(void) {}
    223 static WEBP_INLINE float GammaToLinearF(int v) {
    224   const float norm = 1.f / MAX_Y_T;
    225   return norm * v;
    226 }
    227 static WEBP_INLINE float LinearToGammaF(float value) {
    228   return MAX_Y_T * value;
    229 }
    230 
    231 #endif    // USE_GAMMA_COMPRESSION
    232 
    233 //------------------------------------------------------------------------------
    234 
    235 // precision: YFIX -> TFIX
    236 static WEBP_INLINE int FixedYToW(int v) {
    237 #if TFIX == YFIX
    238   return v;
    239 #elif TFIX >= YFIX
    240   return v << (TFIX - YFIX);
    241 #else
    242   return v >> (YFIX - TFIX);
    243 #endif
    244 }
    245 
    246 static WEBP_INLINE int FixedWToY(int v) {
    247 #if TFIX == YFIX
    248   return v;
    249 #elif YFIX >= TFIX
    250   return v << (YFIX - TFIX);
    251 #else
    252   return v >> (TFIX - YFIX);
    253 #endif
    254 }
    255 
    256 static uint8_t clip_8b(fixed_t v) {
    257   return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u;
    258 }
    259 
    260 static fixed_y_t clip_y(int y) {
    261   return (!(y & ~MAX_Y_T)) ? (fixed_y_t)y : (y < 0) ? 0 : MAX_Y_T;
    262 }
    263 
    264 // precision: TFIX -> YFIX
    265 static fixed_y_t clip_fixed_t(fixed_t v) {
    266   const int y = FixedWToY(v);
    267   const fixed_y_t w = clip_y(y);
    268   return w;
    269 }
    270 
    271 //------------------------------------------------------------------------------
    272 
    273 static int RGBToGray(int r, int g, int b) {
    274   const int luma = 19595 * r + 38470 * g + 7471 * b + YUV_HALF;
    275   return (luma >> YUV_FIX);
    276 }
    277 
    278 static float RGBToGrayF(float r, float g, float b) {
    279   return 0.299f * r + 0.587f * g + 0.114f * b;
    280 }
    281 
    282 static float ScaleDown(int a, int b, int c, int d) {
    283   const float A = GammaToLinearF(a);
    284   const float B = GammaToLinearF(b);
    285   const float C = GammaToLinearF(c);
    286   const float D = GammaToLinearF(d);
    287   return LinearToGammaF(0.25f * (A + B + C + D));
    288 }
    289 
    290 static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int len) {
    291   while (len-- > 0) {
    292     const float R = GammaToLinearF(src[0]);
    293     const float G = GammaToLinearF(src[1]);
    294     const float B = GammaToLinearF(src[2]);
    295     const float Y = RGBToGrayF(R, G, B);
    296     *dst++ = (fixed_y_t)(LinearToGammaF(Y) + .5);
    297     src += 3;
    298   }
    299 }
    300 
    301 static WEBP_INLINE void UpdateChroma(const fixed_y_t* src1,
    302                                      const fixed_y_t* src2,
    303                                      fixed_t* dst, fixed_y_t* tmp, int len) {
    304   while (len--> 0) {
    305     const float r = ScaleDown(src1[0], src1[3], src2[0], src2[3]);
    306     const float g = ScaleDown(src1[1], src1[4], src2[1], src2[4]);
    307     const float b = ScaleDown(src1[2], src1[5], src2[2], src2[5]);
    308     const float W = RGBToGrayF(r, g, b);
    309     dst[0] = (fixed_t)FixedYToW((int)(r - W));
    310     dst[1] = (fixed_t)FixedYToW((int)(g - W));
    311     dst[2] = (fixed_t)FixedYToW((int)(b - W));
    312     dst += 3;
    313     src1 += 6;
    314     src2 += 6;
    315     if (tmp != NULL) {
    316       tmp[0] = tmp[1] = clip_y((int)(W + .5));
    317       tmp += 2;
    318     }
    319   }
    320 }
    321 
    322 //------------------------------------------------------------------------------
    323 
    324 static WEBP_INLINE int Filter(const fixed_t* const A, const fixed_t* const B,
    325                               int rightwise) {
    326   int v;
    327   if (!rightwise) {
    328     v = (A[0] * 9 + A[-3] * 3 + B[0] * 3 + B[-3]);
    329   } else {
    330     v = (A[0] * 9 + A[+3] * 3 + B[0] * 3 + B[+3]);
    331   }
    332   return (v + 8) >> 4;
    333 }
    334 
    335 static WEBP_INLINE int Filter2(int A, int B) { return (A * 3 + B + 2) >> 2; }
    336 
    337 //------------------------------------------------------------------------------
    338 
    339 // 8bit -> YFIX
    340 static WEBP_INLINE fixed_y_t UpLift(uint8_t a) {
    341   return ((fixed_y_t)a << YFIX) | (1 << (YFIX - 1));
    342 }
    343 
    344 static void ImportOneRow(const uint8_t* const r_ptr,
    345                          const uint8_t* const g_ptr,
    346                          const uint8_t* const b_ptr,
    347                          int step,
    348                          int pic_width,
    349                          fixed_y_t* const dst) {
    350   int i;
    351   for (i = 0; i < pic_width; ++i) {
    352     const int off = i * step;
    353     dst[3 * i + 0] = UpLift(r_ptr[off]);
    354     dst[3 * i + 1] = UpLift(g_ptr[off]);
    355     dst[3 * i + 2] = UpLift(b_ptr[off]);
    356   }
    357   if (pic_width & 1) {  // replicate rightmost pixel
    358     memcpy(dst + 3 * pic_width, dst + 3 * (pic_width - 1), 3 * sizeof(*dst));
    359   }
    360 }
    361 
    362 static void InterpolateTwoRows(const fixed_y_t* const best_y,
    363                                const fixed_t* const prev_uv,
    364                                const fixed_t* const cur_uv,
    365                                const fixed_t* const next_uv,
    366                                int w,
    367                                fixed_y_t* const out1,
    368                                fixed_y_t* const out2) {
    369   int i, k;
    370   {  // special boundary case for i==0
    371     const int W0 = FixedYToW(best_y[0]);
    372     const int W1 = FixedYToW(best_y[w]);
    373     for (k = 0; k <= 2; ++k) {
    374       out1[k] = clip_fixed_t(Filter2(cur_uv[k], prev_uv[k]) + W0);
    375       out2[k] = clip_fixed_t(Filter2(cur_uv[k], next_uv[k]) + W1);
    376     }
    377   }
    378   for (i = 1; i < w - 1; ++i) {
    379     const int W0 = FixedYToW(best_y[i + 0]);
    380     const int W1 = FixedYToW(best_y[i + w]);
    381     const int off = 3 * (i >> 1);
    382     for (k = 0; k <= 2; ++k) {
    383       const int tmp0 = Filter(cur_uv + off + k, prev_uv + off + k, i & 1);
    384       const int tmp1 = Filter(cur_uv + off + k, next_uv + off + k, i & 1);
    385       out1[3 * i + k] = clip_fixed_t(tmp0 + W0);
    386       out2[3 * i + k] = clip_fixed_t(tmp1 + W1);
    387     }
    388   }
    389   {  // special boundary case for i == w - 1
    390     const int W0 = FixedYToW(best_y[i + 0]);
    391     const int W1 = FixedYToW(best_y[i + w]);
    392     const int off = 3 * (i >> 1);
    393     for (k = 0; k <= 2; ++k) {
    394       out1[3 * i + k] =
    395           clip_fixed_t(Filter2(cur_uv[off + k], prev_uv[off + k]) + W0);
    396       out2[3 * i + k] =
    397           clip_fixed_t(Filter2(cur_uv[off + k], next_uv[off + k]) + W1);
    398     }
    399   }
    400 }
    401 
    402 static WEBP_INLINE uint8_t ConvertRGBToY(int r, int g, int b) {
    403   const int luma = 16839 * r + 33059 * g + 6420 * b + TROUNDER;
    404   return clip_8b(16 + (luma >> (YUV_FIX + TFIX)));
    405 }
    406 
    407 static WEBP_INLINE uint8_t ConvertRGBToU(int r, int g, int b) {
    408   const int u =  -9719 * r - 19081 * g + 28800 * b + TROUNDER;
    409   return clip_8b(128 + (u >> (YUV_FIX + TFIX)));
    410 }
    411 
    412 static WEBP_INLINE uint8_t ConvertRGBToV(int r, int g, int b) {
    413   const int v = +28800 * r - 24116 * g -  4684 * b + TROUNDER;
    414   return clip_8b(128 + (v >> (YUV_FIX + TFIX)));
    415 }
    416 
    417 static int ConvertWRGBToYUV(const fixed_y_t* const best_y,
    418                             const fixed_t* const best_uv,
    419                             WebPPicture* const picture) {
    420   int i, j;
    421   const int w = (picture->width + 1) & ~1;
    422   const int h = (picture->height + 1) & ~1;
    423   const int uv_w = w >> 1;
    424   const int uv_h = h >> 1;
    425   for (j = 0; j < picture->height; ++j) {
    426     for (i = 0; i < picture->width; ++i) {
    427       const int off = 3 * ((i >> 1) + (j >> 1) * uv_w);
    428       const int off2 = i + j * picture->y_stride;
    429       const int W = FixedYToW(best_y[i + j * w]);
    430       const int r = best_uv[off + 0] + W;
    431       const int g = best_uv[off + 1] + W;
    432       const int b = best_uv[off + 2] + W;
    433       picture->y[off2] = ConvertRGBToY(r, g, b);
    434     }
    435   }
    436   for (j = 0; j < uv_h; ++j) {
    437     uint8_t* const dst_u = picture->u + j * picture->uv_stride;
    438     uint8_t* const dst_v = picture->v + j * picture->uv_stride;
    439     for (i = 0; i < uv_w; ++i) {
    440       const int off = 3 * (i + j * uv_w);
    441       const int r = best_uv[off + 0];
    442       const int g = best_uv[off + 1];
    443       const int b = best_uv[off + 2];
    444       dst_u[i] = ConvertRGBToU(r, g, b);
    445       dst_v[i] = ConvertRGBToV(r, g, b);
    446     }
    447   }
    448   return 1;
    449 }
    450 
    451 //------------------------------------------------------------------------------
    452 // Main function
    453 
    454 #define SAFE_ALLOC(W, H, T) ((T*)WebPSafeMalloc((W) * (H), sizeof(T)))
    455 
    456 static int PreprocessARGB(const uint8_t* const r_ptr,
    457                           const uint8_t* const g_ptr,
    458                           const uint8_t* const b_ptr,
    459                           int step, int rgb_stride,
    460                           WebPPicture* const picture) {
    461   // we expand the right/bottom border if needed
    462   const int w = (picture->width + 1) & ~1;
    463   const int h = (picture->height + 1) & ~1;
    464   const int uv_w = w >> 1;
    465   const int uv_h = h >> 1;
    466   int i, j, iter;
    467 
    468   // TODO(skal): allocate one big memory chunk. But for now, it's easier
    469   // for valgrind debugging to have several chunks.
    470   fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t);   // scratch
    471   fixed_y_t* const best_y = SAFE_ALLOC(w, h, fixed_y_t);
    472   fixed_y_t* const target_y = SAFE_ALLOC(w, h, fixed_y_t);
    473   fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t);
    474   fixed_t* const best_uv = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
    475   fixed_t* const target_uv = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
    476   fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t);
    477   int ok;
    478 
    479   if (best_y == NULL || best_uv == NULL ||
    480       target_y == NULL || target_uv == NULL ||
    481       best_rgb_y == NULL || best_rgb_uv == NULL ||
    482       tmp_buffer == NULL) {
    483     ok = WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
    484     goto End;
    485   }
    486   assert(picture->width >= kMinDimensionIterativeConversion);
    487   assert(picture->height >= kMinDimensionIterativeConversion);
    488 
    489   // Import RGB samples to W/RGB representation.
    490   for (j = 0; j < picture->height; j += 2) {
    491     const int is_last_row = (j == picture->height - 1);
    492     fixed_y_t* const src1 = tmp_buffer;
    493     fixed_y_t* const src2 = tmp_buffer + 3 * w;
    494     const int off1 = j * rgb_stride;
    495     const int off2 = off1 + rgb_stride;
    496     const int uv_off = (j >> 1) * 3 * uv_w;
    497     fixed_y_t* const dst_y = best_y + j * w;
    498 
    499     // prepare two rows of input
    500     ImportOneRow(r_ptr + off1, g_ptr + off1, b_ptr + off1,
    501                  step, picture->width, src1);
    502     if (!is_last_row) {
    503       ImportOneRow(r_ptr + off2, g_ptr + off2, b_ptr + off2,
    504                    step, picture->width, src2);
    505     } else {
    506       memcpy(src2, src1, 3 * w * sizeof(*src2));
    507     }
    508     UpdateW(src1, target_y + (j + 0) * w, w);
    509     UpdateW(src2, target_y + (j + 1) * w, w);
    510     UpdateChroma(src1, src2, target_uv + uv_off, dst_y, uv_w);
    511     memcpy(best_uv + uv_off, target_uv + uv_off, 3 * uv_w * sizeof(*best_uv));
    512     memcpy(dst_y + w, dst_y, w * sizeof(*dst_y));
    513   }
    514 
    515   // Iterate and resolve clipping conflicts.
    516   for (iter = 0; iter < kNumIterations; ++iter) {
    517     int k;
    518     const fixed_t* cur_uv = best_uv;
    519     const fixed_t* prev_uv = best_uv;
    520     for (j = 0; j < h; j += 2) {
    521       fixed_y_t* const src1 = tmp_buffer;
    522       fixed_y_t* const src2 = tmp_buffer + 3 * w;
    523 
    524       {
    525         const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);
    526         InterpolateTwoRows(best_y + j * w, prev_uv, cur_uv, next_uv,
    527                            w, src1, src2);
    528         prev_uv = cur_uv;
    529         cur_uv = next_uv;
    530       }
    531 
    532       UpdateW(src1, best_rgb_y + 0 * w, w);
    533       UpdateW(src2, best_rgb_y + 1 * w, w);
    534       UpdateChroma(src1, src2, best_rgb_uv, NULL, uv_w);
    535 
    536       // update two rows of Y and one row of RGB
    537       for (i = 0; i < 2 * w; ++i) {
    538         const int off = i + j * w;
    539         const int diff_y = target_y[off] - best_rgb_y[i];
    540         const int new_y = (int)best_y[off] + diff_y;
    541         best_y[off] = clip_y(new_y);
    542       }
    543       for (i = 0; i < uv_w; ++i) {
    544         const int off = 3 * (i + (j >> 1) * uv_w);
    545         int W;
    546         for (k = 0; k <= 2; ++k) {
    547           const int diff_uv = (int)target_uv[off + k] - best_rgb_uv[3 * i + k];
    548           best_uv[off + k] += diff_uv;
    549         }
    550         W = RGBToGray(best_uv[off + 0], best_uv[off + 1], best_uv[off + 2]);
    551         for (k = 0; k <= 2; ++k) {
    552           best_uv[off + k] -= W;
    553         }
    554       }
    555     }
    556     // TODO(skal): add early-termination criterion
    557   }
    558 
    559   // final reconstruction
    560   ok = ConvertWRGBToYUV(best_y, best_uv, picture);
    561 
    562  End:
    563   WebPSafeFree(best_y);
    564   WebPSafeFree(best_uv);
    565   WebPSafeFree(target_y);
    566   WebPSafeFree(target_uv);
    567   WebPSafeFree(best_rgb_y);
    568   WebPSafeFree(best_rgb_uv);
    569   WebPSafeFree(tmp_buffer);
    570   return ok;
    571 }
    572 #undef SAFE_ALLOC
    573 
    574 //------------------------------------------------------------------------------
    575 // "Fast" regular RGB->YUV
    576 
    577 #define SUM4(ptr, step) LinearToGamma(                     \
    578     GammaToLinear((ptr)[0]) +                              \
    579     GammaToLinear((ptr)[(step)]) +                         \
    580     GammaToLinear((ptr)[rgb_stride]) +                     \
    581     GammaToLinear((ptr)[rgb_stride + (step)]), 0)          \
    582 
    583 #define SUM2(ptr) \
    584     LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[rgb_stride]), 1)
    585 
    586 #define SUM2ALPHA(ptr) ((ptr)[0] + (ptr)[rgb_stride])
    587 #define SUM4ALPHA(ptr) (SUM2ALPHA(ptr) + SUM2ALPHA((ptr) + 4))
    588 
    589 #if defined(USE_INVERSE_ALPHA_TABLE)
    590 
    591 static const int kAlphaFix = 19;
    592 // Following table is (1 << kAlphaFix) / a. The (v * kInvAlpha[a]) >> kAlphaFix
    593 // formula is then equal to v / a in most (99.6%) cases. Note that this table
    594 // and constant are adjusted very tightly to fit 32b arithmetic.
    595 // In particular, they use the fact that the operands for 'v / a' are actually
    596 // derived as v = (a0.p0 + a1.p1 + a2.p2 + a3.p3) and a = a0 + a1 + a2 + a3
    597 // with ai in [0..255] and pi in [0..1<<kGammaFix). The constraint to avoid
    598 // overflow is: kGammaFix + kAlphaFix <= 31.
    599 static const uint32_t kInvAlpha[4 * 0xff + 1] = {
    600   0,  /* alpha = 0 */
    601   524288, 262144, 174762, 131072, 104857, 87381, 74898, 65536,
    602   58254, 52428, 47662, 43690, 40329, 37449, 34952, 32768,
    603   30840, 29127, 27594, 26214, 24966, 23831, 22795, 21845,
    604   20971, 20164, 19418, 18724, 18078, 17476, 16912, 16384,
    605   15887, 15420, 14979, 14563, 14169, 13797, 13443, 13107,
    606   12787, 12483, 12192, 11915, 11650, 11397, 11155, 10922,
    607   10699, 10485, 10280, 10082, 9892, 9709, 9532, 9362,
    608   9198, 9039, 8886, 8738, 8594, 8456, 8322, 8192,
    609   8065, 7943, 7825, 7710, 7598, 7489, 7384, 7281,
    610   7182, 7084, 6990, 6898, 6808, 6721, 6636, 6553,
    611   6472, 6393, 6316, 6241, 6168, 6096, 6026, 5957,
    612   5890, 5825, 5761, 5698, 5637, 5577, 5518, 5461,
    613   5405, 5349, 5295, 5242, 5190, 5140, 5090, 5041,
    614   4993, 4946, 4899, 4854, 4809, 4766, 4723, 4681,
    615   4639, 4599, 4559, 4519, 4481, 4443, 4405, 4369,
    616   4332, 4297, 4262, 4228, 4194, 4161, 4128, 4096,
    617   4064, 4032, 4002, 3971, 3942, 3912, 3883, 3855,
    618   3826, 3799, 3771, 3744, 3718, 3692, 3666, 3640,
    619   3615, 3591, 3566, 3542, 3518, 3495, 3472, 3449,
    620   3426, 3404, 3382, 3360, 3339, 3318, 3297, 3276,
    621   3256, 3236, 3216, 3196, 3177, 3158, 3139, 3120,
    622   3102, 3084, 3066, 3048, 3030, 3013, 2995, 2978,
    623   2962, 2945, 2928, 2912, 2896, 2880, 2864, 2849,
    624   2833, 2818, 2803, 2788, 2774, 2759, 2744, 2730,
    625   2716, 2702, 2688, 2674, 2661, 2647, 2634, 2621,
    626   2608, 2595, 2582, 2570, 2557, 2545, 2532, 2520,
    627   2508, 2496, 2484, 2473, 2461, 2449, 2438, 2427,
    628   2416, 2404, 2394, 2383, 2372, 2361, 2351, 2340,
    629   2330, 2319, 2309, 2299, 2289, 2279, 2269, 2259,
    630   2250, 2240, 2231, 2221, 2212, 2202, 2193, 2184,
    631   2175, 2166, 2157, 2148, 2139, 2131, 2122, 2114,
    632   2105, 2097, 2088, 2080, 2072, 2064, 2056, 2048,
    633   2040, 2032, 2024, 2016, 2008, 2001, 1993, 1985,
    634   1978, 1971, 1963, 1956, 1949, 1941, 1934, 1927,
    635   1920, 1913, 1906, 1899, 1892, 1885, 1879, 1872,
    636   1865, 1859, 1852, 1846, 1839, 1833, 1826, 1820,
    637   1814, 1807, 1801, 1795, 1789, 1783, 1777, 1771,
    638   1765, 1759, 1753, 1747, 1741, 1736, 1730, 1724,
    639   1718, 1713, 1707, 1702, 1696, 1691, 1685, 1680,
    640   1675, 1669, 1664, 1659, 1653, 1648, 1643, 1638,
    641   1633, 1628, 1623, 1618, 1613, 1608, 1603, 1598,
    642   1593, 1588, 1583, 1579, 1574, 1569, 1565, 1560,
    643   1555, 1551, 1546, 1542, 1537, 1533, 1528, 1524,
    644   1519, 1515, 1510, 1506, 1502, 1497, 1493, 1489,
    645   1485, 1481, 1476, 1472, 1468, 1464, 1460, 1456,
    646   1452, 1448, 1444, 1440, 1436, 1432, 1428, 1424,
    647   1420, 1416, 1413, 1409, 1405, 1401, 1398, 1394,
    648   1390, 1387, 1383, 1379, 1376, 1372, 1368, 1365,
    649   1361, 1358, 1354, 1351, 1347, 1344, 1340, 1337,
    650   1334, 1330, 1327, 1323, 1320, 1317, 1314, 1310,
    651   1307, 1304, 1300, 1297, 1294, 1291, 1288, 1285,
    652   1281, 1278, 1275, 1272, 1269, 1266, 1263, 1260,
    653   1257, 1254, 1251, 1248, 1245, 1242, 1239, 1236,
    654   1233, 1230, 1227, 1224, 1222, 1219, 1216, 1213,
    655   1210, 1208, 1205, 1202, 1199, 1197, 1194, 1191,
    656   1188, 1186, 1183, 1180, 1178, 1175, 1172, 1170,
    657   1167, 1165, 1162, 1159, 1157, 1154, 1152, 1149,
    658   1147, 1144, 1142, 1139, 1137, 1134, 1132, 1129,
    659   1127, 1125, 1122, 1120, 1117, 1115, 1113, 1110,
    660   1108, 1106, 1103, 1101, 1099, 1096, 1094, 1092,
    661   1089, 1087, 1085, 1083, 1081, 1078, 1076, 1074,
    662   1072, 1069, 1067, 1065, 1063, 1061, 1059, 1057,
    663   1054, 1052, 1050, 1048, 1046, 1044, 1042, 1040,
    664   1038, 1036, 1034, 1032, 1030, 1028, 1026, 1024,
    665   1022, 1020, 1018, 1016, 1014, 1012, 1010, 1008,
    666   1006, 1004, 1002, 1000, 998, 996, 994, 992,
    667   991, 989, 987, 985, 983, 981, 979, 978,
    668   976, 974, 972, 970, 969, 967, 965, 963,
    669   961, 960, 958, 956, 954, 953, 951, 949,
    670   948, 946, 944, 942, 941, 939, 937, 936,
    671   934, 932, 931, 929, 927, 926, 924, 923,
    672   921, 919, 918, 916, 914, 913, 911, 910,
    673   908, 907, 905, 903, 902, 900, 899, 897,
    674   896, 894, 893, 891, 890, 888, 887, 885,
    675   884, 882, 881, 879, 878, 876, 875, 873,
    676   872, 870, 869, 868, 866, 865, 863, 862,
    677   860, 859, 858, 856, 855, 853, 852, 851,
    678   849, 848, 846, 845, 844, 842, 841, 840,
    679   838, 837, 836, 834, 833, 832, 830, 829,
    680   828, 826, 825, 824, 823, 821, 820, 819,
    681   817, 816, 815, 814, 812, 811, 810, 809,
    682   807, 806, 805, 804, 802, 801, 800, 799,
    683   798, 796, 795, 794, 793, 791, 790, 789,
    684   788, 787, 786, 784, 783, 782, 781, 780,
    685   779, 777, 776, 775, 774, 773, 772, 771,
    686   769, 768, 767, 766, 765, 764, 763, 762,
    687   760, 759, 758, 757, 756, 755, 754, 753,
    688   752, 751, 750, 748, 747, 746, 745, 744,
    689   743, 742, 741, 740, 739, 738, 737, 736,
    690   735, 734, 733, 732, 731, 730, 729, 728,
    691   727, 726, 725, 724, 723, 722, 721, 720,
    692   719, 718, 717, 716, 715, 714, 713, 712,
    693   711, 710, 709, 708, 707, 706, 705, 704,
    694   703, 702, 701, 700, 699, 699, 698, 697,
    695   696, 695, 694, 693, 692, 691, 690, 689,
    696   688, 688, 687, 686, 685, 684, 683, 682,
    697   681, 680, 680, 679, 678, 677, 676, 675,
    698   674, 673, 673, 672, 671, 670, 669, 668,
    699   667, 667, 666, 665, 664, 663, 662, 661,
    700   661, 660, 659, 658, 657, 657, 656, 655,
    701   654, 653, 652, 652, 651, 650, 649, 648,
    702   648, 647, 646, 645, 644, 644, 643, 642,
    703   641, 640, 640, 639, 638, 637, 637, 636,
    704   635, 634, 633, 633, 632, 631, 630, 630,
    705   629, 628, 627, 627, 626, 625, 624, 624,
    706   623, 622, 621, 621, 620, 619, 618, 618,
    707   617, 616, 616, 615, 614, 613, 613, 612,
    708   611, 611, 610, 609, 608, 608, 607, 606,
    709   606, 605, 604, 604, 603, 602, 601, 601,
    710   600, 599, 599, 598, 597, 597, 596, 595,
    711   595, 594, 593, 593, 592, 591, 591, 590,
    712   589, 589, 588, 587, 587, 586, 585, 585,
    713   584, 583, 583, 582, 581, 581, 580, 579,
    714   579, 578, 578, 577, 576, 576, 575, 574,
    715   574, 573, 572, 572, 571, 571, 570, 569,
    716   569, 568, 568, 567, 566, 566, 565, 564,
    717   564, 563, 563, 562, 561, 561, 560, 560,
    718   559, 558, 558, 557, 557, 556, 555, 555,
    719   554, 554, 553, 553, 552, 551, 551, 550,
    720   550, 549, 548, 548, 547, 547, 546, 546,
    721   545, 544, 544, 543, 543, 542, 542, 541,
    722   541, 540, 539, 539, 538, 538, 537, 537,
    723   536, 536, 535, 534, 534, 533, 533, 532,
    724   532, 531, 531, 530, 530, 529, 529, 528,
    725   527, 527, 526, 526, 525, 525, 524, 524,
    726   523, 523, 522, 522, 521, 521, 520, 520,
    727   519, 519, 518, 518, 517, 517, 516, 516,
    728   515, 515, 514, 514
    729 };
    730 
    731 // Note that LinearToGamma() expects the values to be premultiplied by 4,
    732 // so we incorporate this factor 4 inside the DIVIDE_BY_ALPHA macro directly.
    733 #define DIVIDE_BY_ALPHA(sum, a)  (((sum) * kInvAlpha[(a)]) >> (kAlphaFix - 2))
    734 
    735 #else
    736 
    737 #define DIVIDE_BY_ALPHA(sum, a) (4 * (sum) / (a))
    738 
    739 #endif  // USE_INVERSE_ALPHA_TABLE
    740 
    741 static WEBP_INLINE int LinearToGammaWeighted(const uint8_t* src,
    742                                              const uint8_t* a_ptr,
    743                                              uint32_t total_a, int step,
    744                                              int rgb_stride) {
    745   const uint32_t sum =
    746       a_ptr[0] * GammaToLinear(src[0]) +
    747       a_ptr[step] * GammaToLinear(src[step]) +
    748       a_ptr[rgb_stride] * GammaToLinear(src[rgb_stride]) +
    749       a_ptr[rgb_stride + step] * GammaToLinear(src[rgb_stride + step]);
    750   assert(total_a > 0 && total_a <= 4 * 0xff);
    751 #if defined(USE_INVERSE_ALPHA_TABLE)
    752   assert((uint64_t)sum * kInvAlpha[total_a] < ((uint64_t)1 << 32));
    753 #endif
    754   return LinearToGamma(DIVIDE_BY_ALPHA(sum, total_a), 0);
    755 }
    756 
    757 static WEBP_INLINE void ConvertRowToY(const uint8_t* const r_ptr,
    758                                       const uint8_t* const g_ptr,
    759                                       const uint8_t* const b_ptr,
    760                                       int step,
    761                                       uint8_t* const dst_y,
    762                                       int width,
    763                                       VP8Random* const rg) {
    764   int i, j;
    765   for (i = 0, j = 0; i < width; ++i, j += step) {
    766     dst_y[i] = RGBToY(r_ptr[j], g_ptr[j], b_ptr[j], rg);
    767   }
    768 }
    769 
    770 static WEBP_INLINE void ConvertRowsToUVWithAlpha(const uint8_t* const r_ptr,
    771                                                  const uint8_t* const g_ptr,
    772                                                  const uint8_t* const b_ptr,
    773                                                  const uint8_t* const a_ptr,
    774                                                  int rgb_stride,
    775                                                  uint8_t* const dst_u,
    776                                                  uint8_t* const dst_v,
    777                                                  int width,
    778                                                  VP8Random* const rg) {
    779   int i, j;
    780   // we loop over 2x2 blocks and produce one U/V value for each.
    781   for (i = 0, j = 0; i < (width >> 1); ++i, j += 2 * sizeof(uint32_t)) {
    782     const uint32_t a = SUM4ALPHA(a_ptr + j);
    783     int r, g, b;
    784     if (a == 4 * 0xff || a == 0) {
    785       r = SUM4(r_ptr + j, 4);
    786       g = SUM4(g_ptr + j, 4);
    787       b = SUM4(b_ptr + j, 4);
    788     } else {
    789       r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 4, rgb_stride);
    790       g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 4, rgb_stride);
    791       b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 4, rgb_stride);
    792     }
    793     dst_u[i] = RGBToU(r, g, b, rg);
    794     dst_v[i] = RGBToV(r, g, b, rg);
    795   }
    796   if (width & 1) {
    797     const uint32_t a = 2u * SUM2ALPHA(a_ptr + j);
    798     int r, g, b;
    799     if (a == 4 * 0xff || a == 0) {
    800       r = SUM2(r_ptr + j);
    801       g = SUM2(g_ptr + j);
    802       b = SUM2(b_ptr + j);
    803     } else {
    804       r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 0, rgb_stride);
    805       g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 0, rgb_stride);
    806       b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 0, rgb_stride);
    807     }
    808     dst_u[i] = RGBToU(r, g, b, rg);
    809     dst_v[i] = RGBToV(r, g, b, rg);
    810   }
    811 }
    812 
    813 static WEBP_INLINE void ConvertRowsToUV(const uint8_t* const r_ptr,
    814                                         const uint8_t* const g_ptr,
    815                                         const uint8_t* const b_ptr,
    816                                         int step, int rgb_stride,
    817                                         uint8_t* const dst_u,
    818                                         uint8_t* const dst_v,
    819                                         int width,
    820                                         VP8Random* const rg) {
    821   int i, j;
    822   for (i = 0, j = 0; i < (width >> 1); ++i, j += 2 * step) {
    823     const int r = SUM4(r_ptr + j, step);
    824     const int g = SUM4(g_ptr + j, step);
    825     const int b = SUM4(b_ptr + j, step);
    826     dst_u[i] = RGBToU(r, g, b, rg);
    827     dst_v[i] = RGBToV(r, g, b, rg);
    828   }
    829   if (width & 1) {
    830     const int r = SUM2(r_ptr + j);
    831     const int g = SUM2(g_ptr + j);
    832     const int b = SUM2(b_ptr + j);
    833     dst_u[i] = RGBToU(r, g, b, rg);
    834     dst_v[i] = RGBToV(r, g, b, rg);
    835   }
    836 }
    837 
    838 static int ImportYUVAFromRGBA(const uint8_t* const r_ptr,
    839                               const uint8_t* const g_ptr,
    840                               const uint8_t* const b_ptr,
    841                               const uint8_t* const a_ptr,
    842                               int step,         // bytes per pixel
    843                               int rgb_stride,   // bytes per scanline
    844                               float dithering,
    845                               int use_iterative_conversion,
    846                               WebPPicture* const picture) {
    847   int y;
    848   const int width = picture->width;
    849   const int height = picture->height;
    850   const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride);
    851 
    852   picture->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420;
    853   picture->use_argb = 0;
    854 
    855   // disable smart conversion if source is too small (overkill).
    856   if (width < kMinDimensionIterativeConversion ||
    857       height < kMinDimensionIterativeConversion) {
    858     use_iterative_conversion = 0;
    859   }
    860 
    861   if (!WebPPictureAllocYUVA(picture, width, height)) {
    862     return 0;
    863   }
    864   if (has_alpha) {
    865     WebPInitAlphaProcessing();
    866     assert(step == 4);
    867 #if defined(USE_INVERSE_ALPHA_TABLE)
    868     assert(kAlphaFix + kGammaFix <= 31);
    869 #endif
    870   }
    871 
    872   if (use_iterative_conversion) {
    873     InitGammaTablesF();
    874     if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) {
    875       return 0;
    876     }
    877     if (has_alpha) {
    878       WebPExtractAlpha(a_ptr, rgb_stride, width, height,
    879                        picture->a, picture->a_stride);
    880     }
    881   } else {
    882     uint8_t* dst_y = picture->y;
    883     uint8_t* dst_u = picture->u;
    884     uint8_t* dst_v = picture->v;
    885     uint8_t* dst_a = picture->a;
    886 
    887     VP8Random base_rg;
    888     VP8Random* rg = NULL;
    889     if (dithering > 0.) {
    890       VP8InitRandom(&base_rg, dithering);
    891       rg = &base_rg;
    892     }
    893 
    894     InitGammaTables();
    895 
    896     // Downsample Y/U/V planes, two rows at a time
    897     for (y = 0; y < (height >> 1); ++y) {
    898       int rows_have_alpha = has_alpha;
    899       const int off1 = (2 * y + 0) * rgb_stride;
    900       const int off2 = (2 * y + 1) * rgb_stride;
    901       ConvertRowToY(r_ptr + off1, g_ptr + off1, b_ptr + off1, step,
    902                     dst_y, width, rg);
    903       ConvertRowToY(r_ptr + off2, g_ptr + off2, b_ptr + off2, step,
    904                     dst_y + picture->y_stride, width, rg);
    905       dst_y += 2 * picture->y_stride;
    906       if (has_alpha) {
    907         rows_have_alpha &= !WebPExtractAlpha(a_ptr + off1, rgb_stride,
    908                                              width, 2,
    909                                              dst_a, picture->a_stride);
    910         dst_a += 2 * picture->a_stride;
    911       }
    912       if (!rows_have_alpha) {
    913         ConvertRowsToUV(r_ptr + off1, g_ptr + off1, b_ptr + off1,
    914                         step, rgb_stride, dst_u, dst_v, width, rg);
    915       } else {
    916         ConvertRowsToUVWithAlpha(r_ptr + off1, g_ptr + off1, b_ptr + off1,
    917                                  a_ptr + off1, rgb_stride,
    918                                  dst_u, dst_v, width, rg);
    919       }
    920       dst_u += picture->uv_stride;
    921       dst_v += picture->uv_stride;
    922     }
    923     if (height & 1) {    // extra last row
    924       const int off = 2 * y * rgb_stride;
    925       int row_has_alpha = has_alpha;
    926       ConvertRowToY(r_ptr + off, g_ptr + off, b_ptr + off, step,
    927                     dst_y, width, rg);
    928       if (row_has_alpha) {
    929         row_has_alpha &= !WebPExtractAlpha(a_ptr + off, 0, width, 1, dst_a, 0);
    930       }
    931       if (!row_has_alpha) {
    932         ConvertRowsToUV(r_ptr + off, g_ptr + off, b_ptr + off,
    933                         step, 0, dst_u, dst_v, width, rg);
    934       } else {
    935         ConvertRowsToUVWithAlpha(r_ptr + off, g_ptr + off, b_ptr + off,
    936                                  a_ptr + off, 0,
    937                                  dst_u, dst_v, width, rg);
    938       }
    939     }
    940   }
    941   return 1;
    942 }
    943 
    944 #undef SUM4
    945 #undef SUM2
    946 #undef SUM4ALPHA
    947 #undef SUM2ALPHA
    948 
    949 //------------------------------------------------------------------------------
    950 // call for ARGB->YUVA conversion
    951 
    952 static int PictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace,
    953                              float dithering, int use_iterative_conversion) {
    954   if (picture == NULL) return 0;
    955   if (picture->argb == NULL) {
    956     return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
    957   } else if ((colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
    958     return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
    959   } else {
    960     const uint8_t* const argb = (const uint8_t*)picture->argb;
    961     const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1;
    962     const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2;
    963     const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3;
    964     const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0;
    965 
    966     picture->colorspace = WEBP_YUV420;
    967     return ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride,
    968                               dithering, use_iterative_conversion, picture);
    969   }
    970 }
    971 
    972 int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace,
    973                                   float dithering) {
    974   return PictureARGBToYUVA(picture, colorspace, dithering, 0);
    975 }
    976 
    977 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) {
    978   return PictureARGBToYUVA(picture, colorspace, 0.f, 0);
    979 }
    980 
    981 #if WEBP_ENCODER_ABI_VERSION > 0x0204
    982 int WebPPictureSmartARGBToYUVA(WebPPicture* picture) {
    983   return PictureARGBToYUVA(picture, WEBP_YUV420, 0.f, 1);
    984 }
    985 #endif
    986 
    987 //------------------------------------------------------------------------------
    988 // call for YUVA -> ARGB conversion
    989 
    990 int WebPPictureYUVAToARGB(WebPPicture* picture) {
    991   if (picture == NULL) return 0;
    992   if (picture->y == NULL || picture->u == NULL || picture->v == NULL) {
    993     return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
    994   }
    995   if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) {
    996     return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
    997   }
    998   if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
    999     return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
   1000   }
   1001   // Allocate a new argb buffer (discarding the previous one).
   1002   if (!WebPPictureAllocARGB(picture, picture->width, picture->height)) return 0;
   1003   picture->use_argb = 1;
   1004 
   1005   // Convert
   1006   {
   1007     int y;
   1008     const int width = picture->width;
   1009     const int height = picture->height;
   1010     const int argb_stride = 4 * picture->argb_stride;
   1011     uint8_t* dst = (uint8_t*)picture->argb;
   1012     const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y;
   1013     WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST);
   1014 
   1015     // First row, with replicated top samples.
   1016     upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
   1017     cur_y += picture->y_stride;
   1018     dst += argb_stride;
   1019     // Center rows.
   1020     for (y = 1; y + 1 < height; y += 2) {
   1021       const uint8_t* const top_u = cur_u;
   1022       const uint8_t* const top_v = cur_v;
   1023       cur_u += picture->uv_stride;
   1024       cur_v += picture->uv_stride;
   1025       upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v,
   1026                dst, dst + argb_stride, width);
   1027       cur_y += 2 * picture->y_stride;
   1028       dst += 2 * argb_stride;
   1029     }
   1030     // Last row (if needed), with replicated bottom samples.
   1031     if (height > 1 && !(height & 1)) {
   1032       upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
   1033     }
   1034     // Insert alpha values if needed, in replacement for the default 0xff ones.
   1035     if (picture->colorspace & WEBP_CSP_ALPHA_BIT) {
   1036       for (y = 0; y < height; ++y) {
   1037         uint32_t* const argb_dst = picture->argb + y * picture->argb_stride;
   1038         const uint8_t* const src = picture->a + y * picture->a_stride;
   1039         int x;
   1040         for (x = 0; x < width; ++x) {
   1041           argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24);
   1042         }
   1043       }
   1044     }
   1045   }
   1046   return 1;
   1047 }
   1048 
   1049 //------------------------------------------------------------------------------
   1050 // automatic import / conversion
   1051 
   1052 static int Import(WebPPicture* const picture,
   1053                   const uint8_t* const rgb, int rgb_stride,
   1054                   int step, int swap_rb, int import_alpha) {
   1055   int y;
   1056   const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0);
   1057   const uint8_t* const g_ptr = rgb + 1;
   1058   const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2);
   1059   const uint8_t* const a_ptr = import_alpha ? rgb + 3 : NULL;
   1060   const int width = picture->width;
   1061   const int height = picture->height;
   1062 
   1063   if (!picture->use_argb) {
   1064     return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride,
   1065                               0.f /* no dithering */, 0, picture);
   1066   }
   1067   if (!WebPPictureAlloc(picture)) return 0;
   1068 
   1069   assert(step >= (import_alpha ? 4 : 3));
   1070   for (y = 0; y < height; ++y) {
   1071     uint32_t* const dst = &picture->argb[y * picture->argb_stride];
   1072     int x;
   1073     for (x = 0; x < width; ++x) {
   1074       const int offset = step * x + y * rgb_stride;
   1075       dst[x] = MakeARGB32(import_alpha ? a_ptr[offset] : 0xff,
   1076                           r_ptr[offset], g_ptr[offset], b_ptr[offset]);
   1077     }
   1078   }
   1079   return 1;
   1080 }
   1081 
   1082 // Public API
   1083 
   1084 int WebPPictureImportRGB(WebPPicture* picture,
   1085                          const uint8_t* rgb, int rgb_stride) {
   1086   return (picture != NULL) ? Import(picture, rgb, rgb_stride, 3, 0, 0) : 0;
   1087 }
   1088 
   1089 int WebPPictureImportBGR(WebPPicture* picture,
   1090                          const uint8_t* rgb, int rgb_stride) {
   1091   return (picture != NULL) ? Import(picture, rgb, rgb_stride, 3, 1, 0) : 0;
   1092 }
   1093 
   1094 int WebPPictureImportRGBA(WebPPicture* picture,
   1095                           const uint8_t* rgba, int rgba_stride) {
   1096   return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 0, 1) : 0;
   1097 }
   1098 
   1099 int WebPPictureImportBGRA(WebPPicture* picture,
   1100                           const uint8_t* rgba, int rgba_stride) {
   1101   return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 1, 1) : 0;
   1102 }
   1103 
   1104 int WebPPictureImportRGBX(WebPPicture* picture,
   1105                           const uint8_t* rgba, int rgba_stride) {
   1106   return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 0, 0) : 0;
   1107 }
   1108 
   1109 int WebPPictureImportBGRX(WebPPicture* picture,
   1110                           const uint8_t* rgba, int rgba_stride) {
   1111   return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 1, 0) : 0;
   1112 }
   1113 
   1114 //------------------------------------------------------------------------------
   1115