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