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