• 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 "src/enc/vp8i_enc.h"
19 #include "src/utils/random_utils.h"
20 #include "src/utils/utils.h"
21 #include "src/dsp/dsp.h"
22 #include "src/dsp/lossless.h"
23 #include "src/dsp/yuv.h"
24 
25 // Uncomment to disable gamma-compression during RGB->U/V averaging
26 #define USE_GAMMA_COMPRESSION
27 
28 // If defined, use table to compute x / alpha.
29 #define USE_INVERSE_ALPHA_TABLE
30 
31 static const union {
32   uint32_t argb;
33   uint8_t  bytes[4];
34 } test_endian = { 0xff000000u };
35 #define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff)
36 
37 //------------------------------------------------------------------------------
38 // Detection of non-trivial transparency
39 
40 // Returns true if alpha[] has non-0xff values.
CheckNonOpaque(const uint8_t * alpha,int width,int height,int x_step,int y_step)41 static int CheckNonOpaque(const uint8_t* alpha, int width, int height,
42                           int x_step, int y_step) {
43   if (alpha == NULL) return 0;
44   WebPInitAlphaProcessing();
45   if (x_step == 1) {
46     for (; height-- > 0; alpha += y_step) {
47       if (WebPHasAlpha8b(alpha, width)) return 1;
48     }
49   } else {
50     for (; height-- > 0; alpha += y_step) {
51       if (WebPHasAlpha32b(alpha, width)) return 1;
52     }
53   }
54   return 0;
55 }
56 
57 // Checking for the presence of non-opaque alpha.
WebPPictureHasTransparency(const WebPPicture * picture)58 int WebPPictureHasTransparency(const WebPPicture* picture) {
59   if (picture == NULL) return 0;
60   if (!picture->use_argb) {
61     return CheckNonOpaque(picture->a, picture->width, picture->height,
62                           1, picture->a_stride);
63   } else {
64     const int alpha_offset = ALPHA_IS_LAST ? 3 : 0;
65     return CheckNonOpaque((const uint8_t*)picture->argb + alpha_offset,
66                           picture->width, picture->height,
67                           4, picture->argb_stride * sizeof(*picture->argb));
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 / BT2020
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.09929682680944;
187     const double thresh = 0.018053968510807;
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     assert(step == 4);
860 #if defined(USE_GAMMA_COMPRESSION) && defined(USE_INVERSE_ALPHA_TABLE)
861     assert(kAlphaFix + kGammaFix <= 31);
862 #endif
863   }
864 
865   if (use_iterative_conversion) {
866     InitGammaTablesF();
867     if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) {
868       return 0;
869     }
870     if (has_alpha) {
871       WebPExtractAlpha(a_ptr, rgb_stride, width, height,
872                        picture->a, picture->a_stride);
873     }
874   } else {
875     const int uv_width = (width + 1) >> 1;
876     int use_dsp = (step == 3);  // use special function in this case
877     // temporary storage for accumulated R/G/B values during conversion to U/V
878     uint16_t* const tmp_rgb =
879         (uint16_t*)WebPSafeMalloc(4 * uv_width, sizeof(*tmp_rgb));
880     uint8_t* dst_y = picture->y;
881     uint8_t* dst_u = picture->u;
882     uint8_t* dst_v = picture->v;
883     uint8_t* dst_a = picture->a;
884 
885     VP8Random base_rg;
886     VP8Random* rg = NULL;
887     if (dithering > 0.) {
888       VP8InitRandom(&base_rg, dithering);
889       rg = &base_rg;
890       use_dsp = 0;   // can't use dsp in this case
891     }
892     WebPInitConvertARGBToYUV();
893     InitGammaTables();
894 
895     if (tmp_rgb == NULL) return 0;  // malloc error
896 
897     // Downsample Y/U/V planes, two rows at a time
898     for (y = 0; y < (height >> 1); ++y) {
899       int rows_have_alpha = has_alpha;
900       if (use_dsp) {
901         if (is_rgb) {
902           WebPConvertRGB24ToY(r_ptr, dst_y, width);
903           WebPConvertRGB24ToY(r_ptr + rgb_stride,
904                               dst_y + picture->y_stride, width);
905         } else {
906           WebPConvertBGR24ToY(b_ptr, dst_y, width);
907           WebPConvertBGR24ToY(b_ptr + rgb_stride,
908                               dst_y + picture->y_stride, width);
909         }
910       } else {
911         ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg);
912         ConvertRowToY(r_ptr + rgb_stride,
913                       g_ptr + rgb_stride,
914                       b_ptr + rgb_stride, step,
915                       dst_y + picture->y_stride, width, rg);
916       }
917       dst_y += 2 * picture->y_stride;
918       if (has_alpha) {
919         rows_have_alpha &= !WebPExtractAlpha(a_ptr, rgb_stride, width, 2,
920                                              dst_a, picture->a_stride);
921         dst_a += 2 * picture->a_stride;
922       }
923       // Collect averaged R/G/B(/A)
924       if (!rows_have_alpha) {
925         AccumulateRGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, tmp_rgb, width);
926       } else {
927         AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, rgb_stride, tmp_rgb, width);
928       }
929       // Convert to U/V
930       if (rg == NULL) {
931         WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width);
932       } else {
933         ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg);
934       }
935       dst_u += picture->uv_stride;
936       dst_v += picture->uv_stride;
937       r_ptr += 2 * rgb_stride;
938       b_ptr += 2 * rgb_stride;
939       g_ptr += 2 * rgb_stride;
940       if (has_alpha) a_ptr += 2 * rgb_stride;
941     }
942     if (height & 1) {    // extra last row
943       int row_has_alpha = has_alpha;
944       if (use_dsp) {
945         if (r_ptr < b_ptr) {
946           WebPConvertRGB24ToY(r_ptr, dst_y, width);
947         } else {
948           WebPConvertBGR24ToY(b_ptr, dst_y, width);
949         }
950       } else {
951         ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg);
952       }
953       if (row_has_alpha) {
954         row_has_alpha &= !WebPExtractAlpha(a_ptr, 0, width, 1, dst_a, 0);
955       }
956       // Collect averaged R/G/B(/A)
957       if (!row_has_alpha) {
958         // Collect averaged R/G/B
959         AccumulateRGB(r_ptr, g_ptr, b_ptr, step, /* rgb_stride = */ 0,
960                       tmp_rgb, width);
961       } else {
962         AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, /* rgb_stride = */ 0,
963                        tmp_rgb, width);
964       }
965       if (rg == NULL) {
966         WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width);
967       } else {
968         ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg);
969       }
970     }
971     WebPSafeFree(tmp_rgb);
972   }
973   return 1;
974 }
975 
976 #undef SUM4
977 #undef SUM2
978 #undef SUM4ALPHA
979 #undef SUM2ALPHA
980 
981 //------------------------------------------------------------------------------
982 // call for ARGB->YUVA conversion
983 
PictureARGBToYUVA(WebPPicture * picture,WebPEncCSP colorspace,float dithering,int use_iterative_conversion)984 static int PictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace,
985                              float dithering, int use_iterative_conversion) {
986   if (picture == NULL) return 0;
987   if (picture->argb == NULL) {
988     return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
989   } else if ((colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
990     return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
991   } else {
992     const uint8_t* const argb = (const uint8_t*)picture->argb;
993     const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1;
994     const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2;
995     const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3;
996     const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0;
997 
998     picture->colorspace = WEBP_YUV420;
999     return ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride,
1000                               dithering, use_iterative_conversion, picture);
1001   }
1002 }
1003 
WebPPictureARGBToYUVADithered(WebPPicture * picture,WebPEncCSP colorspace,float dithering)1004 int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace,
1005                                   float dithering) {
1006   return PictureARGBToYUVA(picture, colorspace, dithering, 0);
1007 }
1008 
WebPPictureARGBToYUVA(WebPPicture * picture,WebPEncCSP colorspace)1009 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) {
1010   return PictureARGBToYUVA(picture, colorspace, 0.f, 0);
1011 }
1012 
WebPPictureSharpARGBToYUVA(WebPPicture * picture)1013 int WebPPictureSharpARGBToYUVA(WebPPicture* picture) {
1014   return PictureARGBToYUVA(picture, WEBP_YUV420, 0.f, 1);
1015 }
1016 // for backward compatibility
WebPPictureSmartARGBToYUVA(WebPPicture * picture)1017 int WebPPictureSmartARGBToYUVA(WebPPicture* picture) {
1018   return WebPPictureSharpARGBToYUVA(picture);
1019 }
1020 
1021 //------------------------------------------------------------------------------
1022 // call for YUVA -> ARGB conversion
1023 
WebPPictureYUVAToARGB(WebPPicture * picture)1024 int WebPPictureYUVAToARGB(WebPPicture* picture) {
1025   if (picture == NULL) return 0;
1026   if (picture->y == NULL || picture->u == NULL || picture->v == NULL) {
1027     return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
1028   }
1029   if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) {
1030     return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
1031   }
1032   if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
1033     return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
1034   }
1035   // Allocate a new argb buffer (discarding the previous one).
1036   if (!WebPPictureAllocARGB(picture, picture->width, picture->height)) return 0;
1037   picture->use_argb = 1;
1038 
1039   // Convert
1040   {
1041     int y;
1042     const int width = picture->width;
1043     const int height = picture->height;
1044     const int argb_stride = 4 * picture->argb_stride;
1045     uint8_t* dst = (uint8_t*)picture->argb;
1046     const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y;
1047     WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST);
1048 
1049     // First row, with replicated top samples.
1050     upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
1051     cur_y += picture->y_stride;
1052     dst += argb_stride;
1053     // Center rows.
1054     for (y = 1; y + 1 < height; y += 2) {
1055       const uint8_t* const top_u = cur_u;
1056       const uint8_t* const top_v = cur_v;
1057       cur_u += picture->uv_stride;
1058       cur_v += picture->uv_stride;
1059       upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v,
1060                dst, dst + argb_stride, width);
1061       cur_y += 2 * picture->y_stride;
1062       dst += 2 * argb_stride;
1063     }
1064     // Last row (if needed), with replicated bottom samples.
1065     if (height > 1 && !(height & 1)) {
1066       upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
1067     }
1068     // Insert alpha values if needed, in replacement for the default 0xff ones.
1069     if (picture->colorspace & WEBP_CSP_ALPHA_BIT) {
1070       for (y = 0; y < height; ++y) {
1071         uint32_t* const argb_dst = picture->argb + y * picture->argb_stride;
1072         const uint8_t* const src = picture->a + y * picture->a_stride;
1073         int x;
1074         for (x = 0; x < width; ++x) {
1075           argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24);
1076         }
1077       }
1078     }
1079   }
1080   return 1;
1081 }
1082 
1083 //------------------------------------------------------------------------------
1084 // automatic import / conversion
1085 
Import(WebPPicture * const picture,const uint8_t * rgb,int rgb_stride,int step,int swap_rb,int import_alpha)1086 static int Import(WebPPicture* const picture,
1087                   const uint8_t* rgb, int rgb_stride,
1088                   int step, int swap_rb, int import_alpha) {
1089   int y;
1090   const uint8_t* r_ptr = rgb + (swap_rb ? 2 : 0);
1091   const uint8_t* g_ptr = rgb + 1;
1092   const uint8_t* b_ptr = rgb + (swap_rb ? 0 : 2);
1093   const int width = picture->width;
1094   const int height = picture->height;
1095 
1096   if (!picture->use_argb) {
1097     const uint8_t* a_ptr = import_alpha ? rgb + 3 : NULL;
1098     return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride,
1099                               0.f /* no dithering */, 0, picture);
1100   }
1101   if (!WebPPictureAlloc(picture)) return 0;
1102 
1103   VP8LDspInit();
1104   WebPInitAlphaProcessing();
1105 
1106   if (import_alpha) {
1107     uint32_t* dst = picture->argb;
1108     const int do_copy =
1109         (!swap_rb && !ALPHA_IS_LAST) || (swap_rb && ALPHA_IS_LAST);
1110     assert(step == 4);
1111     for (y = 0; y < height; ++y) {
1112       if (do_copy) {
1113         memcpy(dst, rgb, width * 4);
1114       } else {
1115         // RGBA input order. Need to swap R and B.
1116         VP8LConvertBGRAToRGBA((const uint32_t*)rgb, width, (uint8_t*)dst);
1117       }
1118       rgb += rgb_stride;
1119       dst += picture->argb_stride;
1120     }
1121   } else {
1122     uint32_t* dst = picture->argb;
1123     assert(step >= 3);
1124     for (y = 0; y < height; ++y) {
1125       WebPPackRGB(r_ptr, g_ptr, b_ptr, width, step, dst);
1126       r_ptr += rgb_stride;
1127       g_ptr += rgb_stride;
1128       b_ptr += rgb_stride;
1129       dst += picture->argb_stride;
1130     }
1131   }
1132   return 1;
1133 }
1134 
1135 // Public API
1136 
1137 #if !defined(WEBP_REDUCE_CSP)
1138 
WebPPictureImportBGR(WebPPicture * picture,const uint8_t * rgb,int rgb_stride)1139 int WebPPictureImportBGR(WebPPicture* picture,
1140                          const uint8_t* rgb, int rgb_stride) {
1141   return (picture != NULL && rgb != NULL)
1142              ? Import(picture, rgb, rgb_stride, 3, 1, 0)
1143              : 0;
1144 }
1145 
WebPPictureImportBGRA(WebPPicture * picture,const uint8_t * rgba,int rgba_stride)1146 int WebPPictureImportBGRA(WebPPicture* picture,
1147                           const uint8_t* rgba, int rgba_stride) {
1148   return (picture != NULL && rgba != NULL)
1149              ? Import(picture, rgba, rgba_stride, 4, 1, 1)
1150              : 0;
1151 }
1152 
1153 
WebPPictureImportBGRX(WebPPicture * picture,const uint8_t * rgba,int rgba_stride)1154 int WebPPictureImportBGRX(WebPPicture* picture,
1155                           const uint8_t* rgba, int rgba_stride) {
1156   return (picture != NULL && rgba != NULL)
1157              ? Import(picture, rgba, rgba_stride, 4, 1, 0)
1158              : 0;
1159 }
1160 
1161 #endif   // WEBP_REDUCE_CSP
1162 
WebPPictureImportRGB(WebPPicture * picture,const uint8_t * rgb,int rgb_stride)1163 int WebPPictureImportRGB(WebPPicture* picture,
1164                          const uint8_t* rgb, int rgb_stride) {
1165   return (picture != NULL && rgb != NULL)
1166              ? Import(picture, rgb, rgb_stride, 3, 0, 0)
1167              : 0;
1168 }
1169 
WebPPictureImportRGBA(WebPPicture * picture,const uint8_t * rgba,int rgba_stride)1170 int WebPPictureImportRGBA(WebPPicture* picture,
1171                           const uint8_t* rgba, int rgba_stride) {
1172   return (picture != NULL && rgba != NULL)
1173              ? Import(picture, rgba, rgba_stride, 4, 0, 1)
1174              : 0;
1175 }
1176 
WebPPictureImportRGBX(WebPPicture * picture,const uint8_t * rgba,int rgba_stride)1177 int WebPPictureImportRGBX(WebPPicture* picture,
1178                           const uint8_t* rgba, int rgba_stride) {
1179   return (picture != NULL && rgba != NULL)
1180              ? Import(picture, rgba, rgba_stride, 4, 0, 0)
1181              : 0;
1182 }
1183 
1184 //------------------------------------------------------------------------------
1185