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 //------------------------------------------------------------------------------
24 // 0...3 "RIFF" 4-byte tag
25 // 4...7 size of image data (including metadata) starting at offset 8
26 // 8...11 "WEBP" our form-type signature
27 // The RIFF container (12 bytes) is followed by appropriate chunks:
28 // 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format
29 // 16..19 size of the raw VP8 image data, starting at offset 20
30 // 20.... the VP8 bytes
32 // 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format
33 // 16..19 size of the raw VP8L image data, starting at offset 20
34 // 20.... the VP8L bytes
36 // 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk.
37 // 16..19 size of the VP8X chunk starting at offset 20.
38 // 20..23 VP8X flags bit-map corresponding to the chunk-types present.
39 // 24..26 Width of the Canvas Image.
40 // 27..29 Height of the Canvas Image.
41 // There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8,
42 // VP8L, XMP, EXIF ...)
43 // All sizes are in little-endian order.
44 // Note: chunk data size must be padded to multiple of 2 when written.
46 static WEBP_INLINE
uint32_t get_le24(const uint8_t* const data
) {
47 return data
[0] | (data
[1] << 8) | (data
[2] << 16);
50 static WEBP_INLINE
uint32_t get_le32(const uint8_t* const data
) {
51 return (uint32_t)get_le24(data
) | (data
[3] << 24);
54 // Validates the RIFF container (if detected) and skips over it.
55 // If a RIFF container is detected, returns:
56 // VP8_STATUS_BITSTREAM_ERROR for invalid header,
57 // VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true,
58 // and VP8_STATUS_OK otherwise.
59 // In case there are not enough bytes (partial RIFF container), return 0 for
60 // *riff_size. Else return the RIFF size extracted from the header.
61 static VP8StatusCode
ParseRIFF(const uint8_t** const data
,
62 size_t* const data_size
, int have_all_data
,
63 size_t* const riff_size
) {
65 assert(data_size
!= NULL
);
66 assert(riff_size
!= NULL
);
68 *riff_size
= 0; // Default: no RIFF present.
69 if (*data_size
>= RIFF_HEADER_SIZE
&& !memcmp(*data
, "RIFF", TAG_SIZE
)) {
70 if (memcmp(*data
+ 8, "WEBP", TAG_SIZE
)) {
71 return VP8_STATUS_BITSTREAM_ERROR
; // Wrong image file signature.
73 const uint32_t size
= get_le32(*data
+ TAG_SIZE
);
74 // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
75 if (size
< TAG_SIZE
+ CHUNK_HEADER_SIZE
) {
76 return VP8_STATUS_BITSTREAM_ERROR
;
78 if (size
> MAX_CHUNK_PAYLOAD
) {
79 return VP8_STATUS_BITSTREAM_ERROR
;
81 if (have_all_data
&& (size
> *data_size
- CHUNK_HEADER_SIZE
)) {
82 return VP8_STATUS_NOT_ENOUGH_DATA
; // Truncated bitstream.
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
, int have_all_data
,
231 size_t riff_size
, size_t* const chunk_size
,
232 int* const is_lossless
) {
233 const uint8_t* const data
= *data_ptr
;
234 const int is_vp8
= !memcmp(data
, "VP8 ", TAG_SIZE
);
235 const int is_vp8l
= !memcmp(data
, "VP8L", TAG_SIZE
);
236 const uint32_t minimal_size
=
237 TAG_SIZE
+ CHUNK_HEADER_SIZE
; // "WEBP" + "VP8 nnnn" OR
238 // "WEBP" + "VP8Lnnnn"
239 assert(data
!= NULL
);
240 assert(data_size
!= NULL
);
241 assert(chunk_size
!= NULL
);
242 assert(is_lossless
!= NULL
);
244 if (*data_size
< CHUNK_HEADER_SIZE
) {
245 return VP8_STATUS_NOT_ENOUGH_DATA
; // Insufficient data.
248 if (is_vp8
|| is_vp8l
) {
249 // Bitstream contains VP8/VP8L header.
250 const uint32_t size
= get_le32(data
+ TAG_SIZE
);
251 if ((riff_size
>= minimal_size
) && (size
> riff_size
- minimal_size
)) {
252 return VP8_STATUS_BITSTREAM_ERROR
; // Inconsistent size information.
254 if (have_all_data
&& (size
> *data_size
- CHUNK_HEADER_SIZE
)) {
255 return VP8_STATUS_NOT_ENOUGH_DATA
; // Truncated bitstream.
257 // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
259 *data_ptr
+= CHUNK_HEADER_SIZE
;
260 *data_size
-= CHUNK_HEADER_SIZE
;
261 *is_lossless
= is_vp8l
;
263 // Raw VP8/VP8L bitstream (no header).
264 *is_lossless
= VP8LCheckSignature(data
, *data_size
);
265 *chunk_size
= *data_size
;
268 return VP8_STATUS_OK
;
271 //------------------------------------------------------------------------------
273 // Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
274 // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
275 // minimal amount will be read to fetch the remaining parameters.
276 // If 'headers' is non-NULL this function will attempt to locate both alpha
277 // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
278 // Note: The following chunk sequences (before the raw VP8/VP8L data) are
279 // considered valid by this function:
281 // RIFF + VP8X + (optional chunks) + VP8(L)
282 // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
283 // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
284 static VP8StatusCode
ParseHeadersInternal(const uint8_t* data
,
288 int* const has_alpha
,
289 int* const has_animation
,
291 WebPHeaderStructure
* const headers
) {
292 int canvas_width
= 0;
293 int canvas_height
= 0;
295 int image_height
= 0;
298 int animation_present
= 0;
299 int fragments_present
= 0;
300 const int have_all_data
= (headers
!= NULL
) ? headers
->have_all_data
: 0;
302 VP8StatusCode status
;
303 WebPHeaderStructure hdrs
;
305 if (data
== NULL
|| data_size
< RIFF_HEADER_SIZE
) {
306 return VP8_STATUS_NOT_ENOUGH_DATA
;
308 memset(&hdrs
, 0, sizeof(hdrs
));
310 hdrs
.data_size
= data_size
;
312 // Skip over RIFF header.
313 status
= ParseRIFF(&data
, &data_size
, have_all_data
, &hdrs
.riff_size
);
314 if (status
!= VP8_STATUS_OK
) {
315 return status
; // Wrong RIFF header / insufficient data.
317 found_riff
= (hdrs
.riff_size
> 0);
322 status
= ParseVP8X(&data
, &data_size
, &found_vp8x
,
323 &canvas_width
, &canvas_height
, &flags
);
324 if (status
!= VP8_STATUS_OK
) {
325 return status
; // Wrong VP8X / insufficient data.
327 animation_present
= !!(flags
& ANIMATION_FLAG
);
328 fragments_present
= !!(flags
& FRAGMENTS_FLAG
);
329 if (!found_riff
&& found_vp8x
) {
330 // Note: This restriction may be removed in the future, if it becomes
331 // necessary to send VP8X chunk to the decoder.
332 return VP8_STATUS_BITSTREAM_ERROR
;
334 if (has_alpha
!= NULL
) *has_alpha
= !!(flags
& ALPHA_FLAG
);
335 if (has_animation
!= NULL
) *has_animation
= animation_present
;
336 if (format
!= NULL
) *format
= 0; // default = undefined
338 if (found_vp8x
&& (animation_present
|| fragments_present
) &&
340 if (width
!= NULL
) *width
= canvas_width
;
341 if (height
!= NULL
) *height
= canvas_height
;
342 return VP8_STATUS_OK
; // Just return features from VP8X header.
346 if (data_size
< TAG_SIZE
) return VP8_STATUS_NOT_ENOUGH_DATA
;
348 // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
349 if ((found_riff
&& found_vp8x
) ||
350 (!found_riff
&& !found_vp8x
&& !memcmp(data
, "ALPH", TAG_SIZE
))) {
351 status
= ParseOptionalChunks(&data
, &data_size
, hdrs
.riff_size
,
352 &hdrs
.alpha_data
, &hdrs
.alpha_data_size
);
353 if (status
!= VP8_STATUS_OK
) {
354 return status
; // Found an invalid chunk size / insufficient data.
358 // Skip over VP8/VP8L header.
359 status
= ParseVP8Header(&data
, &data_size
, have_all_data
, hdrs
.riff_size
,
360 &hdrs
.compressed_size
, &hdrs
.is_lossless
);
361 if (status
!= VP8_STATUS_OK
) {
362 return status
; // Wrong VP8/VP8L chunk-header / insufficient data.
364 if (hdrs
.compressed_size
> MAX_CHUNK_PAYLOAD
) {
365 return VP8_STATUS_BITSTREAM_ERROR
;
368 if (format
!= NULL
&& !(animation_present
|| fragments_present
)) {
369 *format
= hdrs
.is_lossless
? 2 : 1;
372 if (!hdrs
.is_lossless
) {
373 if (data_size
< VP8_FRAME_HEADER_SIZE
) {
374 return VP8_STATUS_NOT_ENOUGH_DATA
;
376 // Validates raw VP8 data.
377 if (!VP8GetInfo(data
, data_size
, (uint32_t)hdrs
.compressed_size
,
378 &image_width
, &image_height
)) {
379 return VP8_STATUS_BITSTREAM_ERROR
;
382 if (data_size
< VP8L_FRAME_HEADER_SIZE
) {
383 return VP8_STATUS_NOT_ENOUGH_DATA
;
385 // Validates raw VP8L data.
386 if (!VP8LGetInfo(data
, data_size
, &image_width
, &image_height
, has_alpha
)) {
387 return VP8_STATUS_BITSTREAM_ERROR
;
390 // Validates image size coherency.
392 if (canvas_width
!= image_width
|| canvas_height
!= image_height
) {
393 return VP8_STATUS_BITSTREAM_ERROR
;
396 if (width
!= NULL
) *width
= image_width
;
397 if (height
!= NULL
) *height
= image_height
;
398 if (has_alpha
!= NULL
) {
399 // If the data did not contain a VP8X/VP8L chunk the only definitive way
400 // to set this is by looking for alpha data (from an ALPH chunk).
401 *has_alpha
|= (hdrs
.alpha_data
!= NULL
);
403 if (headers
!= NULL
) {
405 headers
->offset
= data
- headers
->data
;
406 assert((uint64_t)(data
- headers
->data
) < MAX_CHUNK_PAYLOAD
);
407 assert(headers
->offset
== headers
->data_size
- data_size
);
409 return VP8_STATUS_OK
; // Return features from VP8 header.
412 VP8StatusCode
WebPParseHeaders(WebPHeaderStructure
* const headers
) {
413 VP8StatusCode status
;
414 int has_animation
= 0;
415 assert(headers
!= NULL
);
416 // fill out headers, ignore width/height/has_alpha.
417 status
= ParseHeadersInternal(headers
->data
, headers
->data_size
,
418 NULL
, NULL
, NULL
, &has_animation
,
420 if (status
== VP8_STATUS_OK
|| status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
421 // TODO(jzern): full support of animation frames will require API additions.
423 status
= VP8_STATUS_UNSUPPORTED_FEATURE
;
429 //------------------------------------------------------------------------------
432 void WebPResetDecParams(WebPDecParams
* const params
) {
433 if (params
!= NULL
) {
434 memset(params
, 0, sizeof(*params
));
438 //------------------------------------------------------------------------------
439 // "Into" decoding variants
442 static VP8StatusCode
DecodeInto(const uint8_t* const data
, size_t data_size
,
443 WebPDecParams
* const params
) {
444 VP8StatusCode status
;
446 WebPHeaderStructure headers
;
449 headers
.data_size
= data_size
;
450 headers
.have_all_data
= 1;
451 status
= WebPParseHeaders(&headers
); // Process Pre-VP8 chunks.
452 if (status
!= VP8_STATUS_OK
) {
456 assert(params
!= NULL
);
458 io
.data
= headers
.data
+ headers
.offset
;
459 io
.data_size
= headers
.data_size
- headers
.offset
;
460 WebPInitCustomIo(params
, &io
); // Plug the I/O functions.
462 if (!headers
.is_lossless
) {
463 VP8Decoder
* const dec
= VP8New();
465 return VP8_STATUS_OUT_OF_MEMORY
;
467 dec
->alpha_data_
= headers
.alpha_data
;
468 dec
->alpha_data_size_
= headers
.alpha_data_size
;
470 // Decode bitstream header, update io->width/io->height.
471 if (!VP8GetHeaders(dec
, &io
)) {
472 status
= dec
->status_
; // An error occurred. Grab error status.
474 // Allocate/check output buffers.
475 status
= WebPAllocateDecBuffer(io
.width
, io
.height
, params
->options
,
477 if (status
== VP8_STATUS_OK
) { // Decode
478 // This change must be done before calling VP8Decode()
479 dec
->mt_method_
= VP8GetThreadMethod(params
->options
, &headers
,
480 io
.width
, io
.height
);
481 VP8InitDithering(params
->options
, dec
);
482 if (!VP8Decode(dec
, &io
)) {
483 status
= dec
->status_
;
489 VP8LDecoder
* const dec
= VP8LNew();
491 return VP8_STATUS_OUT_OF_MEMORY
;
493 if (!VP8LDecodeHeader(dec
, &io
)) {
494 status
= dec
->status_
; // An error occurred. Grab error status.
496 // Allocate/check output buffers.
497 status
= WebPAllocateDecBuffer(io
.width
, io
.height
, params
->options
,
499 if (status
== VP8_STATUS_OK
) { // Decode
500 if (!VP8LDecodeImage(dec
)) {
501 status
= dec
->status_
;
508 if (status
!= VP8_STATUS_OK
) {
509 WebPFreeDecBuffer(params
->output
);
512 #if WEBP_DECODER_ABI_VERSION > 0x0203
513 if (params
->options
!= NULL
&& params
->options
->flip
) {
514 status
= WebPFlipBuffer(params
->output
);
521 static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace
,
522 const uint8_t* const data
,
525 int stride
, size_t size
) {
526 WebPDecParams params
;
531 WebPInitDecBuffer(&buf
);
532 WebPResetDecParams(¶ms
);
533 params
.output
= &buf
;
534 buf
.colorspace
= colorspace
;
535 buf
.u
.RGBA
.rgba
= rgba
;
536 buf
.u
.RGBA
.stride
= stride
;
537 buf
.u
.RGBA
.size
= size
;
538 buf
.is_external_memory
= 1;
539 if (DecodeInto(data
, data_size
, ¶ms
) != VP8_STATUS_OK
) {
545 uint8_t* WebPDecodeRGBInto(const uint8_t* data
, size_t data_size
,
546 uint8_t* output
, size_t size
, int stride
) {
547 return DecodeIntoRGBABuffer(MODE_RGB
, data
, data_size
, output
, stride
, size
);
550 uint8_t* WebPDecodeRGBAInto(const uint8_t* data
, size_t data_size
,
551 uint8_t* output
, size_t size
, int stride
) {
552 return DecodeIntoRGBABuffer(MODE_RGBA
, data
, data_size
, output
, stride
, size
);
555 uint8_t* WebPDecodeARGBInto(const uint8_t* data
, size_t data_size
,
556 uint8_t* output
, size_t size
, int stride
) {
557 return DecodeIntoRGBABuffer(MODE_ARGB
, data
, data_size
, output
, stride
, size
);
560 uint8_t* WebPDecodeBGRInto(const uint8_t* data
, size_t data_size
,
561 uint8_t* output
, size_t size
, int stride
) {
562 return DecodeIntoRGBABuffer(MODE_BGR
, data
, data_size
, output
, stride
, size
);
565 uint8_t* WebPDecodeBGRAInto(const uint8_t* data
, size_t data_size
,
566 uint8_t* output
, size_t size
, int stride
) {
567 return DecodeIntoRGBABuffer(MODE_BGRA
, data
, data_size
, output
, stride
, size
);
570 uint8_t* WebPDecodeYUVInto(const uint8_t* data
, size_t data_size
,
571 uint8_t* luma
, size_t luma_size
, int luma_stride
,
572 uint8_t* u
, size_t u_size
, int u_stride
,
573 uint8_t* v
, size_t v_size
, int v_stride
) {
574 WebPDecParams params
;
575 WebPDecBuffer output
;
576 if (luma
== NULL
) return NULL
;
577 WebPInitDecBuffer(&output
);
578 WebPResetDecParams(¶ms
);
579 params
.output
= &output
;
580 output
.colorspace
= MODE_YUV
;
581 output
.u
.YUVA
.y
= luma
;
582 output
.u
.YUVA
.y_stride
= luma_stride
;
583 output
.u
.YUVA
.y_size
= luma_size
;
585 output
.u
.YUVA
.u_stride
= u_stride
;
586 output
.u
.YUVA
.u_size
= u_size
;
588 output
.u
.YUVA
.v_stride
= v_stride
;
589 output
.u
.YUVA
.v_size
= v_size
;
590 output
.is_external_memory
= 1;
591 if (DecodeInto(data
, data_size
, ¶ms
) != VP8_STATUS_OK
) {
597 //------------------------------------------------------------------------------
599 static uint8_t* Decode(WEBP_CSP_MODE mode
, const uint8_t* const data
,
600 size_t data_size
, int* const width
, int* const height
,
601 WebPDecBuffer
* const keep_info
) {
602 WebPDecParams params
;
603 WebPDecBuffer output
;
605 WebPInitDecBuffer(&output
);
606 WebPResetDecParams(¶ms
);
607 params
.output
= &output
;
608 output
.colorspace
= mode
;
610 // Retrieve (and report back) the required dimensions from bitstream.
611 if (!WebPGetInfo(data
, data_size
, &output
.width
, &output
.height
)) {
614 if (width
!= NULL
) *width
= output
.width
;
615 if (height
!= NULL
) *height
= output
.height
;
618 if (DecodeInto(data
, data_size
, ¶ms
) != VP8_STATUS_OK
) {
621 if (keep_info
!= NULL
) { // keep track of the side-info
622 WebPCopyDecBuffer(&output
, keep_info
);
624 // return decoded samples (don't clear 'output'!)
625 return WebPIsRGBMode(mode
) ? output
.u
.RGBA
.rgba
: output
.u
.YUVA
.y
;
628 uint8_t* WebPDecodeRGB(const uint8_t* data
, size_t data_size
,
629 int* width
, int* height
) {
630 return Decode(MODE_RGB
, data
, data_size
, width
, height
, NULL
);
633 uint8_t* WebPDecodeRGBA(const uint8_t* data
, size_t data_size
,
634 int* width
, int* height
) {
635 return Decode(MODE_RGBA
, data
, data_size
, width
, height
, NULL
);
638 uint8_t* WebPDecodeARGB(const uint8_t* data
, size_t data_size
,
639 int* width
, int* height
) {
640 return Decode(MODE_ARGB
, data
, data_size
, width
, height
, NULL
);
643 uint8_t* WebPDecodeBGR(const uint8_t* data
, size_t data_size
,
644 int* width
, int* height
) {
645 return Decode(MODE_BGR
, data
, data_size
, width
, height
, NULL
);
648 uint8_t* WebPDecodeBGRA(const uint8_t* data
, size_t data_size
,
649 int* width
, int* height
) {
650 return Decode(MODE_BGRA
, data
, data_size
, width
, height
, NULL
);
653 uint8_t* WebPDecodeYUV(const uint8_t* data
, size_t data_size
,
654 int* width
, int* height
, uint8_t** u
, uint8_t** v
,
655 int* stride
, int* uv_stride
) {
656 WebPDecBuffer output
; // only to preserve the side-infos
657 uint8_t* const out
= Decode(MODE_YUV
, data
, data_size
,
658 width
, height
, &output
);
661 const WebPYUVABuffer
* const buf
= &output
.u
.YUVA
;
664 *stride
= buf
->y_stride
;
665 *uv_stride
= buf
->u_stride
;
666 assert(buf
->u_stride
== buf
->v_stride
);
671 static void DefaultFeatures(WebPBitstreamFeatures
* const features
) {
672 assert(features
!= NULL
);
673 memset(features
, 0, sizeof(*features
));
676 static VP8StatusCode
GetFeatures(const uint8_t* const data
, size_t data_size
,
677 WebPBitstreamFeatures
* const features
) {
678 if (features
== NULL
|| data
== NULL
) {
679 return VP8_STATUS_INVALID_PARAM
;
681 DefaultFeatures(features
);
683 // Only parse enough of the data to retrieve the features.
684 return ParseHeadersInternal(data
, data_size
,
685 &features
->width
, &features
->height
,
686 &features
->has_alpha
, &features
->has_animation
,
687 &features
->format
, NULL
);
690 //------------------------------------------------------------------------------
693 int WebPGetInfo(const uint8_t* data
, size_t data_size
,
694 int* width
, int* height
) {
695 WebPBitstreamFeatures features
;
697 if (GetFeatures(data
, data_size
, &features
) != VP8_STATUS_OK
) {
702 *width
= features
.width
;
704 if (height
!= NULL
) {
705 *height
= features
.height
;
711 //------------------------------------------------------------------------------
712 // Advance decoding API
714 int WebPInitDecoderConfigInternal(WebPDecoderConfig
* config
,
716 if (WEBP_ABI_IS_INCOMPATIBLE(version
, WEBP_DECODER_ABI_VERSION
)) {
717 return 0; // version mismatch
719 if (config
== NULL
) {
722 memset(config
, 0, sizeof(*config
));
723 DefaultFeatures(&config
->input
);
724 WebPInitDecBuffer(&config
->output
);
728 VP8StatusCode
WebPGetFeaturesInternal(const uint8_t* data
, size_t data_size
,
729 WebPBitstreamFeatures
* features
,
731 if (WEBP_ABI_IS_INCOMPATIBLE(version
, WEBP_DECODER_ABI_VERSION
)) {
732 return VP8_STATUS_INVALID_PARAM
; // version mismatch
734 if (features
== NULL
) {
735 return VP8_STATUS_INVALID_PARAM
;
737 return GetFeatures(data
, data_size
, features
);
740 VP8StatusCode
WebPDecode(const uint8_t* data
, size_t data_size
,
741 WebPDecoderConfig
* config
) {
742 WebPDecParams params
;
743 VP8StatusCode status
;
745 if (config
== NULL
) {
746 return VP8_STATUS_INVALID_PARAM
;
749 status
= GetFeatures(data
, data_size
, &config
->input
);
750 if (status
!= VP8_STATUS_OK
) {
751 if (status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
752 return VP8_STATUS_BITSTREAM_ERROR
; // Not-enough-data treated as error.
757 WebPResetDecParams(¶ms
);
758 params
.output
= &config
->output
;
759 params
.options
= &config
->options
;
760 status
= DecodeInto(data
, data_size
, ¶ms
);
765 //------------------------------------------------------------------------------
766 // Cropping and rescaling.
768 int WebPIoInitFromOptions(const WebPDecoderOptions
* const options
,
769 VP8Io
* const io
, WEBP_CSP_MODE src_colorspace
) {
770 const int W
= io
->width
;
771 const int H
= io
->height
;
772 int x
= 0, y
= 0, w
= W
, h
= H
;
775 io
->use_cropping
= (options
!= NULL
) && (options
->use_cropping
> 0);
776 if (io
->use_cropping
) {
777 w
= options
->crop_width
;
778 h
= options
->crop_height
;
779 x
= options
->crop_left
;
780 y
= options
->crop_top
;
781 if (!WebPIsRGBMode(src_colorspace
)) { // only snap for YUV420
785 if (x
< 0 || y
< 0 || w
<= 0 || h
<= 0 || x
+ w
> W
|| y
+ h
> H
) {
786 return 0; // out of frame boundary error
791 io
->crop_right
= x
+ w
;
792 io
->crop_bottom
= y
+ h
;
797 io
->use_scaling
= (options
!= NULL
) && (options
->use_scaling
> 0);
798 if (io
->use_scaling
) {
799 if (options
->scaled_width
<= 0 || options
->scaled_height
<= 0) {
802 io
->scaled_width
= options
->scaled_width
;
803 io
->scaled_height
= options
->scaled_height
;
807 io
->bypass_filtering
= options
&& options
->bypass_filtering
;
810 #ifdef FANCY_UPSAMPLING
811 io
->fancy_upsampling
= (options
== NULL
) || (!options
->no_fancy_upsampling
);
814 if (io
->use_scaling
) {
815 // disable filter (only for large downscaling ratio).
816 io
->bypass_filtering
= (io
->scaled_width
< W
* 3 / 4) &&
817 (io
->scaled_height
< H
* 3 / 4);
818 io
->fancy_upsampling
= 0;
823 //------------------------------------------------------------------------------