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