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/alpha_processing.h"
20 #include "../utils/random.h"
21 #include "../utils/rescaler.h"
22 #include "../utils/utils.h"
23 #include "../dsp/dsp.h"
24 #include "../dsp/yuv.h"
26 // Uncomment to disable gamma-compression during RGB->U/V averaging
27 #define USE_GAMMA_COMPRESSION
29 #define HALVE(x) (((x) + 1) >> 1)
30 #define IS_YUV_CSP(csp, YUV_CSP) (((csp) & WEBP_CSP_UV_MASK) == (YUV_CSP))
35 } test_endian
= { 0xff000000u
};
36 #define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff)
38 static WEBP_INLINE
uint32_t MakeARGB32(int r
, int g
, int b
) {
39 return (0xff000000u
| (r
<< 16) | (g
<< 8) | b
);
42 //------------------------------------------------------------------------------
44 //------------------------------------------------------------------------------
46 int WebPPictureAlloc(WebPPicture
* picture
) {
47 if (picture
!= NULL
) {
48 const WebPEncCSP uv_csp
= picture
->colorspace
& WEBP_CSP_UV_MASK
;
49 const int has_alpha
= picture
->colorspace
& WEBP_CSP_ALPHA_BIT
;
50 const int width
= picture
->width
;
51 const int height
= picture
->height
;
53 if (!picture
->use_argb
) {
54 const int y_stride
= width
;
55 const int uv_width
= HALVE(width
);
56 const int uv_height
= HALVE(height
);
57 const int uv_stride
= uv_width
;
59 int a_width
, a_stride
;
60 uint64_t y_size
, uv_size
, uv0_size
, a_size
, total_size
;
67 #ifdef WEBP_EXPERIMENTAL_FEATURES
68 case WEBP_YUV400
: // for now, we'll just reset the U/V samples
71 uv0_stride
= uv_width
;
80 uv0_size
= height
* uv0_stride
;
83 a_width
= has_alpha
? width
: 0;
85 y_size
= (uint64_t)y_stride
* height
;
86 uv_size
= (uint64_t)uv_stride
* uv_height
;
87 a_size
= (uint64_t)a_stride
* height
;
89 total_size
= y_size
+ a_size
+ 2 * uv_size
+ 2 * uv0_size
;
91 // Security and validation checks
92 if (width
<= 0 || height
<= 0 || // luma/alpha param error
93 uv_width
< 0 || uv_height
< 0) { // u/v param error
96 // Clear previous buffer and allocate a new one.
97 WebPPictureFree(picture
); // erase previous buffer
98 mem
= (uint8_t*)WebPSafeMalloc(total_size
, sizeof(*mem
));
99 if (mem
== NULL
) return 0;
101 // From now on, we're in the clear, we can no longer fail...
102 picture
->memory_
= (void*)mem
;
103 picture
->y_stride
= y_stride
;
104 picture
->uv_stride
= uv_stride
;
105 picture
->a_stride
= a_stride
;
106 picture
->uv0_stride
= uv0_stride
;
107 // TODO(skal): we could align the y/u/v planes and adjust stride.
126 (void)mem
; // makes the static analyzer happy
129 const uint64_t argb_size
= (uint64_t)width
* height
;
130 if (width
<= 0 || height
<= 0) {
133 // Clear previous buffer and allocate a new one.
134 WebPPictureFree(picture
); // erase previous buffer
135 memory
= WebPSafeMalloc(argb_size
, sizeof(*picture
->argb
));
136 if (memory
== NULL
) return 0;
138 // TODO(skal): align plane to cache line?
139 picture
->memory_argb_
= memory
;
140 picture
->argb
= (uint32_t*)memory
;
141 picture
->argb_stride
= width
;
147 // Remove reference to the ARGB buffer (doesn't free anything).
148 static void PictureResetARGB(WebPPicture
* const picture
) {
149 picture
->memory_argb_
= NULL
;
150 picture
->argb
= NULL
;
151 picture
->argb_stride
= 0;
154 // Remove reference to the YUVA buffer (doesn't free anything).
155 static void PictureResetYUVA(WebPPicture
* const picture
) {
156 picture
->memory_
= NULL
;
157 picture
->y
= picture
->u
= picture
->v
= picture
->a
= NULL
;
158 picture
->u0
= picture
->v0
= NULL
;
159 picture
->y_stride
= picture
->uv_stride
= 0;
160 picture
->a_stride
= 0;
161 picture
->uv0_stride
= 0;
164 // Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
165 // into 'dst'. Mark 'dst' as not owning any memory.
166 static void WebPPictureGrabSpecs(const WebPPicture
* const src
,
167 WebPPicture
* const dst
) {
168 assert(src
!= NULL
&& dst
!= NULL
);
170 PictureResetYUVA(dst
);
171 PictureResetARGB(dst
);
174 // Allocate a new argb buffer, discarding any existing one and preserving
175 // the other YUV(A) buffer.
176 static int PictureAllocARGB(WebPPicture
* const picture
) {
178 free(picture
->memory_argb_
);
179 PictureResetARGB(picture
);
180 picture
->use_argb
= 1;
181 WebPPictureGrabSpecs(picture
, &tmp
);
182 if (!WebPPictureAlloc(&tmp
)) {
183 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_OUT_OF_MEMORY
);
185 picture
->memory_argb_
= tmp
.memory_argb_
;
186 picture
->argb
= tmp
.argb
;
187 picture
->argb_stride
= tmp
.argb_stride
;
191 // Release memory owned by 'picture' (both YUV and ARGB buffers).
192 void WebPPictureFree(WebPPicture
* picture
) {
193 if (picture
!= NULL
) {
194 free(picture
->memory_
);
195 free(picture
->memory_argb_
);
196 PictureResetYUVA(picture
);
197 PictureResetARGB(picture
);
201 //------------------------------------------------------------------------------
204 // Not worth moving to dsp/enc.c (only used here).
205 static void CopyPlane(const uint8_t* src
, int src_stride
,
206 uint8_t* dst
, int dst_stride
, int width
, int height
) {
207 while (height
-- > 0) {
208 memcpy(dst
, src
, width
);
214 // Adjust top-left corner to chroma sample position.
215 static void SnapTopLeftPosition(const WebPPicture
* const pic
,
216 int* const left
, int* const top
) {
217 if (!pic
->use_argb
) {
218 const int is_yuv422
= IS_YUV_CSP(pic
->colorspace
, WEBP_YUV422
);
219 if (IS_YUV_CSP(pic
->colorspace
, WEBP_YUV420
) || is_yuv422
) {
221 if (!is_yuv422
) *top
&= ~1;
226 // Adjust top-left corner and verify that the sub-rectangle is valid.
227 static int AdjustAndCheckRectangle(const WebPPicture
* const pic
,
228 int* const left
, int* const top
,
229 int width
, int height
) {
230 SnapTopLeftPosition(pic
, left
, top
);
231 if ((*left
) < 0 || (*top
) < 0) return 0;
232 if (width
<= 0 || height
<= 0) return 0;
233 if ((*left
) + width
> pic
->width
) return 0;
234 if ((*top
) + height
> pic
->height
) return 0;
238 int WebPPictureCopy(const WebPPicture
* src
, WebPPicture
* dst
) {
239 if (src
== NULL
|| dst
== NULL
) return 0;
240 if (src
== dst
) return 1;
242 WebPPictureGrabSpecs(src
, dst
);
243 if (!WebPPictureAlloc(dst
)) return 0;
245 if (!src
->use_argb
) {
246 CopyPlane(src
->y
, src
->y_stride
,
247 dst
->y
, dst
->y_stride
, dst
->width
, dst
->height
);
248 CopyPlane(src
->u
, src
->uv_stride
,
249 dst
->u
, dst
->uv_stride
, HALVE(dst
->width
), HALVE(dst
->height
));
250 CopyPlane(src
->v
, src
->uv_stride
,
251 dst
->v
, dst
->uv_stride
, HALVE(dst
->width
), HALVE(dst
->height
));
252 if (dst
->a
!= NULL
) {
253 CopyPlane(src
->a
, src
->a_stride
,
254 dst
->a
, dst
->a_stride
, dst
->width
, dst
->height
);
256 #ifdef WEBP_EXPERIMENTAL_FEATURES
257 if (dst
->u0
!= NULL
) {
258 int uv0_width
= src
->width
;
259 if (IS_YUV_CSP(dst
->colorspace
, WEBP_YUV422
)) {
260 uv0_width
= HALVE(uv0_width
);
262 CopyPlane(src
->u0
, src
->uv0_stride
,
263 dst
->u0
, dst
->uv0_stride
, uv0_width
, dst
->height
);
264 CopyPlane(src
->v0
, src
->uv0_stride
,
265 dst
->v0
, dst
->uv0_stride
, uv0_width
, dst
->height
);
269 CopyPlane((const uint8_t*)src
->argb
, 4 * src
->argb_stride
,
270 (uint8_t*)dst
->argb
, 4 * dst
->argb_stride
,
271 4 * dst
->width
, dst
->height
);
276 int WebPPictureIsView(const WebPPicture
* picture
) {
277 if (picture
== NULL
) return 0;
278 if (picture
->use_argb
) {
279 return (picture
->memory_argb_
== NULL
);
281 return (picture
->memory_
== NULL
);
284 int WebPPictureView(const WebPPicture
* src
,
285 int left
, int top
, int width
, int height
,
287 if (src
== NULL
|| dst
== NULL
) return 0;
289 // verify rectangle position.
290 if (!AdjustAndCheckRectangle(src
, &left
, &top
, width
, height
)) return 0;
292 if (src
!= dst
) { // beware of aliasing! We don't want to leak 'memory_'.
293 WebPPictureGrabSpecs(src
, dst
);
296 dst
->height
= height
;
297 if (!src
->use_argb
) {
298 dst
->y
= src
->y
+ top
* src
->y_stride
+ left
;
299 dst
->u
= src
->u
+ (top
>> 1) * src
->uv_stride
+ (left
>> 1);
300 dst
->v
= src
->v
+ (top
>> 1) * src
->uv_stride
+ (left
>> 1);
301 dst
->y_stride
= src
->y_stride
;
302 dst
->uv_stride
= src
->uv_stride
;
303 if (src
->a
!= NULL
) {
304 dst
->a
= src
->a
+ top
* src
->a_stride
+ left
;
305 dst
->a_stride
= src
->a_stride
;
307 #ifdef WEBP_EXPERIMENTAL_FEATURES
308 if (src
->u0
!= NULL
) {
310 IS_YUV_CSP(dst
->colorspace
, WEBP_YUV422
) ? (left
>> 1) : left
;
311 dst
->u0
= src
->u0
+ top
* src
->uv0_stride
+ left_pos
;
312 dst
->v0
= src
->v0
+ top
* src
->uv0_stride
+ left_pos
;
313 dst
->uv0_stride
= src
->uv0_stride
;
317 dst
->argb
= src
->argb
+ top
* src
->argb_stride
+ left
;
318 dst
->argb_stride
= src
->argb_stride
;
323 //------------------------------------------------------------------------------
326 int WebPPictureCrop(WebPPicture
* pic
,
327 int left
, int top
, int width
, int height
) {
330 if (pic
== NULL
) return 0;
331 if (!AdjustAndCheckRectangle(pic
, &left
, &top
, width
, height
)) return 0;
333 WebPPictureGrabSpecs(pic
, &tmp
);
336 if (!WebPPictureAlloc(&tmp
)) return 0;
338 if (!pic
->use_argb
) {
339 const int y_offset
= top
* pic
->y_stride
+ left
;
340 const int uv_offset
= (top
/ 2) * pic
->uv_stride
+ left
/ 2;
341 CopyPlane(pic
->y
+ y_offset
, pic
->y_stride
,
342 tmp
.y
, tmp
.y_stride
, width
, height
);
343 CopyPlane(pic
->u
+ uv_offset
, pic
->uv_stride
,
344 tmp
.u
, tmp
.uv_stride
, HALVE(width
), HALVE(height
));
345 CopyPlane(pic
->v
+ uv_offset
, pic
->uv_stride
,
346 tmp
.v
, tmp
.uv_stride
, HALVE(width
), HALVE(height
));
349 const int a_offset
= top
* pic
->a_stride
+ left
;
350 CopyPlane(pic
->a
+ a_offset
, pic
->a_stride
,
351 tmp
.a
, tmp
.a_stride
, width
, height
);
353 #ifdef WEBP_EXPERIMENTAL_FEATURES
354 if (tmp
.u0
!= NULL
) {
357 if (IS_YUV_CSP(tmp
.colorspace
, WEBP_YUV422
)) {
359 left_pos
= HALVE(left_pos
);
361 CopyPlane(pic
->u0
+ top
* pic
->uv0_stride
+ left_pos
, pic
->uv0_stride
,
362 tmp
.u0
, tmp
.uv0_stride
, w
, height
);
363 CopyPlane(pic
->v0
+ top
* pic
->uv0_stride
+ left_pos
, pic
->uv0_stride
,
364 tmp
.v0
, tmp
.uv0_stride
, w
, height
);
368 const uint8_t* const src
=
369 (const uint8_t*)(pic
->argb
+ top
* pic
->argb_stride
+ left
);
370 CopyPlane(src
, pic
->argb_stride
* 4,
371 (uint8_t*)tmp
.argb
, tmp
.argb_stride
* 4,
374 WebPPictureFree(pic
);
379 //------------------------------------------------------------------------------
380 // Simple picture rescaler
382 static void RescalePlane(const uint8_t* src
,
383 int src_width
, int src_height
, int src_stride
,
385 int dst_width
, int dst_height
, int dst_stride
,
388 WebPRescaler rescaler
;
390 WebPRescalerInit(&rescaler
, src_width
, src_height
,
391 dst
, dst_width
, dst_height
, dst_stride
,
393 src_width
, dst_width
,
394 src_height
, dst_height
,
396 memset(work
, 0, 2 * dst_width
* num_channels
* sizeof(*work
));
397 while (y
< src_height
) {
398 y
+= WebPRescalerImport(&rescaler
, src_height
- y
,
399 src
+ y
* src_stride
, src_stride
);
400 WebPRescalerExport(&rescaler
);
404 static void AlphaMultiplyARGB(WebPPicture
* const pic
, int inverse
) {
405 uint32_t* ptr
= pic
->argb
;
407 for (y
= 0; y
< pic
->height
; ++y
) {
408 WebPMultARGBRow(ptr
, pic
->width
, inverse
);
409 ptr
+= pic
->argb_stride
;
413 static void AlphaMultiplyY(WebPPicture
* const pic
, int inverse
) {
414 const uint8_t* ptr_a
= pic
->a
;
416 uint8_t* ptr_y
= pic
->y
;
418 for (y
= 0; y
< pic
->height
; ++y
) {
419 WebPMultRow(ptr_y
, ptr_a
, pic
->width
, inverse
);
420 ptr_y
+= pic
->y_stride
;
421 ptr_a
+= pic
->a_stride
;
426 int WebPPictureRescale(WebPPicture
* pic
, int width
, int height
) {
428 int prev_width
, prev_height
;
431 if (pic
== NULL
) return 0;
432 prev_width
= pic
->width
;
433 prev_height
= pic
->height
;
434 // if width is unspecified, scale original proportionally to height ratio.
436 width
= (prev_width
* height
+ prev_height
/ 2) / prev_height
;
438 // if height is unspecified, scale original proportionally to width ratio.
440 height
= (prev_height
* width
+ prev_width
/ 2) / prev_width
;
442 // Check if the overall dimensions still make sense.
443 if (width
<= 0 || height
<= 0) return 0;
445 WebPPictureGrabSpecs(pic
, &tmp
);
448 if (!WebPPictureAlloc(&tmp
)) return 0;
450 if (!pic
->use_argb
) {
451 work
= (int32_t*)WebPSafeMalloc(2ULL * width
, sizeof(*work
));
453 WebPPictureFree(&tmp
);
456 // If present, we need to rescale alpha first (for AlphaMultiplyY).
457 if (pic
->a
!= NULL
) {
458 RescalePlane(pic
->a
, prev_width
, prev_height
, pic
->a_stride
,
459 tmp
.a
, width
, height
, tmp
.a_stride
, work
, 1);
462 // We take transparency into account on the luma plane only. That's not
463 // totally exact blending, but still is a good approximation.
464 AlphaMultiplyY(pic
, 0);
465 RescalePlane(pic
->y
, prev_width
, prev_height
, pic
->y_stride
,
466 tmp
.y
, width
, height
, tmp
.y_stride
, work
, 1);
467 AlphaMultiplyY(&tmp
, 1);
470 HALVE(prev_width
), HALVE(prev_height
), pic
->uv_stride
,
472 HALVE(width
), HALVE(height
), tmp
.uv_stride
, work
, 1);
474 HALVE(prev_width
), HALVE(prev_height
), pic
->uv_stride
,
476 HALVE(width
), HALVE(height
), tmp
.uv_stride
, work
, 1);
478 #ifdef WEBP_EXPERIMENTAL_FEATURES
479 if (tmp
.u0
!= NULL
) {
480 const int s
= IS_YUV_CSP(tmp
.colorspace
, WEBP_YUV422
) ? 2 : 1;
482 pic
->u0
, (prev_width
+ s
/ 2) / s
, prev_height
, pic
->uv0_stride
,
483 tmp
.u0
, (width
+ s
/ 2) / s
, height
, tmp
.uv0_stride
, work
, 1);
485 pic
->v0
, (prev_width
+ s
/ 2) / s
, prev_height
, pic
->uv0_stride
,
486 tmp
.v0
, (width
+ s
/ 2) / s
, height
, tmp
.uv0_stride
, work
, 1);
490 work
= (int32_t*)WebPSafeMalloc(2ULL * width
* 4, sizeof(*work
));
492 WebPPictureFree(&tmp
);
495 // In order to correctly interpolate colors, we need to apply the alpha
496 // weighting first (black-matting), scale the RGB values, and remove
497 // the premultiplication afterward (while preserving the alpha channel).
498 AlphaMultiplyARGB(pic
, 0);
499 RescalePlane((const uint8_t*)pic
->argb
, prev_width
, prev_height
,
500 pic
->argb_stride
* 4,
501 (uint8_t*)tmp
.argb
, width
, height
,
504 AlphaMultiplyARGB(&tmp
, 1);
506 WebPPictureFree(pic
);
512 //------------------------------------------------------------------------------
513 // WebPMemoryWriter: Write-to-memory
515 void WebPMemoryWriterInit(WebPMemoryWriter
* writer
) {
518 writer
->max_size
= 0;
521 int WebPMemoryWrite(const uint8_t* data
, size_t data_size
,
522 const WebPPicture
* picture
) {
523 WebPMemoryWriter
* const w
= (WebPMemoryWriter
*)picture
->custom_ptr
;
528 next_size
= (uint64_t)w
->size
+ data_size
;
529 if (next_size
> w
->max_size
) {
531 uint64_t next_max_size
= 2ULL * w
->max_size
;
532 if (next_max_size
< next_size
) next_max_size
= next_size
;
533 if (next_max_size
< 8192ULL) next_max_size
= 8192ULL;
534 new_mem
= (uint8_t*)WebPSafeMalloc(next_max_size
, 1);
535 if (new_mem
== NULL
) {
539 memcpy(new_mem
, w
->mem
, w
->size
);
543 // down-cast is ok, thanks to WebPSafeMalloc
544 w
->max_size
= (size_t)next_max_size
;
547 memcpy(w
->mem
+ w
->size
, data
, data_size
);
548 w
->size
+= data_size
;
553 //------------------------------------------------------------------------------
554 // Detection of non-trivial transparency
556 // Returns true if alpha[] has non-0xff values.
557 static int CheckNonOpaque(const uint8_t* alpha
, int width
, int height
,
558 int x_step
, int y_step
) {
559 if (alpha
== NULL
) return 0;
560 while (height
-- > 0) {
562 for (x
= 0; x
< width
* x_step
; x
+= x_step
) {
563 if (alpha
[x
] != 0xff) return 1; // TODO(skal): check 4/8 bytes at a time.
570 // Checking for the presence of non-opaque alpha.
571 int WebPPictureHasTransparency(const WebPPicture
* picture
) {
572 if (picture
== NULL
) return 0;
573 if (!picture
->use_argb
) {
574 return CheckNonOpaque(picture
->a
, picture
->width
, picture
->height
,
575 1, picture
->a_stride
);
578 const uint32_t* argb
= picture
->argb
;
579 if (argb
== NULL
) return 0;
580 for (y
= 0; y
< picture
->height
; ++y
) {
581 for (x
= 0; x
< picture
->width
; ++x
) {
582 if (argb
[x
] < 0xff000000u
) return 1; // test any alpha values != 0xff
584 argb
+= picture
->argb_stride
;
590 //------------------------------------------------------------------------------
591 // RGB -> YUV conversion
593 static int RGBToY(int r
, int g
, int b
, VP8Random
* const rg
) {
594 return VP8RGBToY(r
, g
, b
, VP8RandomBits(rg
, YUV_FIX
));
597 static int RGBToU(int r
, int g
, int b
, VP8Random
* const rg
) {
598 return VP8RGBToU(r
, g
, b
, VP8RandomBits(rg
, YUV_FIX
+ 2));
601 static int RGBToV(int r
, int g
, int b
, VP8Random
* const rg
) {
602 return VP8RGBToV(r
, g
, b
, VP8RandomBits(rg
, YUV_FIX
+ 2));
605 //------------------------------------------------------------------------------
607 #if defined(USE_GAMMA_COMPRESSION)
609 // gamma-compensates loss of resolution during chroma subsampling
611 #define kGammaFix 12 // fixed-point precision for linear values
612 #define kGammaScale ((1 << kGammaFix) - 1)
613 #define kGammaTabFix 7 // fixed-point fractional bits precision
614 #define kGammaTabScale (1 << kGammaTabFix)
615 #define kGammaTabRounder (kGammaTabScale >> 1)
616 #define kGammaTabSize (1 << (kGammaFix - kGammaTabFix))
618 static int kLinearToGammaTab
[kGammaTabSize
+ 1];
619 static uint16_t kGammaToLinearTab
[256];
620 static int kGammaTablesOk
= 0;
622 static void InitGammaTables(void) {
623 if (!kGammaTablesOk
) {
625 const double scale
= 1. / kGammaScale
;
626 for (v
= 0; v
<= 255; ++v
) {
627 kGammaToLinearTab
[v
] =
628 (uint16_t)(pow(v
/ 255., kGamma
) * kGammaScale
+ .5);
630 for (v
= 0; v
<= kGammaTabSize
; ++v
) {
631 const double x
= scale
* (v
<< kGammaTabFix
);
632 kLinearToGammaTab
[v
] = (int)(pow(x
, 1. / kGamma
) * 255. + .5);
638 static WEBP_INLINE
uint32_t GammaToLinear(uint8_t v
) {
639 return kGammaToLinearTab
[v
];
642 // Convert a linear value 'v' to YUV_FIX+2 fixed-point precision
643 // U/V value, suitable for RGBToU/V calls.
644 static WEBP_INLINE
int LinearToGamma(uint32_t base_value
, int shift
) {
645 const int v
= base_value
<< shift
; // final uplifted value
646 const int tab_pos
= v
>> (kGammaTabFix
+ 2); // integer part
647 const int x
= v
& ((kGammaTabScale
<< 2) - 1); // fractional part
648 const int v0
= kLinearToGammaTab
[tab_pos
];
649 const int v1
= kLinearToGammaTab
[tab_pos
+ 1];
650 const int y
= v1
* x
+ v0
* ((kGammaTabScale
<< 2) - x
); // interpolate
651 return (y
+ kGammaTabRounder
) >> kGammaTabFix
; // descale
656 static void InitGammaTables(void) {}
657 static WEBP_INLINE
uint32_t GammaToLinear(uint8_t v
) { return v
; }
658 static WEBP_INLINE
int LinearToGamma(uint32_t base_value
, int shift
) {
663 #endif // USE_GAMMA_COMPRESSION
665 //------------------------------------------------------------------------------
667 #define SUM4(ptr) LinearToGamma( \
668 GammaToLinear((ptr)[0]) + \
669 GammaToLinear((ptr)[step]) + \
670 GammaToLinear((ptr)[rgb_stride]) + \
671 GammaToLinear((ptr)[rgb_stride + step]), 0) \
674 LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[step]), 1)
676 LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[rgb_stride]), 1)
678 LinearToGamma(GammaToLinear((ptr)[0]), 2)
680 #define RGB_TO_UV(x, y, SUM) { \
681 const int src = (2 * (step * (x) + (y) * rgb_stride)); \
682 const int dst = (x) + (y) * picture->uv_stride; \
683 const int r = SUM(r_ptr + src); \
684 const int g = SUM(g_ptr + src); \
685 const int b = SUM(b_ptr + src); \
686 picture->u[dst] = RGBToU(r, g, b, &rg); \
687 picture->v[dst] = RGBToV(r, g, b, &rg); \
690 #define RGB_TO_UV0(x_in, x_out, y, SUM) { \
691 const int src = (step * (x_in) + (y) * rgb_stride); \
692 const int dst = (x_out) + (y) * picture->uv0_stride; \
693 const int r = SUM(r_ptr + src); \
694 const int g = SUM(g_ptr + src); \
695 const int b = SUM(b_ptr + src); \
696 picture->u0[dst] = RGBToU(r, g, b, &rg); \
697 picture->v0[dst] = RGBToV(r, g, b, &rg); \
700 static void MakeGray(WebPPicture
* const picture
) {
702 const int uv_width
= HALVE(picture
->width
);
703 const int uv_height
= HALVE(picture
->height
);
704 for (y
= 0; y
< uv_height
; ++y
) {
705 memset(picture
->u
+ y
* picture
->uv_stride
, 128, uv_width
);
706 memset(picture
->v
+ y
* picture
->uv_stride
, 128, uv_width
);
710 static int ImportYUVAFromRGBA(const uint8_t* const r_ptr
,
711 const uint8_t* const g_ptr
,
712 const uint8_t* const b_ptr
,
713 const uint8_t* const a_ptr
,
714 int step
, // bytes per pixel
715 int rgb_stride
, // bytes per scanline
717 WebPPicture
* const picture
) {
718 const WebPEncCSP uv_csp
= picture
->colorspace
& WEBP_CSP_UV_MASK
;
720 const int width
= picture
->width
;
721 const int height
= picture
->height
;
722 const int has_alpha
= CheckNonOpaque(a_ptr
, width
, height
, step
, rgb_stride
);
725 picture
->colorspace
= uv_csp
;
726 picture
->use_argb
= 0;
728 picture
->colorspace
|= WEBP_CSP_ALPHA_BIT
;
730 if (!WebPPictureAlloc(picture
)) return 0;
732 VP8InitRandom(&rg
, dithering
);
736 for (y
= 0; y
< height
; ++y
) {
737 for (x
= 0; x
< width
; ++x
) {
738 const int offset
= step
* x
+ y
* rgb_stride
;
739 picture
->y
[x
+ y
* picture
->y_stride
] =
740 RGBToY(r_ptr
[offset
], g_ptr
[offset
], b_ptr
[offset
], &rg
);
744 // Downsample U/V plane
745 if (uv_csp
!= WEBP_YUV400
) {
746 for (y
= 0; y
< (height
>> 1); ++y
) {
747 for (x
= 0; x
< (width
>> 1); ++x
) {
748 RGB_TO_UV(x
, y
, SUM4
);
751 RGB_TO_UV(x
, y
, SUM2V
);
755 for (x
= 0; x
< (width
>> 1); ++x
) {
756 RGB_TO_UV(x
, y
, SUM2H
);
759 RGB_TO_UV(x
, y
, SUM1
);
763 #ifdef WEBP_EXPERIMENTAL_FEATURES
764 // Store original U/V samples too
765 if (uv_csp
== WEBP_YUV422
) {
766 for (y
= 0; y
< height
; ++y
) {
767 for (x
= 0; x
< (width
>> 1); ++x
) {
768 RGB_TO_UV0(2 * x
, x
, y
, SUM2H
);
771 RGB_TO_UV0(2 * x
, x
, y
, SUM1
);
774 } else if (uv_csp
== WEBP_YUV444
) {
775 for (y
= 0; y
< height
; ++y
) {
776 for (x
= 0; x
< width
; ++x
) {
777 RGB_TO_UV0(x
, x
, y
, SUM1
);
788 assert(picture
->a
!= NULL
);
789 for (y
= 0; y
< height
; ++y
) {
790 for (x
= 0; x
< width
; ++x
) {
791 picture
->a
[x
+ y
* picture
->a_stride
] =
792 a_ptr
[step
* x
+ y
* rgb_stride
];
799 static int Import(WebPPicture
* const picture
,
800 const uint8_t* const rgb
, int rgb_stride
,
801 int step
, int swap_rb
, int import_alpha
) {
802 const uint8_t* const r_ptr
= rgb
+ (swap_rb
? 2 : 0);
803 const uint8_t* const g_ptr
= rgb
+ 1;
804 const uint8_t* const b_ptr
= rgb
+ (swap_rb
? 0 : 2);
805 const uint8_t* const a_ptr
= import_alpha
? rgb
+ 3 : NULL
;
806 const int width
= picture
->width
;
807 const int height
= picture
->height
;
809 if (!picture
->use_argb
) {
810 return ImportYUVAFromRGBA(r_ptr
, g_ptr
, b_ptr
, a_ptr
, step
, rgb_stride
,
811 0.f
/* no dithering */, picture
);
814 picture
->colorspace
|= WEBP_CSP_ALPHA_BIT
;
816 picture
->colorspace
&= ~WEBP_CSP_ALPHA_BIT
;
818 if (!WebPPictureAlloc(picture
)) return 0;
822 for (y
= 0; y
< height
; ++y
) {
823 for (x
= 0; x
< width
; ++x
) {
824 const int offset
= step
* x
+ y
* rgb_stride
;
825 const uint32_t argb
=
826 MakeARGB32(r_ptr
[offset
], g_ptr
[offset
], b_ptr
[offset
]);
827 picture
->argb
[x
+ y
* picture
->argb_stride
] = argb
;
833 for (y
= 0; y
< height
; ++y
) {
834 for (x
= 0; x
< width
; ++x
) {
835 const int offset
= step
* x
+ y
* rgb_stride
;
836 const uint32_t argb
= ((uint32_t)a_ptr
[offset
] << 24) |
837 (r_ptr
[offset
] << 16) |
838 (g_ptr
[offset
] << 8) |
840 picture
->argb
[x
+ y
* picture
->argb_stride
] = argb
;
852 int WebPPictureImportRGB(WebPPicture
* picture
,
853 const uint8_t* rgb
, int rgb_stride
) {
854 return Import(picture
, rgb
, rgb_stride
, 3, 0, 0);
857 int WebPPictureImportBGR(WebPPicture
* picture
,
858 const uint8_t* rgb
, int rgb_stride
) {
859 return Import(picture
, rgb
, rgb_stride
, 3, 1, 0);
862 int WebPPictureImportRGBA(WebPPicture
* picture
,
863 const uint8_t* rgba
, int rgba_stride
) {
864 return Import(picture
, rgba
, rgba_stride
, 4, 0, 1);
867 int WebPPictureImportBGRA(WebPPicture
* picture
,
868 const uint8_t* rgba
, int rgba_stride
) {
869 return Import(picture
, rgba
, rgba_stride
, 4, 1, 1);
872 int WebPPictureImportRGBX(WebPPicture
* picture
,
873 const uint8_t* rgba
, int rgba_stride
) {
874 return Import(picture
, rgba
, rgba_stride
, 4, 0, 0);
877 int WebPPictureImportBGRX(WebPPicture
* picture
,
878 const uint8_t* rgba
, int rgba_stride
) {
879 return Import(picture
, rgba
, rgba_stride
, 4, 1, 0);
882 //------------------------------------------------------------------------------
883 // Automatic YUV <-> ARGB conversions.
885 int WebPPictureYUVAToARGB(WebPPicture
* picture
) {
886 if (picture
== NULL
) return 0;
887 if (picture
->y
== NULL
|| picture
->u
== NULL
|| picture
->v
== NULL
) {
888 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_NULL_PARAMETER
);
890 if ((picture
->colorspace
& WEBP_CSP_ALPHA_BIT
) && picture
->a
== NULL
) {
891 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_NULL_PARAMETER
);
893 if ((picture
->colorspace
& WEBP_CSP_UV_MASK
) != WEBP_YUV420
) {
894 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_INVALID_CONFIGURATION
);
896 // Allocate a new argb buffer (discarding the previous one).
897 if (!PictureAllocARGB(picture
)) return 0;
902 const int width
= picture
->width
;
903 const int height
= picture
->height
;
904 const int argb_stride
= 4 * picture
->argb_stride
;
905 uint8_t* dst
= (uint8_t*)picture
->argb
;
906 const uint8_t *cur_u
= picture
->u
, *cur_v
= picture
->v
, *cur_y
= picture
->y
;
907 WebPUpsampleLinePairFunc upsample
= WebPGetLinePairConverter(ALPHA_IS_LAST
);
909 // First row, with replicated top samples.
910 upsample(cur_y
, NULL
, cur_u
, cur_v
, cur_u
, cur_v
, dst
, NULL
, width
);
911 cur_y
+= picture
->y_stride
;
914 for (y
= 1; y
+ 1 < height
; y
+= 2) {
915 const uint8_t* const top_u
= cur_u
;
916 const uint8_t* const top_v
= cur_v
;
917 cur_u
+= picture
->uv_stride
;
918 cur_v
+= picture
->uv_stride
;
919 upsample(cur_y
, cur_y
+ picture
->y_stride
, top_u
, top_v
, cur_u
, cur_v
,
920 dst
, dst
+ argb_stride
, width
);
921 cur_y
+= 2 * picture
->y_stride
;
922 dst
+= 2 * argb_stride
;
924 // Last row (if needed), with replicated bottom samples.
925 if (height
> 1 && !(height
& 1)) {
926 upsample(cur_y
, NULL
, cur_u
, cur_v
, cur_u
, cur_v
, dst
, NULL
, width
);
928 // Insert alpha values if needed, in replacement for the default 0xff ones.
929 if (picture
->colorspace
& WEBP_CSP_ALPHA_BIT
) {
930 for (y
= 0; y
< height
; ++y
) {
931 uint32_t* const argb_dst
= picture
->argb
+ y
* picture
->argb_stride
;
932 const uint8_t* const src
= picture
->a
+ y
* picture
->a_stride
;
934 for (x
= 0; x
< width
; ++x
) {
935 argb_dst
[x
] = (argb_dst
[x
] & 0x00ffffffu
) | ((uint32_t)src
[x
] << 24);
943 int WebPPictureARGBToYUVADithered(WebPPicture
* picture
, WebPEncCSP colorspace
,
945 if (picture
== NULL
) return 0;
946 if (picture
->argb
== NULL
) {
947 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_NULL_PARAMETER
);
949 const uint8_t* const argb
= (const uint8_t*)picture
->argb
;
950 const uint8_t* const r
= ALPHA_IS_LAST
? argb
+ 2 : argb
+ 1;
951 const uint8_t* const g
= ALPHA_IS_LAST
? argb
+ 1 : argb
+ 2;
952 const uint8_t* const b
= ALPHA_IS_LAST
? argb
+ 0 : argb
+ 3;
953 const uint8_t* const a
= ALPHA_IS_LAST
? argb
+ 3 : argb
+ 0;
954 // We work on a tmp copy of 'picture', because ImportYUVAFromRGBA()
955 // would be calling WebPPictureFree(picture) otherwise.
956 WebPPicture tmp
= *picture
;
957 PictureResetARGB(&tmp
); // reset ARGB buffer so that it's not free()'d.
959 tmp
.colorspace
= colorspace
& WEBP_CSP_UV_MASK
;
960 if (!ImportYUVAFromRGBA(r
, g
, b
, a
, 4, 4 * picture
->argb_stride
, dithering
,
962 return WebPEncodingSetError(picture
, VP8_ENC_ERROR_OUT_OF_MEMORY
);
964 // Copy back the YUV specs into 'picture'.
965 tmp
.argb
= picture
->argb
;
966 tmp
.argb_stride
= picture
->argb_stride
;
967 tmp
.memory_argb_
= picture
->memory_argb_
;
973 int WebPPictureARGBToYUVA(WebPPicture
* picture
, WebPEncCSP colorspace
) {
974 return WebPPictureARGBToYUVADithered(picture
, colorspace
, 0.f
);
977 //------------------------------------------------------------------------------
978 // Helper: clean up fully transparent area to help compressibility.
981 #define SIZE2 (SIZE / 2)
982 static int is_transparent_area(const uint8_t* ptr
, int stride
, int size
) {
984 for (y
= 0; y
< size
; ++y
) {
985 for (x
= 0; x
< size
; ++x
) {
995 static WEBP_INLINE
void flatten(uint8_t* ptr
, int v
, int stride
, int size
) {
997 for (y
= 0; y
< size
; ++y
) {
998 memset(ptr
, v
, size
);
1003 void WebPCleanupTransparentArea(WebPPicture
* pic
) {
1005 const uint8_t* a_ptr
;
1006 int values
[3] = { 0 };
1008 if (pic
== NULL
) return;
1011 if (a_ptr
== NULL
) return; // nothing to do
1013 w
= pic
->width
/ SIZE
;
1014 h
= pic
->height
/ SIZE
;
1015 for (y
= 0; y
< h
; ++y
) {
1017 for (x
= 0; x
< w
; ++x
) {
1018 const int off_a
= (y
* pic
->a_stride
+ x
) * SIZE
;
1019 const int off_y
= (y
* pic
->y_stride
+ x
) * SIZE
;
1020 const int off_uv
= (y
* pic
->uv_stride
+ x
) * SIZE2
;
1021 if (is_transparent_area(a_ptr
+ off_a
, pic
->a_stride
, SIZE
)) {
1023 values
[0] = pic
->y
[off_y
];
1024 values
[1] = pic
->u
[off_uv
];
1025 values
[2] = pic
->v
[off_uv
];
1028 flatten(pic
->y
+ off_y
, values
[0], pic
->y_stride
, SIZE
);
1029 flatten(pic
->u
+ off_uv
, values
[1], pic
->uv_stride
, SIZE2
);
1030 flatten(pic
->v
+ off_uv
, values
[2], pic
->uv_stride
, SIZE2
);
1035 // ignore the left-overs on right/bottom
1042 //------------------------------------------------------------------------------
1043 // Blend color and remove transparency info
1045 #define BLEND(V0, V1, ALPHA) \
1046 ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 16)
1047 #define BLEND_10BIT(V0, V1, ALPHA) \
1048 ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 18)
1050 void WebPBlendAlpha(WebPPicture
* pic
, uint32_t background_rgb
) {
1051 const int red
= (background_rgb
>> 16) & 0xff;
1052 const int green
= (background_rgb
>> 8) & 0xff;
1053 const int blue
= (background_rgb
>> 0) & 0xff;
1056 if (pic
== NULL
) return;
1057 VP8InitRandom(&rg
, 0.f
);
1058 if (!pic
->use_argb
) {
1059 const int uv_width
= (pic
->width
>> 1); // omit last pixel during u/v loop
1060 const int Y0
= RGBToY(red
, green
, blue
, &rg
);
1061 // VP8RGBToU/V expects the u/v values summed over four pixels
1062 const int U0
= RGBToU(4 * red
, 4 * green
, 4 * blue
, &rg
);
1063 const int V0
= RGBToV(4 * red
, 4 * green
, 4 * blue
, &rg
);
1064 const int has_alpha
= pic
->colorspace
& WEBP_CSP_ALPHA_BIT
;
1065 if (!has_alpha
|| pic
->a
== NULL
) return; // nothing to do
1066 for (y
= 0; y
< pic
->height
; ++y
) {
1068 uint8_t* const y_ptr
= pic
->y
+ y
* pic
->y_stride
;
1069 uint8_t* const a_ptr
= pic
->a
+ y
* pic
->a_stride
;
1070 for (x
= 0; x
< pic
->width
; ++x
) {
1071 const int alpha
= a_ptr
[x
];
1073 y_ptr
[x
] = BLEND(Y0
, y_ptr
[x
], a_ptr
[x
]);
1076 // Chroma blending every even line
1078 uint8_t* const u
= pic
->u
+ (y
>> 1) * pic
->uv_stride
;
1079 uint8_t* const v
= pic
->v
+ (y
>> 1) * pic
->uv_stride
;
1080 uint8_t* const a_ptr2
=
1081 (y
+ 1 == pic
->height
) ? a_ptr
: a_ptr
+ pic
->a_stride
;
1082 for (x
= 0; x
< uv_width
; ++x
) {
1083 // Average four alpha values into a single blending weight.
1084 // TODO(skal): might lead to visible contouring. Can we do better?
1086 a_ptr
[2 * x
+ 0] + a_ptr
[2 * x
+ 1] +
1087 a_ptr2
[2 * x
+ 0] + a_ptr2
[2 * x
+ 1];
1088 u
[x
] = BLEND_10BIT(U0
, u
[x
], alpha
);
1089 v
[x
] = BLEND_10BIT(V0
, v
[x
], alpha
);
1091 if (pic
->width
& 1) { // rightmost pixel
1092 const int alpha
= 2 * (a_ptr
[2 * x
+ 0] + a_ptr2
[2 * x
+ 0]);
1093 u
[x
] = BLEND_10BIT(U0
, u
[x
], alpha
);
1094 v
[x
] = BLEND_10BIT(V0
, v
[x
], alpha
);
1097 memset(a_ptr
, 0xff, pic
->width
);
1100 uint32_t* argb
= pic
->argb
;
1101 const uint32_t background
= MakeARGB32(red
, green
, blue
);
1102 for (y
= 0; y
< pic
->height
; ++y
) {
1103 for (x
= 0; x
< pic
->width
; ++x
) {
1104 const int alpha
= (argb
[x
] >> 24) & 0xff;
1105 if (alpha
!= 0xff) {
1107 int r
= (argb
[x
] >> 16) & 0xff;
1108 int g
= (argb
[x
] >> 8) & 0xff;
1109 int b
= (argb
[x
] >> 0) & 0xff;
1110 r
= BLEND(red
, r
, alpha
);
1111 g
= BLEND(green
, g
, alpha
);
1112 b
= BLEND(blue
, b
, alpha
);
1113 argb
[x
] = MakeARGB32(r
, g
, b
);
1115 argb
[x
] = background
;
1119 argb
+= pic
->argb_stride
;
1127 //------------------------------------------------------------------------------
1128 // local-min distortion
1130 // For every pixel in the *reference* picture, we search for the local best
1131 // match in the compressed image. This is not a symmetrical measure.
1133 // search radius. Shouldn't be too large.
1136 static float AccumulateLSIM(const uint8_t* src
, int src_stride
,
1137 const uint8_t* ref
, int ref_stride
,
1140 double total_sse
= 0.;
1141 for (y
= 0; y
< h
; ++y
) {
1142 const int y_0
= (y
- RADIUS
< 0) ? 0 : y
- RADIUS
;
1143 const int y_1
= (y
+ RADIUS
+ 1 >= h
) ? h
: y
+ RADIUS
+ 1;
1144 for (x
= 0; x
< w
; ++x
) {
1145 const int x_0
= (x
- RADIUS
< 0) ? 0 : x
- RADIUS
;
1146 const int x_1
= (x
+ RADIUS
+ 1 >= w
) ? w
: x
+ RADIUS
+ 1;
1147 double best_sse
= 255. * 255.;
1148 const double value
= (double)ref
[y
* ref_stride
+ x
];
1150 for (j
= y_0
; j
< y_1
; ++j
) {
1151 const uint8_t* s
= src
+ j
* src_stride
;
1152 for (i
= x_0
; i
< x_1
; ++i
) {
1153 const double sse
= (double)(s
[i
] - value
) * (s
[i
] - value
);
1154 if (sse
< best_sse
) best_sse
= sse
;
1157 total_sse
+= best_sse
;
1160 return (float)total_sse
;
1164 //------------------------------------------------------------------------------
1167 // Max value returned in case of exact similarity.
1168 static const double kMinDistortion_dB
= 99.;
1169 static float GetPSNR(const double v
) {
1170 return (float)((v
> 0.) ? -4.3429448 * log(v
/ (255 * 255.))
1171 : kMinDistortion_dB
);
1174 int WebPPictureDistortion(const WebPPicture
* src
, const WebPPicture
* ref
,
1175 int type
, float result
[5]) {
1176 DistoStats stats
[5];
1180 if (src
== NULL
|| ref
== NULL
||
1181 src
->width
!= ref
->width
|| src
->height
!= ref
->height
||
1182 src
->y
== NULL
|| ref
->y
== NULL
||
1183 src
->u
== NULL
|| ref
->u
== NULL
||
1184 src
->v
== NULL
|| ref
->v
== NULL
||
1188 // TODO(skal): provide distortion for ARGB too.
1189 if (src
->use_argb
== 1 || src
->use_argb
!= ref
->use_argb
) {
1193 has_alpha
= !!(src
->colorspace
& WEBP_CSP_ALPHA_BIT
);
1194 if (has_alpha
!= !!(ref
->colorspace
& WEBP_CSP_ALPHA_BIT
) ||
1195 (has_alpha
&& (src
->a
== NULL
|| ref
->a
== NULL
))) {
1199 memset(stats
, 0, sizeof(stats
));
1201 uv_w
= HALVE(src
->width
);
1202 uv_h
= HALVE(src
->height
);
1205 sse
[0] = AccumulateLSIM(src
->y
, src
->y_stride
,
1206 ref
->y
, ref
->y_stride
, src
->width
, src
->height
);
1207 sse
[1] = AccumulateLSIM(src
->u
, src
->uv_stride
,
1208 ref
->u
, ref
->uv_stride
, uv_w
, uv_h
);
1209 sse
[2] = AccumulateLSIM(src
->v
, src
->uv_stride
,
1210 ref
->v
, ref
->uv_stride
, uv_w
, uv_h
);
1211 sse
[3] = has_alpha
? AccumulateLSIM(src
->a
, src
->a_stride
,
1212 ref
->a
, ref
->a_stride
,
1213 src
->width
, src
->height
)
1215 result
[0] = GetPSNR(sse
[0] / (src
->width
* src
->height
));
1216 result
[1] = GetPSNR(sse
[1] / (uv_w
* uv_h
));
1217 result
[2] = GetPSNR(sse
[2] / (uv_w
* uv_h
));
1218 result
[3] = GetPSNR(sse
[3] / (src
->width
* src
->height
));
1220 double total_sse
= sse
[0] + sse
[1] + sse
[2];
1221 int total_pixels
= src
->width
* src
->height
+ 2 * uv_w
* uv_h
;
1223 total_pixels
+= src
->width
* src
->height
;
1224 total_sse
+= sse
[3];
1226 result
[4] = GetPSNR(total_sse
/ total_pixels
);
1230 VP8SSIMAccumulatePlane(src
->y
, src
->y_stride
,
1231 ref
->y
, ref
->y_stride
,
1232 src
->width
, src
->height
, &stats
[0]);
1233 VP8SSIMAccumulatePlane(src
->u
, src
->uv_stride
,
1234 ref
->u
, ref
->uv_stride
,
1235 uv_w
, uv_h
, &stats
[1]);
1236 VP8SSIMAccumulatePlane(src
->v
, src
->uv_stride
,
1237 ref
->v
, ref
->uv_stride
,
1238 uv_w
, uv_h
, &stats
[2]);
1240 VP8SSIMAccumulatePlane(src
->a
, src
->a_stride
,
1241 ref
->a
, ref
->a_stride
,
1242 src
->width
, src
->height
, &stats
[3]);
1244 for (c
= 0; c
<= 4; ++c
) {
1246 const double v
= VP8SSIMGet(&stats
[c
]);
1247 result
[c
] = (float)((v
< 1.) ? -10.0 * log10(1. - v
)
1248 : kMinDistortion_dB
);
1250 const double v
= VP8SSIMGetSquaredError(&stats
[c
]);
1251 result
[c
] = GetPSNR(v
);
1253 // Accumulate forward
1254 if (c
< 4) VP8SSIMAddStats(&stats
[c
], &stats
[4]);
1260 //------------------------------------------------------------------------------
1261 // Simplest high-level calls:
1263 typedef int (*Importer
)(WebPPicture
* const, const uint8_t* const, int);
1265 static size_t Encode(const uint8_t* rgba
, int width
, int height
, int stride
,
1266 Importer import
, float quality_factor
, int lossless
,
1270 WebPMemoryWriter wrt
;
1273 if (!WebPConfigPreset(&config
, WEBP_PRESET_DEFAULT
, quality_factor
) ||
1274 !WebPPictureInit(&pic
)) {
1275 return 0; // shouldn't happen, except if system installation is broken
1278 config
.lossless
= !!lossless
;
1279 pic
.use_argb
= !!lossless
;
1281 pic
.height
= height
;
1282 pic
.writer
= WebPMemoryWrite
;
1283 pic
.custom_ptr
= &wrt
;
1284 WebPMemoryWriterInit(&wrt
);
1286 ok
= import(&pic
, rgba
, stride
) && WebPEncode(&config
, &pic
);
1287 WebPPictureFree(&pic
);
1297 #define ENCODE_FUNC(NAME, IMPORTER) \
1298 size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \
1300 return Encode(in, w, h, bps, IMPORTER, q, 0, out); \
1303 ENCODE_FUNC(WebPEncodeRGB
, WebPPictureImportRGB
)
1304 ENCODE_FUNC(WebPEncodeBGR
, WebPPictureImportBGR
)
1305 ENCODE_FUNC(WebPEncodeRGBA
, WebPPictureImportRGBA
)
1306 ENCODE_FUNC(WebPEncodeBGRA
, WebPPictureImportBGRA
)
1310 #define LOSSLESS_DEFAULT_QUALITY 70.
1311 #define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \
1312 size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \
1313 return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \
1316 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB
, WebPPictureImportRGB
)
1317 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR
, WebPPictureImportBGR
)
1318 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA
, WebPPictureImportRGBA
)
1319 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA
, WebPPictureImportBGRA
)
1321 #undef LOSSLESS_ENCODE_FUNC
1323 //------------------------------------------------------------------------------