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