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@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 
MakeARGB32(int a,int r,int g,int b)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.
CheckNonOpaque(const uint8_t * alpha,int width,int height,int x_step,int y_step)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.
WebPPictureHasTransparency(const WebPPicture * picture)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 
InitGammaTables(void)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 
GammaToLinear(uint8_t v)110 static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) {
111   return kGammaToLinearTab[v];
112 }
113 
Interpolate(int v)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.
LinearToGamma(uint32_t base_value,int shift)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 
InitGammaTables(void)133 static void InitGammaTables(void) {}
GammaToLinear(uint8_t v)134 static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; }
LinearToGamma(uint32_t base_value,int shift)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 
RGBToY(int r,int g,int b,VP8Random * const rg)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 
RGBToU(int r,int g,int b,VP8Random * const rg)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 
RGBToV(int r,int g,int b,VP8Random * const rg)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 
InitGammaTablesF(void)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 
GammaToLinearF(int v)206 static WEBP_INLINE float GammaToLinearF(int v) {
207   return kGammaToLinearTabF[v];
208 }
209 
LinearToGammaF(float value)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 
InitGammaTablesF(void)222 static void InitGammaTablesF(void) {}
GammaToLinearF(int v)223 static WEBP_INLINE float GammaToLinearF(int v) {
224   const float norm = 1.f / MAX_Y_T;
225   return norm * v;
226 }
LinearToGammaF(float value)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
FixedYToW(int v)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 
FixedWToY(int v)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 
clip_8b(fixed_t v)256 static uint8_t clip_8b(fixed_t v) {
257   return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u;
258 }
259 
clip_y(int y)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
clip_fixed_t(fixed_t v)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 
RGBToGray(int r,int g,int b)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 
RGBToGrayF(float r,float g,float b)278 static float RGBToGrayF(float r, float g, float b) {
279   return 0.299f * r + 0.587f * g + 0.114f * b;
280 }
281 
ScaleDown(int a,int b,int c,int d)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 
UpdateW(const fixed_y_t * src,fixed_y_t * dst,int len)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 
UpdateChroma(const fixed_y_t * src1,const fixed_y_t * src2,fixed_t * dst,fixed_y_t * tmp,int len)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 
Filter(const fixed_t * const A,const fixed_t * const B,int rightwise)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 
Filter2(int A,int B)335 static WEBP_INLINE int Filter2(int A, int B) { return (A * 3 + B + 2) >> 2; }
336 
337 //------------------------------------------------------------------------------
338 
339 // 8bit -> YFIX
UpLift(uint8_t a)340 static WEBP_INLINE fixed_y_t UpLift(uint8_t a) {
341   return ((fixed_y_t)a << YFIX) | (1 << (YFIX - 1));
342 }
343 
ImportOneRow(const uint8_t * const r_ptr,const uint8_t * const g_ptr,const uint8_t * const b_ptr,int step,int pic_width,fixed_y_t * const dst)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 
InterpolateTwoRows(const fixed_y_t * const best_y,const fixed_t * const prev_uv,const fixed_t * const cur_uv,const fixed_t * const next_uv,int w,fixed_y_t * const out1,fixed_y_t * const out2)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 
ConvertRGBToY(int r,int g,int b)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 
ConvertRGBToU(int r,int g,int b)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 
ConvertRGBToV(int r,int g,int b)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 
ConvertWRGBToYUV(const fixed_y_t * const best_y,const fixed_t * const best_uv,WebPPicture * const picture)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 
PreprocessARGB(const uint8_t * const r_ptr,const uint8_t * const g_ptr,const uint8_t * const b_ptr,int step,int rgb_stride,WebPPicture * const picture)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 
LinearToGammaWeighted(const uint8_t * src,const uint8_t * a_ptr,uint32_t total_a,int step,int rgb_stride)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 
ConvertRowToY(const uint8_t * const r_ptr,const uint8_t * const g_ptr,const uint8_t * const b_ptr,int step,uint8_t * const dst_y,int width,VP8Random * const rg)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 
ConvertRowsToUVWithAlpha(const uint8_t * const r_ptr,const uint8_t * const g_ptr,const uint8_t * const b_ptr,const uint8_t * const a_ptr,int rgb_stride,uint8_t * const dst_u,uint8_t * const dst_v,int width,VP8Random * const rg)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 
ConvertRowsToUV(const uint8_t * const r_ptr,const uint8_t * const g_ptr,const uint8_t * const b_ptr,int step,int rgb_stride,uint8_t * const dst_u,uint8_t * const dst_v,int width,VP8Random * const rg)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 
ImportYUVAFromRGBA(const uint8_t * const r_ptr,const uint8_t * const g_ptr,const uint8_t * const b_ptr,const uint8_t * const a_ptr,int step,int rgb_stride,float dithering,int use_iterative_conversion,WebPPicture * const picture)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 
PictureARGBToYUVA(WebPPicture * picture,WebPEncCSP colorspace,float dithering,int use_iterative_conversion)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 
WebPPictureARGBToYUVADithered(WebPPicture * picture,WebPEncCSP colorspace,float dithering)972 int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace,
973                                   float dithering) {
974   return PictureARGBToYUVA(picture, colorspace, dithering, 0);
975 }
976 
WebPPictureARGBToYUVA(WebPPicture * picture,WebPEncCSP colorspace)977 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) {
978   return PictureARGBToYUVA(picture, colorspace, 0.f, 0);
979 }
980 
981 #if WEBP_ENCODER_ABI_VERSION > 0x0204
WebPPictureSmartARGBToYUVA(WebPPicture * picture)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 
WebPPictureYUVAToARGB(WebPPicture * picture)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 
Import(WebPPicture * const picture,const uint8_t * const rgb,int rgb_stride,int step,int swap_rb,int import_alpha)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 
WebPPictureImportRGB(WebPPicture * picture,const uint8_t * rgb,int rgb_stride)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 
WebPPictureImportBGR(WebPPicture * picture,const uint8_t * rgb,int rgb_stride)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 
WebPPictureImportRGBA(WebPPicture * picture,const uint8_t * rgba,int rgba_stride)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 
WebPPictureImportBGRA(WebPPicture * picture,const uint8_t * rgba,int rgba_stride)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 
WebPPictureImportRGBX(WebPPicture * picture,const uint8_t * rgba,int rgba_stride)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 
WebPPictureImportBGRX(WebPPicture * picture,const uint8_t * rgba,int rgba_stride)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