1 // Copyright 2011 Google Inc. All Rights Reserved.
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 // -----------------------------------------------------------------------------
10 // WebPPicture utils: colorspace conversion, crop, ...
12 // Author: Skal (pascal.massimino@gmail.com)
18 #include "./vp8enci.h"
19 #include "../utils/rescaler.h"
20 #include "../utils/utils.h"
21 #include "../dsp/dsp.h"
22 #include "../dsp/yuv.h"
24 #if defined(__cplusplus) || defined(c_plusplus)
28 #define HALVE(x) (((x) + 1) >> 1)
29 #define IS_YUV_CSP(csp, YUV_CSP) (((csp) & WEBP_CSP_UV_MASK) == (YUV_CSP))
34 } test_endian
= { 0xff000000u
};
35 #define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff)
37 //------------------------------------------------------------------------------
39 //------------------------------------------------------------------------------
41 int WebPPictureAlloc(WebPPicture
* picture
) {
42 if (picture
!= NULL
) {
43 const WebPEncCSP uv_csp
= picture
->colorspace
& WEBP_CSP_UV_MASK
;
44 const int has_alpha
= picture
->colorspace
& WEBP_CSP_ALPHA_BIT
;
45 const int width
= picture
->width
;
46 const int height
= picture
->height
;
48 if (!picture
->use_argb
) {
49 const int y_stride
= width
;
50 const int uv_width
= HALVE(width
);
51 const int uv_height
= HALVE(height
);
52 const int uv_stride
= uv_width
;
54 int a_width
, a_stride
;
55 uint64_t y_size
, uv_size
, uv0_size
, a_size
, total_size
;
62 #ifdef WEBP_EXPERIMENTAL_FEATURES
63 case WEBP_YUV400
: // for now, we'll just reset the U/V samples
66 uv0_stride
= uv_width
;
75 uv0_size
= height
* uv0_stride
;
78 a_width
= has_alpha
? width
: 0;
80 y_size
= (uint64_t)y_stride
* height
;
81 uv_size
= (uint64_t)uv_stride
* uv_height
;
82 a_size
= (uint64_t)a_stride
* height
;
84 total_size
= y_size
+ a_size
+ 2 * uv_size
+ 2 * uv0_size
;
86 // Security and validation checks
87 if (width
<= 0 || height
<= 0 || // luma/alpha param error
88 uv_width
< 0 || uv_height
< 0) { // u/v param error
91 // Clear previous buffer and allocate a new one.
92 WebPPictureFree(picture
); // erase previous buffer
93 mem
= (uint8_t*)WebPSafeMalloc(total_size
, sizeof(*mem
));
94 if (mem
== NULL
) return 0;
96 // From now on, we're in the clear, we can no longer fail...
97 picture
->memory_
= (void*)mem
;
98 picture
->y_stride
= y_stride
;
99 picture
->uv_stride
= uv_stride
;
100 picture
->a_stride
= a_stride
;
101 picture
->uv0_stride
= uv0_stride
;
102 // TODO(skal): we could align the y/u/v planes and adjust stride.
123 const uint64_t argb_size
= (uint64_t)width
* height
;
124 if (width
<= 0 || height
<= 0) {
127 // Clear previous buffer and allocate a new one.
128 WebPPictureFree(picture
); // erase previous buffer
129 memory
= WebPSafeMalloc(argb_size
, sizeof(*picture
->argb
));
130 if (memory
== NULL
) return 0;
132 // TODO(skal): align plane to cache line?
133 picture
->memory_argb_
= memory
;
134 picture
->argb
= (uint32_t*)memory
;
135 picture
->argb_stride
= width
;
141 // Remove reference to the ARGB buffer (doesn't free anything).
142 static void PictureResetARGB(WebPPicture
* const picture
) {
143 picture
->memory_argb_
= NULL
;
144 picture
->argb
= NULL
;
145 picture
->argb_stride
= 0;
148 // Remove reference to the YUVA buffer (doesn't free anything).
149 static void PictureResetYUVA(WebPPicture
* const picture
) {
150 picture
->memory_
= NULL
;
151 picture
->y
= picture
->u
= picture
->v
= picture
->a
= NULL
;
152 picture
->u0
= picture
->v0
= NULL
;
153 picture
->y_stride
= picture
->uv_stride
= 0;
154 picture
->a_stride
= 0;
155 picture
->uv0_stride
= 0;
158 // Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
159 // into 'dst'. Mark 'dst' as not owning any memory.
160 static void WebPPictureGrabSpecs(const WebPPicture
* const src
,
161 WebPPicture
* const dst
) {
162 assert(src
!= NULL
&& dst
!= NULL
);
164 PictureResetYUVA(dst
);
165 PictureResetARGB(dst
);
168 // Allocate a new argb buffer, discarding any existing one and preserving
169 // the other YUV(A) buffer.
170 static int PictureAllocARGB(WebPPicture
* const picture
) {
172 free(picture
->memory_argb_
);
173 PictureResetARGB(picture
);
174 picture
->use_argb
= 1;
175 WebPPictureGrabSpecs(picture
, &tmp
);
176 if (!WebPPictureAlloc(&tmp
)) {
177 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_OUT_OF_MEMORY
);
179 picture
->memory_argb_
= tmp
.memory_argb_
;
180 picture
->argb
= tmp
.argb
;
181 picture
->argb_stride
= tmp
.argb_stride
;
185 // Release memory owned by 'picture' (both YUV and ARGB buffers).
186 void WebPPictureFree(WebPPicture
* picture
) {
187 if (picture
!= NULL
) {
188 free(picture
->memory_
);
189 free(picture
->memory_argb_
);
190 PictureResetYUVA(picture
);
191 PictureResetARGB(picture
);
195 //------------------------------------------------------------------------------
198 // Not worth moving to dsp/enc.c (only used here).
199 static void CopyPlane(const uint8_t* src
, int src_stride
,
200 uint8_t* dst
, int dst_stride
, int width
, int height
) {
201 while (height
-- > 0) {
202 memcpy(dst
, src
, width
);
208 // Adjust top-left corner to chroma sample position.
209 static void SnapTopLeftPosition(const WebPPicture
* const pic
,
210 int* const left
, int* const top
) {
211 if (!pic
->use_argb
) {
212 const int is_yuv422
= IS_YUV_CSP(pic
->colorspace
, WEBP_YUV422
);
213 if (IS_YUV_CSP(pic
->colorspace
, WEBP_YUV420
) || is_yuv422
) {
215 if (!is_yuv422
) *top
&= ~1;
220 // Adjust top-left corner and verify that the sub-rectangle is valid.
221 static int AdjustAndCheckRectangle(const WebPPicture
* const pic
,
222 int* const left
, int* const top
,
223 int width
, int height
) {
224 SnapTopLeftPosition(pic
, left
, top
);
225 if ((*left
) < 0 || (*top
) < 0) return 0;
226 if (width
<= 0 || height
<= 0) return 0;
227 if ((*left
) + width
> pic
->width
) return 0;
228 if ((*top
) + height
> pic
->height
) return 0;
232 int WebPPictureCopy(const WebPPicture
* src
, WebPPicture
* dst
) {
233 if (src
== NULL
|| dst
== NULL
) return 0;
234 if (src
== dst
) return 1;
236 WebPPictureGrabSpecs(src
, dst
);
237 if (!WebPPictureAlloc(dst
)) return 0;
239 if (!src
->use_argb
) {
240 CopyPlane(src
->y
, src
->y_stride
,
241 dst
->y
, dst
->y_stride
, dst
->width
, dst
->height
);
242 CopyPlane(src
->u
, src
->uv_stride
,
243 dst
->u
, dst
->uv_stride
, HALVE(dst
->width
), HALVE(dst
->height
));
244 CopyPlane(src
->v
, src
->uv_stride
,
245 dst
->v
, dst
->uv_stride
, HALVE(dst
->width
), HALVE(dst
->height
));
246 if (dst
->a
!= NULL
) {
247 CopyPlane(src
->a
, src
->a_stride
,
248 dst
->a
, dst
->a_stride
, dst
->width
, dst
->height
);
250 #ifdef WEBP_EXPERIMENTAL_FEATURES
251 if (dst
->u0
!= NULL
) {
252 int uv0_width
= src
->width
;
253 if (IS_YUV_CSP(dst
->colorspace
, WEBP_YUV422
)) {
254 uv0_width
= HALVE(uv0_width
);
256 CopyPlane(src
->u0
, src
->uv0_stride
,
257 dst
->u0
, dst
->uv0_stride
, uv0_width
, dst
->height
);
258 CopyPlane(src
->v0
, src
->uv0_stride
,
259 dst
->v0
, dst
->uv0_stride
, uv0_width
, dst
->height
);
263 CopyPlane((const uint8_t*)src
->argb
, 4 * src
->argb_stride
,
264 (uint8_t*)dst
->argb
, 4 * dst
->argb_stride
,
265 4 * dst
->width
, dst
->height
);
270 int WebPPictureIsView(const WebPPicture
* picture
) {
271 if (picture
== NULL
) return 0;
272 if (picture
->use_argb
) {
273 return (picture
->memory_argb_
== NULL
);
275 return (picture
->memory_
== NULL
);
278 int WebPPictureView(const WebPPicture
* src
,
279 int left
, int top
, int width
, int height
,
281 if (src
== NULL
|| dst
== NULL
) return 0;
283 // verify rectangle position.
284 if (!AdjustAndCheckRectangle(src
, &left
, &top
, width
, height
)) return 0;
286 if (src
!= dst
) { // beware of aliasing! We don't want to leak 'memory_'.
287 WebPPictureGrabSpecs(src
, dst
);
290 dst
->height
= height
;
291 if (!src
->use_argb
) {
292 dst
->y
= src
->y
+ top
* src
->y_stride
+ left
;
293 dst
->u
= src
->u
+ (top
>> 1) * src
->uv_stride
+ (left
>> 1);
294 dst
->v
= src
->v
+ (top
>> 1) * src
->uv_stride
+ (left
>> 1);
295 dst
->y_stride
= src
->y_stride
;
296 dst
->uv_stride
= src
->uv_stride
;
297 if (src
->a
!= NULL
) {
298 dst
->a
= src
->a
+ top
* src
->a_stride
+ left
;
299 dst
->a_stride
= src
->a_stride
;
301 #ifdef WEBP_EXPERIMENTAL_FEATURES
302 if (src
->u0
!= NULL
) {
304 IS_YUV_CSP(dst
->colorspace
, WEBP_YUV422
) ? (left
>> 1) : left
;
305 dst
->u0
= src
->u0
+ top
* src
->uv0_stride
+ left_pos
;
306 dst
->v0
= src
->v0
+ top
* src
->uv0_stride
+ left_pos
;
307 dst
->uv0_stride
= src
->uv0_stride
;
311 dst
->argb
= src
->argb
+ top
* src
->argb_stride
+ left
;
312 dst
->argb_stride
= src
->argb_stride
;
317 //------------------------------------------------------------------------------
320 int WebPPictureCrop(WebPPicture
* pic
,
321 int left
, int top
, int width
, int height
) {
324 if (pic
== NULL
) return 0;
325 if (!AdjustAndCheckRectangle(pic
, &left
, &top
, width
, height
)) return 0;
327 WebPPictureGrabSpecs(pic
, &tmp
);
330 if (!WebPPictureAlloc(&tmp
)) return 0;
332 if (!pic
->use_argb
) {
333 const int y_offset
= top
* pic
->y_stride
+ left
;
334 const int uv_offset
= (top
/ 2) * pic
->uv_stride
+ left
/ 2;
335 CopyPlane(pic
->y
+ y_offset
, pic
->y_stride
,
336 tmp
.y
, tmp
.y_stride
, width
, height
);
337 CopyPlane(pic
->u
+ uv_offset
, pic
->uv_stride
,
338 tmp
.u
, tmp
.uv_stride
, HALVE(width
), HALVE(height
));
339 CopyPlane(pic
->v
+ uv_offset
, pic
->uv_stride
,
340 tmp
.v
, tmp
.uv_stride
, HALVE(width
), HALVE(height
));
343 const int a_offset
= top
* pic
->a_stride
+ left
;
344 CopyPlane(pic
->a
+ a_offset
, pic
->a_stride
,
345 tmp
.a
, tmp
.a_stride
, width
, height
);
347 #ifdef WEBP_EXPERIMENTAL_FEATURES
348 if (tmp
.u0
!= NULL
) {
351 if (IS_YUV_CSP(tmp
.colorspace
, WEBP_YUV422
)) {
353 left_pos
= HALVE(left_pos
);
355 CopyPlane(pic
->u0
+ top
* pic
->uv0_stride
+ left_pos
, pic
->uv0_stride
,
356 tmp
.u0
, tmp
.uv0_stride
, w
, height
);
357 CopyPlane(pic
->v0
+ top
* pic
->uv0_stride
+ left_pos
, pic
->uv0_stride
,
358 tmp
.v0
, tmp
.uv0_stride
, w
, height
);
362 const uint8_t* const src
=
363 (const uint8_t*)(pic
->argb
+ top
* pic
->argb_stride
+ left
);
364 CopyPlane(src
, pic
->argb_stride
* 4,
365 (uint8_t*)tmp
.argb
, tmp
.argb_stride
* 4,
368 WebPPictureFree(pic
);
373 //------------------------------------------------------------------------------
374 // Simple picture rescaler
376 static void RescalePlane(const uint8_t* src
,
377 int src_width
, int src_height
, int src_stride
,
379 int dst_width
, int dst_height
, int dst_stride
,
382 WebPRescaler rescaler
;
384 WebPRescalerInit(&rescaler
, src_width
, src_height
,
385 dst
, dst_width
, dst_height
, dst_stride
,
387 src_width
, dst_width
,
388 src_height
, dst_height
,
390 memset(work
, 0, 2 * dst_width
* num_channels
* sizeof(*work
));
391 while (y
< src_height
) {
392 y
+= WebPRescalerImport(&rescaler
, src_height
- y
,
393 src
+ y
* src_stride
, src_stride
);
394 WebPRescalerExport(&rescaler
);
398 int WebPPictureRescale(WebPPicture
* pic
, int width
, int height
) {
400 int prev_width
, prev_height
;
403 if (pic
== NULL
) return 0;
404 prev_width
= pic
->width
;
405 prev_height
= pic
->height
;
406 // if width is unspecified, scale original proportionally to height ratio.
408 width
= (prev_width
* height
+ prev_height
/ 2) / prev_height
;
410 // if height is unspecified, scale original proportionally to width ratio.
412 height
= (prev_height
* width
+ prev_width
/ 2) / prev_width
;
414 // Check if the overall dimensions still make sense.
415 if (width
<= 0 || height
<= 0) return 0;
417 WebPPictureGrabSpecs(pic
, &tmp
);
420 if (!WebPPictureAlloc(&tmp
)) return 0;
422 if (!pic
->use_argb
) {
423 work
= (int32_t*)WebPSafeMalloc(2ULL * width
, sizeof(*work
));
425 WebPPictureFree(&tmp
);
429 RescalePlane(pic
->y
, prev_width
, prev_height
, pic
->y_stride
,
430 tmp
.y
, width
, height
, tmp
.y_stride
, work
, 1);
432 HALVE(prev_width
), HALVE(prev_height
), pic
->uv_stride
,
434 HALVE(width
), HALVE(height
), tmp
.uv_stride
, work
, 1);
436 HALVE(prev_width
), HALVE(prev_height
), pic
->uv_stride
,
438 HALVE(width
), HALVE(height
), tmp
.uv_stride
, work
, 1);
441 RescalePlane(pic
->a
, prev_width
, prev_height
, pic
->a_stride
,
442 tmp
.a
, width
, height
, tmp
.a_stride
, work
, 1);
444 #ifdef WEBP_EXPERIMENTAL_FEATURES
445 if (tmp
.u0
!= NULL
) {
446 const int s
= IS_YUV_CSP(tmp
.colorspace
, WEBP_YUV422
) ? 2 : 1;
448 pic
->u0
, (prev_width
+ s
/ 2) / s
, prev_height
, pic
->uv0_stride
,
449 tmp
.u0
, (width
+ s
/ 2) / s
, height
, tmp
.uv0_stride
, work
, 1);
451 pic
->v0
, (prev_width
+ s
/ 2) / s
, prev_height
, pic
->uv0_stride
,
452 tmp
.v0
, (width
+ s
/ 2) / s
, height
, tmp
.uv0_stride
, work
, 1);
456 work
= (int32_t*)WebPSafeMalloc(2ULL * width
* 4, sizeof(*work
));
458 WebPPictureFree(&tmp
);
462 RescalePlane((const uint8_t*)pic
->argb
, prev_width
, prev_height
,
463 pic
->argb_stride
* 4,
464 (uint8_t*)tmp
.argb
, width
, height
,
468 WebPPictureFree(pic
);
474 //------------------------------------------------------------------------------
475 // WebPMemoryWriter: Write-to-memory
477 void WebPMemoryWriterInit(WebPMemoryWriter
* writer
) {
480 writer
->max_size
= 0;
483 int WebPMemoryWrite(const uint8_t* data
, size_t data_size
,
484 const WebPPicture
* picture
) {
485 WebPMemoryWriter
* const w
= (WebPMemoryWriter
*)picture
->custom_ptr
;
490 next_size
= (uint64_t)w
->size
+ data_size
;
491 if (next_size
> w
->max_size
) {
493 uint64_t next_max_size
= 2ULL * w
->max_size
;
494 if (next_max_size
< next_size
) next_max_size
= next_size
;
495 if (next_max_size
< 8192ULL) next_max_size
= 8192ULL;
496 new_mem
= (uint8_t*)WebPSafeMalloc(next_max_size
, 1);
497 if (new_mem
== NULL
) {
501 memcpy(new_mem
, w
->mem
, w
->size
);
505 // down-cast is ok, thanks to WebPSafeMalloc
506 w
->max_size
= (size_t)next_max_size
;
509 memcpy(w
->mem
+ w
->size
, data
, data_size
);
510 w
->size
+= data_size
;
515 //------------------------------------------------------------------------------
516 // Detection of non-trivial transparency
518 // Returns true if alpha[] has non-0xff values.
519 static int CheckNonOpaque(const uint8_t* alpha
, int width
, int height
,
520 int x_step
, int y_step
) {
521 if (alpha
== NULL
) return 0;
522 while (height
-- > 0) {
524 for (x
= 0; x
< width
* x_step
; x
+= x_step
) {
525 if (alpha
[x
] != 0xff) return 1; // TODO(skal): check 4/8 bytes at a time.
532 // Checking for the presence of non-opaque alpha.
533 int WebPPictureHasTransparency(const WebPPicture
* picture
) {
534 if (picture
== NULL
) return 0;
535 if (!picture
->use_argb
) {
536 return CheckNonOpaque(picture
->a
, picture
->width
, picture
->height
,
537 1, picture
->a_stride
);
540 const uint32_t* argb
= picture
->argb
;
541 if (argb
== NULL
) return 0;
542 for (y
= 0; y
< picture
->height
; ++y
) {
543 for (x
= 0; x
< picture
->width
; ++x
) {
544 if (argb
[x
] < 0xff000000u
) return 1; // test any alpha values != 0xff
546 argb
+= picture
->argb_stride
;
552 //------------------------------------------------------------------------------
553 // RGB -> YUV conversion
555 // TODO: we can do better than simply 2x2 averaging on U/V samples.
556 #define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \
557 (ptr)[rgb_stride] + (ptr)[rgb_stride + step])
558 #define SUM2H(ptr) (2 * (ptr)[0] + 2 * (ptr)[step])
559 #define SUM2V(ptr) (2 * (ptr)[0] + 2 * (ptr)[rgb_stride])
560 #define SUM1(ptr) (4 * (ptr)[0])
561 #define RGB_TO_UV(x, y, SUM) { \
562 const int src = (2 * (step * (x) + (y) * rgb_stride)); \
563 const int dst = (x) + (y) * picture->uv_stride; \
564 const int r = SUM(r_ptr + src); \
565 const int g = SUM(g_ptr + src); \
566 const int b = SUM(b_ptr + src); \
567 picture->u[dst] = VP8RGBToU(r, g, b); \
568 picture->v[dst] = VP8RGBToV(r, g, b); \
571 #define RGB_TO_UV0(x_in, x_out, y, SUM) { \
572 const int src = (step * (x_in) + (y) * rgb_stride); \
573 const int dst = (x_out) + (y) * picture->uv0_stride; \
574 const int r = SUM(r_ptr + src); \
575 const int g = SUM(g_ptr + src); \
576 const int b = SUM(b_ptr + src); \
577 picture->u0[dst] = VP8RGBToU(r, g, b); \
578 picture->v0[dst] = VP8RGBToV(r, g, b); \
581 static void MakeGray(WebPPicture
* const picture
) {
583 const int uv_width
= HALVE(picture
->width
);
584 const int uv_height
= HALVE(picture
->height
);
585 for (y
= 0; y
< uv_height
; ++y
) {
586 memset(picture
->u
+ y
* picture
->uv_stride
, 128, uv_width
);
587 memset(picture
->v
+ y
* picture
->uv_stride
, 128, uv_width
);
591 static int ImportYUVAFromRGBA(const uint8_t* const r_ptr
,
592 const uint8_t* const g_ptr
,
593 const uint8_t* const b_ptr
,
594 const uint8_t* const a_ptr
,
595 int step
, // bytes per pixel
596 int rgb_stride
, // bytes per scanline
597 WebPPicture
* const picture
) {
598 const WebPEncCSP uv_csp
= picture
->colorspace
& WEBP_CSP_UV_MASK
;
600 const int width
= picture
->width
;
601 const int height
= picture
->height
;
602 const int has_alpha
= CheckNonOpaque(a_ptr
, width
, height
, step
, rgb_stride
);
604 picture
->colorspace
= uv_csp
;
605 picture
->use_argb
= 0;
607 picture
->colorspace
|= WEBP_CSP_ALPHA_BIT
;
609 if (!WebPPictureAlloc(picture
)) return 0;
612 for (y
= 0; y
< height
; ++y
) {
613 for (x
= 0; x
< width
; ++x
) {
614 const int offset
= step
* x
+ y
* rgb_stride
;
615 picture
->y
[x
+ y
* picture
->y_stride
] =
616 VP8RGBToY(r_ptr
[offset
], g_ptr
[offset
], b_ptr
[offset
]);
620 // Downsample U/V plane
621 if (uv_csp
!= WEBP_YUV400
) {
622 for (y
= 0; y
< (height
>> 1); ++y
) {
623 for (x
= 0; x
< (width
>> 1); ++x
) {
624 RGB_TO_UV(x
, y
, SUM4
);
627 RGB_TO_UV(x
, y
, SUM2V
);
631 for (x
= 0; x
< (width
>> 1); ++x
) {
632 RGB_TO_UV(x
, y
, SUM2H
);
635 RGB_TO_UV(x
, y
, SUM1
);
639 #ifdef WEBP_EXPERIMENTAL_FEATURES
640 // Store original U/V samples too
641 if (uv_csp
== WEBP_YUV422
) {
642 for (y
= 0; y
< height
; ++y
) {
643 for (x
= 0; x
< (width
>> 1); ++x
) {
644 RGB_TO_UV0(2 * x
, x
, y
, SUM2H
);
647 RGB_TO_UV0(2 * x
, x
, y
, SUM1
);
650 } else if (uv_csp
== WEBP_YUV444
) {
651 for (y
= 0; y
< height
; ++y
) {
652 for (x
= 0; x
< width
; ++x
) {
653 RGB_TO_UV0(x
, x
, y
, SUM1
);
664 for (y
= 0; y
< height
; ++y
) {
665 for (x
= 0; x
< width
; ++x
) {
666 picture
->a
[x
+ y
* picture
->a_stride
] =
667 a_ptr
[step
* x
+ y
* rgb_stride
];
674 static int Import(WebPPicture
* const picture
,
675 const uint8_t* const rgb
, int rgb_stride
,
676 int step
, int swap_rb
, int import_alpha
) {
677 const uint8_t* const r_ptr
= rgb
+ (swap_rb
? 2 : 0);
678 const uint8_t* const g_ptr
= rgb
+ 1;
679 const uint8_t* const b_ptr
= rgb
+ (swap_rb
? 0 : 2);
680 const uint8_t* const a_ptr
= import_alpha
? rgb
+ 3 : NULL
;
681 const int width
= picture
->width
;
682 const int height
= picture
->height
;
684 if (!picture
->use_argb
) {
685 return ImportYUVAFromRGBA(r_ptr
, g_ptr
, b_ptr
, a_ptr
, step
, rgb_stride
,
689 picture
->colorspace
|= WEBP_CSP_ALPHA_BIT
;
691 picture
->colorspace
&= ~WEBP_CSP_ALPHA_BIT
;
693 if (!WebPPictureAlloc(picture
)) return 0;
697 for (y
= 0; y
< height
; ++y
) {
698 for (x
= 0; x
< width
; ++x
) {
699 const int offset
= step
* x
+ y
* rgb_stride
;
700 const uint32_t argb
=
702 (r_ptr
[offset
] << 16) |
703 (g_ptr
[offset
] << 8) |
705 picture
->argb
[x
+ y
* picture
->argb_stride
] = argb
;
711 for (y
= 0; y
< height
; ++y
) {
712 for (x
= 0; x
< width
; ++x
) {
713 const int offset
= step
* x
+ y
* rgb_stride
;
714 const uint32_t argb
= ((uint32_t)a_ptr
[offset
] << 24) |
715 (r_ptr
[offset
] << 16) |
716 (g_ptr
[offset
] << 8) |
718 picture
->argb
[x
+ y
* picture
->argb_stride
] = argb
;
730 int WebPPictureImportRGB(WebPPicture
* picture
,
731 const uint8_t* rgb
, int rgb_stride
) {
732 return Import(picture
, rgb
, rgb_stride
, 3, 0, 0);
735 int WebPPictureImportBGR(WebPPicture
* picture
,
736 const uint8_t* rgb
, int rgb_stride
) {
737 return Import(picture
, rgb
, rgb_stride
, 3, 1, 0);
740 int WebPPictureImportRGBA(WebPPicture
* picture
,
741 const uint8_t* rgba
, int rgba_stride
) {
742 return Import(picture
, rgba
, rgba_stride
, 4, 0, 1);
745 int WebPPictureImportBGRA(WebPPicture
* picture
,
746 const uint8_t* rgba
, int rgba_stride
) {
747 return Import(picture
, rgba
, rgba_stride
, 4, 1, 1);
750 int WebPPictureImportRGBX(WebPPicture
* picture
,
751 const uint8_t* rgba
, int rgba_stride
) {
752 return Import(picture
, rgba
, rgba_stride
, 4, 0, 0);
755 int WebPPictureImportBGRX(WebPPicture
* picture
,
756 const uint8_t* rgba
, int rgba_stride
) {
757 return Import(picture
, rgba
, rgba_stride
, 4, 1, 0);
760 //------------------------------------------------------------------------------
761 // Automatic YUV <-> ARGB conversions.
763 int WebPPictureYUVAToARGB(WebPPicture
* picture
) {
764 if (picture
== NULL
) return 0;
765 if (picture
->memory_
== NULL
|| picture
->y
== NULL
||
766 picture
->u
== NULL
|| picture
->v
== NULL
) {
767 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_NULL_PARAMETER
);
769 if ((picture
->colorspace
& WEBP_CSP_ALPHA_BIT
) && picture
->a
== NULL
) {
770 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_NULL_PARAMETER
);
772 if ((picture
->colorspace
& WEBP_CSP_UV_MASK
) != WEBP_YUV420
) {
773 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_INVALID_CONFIGURATION
);
775 // Allocate a new argb buffer (discarding the previous one).
776 if (!PictureAllocARGB(picture
)) return 0;
781 const int width
= picture
->width
;
782 const int height
= picture
->height
;
783 const int argb_stride
= 4 * picture
->argb_stride
;
784 uint8_t* dst
= (uint8_t*)picture
->argb
;
785 const uint8_t *cur_u
= picture
->u
, *cur_v
= picture
->v
, *cur_y
= picture
->y
;
786 WebPUpsampleLinePairFunc upsample
= WebPGetLinePairConverter(ALPHA_IS_LAST
);
788 // First row, with replicated top samples.
789 upsample(NULL
, cur_y
, cur_u
, cur_v
, cur_u
, cur_v
, NULL
, dst
, width
);
790 cur_y
+= picture
->y_stride
;
793 for (y
= 1; y
+ 1 < height
; y
+= 2) {
794 const uint8_t* const top_u
= cur_u
;
795 const uint8_t* const top_v
= cur_v
;
796 cur_u
+= picture
->uv_stride
;
797 cur_v
+= picture
->uv_stride
;
798 upsample(cur_y
, cur_y
+ picture
->y_stride
, top_u
, top_v
, cur_u
, cur_v
,
799 dst
, dst
+ argb_stride
, width
);
800 cur_y
+= 2 * picture
->y_stride
;
801 dst
+= 2 * argb_stride
;
803 // Last row (if needed), with replicated bottom samples.
804 if (height
> 1 && !(height
& 1)) {
805 upsample(cur_y
, NULL
, cur_u
, cur_v
, cur_u
, cur_v
, dst
, NULL
, width
);
807 // Insert alpha values if needed, in replacement for the default 0xff ones.
808 if (picture
->colorspace
& WEBP_CSP_ALPHA_BIT
) {
809 for (y
= 0; y
< height
; ++y
) {
810 uint32_t* const argb_dst
= picture
->argb
+ y
* picture
->argb_stride
;
811 const uint8_t* const src
= picture
->a
+ y
* picture
->a_stride
;
813 for (x
= 0; x
< width
; ++x
) {
814 argb_dst
[x
] = (argb_dst
[x
] & 0x00ffffffu
) | ((uint32_t)src
[x
] << 24);
822 int WebPPictureARGBToYUVA(WebPPicture
* picture
, WebPEncCSP colorspace
) {
823 if (picture
== NULL
) return 0;
824 if (picture
->argb
== NULL
) {
825 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_NULL_PARAMETER
);
827 const uint8_t* const argb
= (const uint8_t*)picture
->argb
;
828 const uint8_t* const r
= ALPHA_IS_LAST
? argb
+ 2 : argb
+ 1;
829 const uint8_t* const g
= ALPHA_IS_LAST
? argb
+ 1 : argb
+ 2;
830 const uint8_t* const b
= ALPHA_IS_LAST
? argb
+ 0 : argb
+ 3;
831 const uint8_t* const a
= ALPHA_IS_LAST
? argb
+ 3 : argb
+ 0;
832 // We work on a tmp copy of 'picture', because ImportYUVAFromRGBA()
833 // would be calling WebPPictureFree(picture) otherwise.
834 WebPPicture tmp
= *picture
;
835 PictureResetARGB(&tmp
); // reset ARGB buffer so that it's not free()'d.
837 tmp
.colorspace
= colorspace
& WEBP_CSP_UV_MASK
;
838 if (!ImportYUVAFromRGBA(r
, g
, b
, a
, 4, 4 * picture
->argb_stride
, &tmp
)) {
839 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_OUT_OF_MEMORY
);
841 // Copy back the YUV specs into 'picture'.
842 tmp
.argb
= picture
->argb
;
843 tmp
.argb_stride
= picture
->argb_stride
;
844 tmp
.memory_argb_
= picture
->memory_argb_
;
850 //------------------------------------------------------------------------------
851 // Helper: clean up fully transparent area to help compressibility.
854 #define SIZE2 (SIZE / 2)
855 static int is_transparent_area(const uint8_t* ptr
, int stride
, int size
) {
857 for (y
= 0; y
< size
; ++y
) {
858 for (x
= 0; x
< size
; ++x
) {
868 static WEBP_INLINE
void flatten(uint8_t* ptr
, int v
, int stride
, int size
) {
870 for (y
= 0; y
< size
; ++y
) {
871 memset(ptr
, v
, size
);
876 void WebPCleanupTransparentArea(WebPPicture
* pic
) {
878 const uint8_t* a_ptr
;
879 int values
[3] = { 0 };
881 if (pic
== NULL
) return;
884 if (a_ptr
== NULL
) return; // nothing to do
886 w
= pic
->width
/ SIZE
;
887 h
= pic
->height
/ SIZE
;
888 for (y
= 0; y
< h
; ++y
) {
890 for (x
= 0; x
< w
; ++x
) {
891 const int off_a
= (y
* pic
->a_stride
+ x
) * SIZE
;
892 const int off_y
= (y
* pic
->y_stride
+ x
) * SIZE
;
893 const int off_uv
= (y
* pic
->uv_stride
+ x
) * SIZE2
;
894 if (is_transparent_area(a_ptr
+ off_a
, pic
->a_stride
, SIZE
)) {
896 values
[0] = pic
->y
[off_y
];
897 values
[1] = pic
->u
[off_uv
];
898 values
[2] = pic
->v
[off_uv
];
901 flatten(pic
->y
+ off_y
, values
[0], pic
->y_stride
, SIZE
);
902 flatten(pic
->u
+ off_uv
, values
[1], pic
->uv_stride
, SIZE2
);
903 flatten(pic
->v
+ off_uv
, values
[2], pic
->uv_stride
, SIZE2
);
908 // ignore the left-overs on right/bottom
915 //------------------------------------------------------------------------------
916 // local-min distortion
918 // For every pixel in the *reference* picture, we search for the local best
919 // match in the compressed image. This is not a symmetrical measure.
921 // search radius. Shouldn't be too large.
924 static float AccumulateLSIM(const uint8_t* src
, int src_stride
,
925 const uint8_t* ref
, int ref_stride
,
928 double total_sse
= 0.;
929 for (y
= 0; y
< h
; ++y
) {
930 const int y_0
= (y
- RADIUS
< 0) ? 0 : y
- RADIUS
;
931 const int y_1
= (y
+ RADIUS
+ 1 >= h
) ? h
: y
+ RADIUS
+ 1;
932 for (x
= 0; x
< w
; ++x
) {
933 const int x_0
= (x
- RADIUS
< 0) ? 0 : x
- RADIUS
;
934 const int x_1
= (x
+ RADIUS
+ 1 >= w
) ? w
: x
+ RADIUS
+ 1;
935 double best_sse
= 255. * 255.;
936 const double value
= (double)ref
[y
* ref_stride
+ x
];
938 for (j
= y_0
; j
< y_1
; ++j
) {
939 const uint8_t* s
= src
+ j
* src_stride
;
940 for (i
= x_0
; i
< x_1
; ++i
) {
941 const double sse
= (double)(s
[i
] - value
) * (s
[i
] - value
);
942 if (sse
< best_sse
) best_sse
= sse
;
945 total_sse
+= best_sse
;
948 return (float)total_sse
;
952 //------------------------------------------------------------------------------
955 // Max value returned in case of exact similarity.
956 static const double kMinDistortion_dB
= 99.;
957 static float GetPSNR(const double v
) {
958 return (float)((v
> 0.) ? -4.3429448 * log(v
/ (255 * 255.))
959 : kMinDistortion_dB
);
962 int WebPPictureDistortion(const WebPPicture
* src
, const WebPPicture
* ref
,
963 int type
, float result
[5]) {
968 if (src
== NULL
|| ref
== NULL
||
969 src
->width
!= ref
->width
|| src
->height
!= ref
->height
||
970 src
->y
== NULL
|| ref
->y
== NULL
||
971 src
->u
== NULL
|| ref
->u
== NULL
||
972 src
->v
== NULL
|| ref
->v
== NULL
||
976 // TODO(skal): provide distortion for ARGB too.
977 if (src
->use_argb
== 1 || src
->use_argb
!= ref
->use_argb
) {
981 has_alpha
= !!(src
->colorspace
& WEBP_CSP_ALPHA_BIT
);
982 if (has_alpha
!= !!(ref
->colorspace
& WEBP_CSP_ALPHA_BIT
) ||
983 (has_alpha
&& (src
->a
== NULL
|| ref
->a
== NULL
))) {
987 memset(stats
, 0, sizeof(stats
));
989 uv_w
= HALVE(src
->width
);
990 uv_h
= HALVE(src
->height
);
993 sse
[0] = AccumulateLSIM(src
->y
, src
->y_stride
,
994 ref
->y
, ref
->y_stride
, src
->width
, src
->height
);
995 sse
[1] = AccumulateLSIM(src
->u
, src
->uv_stride
,
996 ref
->u
, ref
->uv_stride
, uv_w
, uv_h
);
997 sse
[2] = AccumulateLSIM(src
->v
, src
->uv_stride
,
998 ref
->v
, ref
->uv_stride
, uv_w
, uv_h
);
999 sse
[3] = has_alpha
? AccumulateLSIM(src
->a
, src
->a_stride
,
1000 ref
->a
, ref
->a_stride
,
1001 src
->width
, src
->height
)
1003 result
[0] = GetPSNR(sse
[0] / (src
->width
* src
->height
));
1004 result
[1] = GetPSNR(sse
[1] / (uv_w
* uv_h
));
1005 result
[2] = GetPSNR(sse
[2] / (uv_w
* uv_h
));
1006 result
[3] = GetPSNR(sse
[3] / (src
->width
* src
->height
));
1008 double total_sse
= sse
[0] + sse
[1] + sse
[2];
1009 int total_pixels
= src
->width
* src
->height
+ 2 * uv_w
* uv_h
;
1011 total_pixels
+= src
->width
* src
->height
;
1012 total_sse
+= sse
[3];
1014 result
[4] = GetPSNR(total_sse
/ total_pixels
);
1018 VP8SSIMAccumulatePlane(src
->y
, src
->y_stride
,
1019 ref
->y
, ref
->y_stride
,
1020 src
->width
, src
->height
, &stats
[0]);
1021 VP8SSIMAccumulatePlane(src
->u
, src
->uv_stride
,
1022 ref
->u
, ref
->uv_stride
,
1023 uv_w
, uv_h
, &stats
[1]);
1024 VP8SSIMAccumulatePlane(src
->v
, src
->uv_stride
,
1025 ref
->v
, ref
->uv_stride
,
1026 uv_w
, uv_h
, &stats
[2]);
1028 VP8SSIMAccumulatePlane(src
->a
, src
->a_stride
,
1029 ref
->a
, ref
->a_stride
,
1030 src
->width
, src
->height
, &stats
[3]);
1032 for (c
= 0; c
<= 4; ++c
) {
1034 const double v
= VP8SSIMGet(&stats
[c
]);
1035 result
[c
] = (float)((v
< 1.) ? -10.0 * log10(1. - v
)
1036 : kMinDistortion_dB
);
1038 const double v
= VP8SSIMGetSquaredError(&stats
[c
]);
1039 result
[c
] = GetPSNR(v
);
1041 // Accumulate forward
1042 if (c
< 4) VP8SSIMAddStats(&stats
[c
], &stats
[4]);
1048 //------------------------------------------------------------------------------
1049 // Simplest high-level calls:
1051 typedef int (*Importer
)(WebPPicture
* const, const uint8_t* const, int);
1053 static size_t Encode(const uint8_t* rgba
, int width
, int height
, int stride
,
1054 Importer import
, float quality_factor
, int lossless
,
1058 WebPMemoryWriter wrt
;
1061 if (!WebPConfigPreset(&config
, WEBP_PRESET_DEFAULT
, quality_factor
) ||
1062 !WebPPictureInit(&pic
)) {
1063 return 0; // shouldn't happen, except if system installation is broken
1066 config
.lossless
= !!lossless
;
1067 pic
.use_argb
= !!lossless
;
1069 pic
.height
= height
;
1070 pic
.writer
= WebPMemoryWrite
;
1071 pic
.custom_ptr
= &wrt
;
1072 WebPMemoryWriterInit(&wrt
);
1074 ok
= import(&pic
, rgba
, stride
) && WebPEncode(&config
, &pic
);
1075 WebPPictureFree(&pic
);
1085 #define ENCODE_FUNC(NAME, IMPORTER) \
1086 size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \
1088 return Encode(in, w, h, bps, IMPORTER, q, 0, out); \
1091 ENCODE_FUNC(WebPEncodeRGB
, WebPPictureImportRGB
);
1092 ENCODE_FUNC(WebPEncodeBGR
, WebPPictureImportBGR
);
1093 ENCODE_FUNC(WebPEncodeRGBA
, WebPPictureImportRGBA
);
1094 ENCODE_FUNC(WebPEncodeBGRA
, WebPPictureImportBGRA
);
1098 #define LOSSLESS_DEFAULT_QUALITY 70.
1099 #define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \
1100 size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \
1101 return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \
1104 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB
, WebPPictureImportRGB
);
1105 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR
, WebPPictureImportBGR
);
1106 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA
, WebPPictureImportRGBA
);
1107 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA
, WebPPictureImportBGRA
);
1109 #undef LOSSLESS_ENCODE_FUNC
1111 //------------------------------------------------------------------------------
1113 #if defined(__cplusplus) || defined(c_plusplus)