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

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

Issue 10832153: libwebp: update snapshot to v0.2.0-rc1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 4 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 | Annotate | Revision Log
OLDNEW
1 // Copyright 2011 Google Inc. 1 // Copyright 2011 Google Inc. All Rights Reserved.
2 // 2 //
3 // This code is licensed under the same terms as WebM: 3 // This code is licensed under the same terms as WebM:
4 // Software License Agreement: http://www.webmproject.org/license/software/ 4 // Software License Agreement: http://www.webmproject.org/license/software/
5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ 5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6 // ----------------------------------------------------------------------------- 6 // -----------------------------------------------------------------------------
7 // 7 //
8 // WebPPicture utils: colorspace conversion, crop, ... 8 // WebPPicture utils: colorspace conversion, crop, ...
9 // 9 //
10 // Author: Skal (pascal.massimino@gmail.com) 10 // Author: Skal (pascal.massimino@gmail.com)
11 11
12 #include <assert.h> 12 #include <assert.h>
13 #include <stdlib.h> 13 #include <stdlib.h>
14 #include "vp8enci.h" 14 #include <math.h>
15
16 #include "./vp8enci.h"
17 #include "../utils/rescaler.h"
18 #include "../utils/utils.h"
19 #include "../dsp/dsp.h"
20 #include "../dsp/yuv.h"
15 21
16 #if defined(__cplusplus) || defined(c_plusplus) 22 #if defined(__cplusplus) || defined(c_plusplus)
17 extern "C" { 23 extern "C" {
18 #endif 24 #endif
19 25
26 #define HALVE(x) (((x) + 1) >> 1)
27 #define IS_YUV_CSP(csp, YUV_CSP) (((csp) & WEBP_CSP_UV_MASK) == (YUV_CSP))
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
20 //------------------------------------------------------------------------------ 35 //------------------------------------------------------------------------------
21 // WebPPicture 36 // WebPPicture
22 //------------------------------------------------------------------------------ 37 //------------------------------------------------------------------------------
23 38
24 int WebPPictureAlloc(WebPPicture* const picture) { 39 int WebPPictureAlloc(WebPPicture* picture) {
25 if (picture) { 40 if (picture != NULL) {
26 const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; 41 const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK;
27 const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT; 42 const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT;
28 const int width = picture->width; 43 const int width = picture->width;
29 const int height = picture->height; 44 const int height = picture->height;
30 const int y_stride = width; 45
31 const int uv_width = (width + 1) / 2; 46 if (!picture->use_argb) {
32 const int uv_height = (height + 1) / 2; 47 const int y_stride = width;
33 const int uv_stride = uv_width; 48 const int uv_width = HALVE(width);
34 int uv0_stride = 0; 49 const int uv_height = HALVE(height);
35 int a_width, a_stride; 50 const int uv_stride = uv_width;
36 uint64_t y_size, uv_size, uv0_size, a_size, total_size; 51 int uv0_stride = 0;
37 uint8_t* mem; 52 int a_width, a_stride;
38 53 uint64_t y_size, uv_size, uv0_size, a_size, total_size;
39 // U/V 54 uint8_t* mem;
40 switch (uv_csp) { 55
41 case WEBP_YUV420: 56 // U/V
42 break; 57 switch (uv_csp) {
58 case WEBP_YUV420:
59 break;
43 #ifdef WEBP_EXPERIMENTAL_FEATURES 60 #ifdef WEBP_EXPERIMENTAL_FEATURES
44 case WEBP_YUV400: // for now, we'll just reset the U/V samples 61 case WEBP_YUV400: // for now, we'll just reset the U/V samples
45 break; 62 break;
46 case WEBP_YUV422: 63 case WEBP_YUV422:
47 uv0_stride = uv_width; 64 uv0_stride = uv_width;
48 break; 65 break;
49 case WEBP_YUV444: 66 case WEBP_YUV444:
50 uv0_stride = width; 67 uv0_stride = width;
51 break; 68 break;
52 #endif 69 #endif
53 default: 70 default:
71 return 0;
72 }
73 uv0_size = height * uv0_stride;
74
75 // alpha
76 a_width = has_alpha ? width : 0;
77 a_stride = a_width;
78 y_size = (uint64_t)y_stride * height;
79 uv_size = (uint64_t)uv_stride * uv_height;
80 a_size = (uint64_t)a_stride * height;
81
82 total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size;
83
84 // Security and validation checks
85 if (width <= 0 || height <= 0 || // luma/alpha param error
86 uv_width < 0 || uv_height < 0) { // u/v param error
54 return 0; 87 return 0;
55 } 88 }
56 uv0_size = height * uv0_stride; 89 // Clear previous buffer and allocate a new one.
57 90 WebPPictureFree(picture); // erase previous buffer
58 // alpha 91 mem = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*mem));
59 a_width = has_alpha ? width : 0; 92 if (mem == NULL) return 0;
60 a_stride = a_width; 93
61 y_size = (uint64_t)y_stride * height; 94 // From now on, we're in the clear, we can no longer fail...
62 uv_size = (uint64_t)uv_stride * uv_height; 95 picture->memory_ = (void*)mem;
63 a_size = (uint64_t)a_stride * height; 96 picture->y_stride = y_stride;
64 97 picture->uv_stride = uv_stride;
65 total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size; 98 picture->a_stride = a_stride;
66 99 picture->uv0_stride = uv0_stride;
67 // Security and validation checks 100 // TODO(skal): we could align the y/u/v planes and adjust stride.
68 if (width <= 0 || height <= 0 || // check for luma/alpha param error 101 picture->y = mem;
69 uv_width < 0 || uv_height < 0 || // check for u/v param error 102 mem += y_size;
70 y_size >= (1ULL << 40) || // check for reasonable global size 103
71 (size_t)total_size != total_size) { // check for overflow on 32bit 104 picture->u = mem;
72 return 0; 105 mem += uv_size;
73 } 106 picture->v = mem;
74 picture->y_stride = y_stride; 107 mem += uv_size;
75 picture->uv_stride = uv_stride; 108
76 picture->a_stride = a_stride; 109 if (a_size) {
77 picture->uv0_stride = uv0_stride; 110 picture->a = mem;
78 WebPPictureFree(picture); // erase previous buffer 111 mem += a_size;
79 mem = (uint8_t*)malloc((size_t)total_size); 112 }
80 if (mem == NULL) return 0; 113 if (uv0_size) {
81 114 picture->u0 = mem;
82 picture->y = mem; 115 mem += uv0_size;
83 mem += y_size; 116 picture->v0 = mem;
84 117 mem += uv0_size;
85 picture->u = mem; 118 }
86 mem += uv_size; 119 } else {
87 picture->v = mem; 120 void* memory;
88 mem += uv_size; 121 const uint64_t argb_size = (uint64_t)width * height;
89 122 if (width <= 0 || height <= 0) {
90 if (a_size) { 123 return 0;
91 picture->a = mem; 124 }
92 mem += a_size; 125 // Clear previous buffer and allocate a new one.
93 } 126 WebPPictureFree(picture); // erase previous buffer
94 if (uv0_size) { 127 memory = WebPSafeMalloc(argb_size, sizeof(*picture->argb));
95 picture->u0 = mem; 128 if (memory == NULL) return 0;
96 mem += uv0_size; 129
97 picture->v0 = mem; 130 // TODO(skal): align plane to cache line?
98 mem += uv0_size; 131 picture->memory_argb_ = memory;
99 } 132 picture->argb = (uint32_t*)memory;
100 } 133 picture->argb_stride = width;
101 return 1; 134 }
135 }
136 return 1;
137 }
138
139 // Remove reference to the ARGB buffer (doesn't free anything).
140 static void PictureResetARGB(WebPPicture* const picture) {
141 picture->memory_argb_ = NULL;
142 picture->argb = NULL;
143 picture->argb_stride = 0;
144 }
145
146 // Remove reference to the YUVA buffer (doesn't free anything).
147 static void PictureResetYUVA(WebPPicture* const picture) {
148 picture->memory_ = NULL;
149 picture->y = picture->u = picture->v = picture->a = NULL;
150 picture->u0 = picture->v0 = NULL;
151 picture->y_stride = picture->uv_stride = 0;
152 picture->a_stride = 0;
153 picture->uv0_stride = 0;
102 } 154 }
103 155
104 // Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them 156 // Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
105 // into 'dst'. Mark 'dst' as not owning any memory. 'src' can be NULL. 157 // into 'dst'. Mark 'dst' as not owning any memory.
106 static void WebPPictureGrabSpecs(const WebPPicture* const src, 158 static void WebPPictureGrabSpecs(const WebPPicture* const src,
107 WebPPicture* const dst) { 159 WebPPicture* const dst) {
108 if (src) *dst = *src; 160 assert(src != NULL && dst != NULL);
109 dst->y = dst->u = dst->v = NULL; 161 *dst = *src;
110 dst->u0 = dst->v0 = NULL; 162 PictureResetYUVA(dst);
111 dst->a = NULL; 163 PictureResetARGB(dst);
112 } 164 }
113 165
114 // Release memory owned by 'picture'. 166 // Allocate a new argb buffer, discarding any existing one and preserving
115 void WebPPictureFree(WebPPicture* const picture) { 167 // the other YUV(A) buffer.
116 if (picture) { 168 static int PictureAllocARGB(WebPPicture* const picture) {
117 free(picture->y); 169 WebPPicture tmp;
118 WebPPictureGrabSpecs(NULL, picture); 170 free(picture->memory_argb_);
171 PictureResetARGB(picture);
172 picture->use_argb = 1;
173 WebPPictureGrabSpecs(picture, &tmp);
174 if (!WebPPictureAlloc(&tmp)) {
175 return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
176 }
177 picture->memory_argb_ = tmp.memory_argb_;
178 picture->argb = tmp.argb;
179 picture->argb_stride = tmp.argb_stride;
180 return 1;
181 }
182
183 // Release memory owned by 'picture' (both YUV and ARGB buffers).
184 void WebPPictureFree(WebPPicture* picture) {
185 if (picture != NULL) {
186 free(picture->memory_);
187 free(picture->memory_argb_);
188 PictureResetYUVA(picture);
189 PictureResetARGB(picture);
119 } 190 }
120 } 191 }
121 192
122 //------------------------------------------------------------------------------ 193 //------------------------------------------------------------------------------
123 // Picture copying 194 // Picture copying
124 195
125 int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) { 196 // Not worth moving to dsp/enc.c (only used here).
126 int y; 197 static void CopyPlane(const uint8_t* src, int src_stride,
198 uint8_t* dst, int dst_stride, int width, int height) {
199 while (height-- > 0) {
200 memcpy(dst, src, width);
201 src += src_stride;
202 dst += dst_stride;
203 }
204 }
205
206 // Adjust top-left corner to chroma sample position.
207 static void SnapTopLeftPosition(const WebPPicture* const pic,
208 int* const left, int* const top) {
209 if (!pic->use_argb) {
210 const int is_yuv422 = IS_YUV_CSP(pic->colorspace, WEBP_YUV422);
211 if (IS_YUV_CSP(pic->colorspace, WEBP_YUV420) || is_yuv422) {
212 *left &= ~1;
213 if (!is_yuv422) *top &= ~1;
214 }
215 }
216 }
217
218 // Adjust top-left corner and verify that the sub-rectangle is valid.
219 static int AdjustAndCheckRectangle(const WebPPicture* const pic,
220 int* const left, int* const top,
221 int width, int height) {
222 SnapTopLeftPosition(pic, left, top);
223 if ((*left) < 0 || (*top) < 0) return 0;
224 if (width <= 0 || height <= 0) return 0;
225 if ((*left) + width > pic->width) return 0;
226 if ((*top) + height > pic->height) return 0;
227 return 1;
228 }
229
230 int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
127 if (src == NULL || dst == NULL) return 0; 231 if (src == NULL || dst == NULL) return 0;
128 if (src == dst) return 1; 232 if (src == dst) return 1;
129 233
130 WebPPictureGrabSpecs(src, dst); 234 WebPPictureGrabSpecs(src, dst);
131 if (!WebPPictureAlloc(dst)) return 0; 235 if (!WebPPictureAlloc(dst)) return 0;
132 236
133 for (y = 0; y < dst->height; ++y) { 237 if (!src->use_argb) {
134 memcpy(dst->y + y * dst->y_stride, 238 CopyPlane(src->y, src->y_stride,
135 src->y + y * src->y_stride, src->width); 239 dst->y, dst->y_stride, dst->width, dst->height);
136 } 240 CopyPlane(src->u, src->uv_stride,
137 for (y = 0; y < (dst->height + 1) / 2; ++y) { 241 dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
138 memcpy(dst->u + y * dst->uv_stride, 242 CopyPlane(src->v, src->uv_stride,
139 src->u + y * src->uv_stride, (src->width + 1) / 2); 243 dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
140 memcpy(dst->v + y * dst->uv_stride, 244 if (dst->a != NULL) {
141 src->v + y * src->uv_stride, (src->width + 1) / 2); 245 CopyPlane(src->a, src->a_stride,
142 } 246 dst->a, dst->a_stride, dst->width, dst->height);
247 }
143 #ifdef WEBP_EXPERIMENTAL_FEATURES 248 #ifdef WEBP_EXPERIMENTAL_FEATURES
144 if (dst->a != NULL) { 249 if (dst->u0 != NULL) {
145 for (y = 0; y < dst->height; ++y) { 250 int uv0_width = src->width;
146 memcpy(dst->a + y * dst->a_stride, 251 if (IS_YUV_CSP(dst->colorspace, WEBP_YUV422)) {
147 src->a + y * src->a_stride, src->width); 252 uv0_width = HALVE(uv0_width);
148 } 253 }
149 } 254 CopyPlane(src->u0, src->uv0_stride,
150 if (dst->u0 != NULL) { 255 dst->u0, dst->uv0_stride, uv0_width, dst->height);
151 int uv0_width = src->width; 256 CopyPlane(src->v0, src->uv0_stride,
152 if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { 257 dst->v0, dst->uv0_stride, uv0_width, dst->height);
153 uv0_width = (uv0_width + 1) / 2; 258 }
154 } 259 #endif
155 for (y = 0; y < dst->height; ++y) { 260 } else {
156 memcpy(dst->u0 + y * dst->uv0_stride, 261 CopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride,
157 src->u0 + y * src->uv0_stride, uv0_width); 262 (uint8_t*)dst->argb, 4 * dst->argb_stride,
158 memcpy(dst->v0 + y * dst->uv0_stride, 263 4 * dst->width, dst->height);
159 src->v0 + y * src->uv0_stride, uv0_width); 264 }
160 } 265 return 1;
161 } 266 }
162 #endif 267
268 int WebPPictureIsView(const WebPPicture* picture) {
269 if (picture == NULL) return 0;
270 if (picture->use_argb) {
271 return (picture->memory_argb_ == NULL);
272 }
273 return (picture->memory_ == NULL);
274 }
275
276 int WebPPictureView(const WebPPicture* src,
277 int left, int top, int width, int height,
278 WebPPicture* dst) {
279 if (src == NULL || dst == NULL) return 0;
280
281 // verify rectangle position.
282 if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0;
283
284 if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'.
285 WebPPictureGrabSpecs(src, dst);
286 }
287 dst->width = width;
288 dst->height = height;
289 if (!src->use_argb) {
290 dst->y = src->y + top * src->y_stride + left;
291 dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1);
292 dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1);
293 if (src->a != NULL) {
294 dst->a = src->a + top * src->a_stride + left;
295 }
296 #ifdef WEBP_EXPERIMENTAL_FEATURES
297 if (src->u0 != NULL) {
298 const int left_pos =
299 IS_YUV_CSP(dst->colorspace, WEBP_YUV422) ? (left >> 1) : left;
300 dst->u0 = src->u0 + top * src->uv0_stride + left_pos;
301 dst->v0 = src->v0 + top * src->uv0_stride + left_pos;
302 }
303 #endif
304 } else {
305 dst->argb = src->argb + top * src->argb_stride + left;
306 }
163 return 1; 307 return 1;
164 } 308 }
165 309
166 //------------------------------------------------------------------------------ 310 //------------------------------------------------------------------------------
167 // Picture cropping 311 // Picture cropping
168 312
169 int WebPPictureCrop(WebPPicture* const pic, 313 int WebPPictureCrop(WebPPicture* pic,
170 int left, int top, int width, int height) { 314 int left, int top, int width, int height) {
171 WebPPicture tmp; 315 WebPPicture tmp;
172 int y;
173 316
174 if (pic == NULL) return 0; 317 if (pic == NULL) return 0;
175 if (width <= 0 || height <= 0) return 0; 318 if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0;
176 if (left < 0 || ((left + width + 1) & ~1) > pic->width) return 0;
177 if (top < 0 || ((top + height + 1) & ~1) > pic->height) return 0;
178 319
179 WebPPictureGrabSpecs(pic, &tmp); 320 WebPPictureGrabSpecs(pic, &tmp);
180 tmp.width = width; 321 tmp.width = width;
181 tmp.height = height; 322 tmp.height = height;
182 if (!WebPPictureAlloc(&tmp)) return 0; 323 if (!WebPPictureAlloc(&tmp)) return 0;
183 324
184 for (y = 0; y < height; ++y) { 325 if (!pic->use_argb) {
185 memcpy(tmp.y + y * tmp.y_stride, 326 const int y_offset = top * pic->y_stride + left;
186 pic->y + (top + y) * pic->y_stride + left, width); 327 const int uv_offset = (top / 2) * pic->uv_stride + left / 2;
187 } 328 CopyPlane(pic->y + y_offset, pic->y_stride,
188 for (y = 0; y < (height + 1) / 2; ++y) { 329 tmp.y, tmp.y_stride, width, height);
189 const int offset = (y + top / 2) * pic->uv_stride + left / 2; 330 CopyPlane(pic->u + uv_offset, pic->uv_stride,
190 memcpy(tmp.u + y * tmp.uv_stride, pic->u + offset, (width + 1) / 2); 331 tmp.u, tmp.uv_stride, HALVE(width), HALVE(height));
191 memcpy(tmp.v + y * tmp.uv_stride, pic->v + offset, (width + 1) / 2); 332 CopyPlane(pic->v + uv_offset, pic->uv_stride,
192 } 333 tmp.v, tmp.uv_stride, HALVE(width), HALVE(height));
193 334
335 if (tmp.a != NULL) {
336 const int a_offset = top * pic->a_stride + left;
337 CopyPlane(pic->a + a_offset, pic->a_stride,
338 tmp.a, tmp.a_stride, width, height);
339 }
194 #ifdef WEBP_EXPERIMENTAL_FEATURES 340 #ifdef WEBP_EXPERIMENTAL_FEATURES
195 if (tmp.a) { 341 if (tmp.u0 != NULL) {
196 for (y = 0; y < height; ++y) { 342 int w = width;
197 memcpy(tmp.a + y * tmp.a_stride, 343 int left_pos = left;
198 pic->a + (top + y) * pic->a_stride + left, width); 344 if (IS_YUV_CSP(tmp.colorspace, WEBP_YUV422)) {
199 } 345 w = HALVE(w);
200 } 346 left_pos = HALVE(left_pos);
201 if (tmp.u0) { 347 }
202 int w = width; 348 CopyPlane(pic->u0 + top * pic->uv0_stride + left_pos, pic->uv0_stride,
203 int l = left; 349 tmp.u0, tmp.uv0_stride, w, height);
204 if (tmp.colorspace == WEBP_YUV422) { 350 CopyPlane(pic->v0 + top * pic->uv0_stride + left_pos, pic->uv0_stride,
205 w = (w + 1) / 2; 351 tmp.v0, tmp.uv0_stride, w, height);
206 l = (l + 1) / 2; 352 }
207 } 353 #endif
208 for (y = 0; y < height; ++y) { 354 } else {
209 memcpy(tmp.u0 + y * tmp.uv0_stride, 355 const uint8_t* const src =
210 pic->u0 + (top + y) * pic->uv0_stride + l, w); 356 (const uint8_t*)(pic->argb + top * pic->argb_stride + left);
211 memcpy(tmp.v0 + y * tmp.uv0_stride, 357 CopyPlane(src, pic->argb_stride * 4,
212 pic->v0 + (top + y) * pic->uv0_stride + l, w); 358 (uint8_t*)tmp.argb, tmp.argb_stride * 4,
213 } 359 width * 4, height);
214 } 360 }
215 #endif
216
217 WebPPictureFree(pic); 361 WebPPictureFree(pic);
218 *pic = tmp; 362 *pic = tmp;
219 return 1; 363 return 1;
220 } 364 }
221 365
222 //------------------------------------------------------------------------------ 366 //------------------------------------------------------------------------------
223 // Simple picture rescaler 367 // Simple picture rescaler
224 368
225 #define RFIX 30
226 #define MULT(x,y) (((int64_t)(x) * (y) + (1 << (RFIX - 1))) >> RFIX)
227 static inline void ImportRow(const uint8_t* src, int src_width,
228 int32_t* frow, int32_t* irow, int dst_width) {
229 const int x_expand = (src_width < dst_width);
230 const int fx_scale = (1 << RFIX) / dst_width;
231 int x_in = 0;
232 int x_out;
233 int x_accum = 0;
234 if (!x_expand) {
235 int sum = 0;
236 for (x_out = 0; x_out < dst_width; ++x_out) {
237 x_accum += src_width - dst_width;
238 for (; x_accum > 0; x_accum -= dst_width) {
239 sum += src[x_in++];
240 }
241 { // Emit next horizontal pixel.
242 const int32_t base = src[x_in++];
243 const int32_t frac = base * (-x_accum);
244 frow[x_out] = (sum + base) * dst_width - frac;
245 sum = MULT(frac, fx_scale); // fresh fractional start for next pixel
246 }
247 }
248 } else { // simple bilinear interpolation
249 int left = src[0], right = src[0];
250 for (x_out = 0; x_out < dst_width; ++x_out) {
251 if (x_accum < 0) {
252 left = right;
253 right = src[++x_in];
254 x_accum += dst_width - 1;
255 }
256 frow[x_out] = right * (dst_width - 1) + (left - right) * x_accum;
257 x_accum -= src_width - 1;
258 }
259 }
260 // Accumulate the new row's contribution
261 for (x_out = 0; x_out < dst_width; ++x_out) {
262 irow[x_out] += frow[x_out];
263 }
264 }
265
266 static void ExportRow(int32_t* frow, int32_t* irow, uint8_t* dst, int dst_width,
267 const int yscale, const int64_t fxy_scale) {
268 int x_out;
269 for (x_out = 0; x_out < dst_width; ++x_out) {
270 const int frac = MULT(frow[x_out], yscale);
271 const int v = (int)(MULT(irow[x_out] - frac, fxy_scale));
272 dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
273 irow[x_out] = frac; // new fractional start
274 }
275 }
276
277 static void RescalePlane(const uint8_t* src, 369 static void RescalePlane(const uint8_t* src,
278 int src_width, int src_height, int src_stride, 370 int src_width, int src_height, int src_stride,
279 uint8_t* dst, 371 uint8_t* dst,
280 int dst_width, int dst_height, int dst_stride, 372 int dst_width, int dst_height, int dst_stride,
281 int32_t* const work) { 373 int32_t* const work,
282 const int x_expand = (src_width < dst_width); 374 int num_channels) {
283 const int fy_scale = (1 << RFIX) / dst_height; 375 WebPRescaler rescaler;
284 const int64_t fxy_scale = x_expand ? 376 int y = 0;
285 ((int64_t)dst_height << RFIX) / (dst_width * src_height) : 377 WebPRescalerInit(&rescaler, src_width, src_height,
286 ((int64_t)dst_height << RFIX) / (src_width * src_height); 378 dst, dst_width, dst_height, dst_stride,
287 int y_accum = src_height; 379 num_channels,
288 int y; 380 src_width, dst_width,
289 int32_t* irow = work; // integral contribution 381 src_height, dst_height,
290 int32_t* frow = work + dst_width; // fractional contribution 382 work);
291 383 memset(work, 0, 2 * dst_width * num_channels * sizeof(*work));
292 memset(work, 0, 2 * dst_width * sizeof(*work)); 384 while (y < src_height) {
293 for (y = 0; y < src_height; ++y) { 385 y += WebPRescalerImport(&rescaler, src_height - y,
294 // import new contribution of one source row. 386 src + y * src_stride, src_stride);
295 ImportRow(src, src_width, frow, irow, dst_width); 387 WebPRescalerExport(&rescaler);
296 src += src_stride;
297 // emit output row(s)
298 y_accum -= dst_height;
299 for (; y_accum <= 0; y_accum += src_height) {
300 const int yscale = fy_scale * (-y_accum);
301 ExportRow(frow, irow, dst, dst_width, yscale, fxy_scale);
302 dst += dst_stride;
303 }
304 } 388 }
305 } 389 }
306 #undef MULT
307 #undef RFIX
308 390
309 int WebPPictureRescale(WebPPicture* const pic, int width, int height) { 391 int WebPPictureRescale(WebPPicture* pic, int width, int height) {
310 WebPPicture tmp; 392 WebPPicture tmp;
311 int prev_width, prev_height; 393 int prev_width, prev_height;
312 int32_t* work; 394 int32_t* work;
313 395
314 if (pic == NULL) return 0; 396 if (pic == NULL) return 0;
315 prev_width = pic->width; 397 prev_width = pic->width;
316 prev_height = pic->height; 398 prev_height = pic->height;
317 // if width is unspecified, scale original proportionally to height ratio. 399 // if width is unspecified, scale original proportionally to height ratio.
318 if (width == 0) { 400 if (width == 0) {
319 width = (prev_width * height + prev_height / 2) / prev_height; 401 width = (prev_width * height + prev_height / 2) / prev_height;
320 } 402 }
321 // if height is unspecified, scale original proportionally to width ratio. 403 // if height is unspecified, scale original proportionally to width ratio.
322 if (height == 0) { 404 if (height == 0) {
323 height = (prev_height * width + prev_width / 2) / prev_width; 405 height = (prev_height * width + prev_width / 2) / prev_width;
324 } 406 }
325 // Check if the overall dimensions still make sense. 407 // Check if the overall dimensions still make sense.
326 if (width <= 0 || height <= 0) return 0; 408 if (width <= 0 || height <= 0) return 0;
327 409
328 WebPPictureGrabSpecs(pic, &tmp); 410 WebPPictureGrabSpecs(pic, &tmp);
329 tmp.width = width; 411 tmp.width = width;
330 tmp.height = height; 412 tmp.height = height;
331 if (!WebPPictureAlloc(&tmp)) return 0; 413 if (!WebPPictureAlloc(&tmp)) return 0;
332 414
333 work = malloc(2 * width * sizeof(int32_t)); 415 if (!pic->use_argb) {
334 if (work == NULL) { 416 work = (int32_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
335 WebPPictureFree(&tmp); 417 if (work == NULL) {
336 return 0; 418 WebPPictureFree(&tmp);
419 return 0;
420 }
421
422 RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
423 tmp.y, width, height, tmp.y_stride, work, 1);
424 RescalePlane(pic->u,
425 HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
426 tmp.u,
427 HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
428 RescalePlane(pic->v,
429 HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
430 tmp.v,
431 HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
432
433 if (tmp.a != NULL) {
434 RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
435 tmp.a, width, height, tmp.a_stride, work, 1);
436 }
437 #ifdef WEBP_EXPERIMENTAL_FEATURES
438 if (tmp.u0 != NULL) {
439 const int s = IS_YUV_CSP(tmp.colorspace, WEBP_YUV422) ? 2 : 1;
440 RescalePlane(
441 pic->u0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride,
442 tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1);
443 RescalePlane(
444 pic->v0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride,
445 tmp.v0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1);
446 }
447 #endif
448 } else {
449 work = (int32_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
450 if (work == NULL) {
451 WebPPictureFree(&tmp);
452 return 0;
453 }
454
455 RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
456 pic->argb_stride * 4,
457 (uint8_t*)tmp.argb, width, height,
458 tmp.argb_stride * 4,
459 work, 4);
460
337 } 461 }
338
339 RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
340 tmp.y, width, height, tmp.y_stride, work);
341 RescalePlane(pic->u,
342 (prev_width + 1) / 2, (prev_height + 1) / 2, pic->uv_stride,
343 tmp.u,
344 (width + 1) / 2, (height + 1) / 2, tmp.uv_stride, work);
345 RescalePlane(pic->v,
346 (prev_width + 1) / 2, (prev_height + 1) / 2, pic->uv_stride,
347 tmp.v,
348 (width + 1) / 2, (height + 1) / 2, tmp.uv_stride, work);
349
350 #ifdef WEBP_EXPERIMENTAL_FEATURES
351 if (tmp.a) {
352 RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
353 tmp.a, width, height, tmp.a_stride, work);
354 }
355 if (tmp.u0) {
356 int s = 1;
357 if ((tmp.colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) {
358 s = 2;
359 }
360 RescalePlane(
361 pic->u0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride,
362 tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work);
363 RescalePlane(
364 pic->v0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride,
365 tmp.v0, (width + s / 2) / s, height, tmp.uv0_stride, work);
366 }
367 #endif
368
369 WebPPictureFree(pic); 462 WebPPictureFree(pic);
370 free(work); 463 free(work);
371 *pic = tmp; 464 *pic = tmp;
372 return 1; 465 return 1;
373 } 466 }
374 467
375 //------------------------------------------------------------------------------ 468 //------------------------------------------------------------------------------
376 // Write-to-memory 469 // WebPMemoryWriter: Write-to-memory
377 470
378 typedef struct { 471 void WebPMemoryWriterInit(WebPMemoryWriter* writer) {
379 uint8_t** mem; 472 writer->mem = NULL;
380 size_t max_size; 473 writer->size = 0;
381 size_t* size;
382 } WebPMemoryWriter;
383
384 static void InitMemoryWriter(WebPMemoryWriter* const writer) {
385 *writer->mem = NULL;
386 *writer->size = 0;
387 writer->max_size = 0; 474 writer->max_size = 0;
388 } 475 }
389 476
390 static int WebPMemoryWrite(const uint8_t* data, size_t data_size, 477 int WebPMemoryWrite(const uint8_t* data, size_t data_size,
391 const WebPPicture* const picture) { 478 const WebPPicture* picture) {
392 WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr; 479 WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr;
393 size_t next_size; 480 uint64_t next_size;
394 if (w == NULL) { 481 if (w == NULL) {
395 return 1; 482 return 1;
396 } 483 }
397 next_size = (*w->size) + data_size; 484 next_size = (uint64_t)w->size + data_size;
398 if (next_size > w->max_size) { 485 if (next_size > w->max_size) {
399 uint8_t* new_mem; 486 uint8_t* new_mem;
400 size_t next_max_size = w->max_size * 2; 487 uint64_t next_max_size = 2ULL * w->max_size;
401 if (next_max_size < next_size) next_max_size = next_size; 488 if (next_max_size < next_size) next_max_size = next_size;
402 if (next_max_size < 8192) next_max_size = 8192; 489 if (next_max_size < 8192ULL) next_max_size = 8192ULL;
403 new_mem = (uint8_t*)malloc(next_max_size); 490 new_mem = (uint8_t*)WebPSafeMalloc(next_max_size, 1);
404 if (new_mem == NULL) { 491 if (new_mem == NULL) {
405 return 0; 492 return 0;
406 } 493 }
407 if ((*w->size) > 0) { 494 if (w->size > 0) {
408 memcpy(new_mem, *w->mem, *w->size); 495 memcpy(new_mem, w->mem, w->size);
409 } 496 }
410 free(*w->mem); 497 free(w->mem);
411 *w->mem = new_mem; 498 w->mem = new_mem;
412 w->max_size = next_max_size; 499 // down-cast is ok, thanks to WebPSafeMalloc
500 w->max_size = (size_t)next_max_size;
413 } 501 }
414 if (data_size) { 502 if (data_size > 0) {
415 memcpy((*w->mem) + (*w->size), data, data_size); 503 memcpy(w->mem + w->size, data, data_size);
416 *w->size += data_size; 504 w->size += data_size;
417 } 505 }
418 return 1; 506 return 1;
419 } 507 }
420 508
421 //------------------------------------------------------------------------------ 509 //------------------------------------------------------------------------------
422 // RGB -> YUV conversion 510 // Detection of non-trivial transparency
423 // The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
424 // More information at: http://en.wikipedia.org/wiki/YCbCr
425 // Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
426 // U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
427 // V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
428 // We use 16bit fixed point operations.
429 511
430 enum { YUV_FRAC = 16 }; 512 // Returns true if alpha[] has non-0xff values.
431 513 static int CheckNonOpaque(const uint8_t* alpha, int width, int height,
432 static inline int clip_uv(int v) { 514 int x_step, int y_step) {
433 v = (v + (257 << (YUV_FRAC + 2 - 1))) >> (YUV_FRAC + 2); 515 if (alpha == NULL) return 0;
434 return ((v & ~0xff) == 0) ? v : (v < 0) ? 0 : 255; 516 while (height-- > 0) {
517 int x;
518 for (x = 0; x < width * x_step; x += x_step) {
519 if (alpha[x] != 0xff) return 1; // TODO(skal): check 4/8 bytes at a time.
520 }
521 alpha += y_step;
522 }
523 return 0;
435 } 524 }
436 525
437 static inline int rgb_to_y(int r, int g, int b) { 526 // Checking for the presence of non-opaque alpha.
438 const int kRound = (1 << (YUV_FRAC - 1)) + (16 << YUV_FRAC); 527 int WebPPictureHasTransparency(const WebPPicture* picture) {
439 const int luma = 16839 * r + 33059 * g + 6420 * b; 528 if (picture == NULL) return 0;
440 return (luma + kRound) >> YUV_FRAC; // no need to clip 529 if (!picture->use_argb) {
530 return CheckNonOpaque(picture->a, picture->width, picture->height,
531 1, picture->a_stride);
532 } else {
533 int x, y;
534 const uint32_t* argb = picture->argb;
535 if (argb == NULL) return 0;
536 for (y = 0; y < picture->height; ++y) {
537 for (x = 0; x < picture->width; ++x) {
538 if (argb[x] < 0xff000000u) return 1; // test any alpha values != 0xff
539 }
540 argb += picture->argb_stride;
541 }
542 }
543 return 0;
441 } 544 }
442 545
443 static inline int rgb_to_u(int r, int g, int b) { 546 //------------------------------------------------------------------------------
444 return clip_uv(-9719 * r - 19081 * g + 28800 * b); 547 // RGB -> YUV conversion
445 }
446
447 static inline int rgb_to_v(int r, int g, int b) {
448 return clip_uv(+28800 * r - 24116 * g - 4684 * b);
449 }
450 548
451 // TODO: we can do better than simply 2x2 averaging on U/V samples. 549 // TODO: we can do better than simply 2x2 averaging on U/V samples.
452 #define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \ 550 #define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \
453 (ptr)[rgb_stride] + (ptr)[rgb_stride + step]) 551 (ptr)[rgb_stride] + (ptr)[rgb_stride + step])
454 #define SUM2H(ptr) (2 * (ptr)[0] + 2 * (ptr)[step]) 552 #define SUM2H(ptr) (2 * (ptr)[0] + 2 * (ptr)[step])
455 #define SUM2V(ptr) (2 * (ptr)[0] + 2 * (ptr)[rgb_stride]) 553 #define SUM2V(ptr) (2 * (ptr)[0] + 2 * (ptr)[rgb_stride])
456 #define SUM1(ptr) (4 * (ptr)[0]) 554 #define SUM1(ptr) (4 * (ptr)[0])
457 #define RGB_TO_UV(x, y, SUM) { \ 555 #define RGB_TO_UV(x, y, SUM) { \
458 const int src = (2 * (step * (x) + (y) * rgb_stride)); \ 556 const int src = (2 * (step * (x) + (y) * rgb_stride)); \
459 const int dst = (x) + (y) * picture->uv_stride; \ 557 const int dst = (x) + (y) * picture->uv_stride; \
460 const int r = SUM(r_ptr + src); \ 558 const int r = SUM(r_ptr + src); \
461 const int g = SUM(g_ptr + src); \ 559 const int g = SUM(g_ptr + src); \
462 const int b = SUM(b_ptr + src); \ 560 const int b = SUM(b_ptr + src); \
463 picture->u[dst] = rgb_to_u(r, g, b); \ 561 picture->u[dst] = VP8RGBToU(r, g, b); \
464 picture->v[dst] = rgb_to_v(r, g, b); \ 562 picture->v[dst] = VP8RGBToV(r, g, b); \
465 } 563 }
466 564
467 #define RGB_TO_UV0(x_in, x_out, y, SUM) { \ 565 #define RGB_TO_UV0(x_in, x_out, y, SUM) { \
468 const int src = (step * (x_in) + (y) * rgb_stride); \ 566 const int src = (step * (x_in) + (y) * rgb_stride); \
469 const int dst = (x_out) + (y) * picture->uv0_stride; \ 567 const int dst = (x_out) + (y) * picture->uv0_stride; \
470 const int r = SUM(r_ptr + src); \ 568 const int r = SUM(r_ptr + src); \
471 const int g = SUM(g_ptr + src); \ 569 const int g = SUM(g_ptr + src); \
472 const int b = SUM(b_ptr + src); \ 570 const int b = SUM(b_ptr + src); \
473 picture->u0[dst] = rgb_to_u(r, g, b); \ 571 picture->u0[dst] = VP8RGBToU(r, g, b); \
474 picture->v0[dst] = rgb_to_v(r, g, b); \ 572 picture->v0[dst] = VP8RGBToV(r, g, b); \
475 } 573 }
476 574
477 static void MakeGray(WebPPicture* const picture) { 575 static void MakeGray(WebPPicture* const picture) {
478 int y; 576 int y;
479 const int uv_width = (picture->width + 1) >> 1; 577 const int uv_width = HALVE(picture->width);
480 for (y = 0; y < ((picture->height + 1) >> 1); ++y) { 578 const int uv_height = HALVE(picture->height);
579 for (y = 0; y < uv_height; ++y) {
481 memset(picture->u + y * picture->uv_stride, 128, uv_width); 580 memset(picture->u + y * picture->uv_stride, 128, uv_width);
482 memset(picture->v + y * picture->uv_stride, 128, uv_width); 581 memset(picture->v + y * picture->uv_stride, 128, uv_width);
483 } 582 }
484 } 583 }
485 584
486 static int Import(WebPPicture* const picture, 585 static int ImportYUVAFromRGBA(const uint8_t* const r_ptr,
487 const uint8_t* const rgb, int rgb_stride, 586 const uint8_t* const g_ptr,
488 int step, int swap_rb, int import_alpha) { 587 const uint8_t* const b_ptr,
588 const uint8_t* const a_ptr,
589 int step, // bytes per pixel
590 int rgb_stride, // bytes per scanline
591 WebPPicture* const picture) {
489 const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; 592 const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK;
490 int x, y; 593 int x, y;
491 const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0);
492 const uint8_t* const g_ptr = rgb + 1;
493 const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2);
494 const int width = picture->width; 594 const int width = picture->width;
495 const int height = picture->height; 595 const int height = picture->height;
596 const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride);
597
598 picture->colorspace = uv_csp;
599 picture->use_argb = 0;
600 if (has_alpha) {
601 picture->colorspace |= WEBP_CSP_ALPHA_BIT;
602 }
603 if (!WebPPictureAlloc(picture)) return 0;
496 604
497 // Import luma plane 605 // Import luma plane
498 for (y = 0; y < height; ++y) { 606 for (y = 0; y < height; ++y) {
499 for (x = 0; x < width; ++x) { 607 for (x = 0; x < width; ++x) {
500 const int offset = step * x + y * rgb_stride; 608 const int offset = step * x + y * rgb_stride;
501 picture->y[x + y * picture->y_stride] = 609 picture->y[x + y * picture->y_stride] =
502 rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]); 610 VP8RGBToY(r_ptr[offset], g_ptr[offset], b_ptr[offset]);
503 } 611 }
504 } 612 }
505 613
506 // Downsample U/V plane 614 // Downsample U/V plane
507 if (uv_csp != WEBP_YUV400) { 615 if (uv_csp != WEBP_YUV400) {
508 for (y = 0; y < (height >> 1); ++y) { 616 for (y = 0; y < (height >> 1); ++y) {
509 for (x = 0; x < (width >> 1); ++x) { 617 for (x = 0; x < (width >> 1); ++x) {
510 RGB_TO_UV(x, y, SUM4); 618 RGB_TO_UV(x, y, SUM4);
511 } 619 }
512 if (picture->width & 1) { 620 if (width & 1) {
513 RGB_TO_UV(x, y, SUM2V); 621 RGB_TO_UV(x, y, SUM2V);
514 } 622 }
515 } 623 }
516 if (height & 1) { 624 if (height & 1) {
517 for (x = 0; x < (width >> 1); ++x) { 625 for (x = 0; x < (width >> 1); ++x) {
518 RGB_TO_UV(x, y, SUM2H); 626 RGB_TO_UV(x, y, SUM2H);
519 } 627 }
520 if (width & 1) { 628 if (width & 1) {
521 RGB_TO_UV(x, y, SUM1); 629 RGB_TO_UV(x, y, SUM1);
522 } 630 }
(...skipping 15 matching lines...) Expand all
538 for (x = 0; x < width; ++x) { 646 for (x = 0; x < width; ++x) {
539 RGB_TO_UV0(x, x, y, SUM1); 647 RGB_TO_UV0(x, x, y, SUM1);
540 } 648 }
541 } 649 }
542 } 650 }
543 #endif 651 #endif
544 } else { 652 } else {
545 MakeGray(picture); 653 MakeGray(picture);
546 } 654 }
547 655
548 if (import_alpha) { 656 if (has_alpha) {
549 #ifdef WEBP_EXPERIMENTAL_FEATURES
550 const uint8_t* const a_ptr = rgb + 3;
551 assert(step >= 4); 657 assert(step >= 4);
552 for (y = 0; y < height; ++y) { 658 for (y = 0; y < height; ++y) {
553 for (x = 0; x < width; ++x) { 659 for (x = 0; x < width; ++x) {
554 picture->a[x + y * picture->a_stride] = 660 picture->a[x + y * picture->a_stride] =
555 a_ptr[step * x + y * rgb_stride]; 661 a_ptr[step * x + y * rgb_stride];
556 } 662 }
557 } 663 }
558 #endif
559 } 664 }
560 return 1; 665 return 1;
561 } 666 }
667
668 static int Import(WebPPicture* const picture,
669 const uint8_t* const rgb, int rgb_stride,
670 int step, int swap_rb, int import_alpha) {
671 const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0);
672 const uint8_t* const g_ptr = rgb + 1;
673 const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2);
674 const uint8_t* const a_ptr = import_alpha ? rgb + 3 : NULL;
675 const int width = picture->width;
676 const int height = picture->height;
677
678 if (!picture->use_argb) {
679 return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride,
680 picture);
681 }
682 if (import_alpha) {
683 picture->colorspace |= WEBP_CSP_ALPHA_BIT;
684 } else {
685 picture->colorspace &= ~WEBP_CSP_ALPHA_BIT;
686 }
687 if (!WebPPictureAlloc(picture)) return 0;
688
689 if (!import_alpha) {
690 int x, y;
691 for (y = 0; y < height; ++y) {
692 for (x = 0; x < width; ++x) {
693 const int offset = step * x + y * rgb_stride;
694 const uint32_t argb =
695 0xff000000u |
696 (r_ptr[offset] << 16) |
697 (g_ptr[offset] << 8) |
698 (b_ptr[offset]);
699 picture->argb[x + y * picture->argb_stride] = argb;
700 }
701 }
702 } else {
703 int x, y;
704 assert(step >= 4);
705 for (y = 0; y < height; ++y) {
706 for (x = 0; x < width; ++x) {
707 const int offset = step * x + y * rgb_stride;
708 const uint32_t argb = (a_ptr[offset] << 24) |
709 (r_ptr[offset] << 16) |
710 (g_ptr[offset] << 8) |
711 (b_ptr[offset]);
712 picture->argb[x + y * picture->argb_stride] = argb;
713 }
714 }
715 }
716 return 1;
717 }
562 #undef SUM4 718 #undef SUM4
563 #undef SUM2V 719 #undef SUM2V
564 #undef SUM2H 720 #undef SUM2H
565 #undef SUM1 721 #undef SUM1
566 #undef RGB_TO_UV 722 #undef RGB_TO_UV
567 723
568 int WebPPictureImportRGB(WebPPicture* const picture, 724 int WebPPictureImportRGB(WebPPicture* picture,
569 const uint8_t* const rgb, int rgb_stride) { 725 const uint8_t* rgb, int rgb_stride) {
570 picture->colorspace &= ~WEBP_CSP_ALPHA_BIT;
571 if (!WebPPictureAlloc(picture)) return 0;
572 return Import(picture, rgb, rgb_stride, 3, 0, 0); 726 return Import(picture, rgb, rgb_stride, 3, 0, 0);
573 } 727 }
574 728
575 int WebPPictureImportBGR(WebPPicture* const picture, 729 int WebPPictureImportBGR(WebPPicture* picture,
576 const uint8_t* const rgb, int rgb_stride) { 730 const uint8_t* rgb, int rgb_stride) {
577 picture->colorspace &= ~WEBP_CSP_ALPHA_BIT;
578 if (!WebPPictureAlloc(picture)) return 0;
579 return Import(picture, rgb, rgb_stride, 3, 1, 0); 731 return Import(picture, rgb, rgb_stride, 3, 1, 0);
580 } 732 }
581 733
582 int WebPPictureImportRGBA(WebPPicture* const picture, 734 int WebPPictureImportRGBA(WebPPicture* picture,
583 const uint8_t* const rgba, int rgba_stride) { 735 const uint8_t* rgba, int rgba_stride) {
584 picture->colorspace |= WEBP_CSP_ALPHA_BIT;
585 if (!WebPPictureAlloc(picture)) return 0;
586 return Import(picture, rgba, rgba_stride, 4, 0, 1); 736 return Import(picture, rgba, rgba_stride, 4, 0, 1);
587 } 737 }
588 738
589 int WebPPictureImportBGRA(WebPPicture* const picture, 739 int WebPPictureImportBGRA(WebPPicture* picture,
590 const uint8_t* const rgba, int rgba_stride) { 740 const uint8_t* rgba, int rgba_stride) {
591 picture->colorspace |= WEBP_CSP_ALPHA_BIT;
592 if (!WebPPictureAlloc(picture)) return 0;
593 return Import(picture, rgba, rgba_stride, 4, 1, 1); 741 return Import(picture, rgba, rgba_stride, 4, 1, 1);
594 } 742 }
595 743
596 int WebPPictureImportRGBX(WebPPicture* const picture, 744 int WebPPictureImportRGBX(WebPPicture* picture,
597 const uint8_t* const rgba, int rgba_stride) { 745 const uint8_t* rgba, int rgba_stride) {
598 picture->colorspace &= ~WEBP_CSP_ALPHA_BIT;
599 if (!WebPPictureAlloc(picture)) return 0;
600 return Import(picture, rgba, rgba_stride, 4, 0, 0); 746 return Import(picture, rgba, rgba_stride, 4, 0, 0);
601 } 747 }
602 748
603 int WebPPictureImportBGRX(WebPPicture* const picture, 749 int WebPPictureImportBGRX(WebPPicture* picture,
604 const uint8_t* const rgba, int rgba_stride) { 750 const uint8_t* rgba, int rgba_stride) {
605 picture->colorspace &= ~WEBP_CSP_ALPHA_BIT;
606 if (!WebPPictureAlloc(picture)) return 0;
607 return Import(picture, rgba, rgba_stride, 4, 1, 0); 751 return Import(picture, rgba, rgba_stride, 4, 1, 0);
608 } 752 }
609 753
610 //------------------------------------------------------------------------------ 754 //------------------------------------------------------------------------------
611 // Simplest call: 755 // Automatic YUV <-> ARGB conversions.
756
757 int WebPPictureYUVAToARGB(WebPPicture* picture) {
758 if (picture == NULL) return 0;
759 if (picture->memory_ == NULL || picture->y == NULL ||
760 picture->u == NULL || picture->v == NULL) {
761 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
762 }
763 if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) {
764 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
765 }
766 if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
767 return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
768 }
769 // Allocate a new argb buffer (discarding the previous one).
770 if (!PictureAllocARGB(picture)) return 0;
771
772 // Convert
773 {
774 int y;
775 const int width = picture->width;
776 const int height = picture->height;
777 const int argb_stride = 4 * picture->argb_stride;
778 uint8_t* dst = (uint8_t*)picture->argb;
779 const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y;
780 WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST);
781
782 // First row, with replicated top samples.
783 upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, width);
784 cur_y += picture->y_stride;
785 dst += argb_stride;
786 // Center rows.
787 for (y = 1; y + 1 < height; y += 2) {
788 const uint8_t* const top_u = cur_u;
789 const uint8_t* const top_v = cur_v;
790 cur_u += picture->uv_stride;
791 cur_v += picture->uv_stride;
792 upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v,
793 dst, dst + argb_stride, width);
794 cur_y += 2 * picture->y_stride;
795 dst += 2 * argb_stride;
796 }
797 // Last row (if needed), with replicated bottom samples.
798 if (height > 1 && !(height & 1)) {
799 upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
800 }
801 // Insert alpha values if needed, in replacement for the default 0xff ones.
802 if (picture->colorspace & WEBP_CSP_ALPHA_BIT) {
803 for (y = 0; y < height; ++y) {
804 uint32_t* const dst = picture->argb + y * picture->argb_stride;
805 const uint8_t* const src = picture->a + y * picture->a_stride;
806 int x;
807 for (x = 0; x < width; ++x) {
808 dst[x] = (dst[x] & 0x00ffffffu) | (src[x] << 24);
809 }
810 }
811 }
812 }
813 return 1;
814 }
815
816 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) {
817 if (picture == NULL) return 0;
818 if (picture->argb == NULL) {
819 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
820 } else {
821 const uint8_t* const argb = (const uint8_t*)picture->argb;
822 const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1;
823 const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2;
824 const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3;
825 const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0;
826 // We work on a tmp copy of 'picture', because ImportYUVAFromRGBA()
827 // would be calling WebPPictureFree(picture) otherwise.
828 WebPPicture tmp = *picture;
829 PictureResetARGB(&tmp); // reset ARGB buffer so that it's not free()'d.
830 tmp.use_argb = 0;
831 tmp.colorspace = colorspace & WEBP_CSP_UV_MASK;
832 if (!ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, &tmp)) {
833 return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
834 }
835 // Copy back the YUV specs into 'picture'.
836 tmp.argb = picture->argb;
837 tmp.argb_stride = picture->argb_stride;
838 tmp.memory_argb_ = picture->memory_argb_;
839 *picture = tmp;
840 }
841 return 1;
842 }
843
844 //------------------------------------------------------------------------------
845 // Helper: clean up fully transparent area to help compressibility.
846
847 #define SIZE 8
848 #define SIZE2 (SIZE / 2)
849 static int is_transparent_area(const uint8_t* ptr, int stride, int size) {
850 int y, x;
851 for (y = 0; y < size; ++y) {
852 for (x = 0; x < size; ++x) {
853 if (ptr[x]) {
854 return 0;
855 }
856 }
857 ptr += stride;
858 }
859 return 1;
860 }
861
862 static WEBP_INLINE void flatten(uint8_t* ptr, int v, int stride, int size) {
863 int y;
864 for (y = 0; y < size; ++y) {
865 memset(ptr, v, size);
866 ptr += stride;
867 }
868 }
869
870 void WebPCleanupTransparentArea(WebPPicture* pic) {
871 int x, y, w, h;
872 const uint8_t* a_ptr;
873 int values[3] = { 0 };
874
875 if (pic == NULL) return;
876
877 a_ptr = pic->a;
878 if (a_ptr == NULL) return; // nothing to do
879
880 w = pic->width / SIZE;
881 h = pic->height / SIZE;
882 for (y = 0; y < h; ++y) {
883 int need_reset = 1;
884 for (x = 0; x < w; ++x) {
885 const int off_a = (y * pic->a_stride + x) * SIZE;
886 const int off_y = (y * pic->y_stride + x) * SIZE;
887 const int off_uv = (y * pic->uv_stride + x) * SIZE2;
888 if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) {
889 if (need_reset) {
890 values[0] = pic->y[off_y];
891 values[1] = pic->u[off_uv];
892 values[2] = pic->v[off_uv];
893 need_reset = 0;
894 }
895 flatten(pic->y + off_y, values[0], pic->y_stride, SIZE);
896 flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2);
897 flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2);
898 } else {
899 need_reset = 1;
900 }
901 }
902 // ignore the left-overs on right/bottom
903 }
904 }
905
906 #undef SIZE
907 #undef SIZE2
908
909
910 //------------------------------------------------------------------------------
911 // Distortion
912
913 // Max value returned in case of exact similarity.
914 static const double kMinDistortion_dB = 99.;
915
916 int WebPPictureDistortion(const WebPPicture* pic1, const WebPPicture* pic2,
917 int type, float result[5]) {
918 int c;
919 DistoStats stats[5];
920 int has_alpha;
921
922 if (pic1 == NULL || pic2 == NULL ||
923 pic1->width != pic2->width || pic1->height != pic2->height ||
924 pic1->y == NULL || pic2->y == NULL ||
925 pic1->u == NULL || pic2->u == NULL ||
926 pic1->v == NULL || pic2->v == NULL ||
927 result == NULL) {
928 return 0;
929 }
930 // TODO(skal): provide distortion for ARGB too.
931 if (pic1->use_argb == 1 || pic1->use_argb != pic2->use_argb) {
932 return 0;
933 }
934
935 has_alpha = !!(pic1->colorspace & WEBP_CSP_ALPHA_BIT);
936 if (has_alpha != !!(pic2->colorspace & WEBP_CSP_ALPHA_BIT) ||
937 (has_alpha && (pic1->a == NULL || pic2->a == NULL))) {
938 return 0;
939 }
940
941 memset(stats, 0, sizeof(stats));
942 VP8SSIMAccumulatePlane(pic1->y, pic1->y_stride,
943 pic2->y, pic2->y_stride,
944 pic1->width, pic1->height, &stats[0]);
945 VP8SSIMAccumulatePlane(pic1->u, pic1->uv_stride,
946 pic2->u, pic2->uv_stride,
947 (pic1->width + 1) >> 1, (pic1->height + 1) >> 1,
948 &stats[1]);
949 VP8SSIMAccumulatePlane(pic1->v, pic1->uv_stride,
950 pic2->v, pic2->uv_stride,
951 (pic1->width + 1) >> 1, (pic1->height + 1) >> 1,
952 &stats[2]);
953 if (has_alpha) {
954 VP8SSIMAccumulatePlane(pic1->a, pic1->a_stride,
955 pic2->a, pic2->a_stride,
956 pic1->width, pic1->height, &stats[3]);
957 }
958 for (c = 0; c <= 4; ++c) {
959 if (type == 1) {
960 const double v = VP8SSIMGet(&stats[c]);
961 result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v)
962 : kMinDistortion_dB);
963 } else {
964 const double v = VP8SSIMGetSquaredError(&stats[c]);
965 result[c] = (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.))
966 : kMinDistortion_dB);
967 }
968 // Accumulate forward
969 if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]);
970 }
971 return 1;
972 }
973
974 //------------------------------------------------------------------------------
975 // Simplest high-level calls:
612 976
613 typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int); 977 typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int);
614 978
615 static size_t Encode(const uint8_t* rgba, int width, int height, int stride, 979 static size_t Encode(const uint8_t* rgba, int width, int height, int stride,
616 Importer import, float quality_factor, uint8_t** output) { 980 Importer import, float quality_factor, int lossless,
617 size_t output_size = 0; 981 uint8_t** output) {
618 WebPPicture pic; 982 WebPPicture pic;
619 WebPConfig config; 983 WebPConfig config;
620 WebPMemoryWriter wrt; 984 WebPMemoryWriter wrt;
621 int ok; 985 int ok;
622 986
623 if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) || 987 if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) ||
624 !WebPPictureInit(&pic)) { 988 !WebPPictureInit(&pic)) {
625 return 0; // shouldn't happen, except if system installation is broken 989 return 0; // shouldn't happen, except if system installation is broken
626 } 990 }
627 991
992 config.lossless = !!lossless;
993 pic.use_argb = !!lossless;
628 pic.width = width; 994 pic.width = width;
629 pic.height = height; 995 pic.height = height;
630 pic.writer = WebPMemoryWrite; 996 pic.writer = WebPMemoryWrite;
631 pic.custom_ptr = &wrt; 997 pic.custom_ptr = &wrt;
632 998 WebPMemoryWriterInit(&wrt);
633 wrt.mem = output;
634 wrt.size = &output_size;
635 InitMemoryWriter(&wrt);
636 999
637 ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic); 1000 ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic);
638 WebPPictureFree(&pic); 1001 WebPPictureFree(&pic);
639 if (!ok) { 1002 if (!ok) {
640 free(*output); 1003 free(wrt.mem);
641 *output = NULL; 1004 *output = NULL;
642 return 0; 1005 return 0;
643 } 1006 }
644 return output_size; 1007 *output = wrt.mem;
1008 return wrt.size;
645 } 1009 }
646 1010
647 #define ENCODE_FUNC(NAME, IMPORTER) \ 1011 #define ENCODE_FUNC(NAME, IMPORTER) \
648 size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \ 1012 size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \
649 uint8_t** out) { \ 1013 uint8_t** out) { \
650 return Encode(in, w, h, bps, IMPORTER, q, out); \ 1014 return Encode(in, w, h, bps, IMPORTER, q, 0, out); \
651 } 1015 }
652 1016
653 ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB); 1017 ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB);
654 ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR); 1018 ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR);
655 ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA); 1019 ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA);
656 ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA); 1020 ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA);
657 1021
658 #undef ENCODE_FUNC 1022 #undef ENCODE_FUNC
659 1023
1024 #define LOSSLESS_DEFAULT_QUALITY 70.
1025 #define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \
1026 size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \
1027 return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \
1028 }
1029
1030 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB);
1031 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR);
1032 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA);
1033 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA);
1034
1035 #undef LOSSLESS_ENCODE_FUNC
1036
660 //------------------------------------------------------------------------------ 1037 //------------------------------------------------------------------------------
661 1038
662 #if defined(__cplusplus) || defined(c_plusplus) 1039 #if defined(__cplusplus) || defined(c_plusplus)
663 } // extern "C" 1040 } // extern "C"
664 #endif 1041 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698