Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1289)

Side by Side Diff: third_party/libwebp/enc/picture_csp.c

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

Powered by Google App Engine
This is Rietveld 408576698