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