1 // Copyright 2010 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 // Main decoding functions for WEBP images.
12 // Author: Skal (pascal.massimino@gmail.com)
19 #include "../webp/mux_types.h" // ALPHA_FLAG
21 #if defined(__cplusplus) || defined(c_plusplus)
25 //------------------------------------------------------------------------------
28 // 0...3 "RIFF" 4-byte tag
29 // 4...7 size of image data (including metadata) starting at offset 8
30 // 8...11 "WEBP" our form-type signature
31 // The RIFF container (12 bytes) is followed by appropriate chunks:
32 // 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format
33 // 16..19 size of the raw VP8 image data, starting at offset 20
34 // 20.... the VP8 bytes
36 // 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format
37 // 16..19 size of the raw VP8L image data, starting at offset 20
38 // 20.... the VP8L bytes
40 // 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk.
41 // 16..19 size of the VP8X chunk starting at offset 20.
42 // 20..23 VP8X flags bit-map corresponding to the chunk-types present.
43 // 24..26 Width of the Canvas Image.
44 // 27..29 Height of the Canvas Image.
45 // There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8,
46 // VP8L, XMP, EXIF ...)
47 // All sizes are in little-endian order.
48 // Note: chunk data size must be padded to multiple of 2 when written.
50 static WEBP_INLINE
uint32_t get_le24(const uint8_t* const data
) {
51 return data
[0] | (data
[1] << 8) | (data
[2] << 16);
54 static WEBP_INLINE
uint32_t get_le32(const uint8_t* const data
) {
55 return (uint32_t)get_le24(data
) | (data
[3] << 24);
58 // Validates the RIFF container (if detected) and skips over it.
59 // If a RIFF container is detected,
60 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and
61 // VP8_STATUS_OK otherwise.
62 // In case there are not enough bytes (partial RIFF container), return 0 for
63 // *riff_size. Else return the RIFF size extracted from the header.
64 static VP8StatusCode
ParseRIFF(const uint8_t** const data
,
65 size_t* const data_size
,
66 size_t* const riff_size
) {
68 assert(data_size
!= NULL
);
69 assert(riff_size
!= NULL
);
71 *riff_size
= 0; // Default: no RIFF present.
72 if (*data_size
>= RIFF_HEADER_SIZE
&& !memcmp(*data
, "RIFF", TAG_SIZE
)) {
73 if (memcmp(*data
+ 8, "WEBP", TAG_SIZE
)) {
74 return VP8_STATUS_BITSTREAM_ERROR
; // Wrong image file signature.
76 const uint32_t size
= get_le32(*data
+ TAG_SIZE
);
77 // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
78 if (size
< TAG_SIZE
+ CHUNK_HEADER_SIZE
) {
79 return VP8_STATUS_BITSTREAM_ERROR
;
81 if (size
> MAX_CHUNK_PAYLOAD
) {
82 return VP8_STATUS_BITSTREAM_ERROR
;
84 // We have a RIFF container. Skip it.
86 *data
+= RIFF_HEADER_SIZE
;
87 *data_size
-= RIFF_HEADER_SIZE
;
93 // Validates the VP8X header and skips over it.
94 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header,
95 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
96 // VP8_STATUS_OK otherwise.
97 // If a VP8X chunk is found, found_vp8x is set to true and *width_ptr,
98 // *height_ptr and *flags_ptr are set to the corresponding values extracted
99 // from the VP8X chunk.
100 static VP8StatusCode
ParseVP8X(const uint8_t** const data
,
101 size_t* const data_size
,
102 int* const found_vp8x
,
103 int* const width_ptr
, int* const height_ptr
,
104 uint32_t* const flags_ptr
) {
105 const uint32_t vp8x_size
= CHUNK_HEADER_SIZE
+ VP8X_CHUNK_SIZE
;
106 assert(data
!= NULL
);
107 assert(data_size
!= NULL
);
108 assert(found_vp8x
!= NULL
);
112 if (*data_size
< CHUNK_HEADER_SIZE
) {
113 return VP8_STATUS_NOT_ENOUGH_DATA
; // Insufficient data.
116 if (!memcmp(*data
, "VP8X", TAG_SIZE
)) {
119 const uint32_t chunk_size
= get_le32(*data
+ TAG_SIZE
);
120 if (chunk_size
!= VP8X_CHUNK_SIZE
) {
121 return VP8_STATUS_BITSTREAM_ERROR
; // Wrong chunk size.
124 // Verify if enough data is available to validate the VP8X chunk.
125 if (*data_size
< vp8x_size
) {
126 return VP8_STATUS_NOT_ENOUGH_DATA
; // Insufficient data.
128 flags
= get_le32(*data
+ 8);
129 width
= 1 + get_le24(*data
+ 12);
130 height
= 1 + get_le24(*data
+ 15);
131 if (width
* (uint64_t)height
>= MAX_IMAGE_AREA
) {
132 return VP8_STATUS_BITSTREAM_ERROR
; // image is too large
135 if (flags_ptr
!= NULL
) *flags_ptr
= flags
;
136 if (width_ptr
!= NULL
) *width_ptr
= width
;
137 if (height_ptr
!= NULL
) *height_ptr
= height
;
138 // Skip over VP8X header bytes.
140 *data_size
-= vp8x_size
;
143 return VP8_STATUS_OK
;
146 // Skips to the next VP8/VP8L chunk header in the data given the size of the
147 // RIFF chunk 'riff_size'.
148 // Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered,
149 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
150 // VP8_STATUS_OK otherwise.
151 // If an alpha chunk is found, *alpha_data and *alpha_size are set
153 static VP8StatusCode
ParseOptionalChunks(const uint8_t** const data
,
154 size_t* const data_size
,
155 size_t const riff_size
,
156 const uint8_t** const alpha_data
,
157 size_t* const alpha_size
) {
160 uint32_t total_size
= TAG_SIZE
+ // "WEBP".
161 CHUNK_HEADER_SIZE
+ // "VP8Xnnnn".
162 VP8X_CHUNK_SIZE
; // data.
163 assert(data
!= NULL
);
164 assert(data_size
!= NULL
);
166 buf_size
= *data_size
;
168 assert(alpha_data
!= NULL
);
169 assert(alpha_size
!= NULL
);
175 uint32_t disk_chunk_size
; // chunk_size with padding
178 *data_size
= buf_size
;
180 if (buf_size
< CHUNK_HEADER_SIZE
) { // Insufficient data.
181 return VP8_STATUS_NOT_ENOUGH_DATA
;
184 chunk_size
= get_le32(buf
+ TAG_SIZE
);
185 if (chunk_size
> MAX_CHUNK_PAYLOAD
) {
186 return VP8_STATUS_BITSTREAM_ERROR
; // Not a valid chunk size.
188 // For odd-sized chunk-payload, there's one byte padding at the end.
189 disk_chunk_size
= (CHUNK_HEADER_SIZE
+ chunk_size
+ 1) & ~1;
190 total_size
+= disk_chunk_size
;
192 // Check that total bytes skipped so far does not exceed riff_size.
193 if (riff_size
> 0 && (total_size
> riff_size
)) {
194 return VP8_STATUS_BITSTREAM_ERROR
; // Not a valid chunk size.
197 // Start of a (possibly incomplete) VP8/VP8L chunk implies that we have
198 // parsed all the optional chunks.
199 // Note: This check must occur before the check 'buf_size < disk_chunk_size'
200 // below to allow incomplete VP8/VP8L chunks.
201 if (!memcmp(buf
, "VP8 ", TAG_SIZE
) ||
202 !memcmp(buf
, "VP8L", TAG_SIZE
)) {
203 return VP8_STATUS_OK
;
206 if (buf_size
< disk_chunk_size
) { // Insufficient data.
207 return VP8_STATUS_NOT_ENOUGH_DATA
;
210 if (!memcmp(buf
, "ALPH", TAG_SIZE
)) { // A valid ALPH header.
211 *alpha_data
= buf
+ CHUNK_HEADER_SIZE
;
212 *alpha_size
= chunk_size
;
215 // We have a full and valid chunk; skip it.
216 buf
+= disk_chunk_size
;
217 buf_size
-= disk_chunk_size
;
221 // Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it.
222 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than
223 // riff_size) VP8/VP8L header,
224 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
225 // VP8_STATUS_OK otherwise.
226 // If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes
227 // extracted from the VP8/VP8L chunk header.
228 // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
229 static VP8StatusCode
ParseVP8Header(const uint8_t** const data_ptr
,
230 size_t* const data_size
,
232 size_t* const chunk_size
,
233 int* const is_lossless
) {
234 const uint8_t* const data
= *data_ptr
;
235 const int is_vp8
= !memcmp(data
, "VP8 ", TAG_SIZE
);
236 const int is_vp8l
= !memcmp(data
, "VP8L", TAG_SIZE
);
237 const uint32_t minimal_size
=
238 TAG_SIZE
+ CHUNK_HEADER_SIZE
; // "WEBP" + "VP8 nnnn" OR
239 // "WEBP" + "VP8Lnnnn"
240 assert(data
!= NULL
);
241 assert(data_size
!= NULL
);
242 assert(chunk_size
!= NULL
);
243 assert(is_lossless
!= NULL
);
245 if (*data_size
< CHUNK_HEADER_SIZE
) {
246 return VP8_STATUS_NOT_ENOUGH_DATA
; // Insufficient data.
249 if (is_vp8
|| is_vp8l
) {
250 // Bitstream contains VP8/VP8L header.
251 const uint32_t size
= get_le32(data
+ TAG_SIZE
);
252 if ((riff_size
>= minimal_size
) && (size
> riff_size
- minimal_size
)) {
253 return VP8_STATUS_BITSTREAM_ERROR
; // Inconsistent size information.
255 // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
257 *data_ptr
+= CHUNK_HEADER_SIZE
;
258 *data_size
-= CHUNK_HEADER_SIZE
;
259 *is_lossless
= is_vp8l
;
261 // Raw VP8/VP8L bitstream (no header).
262 *is_lossless
= VP8LCheckSignature(data
, *data_size
);
263 *chunk_size
= *data_size
;
266 return VP8_STATUS_OK
;
269 //------------------------------------------------------------------------------
271 // Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
272 // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
273 // minimal amount will be read to fetch the remaining parameters.
274 // If 'headers' is non-NULL this function will attempt to locate both alpha
275 // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
276 // Note: The following chunk sequences (before the raw VP8/VP8L data) are
277 // considered valid by this function:
279 // RIFF + VP8X + (optional chunks) + VP8(L)
280 // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
281 // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
282 static VP8StatusCode
ParseHeadersInternal(const uint8_t* data
,
286 int* const has_alpha
,
287 int* const has_animation
,
288 WebPHeaderStructure
* const headers
) {
291 VP8StatusCode status
;
292 WebPHeaderStructure hdrs
;
294 if (data
== NULL
|| data_size
< RIFF_HEADER_SIZE
) {
295 return VP8_STATUS_NOT_ENOUGH_DATA
;
297 memset(&hdrs
, 0, sizeof(hdrs
));
299 hdrs
.data_size
= data_size
;
301 // Skip over RIFF header.
302 status
= ParseRIFF(&data
, &data_size
, &hdrs
.riff_size
);
303 if (status
!= VP8_STATUS_OK
) {
304 return status
; // Wrong RIFF header / insufficient data.
306 found_riff
= (hdrs
.riff_size
> 0);
311 status
= ParseVP8X(&data
, &data_size
, &found_vp8x
, width
, height
, &flags
);
312 if (status
!= VP8_STATUS_OK
) {
313 return status
; // Wrong VP8X / insufficient data.
315 if (!found_riff
&& found_vp8x
) {
316 // Note: This restriction may be removed in the future, if it becomes
317 // necessary to send VP8X chunk to the decoder.
318 return VP8_STATUS_BITSTREAM_ERROR
;
320 if (has_alpha
!= NULL
) *has_alpha
= !!(flags
& ALPHA_FLAG
);
321 if (has_animation
!= NULL
) *has_animation
= !!(flags
& ANIMATION_FLAG
);
322 if (found_vp8x
&& headers
== NULL
) {
323 return VP8_STATUS_OK
; // Return features from VP8X header.
327 if (data_size
< TAG_SIZE
) return VP8_STATUS_NOT_ENOUGH_DATA
;
329 // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
330 if ((found_riff
&& found_vp8x
) ||
331 (!found_riff
&& !found_vp8x
&& !memcmp(data
, "ALPH", TAG_SIZE
))) {
332 status
= ParseOptionalChunks(&data
, &data_size
, hdrs
.riff_size
,
333 &hdrs
.alpha_data
, &hdrs
.alpha_data_size
);
334 if (status
!= VP8_STATUS_OK
) {
335 return status
; // Found an invalid chunk size / insufficient data.
339 // Skip over VP8/VP8L header.
340 status
= ParseVP8Header(&data
, &data_size
, hdrs
.riff_size
,
341 &hdrs
.compressed_size
, &hdrs
.is_lossless
);
342 if (status
!= VP8_STATUS_OK
) {
343 return status
; // Wrong VP8/VP8L chunk-header / insufficient data.
345 if (hdrs
.compressed_size
> MAX_CHUNK_PAYLOAD
) {
346 return VP8_STATUS_BITSTREAM_ERROR
;
349 if (!hdrs
.is_lossless
) {
350 if (data_size
< VP8_FRAME_HEADER_SIZE
) {
351 return VP8_STATUS_NOT_ENOUGH_DATA
;
353 // Validates raw VP8 data.
354 if (!VP8GetInfo(data
, data_size
,
355 (uint32_t)hdrs
.compressed_size
, width
, height
)) {
356 return VP8_STATUS_BITSTREAM_ERROR
;
359 if (data_size
< VP8L_FRAME_HEADER_SIZE
) {
360 return VP8_STATUS_NOT_ENOUGH_DATA
;
362 // Validates raw VP8L data.
363 if (!VP8LGetInfo(data
, data_size
, width
, height
, has_alpha
)) {
364 return VP8_STATUS_BITSTREAM_ERROR
;
368 if (has_alpha
!= NULL
) {
369 // If the data did not contain a VP8X/VP8L chunk the only definitive way
370 // to set this is by looking for alpha data (from an ALPH chunk).
371 *has_alpha
|= (hdrs
.alpha_data
!= NULL
);
373 if (headers
!= NULL
) {
375 headers
->offset
= data
- headers
->data
;
376 assert((uint64_t)(data
- headers
->data
) < MAX_CHUNK_PAYLOAD
);
377 assert(headers
->offset
== headers
->data_size
- data_size
);
379 return VP8_STATUS_OK
; // Return features from VP8 header.
382 VP8StatusCode
WebPParseHeaders(WebPHeaderStructure
* const headers
) {
383 VP8StatusCode status
;
384 int has_animation
= 0;
385 assert(headers
!= NULL
);
386 // fill out headers, ignore width/height/has_alpha.
387 status
= ParseHeadersInternal(headers
->data
, headers
->data_size
,
388 NULL
, NULL
, NULL
, &has_animation
, headers
);
389 if (status
== VP8_STATUS_OK
|| status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
390 // TODO(jzern): full support of animation frames will require API additions.
392 status
= VP8_STATUS_UNSUPPORTED_FEATURE
;
398 //------------------------------------------------------------------------------
401 void WebPResetDecParams(WebPDecParams
* const params
) {
403 memset(params
, 0, sizeof(*params
));
407 //------------------------------------------------------------------------------
408 // "Into" decoding variants
411 static VP8StatusCode
DecodeInto(const uint8_t* const data
, size_t data_size
,
412 WebPDecParams
* const params
) {
413 VP8StatusCode status
;
415 WebPHeaderStructure headers
;
418 headers
.data_size
= data_size
;
419 status
= WebPParseHeaders(&headers
); // Process Pre-VP8 chunks.
420 if (status
!= VP8_STATUS_OK
) {
424 assert(params
!= NULL
);
426 io
.data
= headers
.data
+ headers
.offset
;
427 io
.data_size
= headers
.data_size
- headers
.offset
;
428 WebPInitCustomIo(params
, &io
); // Plug the I/O functions.
430 if (!headers
.is_lossless
) {
431 VP8Decoder
* const dec
= VP8New();
433 return VP8_STATUS_OUT_OF_MEMORY
;
435 #ifdef WEBP_USE_THREAD
436 dec
->use_threads_
= params
->options
&& (params
->options
->use_threads
> 0);
438 dec
->use_threads_
= 0;
440 dec
->alpha_data_
= headers
.alpha_data
;
441 dec
->alpha_data_size_
= headers
.alpha_data_size
;
443 // Decode bitstream header, update io->width/io->height.
444 if (!VP8GetHeaders(dec
, &io
)) {
445 status
= dec
->status_
; // An error occurred. Grab error status.
447 // Allocate/check output buffers.
448 status
= WebPAllocateDecBuffer(io
.width
, io
.height
, params
->options
,
450 if (status
== VP8_STATUS_OK
) { // Decode
451 if (!VP8Decode(dec
, &io
)) {
452 status
= dec
->status_
;
458 VP8LDecoder
* const dec
= VP8LNew();
460 return VP8_STATUS_OUT_OF_MEMORY
;
462 if (!VP8LDecodeHeader(dec
, &io
)) {
463 status
= dec
->status_
; // An error occurred. Grab error status.
465 // Allocate/check output buffers.
466 status
= WebPAllocateDecBuffer(io
.width
, io
.height
, params
->options
,
468 if (status
== VP8_STATUS_OK
) { // Decode
469 if (!VP8LDecodeImage(dec
)) {
470 status
= dec
->status_
;
477 if (status
!= VP8_STATUS_OK
) {
478 WebPFreeDecBuffer(params
->output
);
484 static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace
,
485 const uint8_t* const data
,
488 int stride
, size_t size
) {
489 WebPDecParams params
;
494 WebPInitDecBuffer(&buf
);
495 WebPResetDecParams(¶ms
);
496 params
.output
= &buf
;
497 buf
.colorspace
= colorspace
;
498 buf
.u
.RGBA
.rgba
= rgba
;
499 buf
.u
.RGBA
.stride
= stride
;
500 buf
.u
.RGBA
.size
= size
;
501 buf
.is_external_memory
= 1;
502 if (DecodeInto(data
, data_size
, ¶ms
) != VP8_STATUS_OK
) {
508 uint8_t* WebPDecodeRGBInto(const uint8_t* data
, size_t data_size
,
509 uint8_t* output
, size_t size
, int stride
) {
510 return DecodeIntoRGBABuffer(MODE_RGB
, data
, data_size
, output
, stride
, size
);
513 uint8_t* WebPDecodeRGBAInto(const uint8_t* data
, size_t data_size
,
514 uint8_t* output
, size_t size
, int stride
) {
515 return DecodeIntoRGBABuffer(MODE_RGBA
, data
, data_size
, output
, stride
, size
);
518 uint8_t* WebPDecodeARGBInto(const uint8_t* data
, size_t data_size
,
519 uint8_t* output
, size_t size
, int stride
) {
520 return DecodeIntoRGBABuffer(MODE_ARGB
, data
, data_size
, output
, stride
, size
);
523 uint8_t* WebPDecodeBGRInto(const uint8_t* data
, size_t data_size
,
524 uint8_t* output
, size_t size
, int stride
) {
525 return DecodeIntoRGBABuffer(MODE_BGR
, data
, data_size
, output
, stride
, size
);
528 uint8_t* WebPDecodeBGRAInto(const uint8_t* data
, size_t data_size
,
529 uint8_t* output
, size_t size
, int stride
) {
530 return DecodeIntoRGBABuffer(MODE_BGRA
, data
, data_size
, output
, stride
, size
);
533 uint8_t* WebPDecodeYUVInto(const uint8_t* data
, size_t data_size
,
534 uint8_t* luma
, size_t luma_size
, int luma_stride
,
535 uint8_t* u
, size_t u_size
, int u_stride
,
536 uint8_t* v
, size_t v_size
, int v_stride
) {
537 WebPDecParams params
;
538 WebPDecBuffer output
;
539 if (luma
== NULL
) return NULL
;
540 WebPInitDecBuffer(&output
);
541 WebPResetDecParams(¶ms
);
542 params
.output
= &output
;
543 output
.colorspace
= MODE_YUV
;
544 output
.u
.YUVA
.y
= luma
;
545 output
.u
.YUVA
.y_stride
= luma_stride
;
546 output
.u
.YUVA
.y_size
= luma_size
;
548 output
.u
.YUVA
.u_stride
= u_stride
;
549 output
.u
.YUVA
.u_size
= u_size
;
551 output
.u
.YUVA
.v_stride
= v_stride
;
552 output
.u
.YUVA
.v_size
= v_size
;
553 output
.is_external_memory
= 1;
554 if (DecodeInto(data
, data_size
, ¶ms
) != VP8_STATUS_OK
) {
560 //------------------------------------------------------------------------------
562 static uint8_t* Decode(WEBP_CSP_MODE mode
, const uint8_t* const data
,
563 size_t data_size
, int* const width
, int* const height
,
564 WebPDecBuffer
* const keep_info
) {
565 WebPDecParams params
;
566 WebPDecBuffer output
;
568 WebPInitDecBuffer(&output
);
569 WebPResetDecParams(¶ms
);
570 params
.output
= &output
;
571 output
.colorspace
= mode
;
573 // Retrieve (and report back) the required dimensions from bitstream.
574 if (!WebPGetInfo(data
, data_size
, &output
.width
, &output
.height
)) {
577 if (width
!= NULL
) *width
= output
.width
;
578 if (height
!= NULL
) *height
= output
.height
;
581 if (DecodeInto(data
, data_size
, ¶ms
) != VP8_STATUS_OK
) {
584 if (keep_info
!= NULL
) { // keep track of the side-info
585 WebPCopyDecBuffer(&output
, keep_info
);
587 // return decoded samples (don't clear 'output'!)
588 return WebPIsRGBMode(mode
) ? output
.u
.RGBA
.rgba
: output
.u
.YUVA
.y
;
591 uint8_t* WebPDecodeRGB(const uint8_t* data
, size_t data_size
,
592 int* width
, int* height
) {
593 return Decode(MODE_RGB
, data
, data_size
, width
, height
, NULL
);
596 uint8_t* WebPDecodeRGBA(const uint8_t* data
, size_t data_size
,
597 int* width
, int* height
) {
598 return Decode(MODE_RGBA
, data
, data_size
, width
, height
, NULL
);
601 uint8_t* WebPDecodeARGB(const uint8_t* data
, size_t data_size
,
602 int* width
, int* height
) {
603 return Decode(MODE_ARGB
, data
, data_size
, width
, height
, NULL
);
606 uint8_t* WebPDecodeBGR(const uint8_t* data
, size_t data_size
,
607 int* width
, int* height
) {
608 return Decode(MODE_BGR
, data
, data_size
, width
, height
, NULL
);
611 uint8_t* WebPDecodeBGRA(const uint8_t* data
, size_t data_size
,
612 int* width
, int* height
) {
613 return Decode(MODE_BGRA
, data
, data_size
, width
, height
, NULL
);
616 uint8_t* WebPDecodeYUV(const uint8_t* data
, size_t data_size
,
617 int* width
, int* height
, uint8_t** u
, uint8_t** v
,
618 int* stride
, int* uv_stride
) {
619 WebPDecBuffer output
; // only to preserve the side-infos
620 uint8_t* const out
= Decode(MODE_YUV
, data
, data_size
,
621 width
, height
, &output
);
624 const WebPYUVABuffer
* const buf
= &output
.u
.YUVA
;
627 *stride
= buf
->y_stride
;
628 *uv_stride
= buf
->u_stride
;
629 assert(buf
->u_stride
== buf
->v_stride
);
634 static void DefaultFeatures(WebPBitstreamFeatures
* const features
) {
635 assert(features
!= NULL
);
636 memset(features
, 0, sizeof(*features
));
637 features
->bitstream_version
= 0;
640 static VP8StatusCode
GetFeatures(const uint8_t* const data
, size_t data_size
,
641 WebPBitstreamFeatures
* const features
) {
642 if (features
== NULL
|| data
== NULL
) {
643 return VP8_STATUS_INVALID_PARAM
;
645 DefaultFeatures(features
);
647 // Only parse enough of the data to retrieve the features.
648 return ParseHeadersInternal(data
, data_size
,
649 &features
->width
, &features
->height
,
650 &features
->has_alpha
, &features
->has_animation
,
654 //------------------------------------------------------------------------------
657 int WebPGetInfo(const uint8_t* data
, size_t data_size
,
658 int* width
, int* height
) {
659 WebPBitstreamFeatures features
;
661 if (GetFeatures(data
, data_size
, &features
) != VP8_STATUS_OK
) {
666 *width
= features
.width
;
668 if (height
!= NULL
) {
669 *height
= features
.height
;
675 //------------------------------------------------------------------------------
676 // Advance decoding API
678 int WebPInitDecoderConfigInternal(WebPDecoderConfig
* config
,
680 if (WEBP_ABI_IS_INCOMPATIBLE(version
, WEBP_DECODER_ABI_VERSION
)) {
681 return 0; // version mismatch
683 if (config
== NULL
) {
686 memset(config
, 0, sizeof(*config
));
687 DefaultFeatures(&config
->input
);
688 WebPInitDecBuffer(&config
->output
);
692 VP8StatusCode
WebPGetFeaturesInternal(const uint8_t* data
, size_t data_size
,
693 WebPBitstreamFeatures
* features
,
695 if (WEBP_ABI_IS_INCOMPATIBLE(version
, WEBP_DECODER_ABI_VERSION
)) {
696 return VP8_STATUS_INVALID_PARAM
; // version mismatch
698 if (features
== NULL
) {
699 return VP8_STATUS_INVALID_PARAM
;
701 return GetFeatures(data
, data_size
, features
);
704 VP8StatusCode
WebPDecode(const uint8_t* data
, size_t data_size
,
705 WebPDecoderConfig
* config
) {
706 WebPDecParams params
;
707 VP8StatusCode status
;
709 if (config
== NULL
) {
710 return VP8_STATUS_INVALID_PARAM
;
713 status
= GetFeatures(data
, data_size
, &config
->input
);
714 if (status
!= VP8_STATUS_OK
) {
715 if (status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
716 return VP8_STATUS_BITSTREAM_ERROR
; // Not-enough-data treated as error.
721 WebPResetDecParams(¶ms
);
722 params
.output
= &config
->output
;
723 params
.options
= &config
->options
;
724 status
= DecodeInto(data
, data_size
, ¶ms
);
729 //------------------------------------------------------------------------------
730 // Cropping and rescaling.
732 int WebPIoInitFromOptions(const WebPDecoderOptions
* const options
,
733 VP8Io
* const io
, WEBP_CSP_MODE src_colorspace
) {
734 const int W
= io
->width
;
735 const int H
= io
->height
;
736 int x
= 0, y
= 0, w
= W
, h
= H
;
739 io
->use_cropping
= (options
!= NULL
) && (options
->use_cropping
> 0);
740 if (io
->use_cropping
) {
741 w
= options
->crop_width
;
742 h
= options
->crop_height
;
743 x
= options
->crop_left
;
744 y
= options
->crop_top
;
745 if (!WebPIsRGBMode(src_colorspace
)) { // only snap for YUV420 or YUV422
747 y
&= ~1; // TODO(later): only for YUV420, not YUV422.
749 if (x
< 0 || y
< 0 || w
<= 0 || h
<= 0 || x
+ w
> W
|| y
+ h
> H
) {
750 return 0; // out of frame boundary error
755 io
->crop_right
= x
+ w
;
756 io
->crop_bottom
= y
+ h
;
761 io
->use_scaling
= (options
!= NULL
) && (options
->use_scaling
> 0);
762 if (io
->use_scaling
) {
763 if (options
->scaled_width
<= 0 || options
->scaled_height
<= 0) {
766 io
->scaled_width
= options
->scaled_width
;
767 io
->scaled_height
= options
->scaled_height
;
771 io
->bypass_filtering
= options
&& options
->bypass_filtering
;
774 #ifdef FANCY_UPSAMPLING
775 io
->fancy_upsampling
= (options
== NULL
) || (!options
->no_fancy_upsampling
);
778 if (io
->use_scaling
) {
779 // disable filter (only for large downscaling ratio).
780 io
->bypass_filtering
= (io
->scaled_width
< W
* 3 / 4) &&
781 (io
->scaled_height
< H
* 3 / 4);
782 io
->fancy_upsampling
= 0;
787 //------------------------------------------------------------------------------
789 #if defined(__cplusplus) || defined(c_plusplus)