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,
56 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and
57 // VP8_STATUS_OK otherwise.
58 // In case there are not enough bytes (partial RIFF container), return 0 for
59 // *riff_size. Else return the RIFF size extracted from the header.
60 static VP8StatusCode
ParseRIFF(const uint8_t** const data
,
61 size_t* const data_size
,
62 size_t* const riff_size
) {
64 assert(data_size
!= NULL
);
65 assert(riff_size
!= NULL
);
67 *riff_size
= 0; // Default: no RIFF present.
68 if (*data_size
>= RIFF_HEADER_SIZE
&& !memcmp(*data
, "RIFF", TAG_SIZE
)) {
69 if (memcmp(*data
+ 8, "WEBP", TAG_SIZE
)) {
70 return VP8_STATUS_BITSTREAM_ERROR
; // Wrong image file signature.
72 const uint32_t size
= get_le32(*data
+ TAG_SIZE
);
73 // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
74 if (size
< TAG_SIZE
+ CHUNK_HEADER_SIZE
) {
75 return VP8_STATUS_BITSTREAM_ERROR
;
77 if (size
> MAX_CHUNK_PAYLOAD
) {
78 return VP8_STATUS_BITSTREAM_ERROR
;
80 // We have a RIFF container. Skip it.
82 *data
+= RIFF_HEADER_SIZE
;
83 *data_size
-= RIFF_HEADER_SIZE
;
89 // Validates the VP8X header and skips over it.
90 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header,
91 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
92 // VP8_STATUS_OK otherwise.
93 // If a VP8X chunk is found, found_vp8x is set to true and *width_ptr,
94 // *height_ptr and *flags_ptr are set to the corresponding values extracted
95 // from the VP8X chunk.
96 static VP8StatusCode
ParseVP8X(const uint8_t** const data
,
97 size_t* const data_size
,
98 int* const found_vp8x
,
99 int* const width_ptr
, int* const height_ptr
,
100 uint32_t* const flags_ptr
) {
101 const uint32_t vp8x_size
= CHUNK_HEADER_SIZE
+ VP8X_CHUNK_SIZE
;
102 assert(data
!= NULL
);
103 assert(data_size
!= NULL
);
104 assert(found_vp8x
!= NULL
);
108 if (*data_size
< CHUNK_HEADER_SIZE
) {
109 return VP8_STATUS_NOT_ENOUGH_DATA
; // Insufficient data.
112 if (!memcmp(*data
, "VP8X", TAG_SIZE
)) {
115 const uint32_t chunk_size
= get_le32(*data
+ TAG_SIZE
);
116 if (chunk_size
!= VP8X_CHUNK_SIZE
) {
117 return VP8_STATUS_BITSTREAM_ERROR
; // Wrong chunk size.
120 // Verify if enough data is available to validate the VP8X chunk.
121 if (*data_size
< vp8x_size
) {
122 return VP8_STATUS_NOT_ENOUGH_DATA
; // Insufficient data.
124 flags
= get_le32(*data
+ 8);
125 width
= 1 + get_le24(*data
+ 12);
126 height
= 1 + get_le24(*data
+ 15);
127 if (width
* (uint64_t)height
>= MAX_IMAGE_AREA
) {
128 return VP8_STATUS_BITSTREAM_ERROR
; // image is too large
131 if (flags_ptr
!= NULL
) *flags_ptr
= flags
;
132 if (width_ptr
!= NULL
) *width_ptr
= width
;
133 if (height_ptr
!= NULL
) *height_ptr
= height
;
134 // Skip over VP8X header bytes.
136 *data_size
-= vp8x_size
;
139 return VP8_STATUS_OK
;
142 // Skips to the next VP8/VP8L chunk header in the data given the size of the
143 // RIFF chunk 'riff_size'.
144 // Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered,
145 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
146 // VP8_STATUS_OK otherwise.
147 // If an alpha chunk is found, *alpha_data and *alpha_size are set
149 static VP8StatusCode
ParseOptionalChunks(const uint8_t** const data
,
150 size_t* const data_size
,
151 size_t const riff_size
,
152 const uint8_t** const alpha_data
,
153 size_t* const alpha_size
) {
156 uint32_t total_size
= TAG_SIZE
+ // "WEBP".
157 CHUNK_HEADER_SIZE
+ // "VP8Xnnnn".
158 VP8X_CHUNK_SIZE
; // data.
159 assert(data
!= NULL
);
160 assert(data_size
!= NULL
);
162 buf_size
= *data_size
;
164 assert(alpha_data
!= NULL
);
165 assert(alpha_size
!= NULL
);
171 uint32_t disk_chunk_size
; // chunk_size with padding
174 *data_size
= buf_size
;
176 if (buf_size
< CHUNK_HEADER_SIZE
) { // Insufficient data.
177 return VP8_STATUS_NOT_ENOUGH_DATA
;
180 chunk_size
= get_le32(buf
+ TAG_SIZE
);
181 if (chunk_size
> MAX_CHUNK_PAYLOAD
) {
182 return VP8_STATUS_BITSTREAM_ERROR
; // Not a valid chunk size.
184 // For odd-sized chunk-payload, there's one byte padding at the end.
185 disk_chunk_size
= (CHUNK_HEADER_SIZE
+ chunk_size
+ 1) & ~1;
186 total_size
+= disk_chunk_size
;
188 // Check that total bytes skipped so far does not exceed riff_size.
189 if (riff_size
> 0 && (total_size
> riff_size
)) {
190 return VP8_STATUS_BITSTREAM_ERROR
; // Not a valid chunk size.
193 // Start of a (possibly incomplete) VP8/VP8L chunk implies that we have
194 // parsed all the optional chunks.
195 // Note: This check must occur before the check 'buf_size < disk_chunk_size'
196 // below to allow incomplete VP8/VP8L chunks.
197 if (!memcmp(buf
, "VP8 ", TAG_SIZE
) ||
198 !memcmp(buf
, "VP8L", TAG_SIZE
)) {
199 return VP8_STATUS_OK
;
202 if (buf_size
< disk_chunk_size
) { // Insufficient data.
203 return VP8_STATUS_NOT_ENOUGH_DATA
;
206 if (!memcmp(buf
, "ALPH", TAG_SIZE
)) { // A valid ALPH header.
207 *alpha_data
= buf
+ CHUNK_HEADER_SIZE
;
208 *alpha_size
= chunk_size
;
211 // We have a full and valid chunk; skip it.
212 buf
+= disk_chunk_size
;
213 buf_size
-= disk_chunk_size
;
217 // Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it.
218 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than
219 // riff_size) VP8/VP8L header,
220 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
221 // VP8_STATUS_OK otherwise.
222 // If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes
223 // extracted from the VP8/VP8L chunk header.
224 // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
225 static VP8StatusCode
ParseVP8Header(const uint8_t** const data_ptr
,
226 size_t* const data_size
,
228 size_t* const chunk_size
,
229 int* const is_lossless
) {
230 const uint8_t* const data
= *data_ptr
;
231 const int is_vp8
= !memcmp(data
, "VP8 ", TAG_SIZE
);
232 const int is_vp8l
= !memcmp(data
, "VP8L", TAG_SIZE
);
233 const uint32_t minimal_size
=
234 TAG_SIZE
+ CHUNK_HEADER_SIZE
; // "WEBP" + "VP8 nnnn" OR
235 // "WEBP" + "VP8Lnnnn"
236 assert(data
!= NULL
);
237 assert(data_size
!= NULL
);
238 assert(chunk_size
!= NULL
);
239 assert(is_lossless
!= NULL
);
241 if (*data_size
< CHUNK_HEADER_SIZE
) {
242 return VP8_STATUS_NOT_ENOUGH_DATA
; // Insufficient data.
245 if (is_vp8
|| is_vp8l
) {
246 // Bitstream contains VP8/VP8L header.
247 const uint32_t size
= get_le32(data
+ TAG_SIZE
);
248 if ((riff_size
>= minimal_size
) && (size
> riff_size
- minimal_size
)) {
249 return VP8_STATUS_BITSTREAM_ERROR
; // Inconsistent size information.
251 // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
253 *data_ptr
+= CHUNK_HEADER_SIZE
;
254 *data_size
-= CHUNK_HEADER_SIZE
;
255 *is_lossless
= is_vp8l
;
257 // Raw VP8/VP8L bitstream (no header).
258 *is_lossless
= VP8LCheckSignature(data
, *data_size
);
259 *chunk_size
= *data_size
;
262 return VP8_STATUS_OK
;
265 //------------------------------------------------------------------------------
267 // Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
268 // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
269 // minimal amount will be read to fetch the remaining parameters.
270 // If 'headers' is non-NULL this function will attempt to locate both alpha
271 // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
272 // Note: The following chunk sequences (before the raw VP8/VP8L data) are
273 // considered valid by this function:
275 // RIFF + VP8X + (optional chunks) + VP8(L)
276 // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
277 // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
278 static VP8StatusCode
ParseHeadersInternal(const uint8_t* data
,
282 int* const has_alpha
,
283 int* const has_animation
,
285 WebPHeaderStructure
* const headers
) {
286 int canvas_width
= 0;
287 int canvas_height
= 0;
289 int image_height
= 0;
292 int animation_present
= 0;
293 int fragments_present
= 0;
295 VP8StatusCode status
;
296 WebPHeaderStructure hdrs
;
298 if (data
== NULL
|| data_size
< RIFF_HEADER_SIZE
) {
299 return VP8_STATUS_NOT_ENOUGH_DATA
;
301 memset(&hdrs
, 0, sizeof(hdrs
));
303 hdrs
.data_size
= data_size
;
305 // Skip over RIFF header.
306 status
= ParseRIFF(&data
, &data_size
, &hdrs
.riff_size
);
307 if (status
!= VP8_STATUS_OK
) {
308 return status
; // Wrong RIFF header / insufficient data.
310 found_riff
= (hdrs
.riff_size
> 0);
315 status
= ParseVP8X(&data
, &data_size
, &found_vp8x
,
316 &canvas_width
, &canvas_height
, &flags
);
317 if (status
!= VP8_STATUS_OK
) {
318 return status
; // Wrong VP8X / insufficient data.
320 animation_present
= !!(flags
& ANIMATION_FLAG
);
321 fragments_present
= !!(flags
& FRAGMENTS_FLAG
);
322 if (!found_riff
&& found_vp8x
) {
323 // Note: This restriction may be removed in the future, if it becomes
324 // necessary to send VP8X chunk to the decoder.
325 return VP8_STATUS_BITSTREAM_ERROR
;
327 if (has_alpha
!= NULL
) *has_alpha
= !!(flags
& ALPHA_FLAG
);
328 if (has_animation
!= NULL
) *has_animation
= animation_present
;
329 if (format
!= NULL
) *format
= 0; // default = undefined
331 if (found_vp8x
&& (animation_present
|| fragments_present
) &&
333 if (width
!= NULL
) *width
= canvas_width
;
334 if (height
!= NULL
) *height
= canvas_height
;
335 return VP8_STATUS_OK
; // Just return features from VP8X header.
339 if (data_size
< TAG_SIZE
) return VP8_STATUS_NOT_ENOUGH_DATA
;
341 // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
342 if ((found_riff
&& found_vp8x
) ||
343 (!found_riff
&& !found_vp8x
&& !memcmp(data
, "ALPH", TAG_SIZE
))) {
344 status
= ParseOptionalChunks(&data
, &data_size
, hdrs
.riff_size
,
345 &hdrs
.alpha_data
, &hdrs
.alpha_data_size
);
346 if (status
!= VP8_STATUS_OK
) {
347 return status
; // Found an invalid chunk size / insufficient data.
351 // Skip over VP8/VP8L header.
352 status
= ParseVP8Header(&data
, &data_size
, hdrs
.riff_size
,
353 &hdrs
.compressed_size
, &hdrs
.is_lossless
);
354 if (status
!= VP8_STATUS_OK
) {
355 return status
; // Wrong VP8/VP8L chunk-header / insufficient data.
357 if (hdrs
.compressed_size
> MAX_CHUNK_PAYLOAD
) {
358 return VP8_STATUS_BITSTREAM_ERROR
;
361 if (format
!= NULL
&& !(animation_present
|| fragments_present
)) {
362 *format
= hdrs
.is_lossless
? 2 : 1;
365 if (!hdrs
.is_lossless
) {
366 if (data_size
< VP8_FRAME_HEADER_SIZE
) {
367 return VP8_STATUS_NOT_ENOUGH_DATA
;
369 // Validates raw VP8 data.
370 if (!VP8GetInfo(data
, data_size
, (uint32_t)hdrs
.compressed_size
,
371 &image_width
, &image_height
)) {
372 return VP8_STATUS_BITSTREAM_ERROR
;
375 if (data_size
< VP8L_FRAME_HEADER_SIZE
) {
376 return VP8_STATUS_NOT_ENOUGH_DATA
;
378 // Validates raw VP8L data.
379 if (!VP8LGetInfo(data
, data_size
, &image_width
, &image_height
, has_alpha
)) {
380 return VP8_STATUS_BITSTREAM_ERROR
;
383 // Validates image size coherency.
385 if (canvas_width
!= image_width
|| canvas_height
!= image_height
) {
386 return VP8_STATUS_BITSTREAM_ERROR
;
389 if (width
!= NULL
) *width
= image_width
;
390 if (height
!= NULL
) *height
= image_height
;
391 if (has_alpha
!= NULL
) {
392 // If the data did not contain a VP8X/VP8L chunk the only definitive way
393 // to set this is by looking for alpha data (from an ALPH chunk).
394 *has_alpha
|= (hdrs
.alpha_data
!= NULL
);
396 if (headers
!= NULL
) {
398 headers
->offset
= data
- headers
->data
;
399 assert((uint64_t)(data
- headers
->data
) < MAX_CHUNK_PAYLOAD
);
400 assert(headers
->offset
== headers
->data_size
- data_size
);
402 return VP8_STATUS_OK
; // Return features from VP8 header.
405 VP8StatusCode
WebPParseHeaders(WebPHeaderStructure
* const headers
) {
406 VP8StatusCode status
;
407 int has_animation
= 0;
408 assert(headers
!= NULL
);
409 // fill out headers, ignore width/height/has_alpha.
410 status
= ParseHeadersInternal(headers
->data
, headers
->data_size
,
411 NULL
, NULL
, NULL
, &has_animation
,
413 if (status
== VP8_STATUS_OK
|| status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
414 // TODO(jzern): full support of animation frames will require API additions.
416 status
= VP8_STATUS_UNSUPPORTED_FEATURE
;
422 //------------------------------------------------------------------------------
425 void WebPResetDecParams(WebPDecParams
* const params
) {
426 if (params
!= NULL
) {
427 memset(params
, 0, sizeof(*params
));
431 //------------------------------------------------------------------------------
432 // "Into" decoding variants
435 static VP8StatusCode
DecodeInto(const uint8_t* const data
, size_t data_size
,
436 WebPDecParams
* const params
) {
437 VP8StatusCode status
;
439 WebPHeaderStructure headers
;
442 headers
.data_size
= data_size
;
443 status
= WebPParseHeaders(&headers
); // Process Pre-VP8 chunks.
444 if (status
!= VP8_STATUS_OK
) {
448 assert(params
!= NULL
);
450 io
.data
= headers
.data
+ headers
.offset
;
451 io
.data_size
= headers
.data_size
- headers
.offset
;
452 WebPInitCustomIo(params
, &io
); // Plug the I/O functions.
454 if (!headers
.is_lossless
) {
455 VP8Decoder
* const dec
= VP8New();
457 return VP8_STATUS_OUT_OF_MEMORY
;
459 dec
->alpha_data_
= headers
.alpha_data
;
460 dec
->alpha_data_size_
= headers
.alpha_data_size
;
462 // Decode bitstream header, update io->width/io->height.
463 if (!VP8GetHeaders(dec
, &io
)) {
464 status
= dec
->status_
; // An error occurred. Grab error status.
466 // Allocate/check output buffers.
467 status
= WebPAllocateDecBuffer(io
.width
, io
.height
, params
->options
,
469 if (status
== VP8_STATUS_OK
) { // Decode
470 // This change must be done before calling VP8Decode()
471 dec
->mt_method_
= VP8GetThreadMethod(params
->options
, &headers
,
472 io
.width
, io
.height
);
473 VP8InitDithering(params
->options
, dec
);
474 if (!VP8Decode(dec
, &io
)) {
475 status
= dec
->status_
;
481 VP8LDecoder
* const dec
= VP8LNew();
483 return VP8_STATUS_OUT_OF_MEMORY
;
485 if (!VP8LDecodeHeader(dec
, &io
)) {
486 status
= dec
->status_
; // An error occurred. Grab error status.
488 // Allocate/check output buffers.
489 status
= WebPAllocateDecBuffer(io
.width
, io
.height
, params
->options
,
491 if (status
== VP8_STATUS_OK
) { // Decode
492 if (!VP8LDecodeImage(dec
)) {
493 status
= dec
->status_
;
500 if (status
!= VP8_STATUS_OK
) {
501 WebPFreeDecBuffer(params
->output
);
507 static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace
,
508 const uint8_t* const data
,
511 int stride
, size_t size
) {
512 WebPDecParams params
;
517 WebPInitDecBuffer(&buf
);
518 WebPResetDecParams(¶ms
);
519 params
.output
= &buf
;
520 buf
.colorspace
= colorspace
;
521 buf
.u
.RGBA
.rgba
= rgba
;
522 buf
.u
.RGBA
.stride
= stride
;
523 buf
.u
.RGBA
.size
= size
;
524 buf
.is_external_memory
= 1;
525 if (DecodeInto(data
, data_size
, ¶ms
) != VP8_STATUS_OK
) {
531 uint8_t* WebPDecodeRGBInto(const uint8_t* data
, size_t data_size
,
532 uint8_t* output
, size_t size
, int stride
) {
533 return DecodeIntoRGBABuffer(MODE_RGB
, data
, data_size
, output
, stride
, size
);
536 uint8_t* WebPDecodeRGBAInto(const uint8_t* data
, size_t data_size
,
537 uint8_t* output
, size_t size
, int stride
) {
538 return DecodeIntoRGBABuffer(MODE_RGBA
, data
, data_size
, output
, stride
, size
);
541 uint8_t* WebPDecodeARGBInto(const uint8_t* data
, size_t data_size
,
542 uint8_t* output
, size_t size
, int stride
) {
543 return DecodeIntoRGBABuffer(MODE_ARGB
, data
, data_size
, output
, stride
, size
);
546 uint8_t* WebPDecodeBGRInto(const uint8_t* data
, size_t data_size
,
547 uint8_t* output
, size_t size
, int stride
) {
548 return DecodeIntoRGBABuffer(MODE_BGR
, data
, data_size
, output
, stride
, size
);
551 uint8_t* WebPDecodeBGRAInto(const uint8_t* data
, size_t data_size
,
552 uint8_t* output
, size_t size
, int stride
) {
553 return DecodeIntoRGBABuffer(MODE_BGRA
, data
, data_size
, output
, stride
, size
);
556 uint8_t* WebPDecodeYUVInto(const uint8_t* data
, size_t data_size
,
557 uint8_t* luma
, size_t luma_size
, int luma_stride
,
558 uint8_t* u
, size_t u_size
, int u_stride
,
559 uint8_t* v
, size_t v_size
, int v_stride
) {
560 WebPDecParams params
;
561 WebPDecBuffer output
;
562 if (luma
== NULL
) return NULL
;
563 WebPInitDecBuffer(&output
);
564 WebPResetDecParams(¶ms
);
565 params
.output
= &output
;
566 output
.colorspace
= MODE_YUV
;
567 output
.u
.YUVA
.y
= luma
;
568 output
.u
.YUVA
.y_stride
= luma_stride
;
569 output
.u
.YUVA
.y_size
= luma_size
;
571 output
.u
.YUVA
.u_stride
= u_stride
;
572 output
.u
.YUVA
.u_size
= u_size
;
574 output
.u
.YUVA
.v_stride
= v_stride
;
575 output
.u
.YUVA
.v_size
= v_size
;
576 output
.is_external_memory
= 1;
577 if (DecodeInto(data
, data_size
, ¶ms
) != VP8_STATUS_OK
) {
583 //------------------------------------------------------------------------------
585 static uint8_t* Decode(WEBP_CSP_MODE mode
, const uint8_t* const data
,
586 size_t data_size
, int* const width
, int* const height
,
587 WebPDecBuffer
* const keep_info
) {
588 WebPDecParams params
;
589 WebPDecBuffer output
;
591 WebPInitDecBuffer(&output
);
592 WebPResetDecParams(¶ms
);
593 params
.output
= &output
;
594 output
.colorspace
= mode
;
596 // Retrieve (and report back) the required dimensions from bitstream.
597 if (!WebPGetInfo(data
, data_size
, &output
.width
, &output
.height
)) {
600 if (width
!= NULL
) *width
= output
.width
;
601 if (height
!= NULL
) *height
= output
.height
;
604 if (DecodeInto(data
, data_size
, ¶ms
) != VP8_STATUS_OK
) {
607 if (keep_info
!= NULL
) { // keep track of the side-info
608 WebPCopyDecBuffer(&output
, keep_info
);
610 // return decoded samples (don't clear 'output'!)
611 return WebPIsRGBMode(mode
) ? output
.u
.RGBA
.rgba
: output
.u
.YUVA
.y
;
614 uint8_t* WebPDecodeRGB(const uint8_t* data
, size_t data_size
,
615 int* width
, int* height
) {
616 return Decode(MODE_RGB
, data
, data_size
, width
, height
, NULL
);
619 uint8_t* WebPDecodeRGBA(const uint8_t* data
, size_t data_size
,
620 int* width
, int* height
) {
621 return Decode(MODE_RGBA
, data
, data_size
, width
, height
, NULL
);
624 uint8_t* WebPDecodeARGB(const uint8_t* data
, size_t data_size
,
625 int* width
, int* height
) {
626 return Decode(MODE_ARGB
, data
, data_size
, width
, height
, NULL
);
629 uint8_t* WebPDecodeBGR(const uint8_t* data
, size_t data_size
,
630 int* width
, int* height
) {
631 return Decode(MODE_BGR
, data
, data_size
, width
, height
, NULL
);
634 uint8_t* WebPDecodeBGRA(const uint8_t* data
, size_t data_size
,
635 int* width
, int* height
) {
636 return Decode(MODE_BGRA
, data
, data_size
, width
, height
, NULL
);
639 uint8_t* WebPDecodeYUV(const uint8_t* data
, size_t data_size
,
640 int* width
, int* height
, uint8_t** u
, uint8_t** v
,
641 int* stride
, int* uv_stride
) {
642 WebPDecBuffer output
; // only to preserve the side-infos
643 uint8_t* const out
= Decode(MODE_YUV
, data
, data_size
,
644 width
, height
, &output
);
647 const WebPYUVABuffer
* const buf
= &output
.u
.YUVA
;
650 *stride
= buf
->y_stride
;
651 *uv_stride
= buf
->u_stride
;
652 assert(buf
->u_stride
== buf
->v_stride
);
657 static void DefaultFeatures(WebPBitstreamFeatures
* const features
) {
658 assert(features
!= NULL
);
659 memset(features
, 0, sizeof(*features
));
662 static VP8StatusCode
GetFeatures(const uint8_t* const data
, size_t data_size
,
663 WebPBitstreamFeatures
* const features
) {
664 if (features
== NULL
|| data
== NULL
) {
665 return VP8_STATUS_INVALID_PARAM
;
667 DefaultFeatures(features
);
669 // Only parse enough of the data to retrieve the features.
670 return ParseHeadersInternal(data
, data_size
,
671 &features
->width
, &features
->height
,
672 &features
->has_alpha
, &features
->has_animation
,
673 &features
->format
, NULL
);
676 //------------------------------------------------------------------------------
679 int WebPGetInfo(const uint8_t* data
, size_t data_size
,
680 int* width
, int* height
) {
681 WebPBitstreamFeatures features
;
683 if (GetFeatures(data
, data_size
, &features
) != VP8_STATUS_OK
) {
688 *width
= features
.width
;
690 if (height
!= NULL
) {
691 *height
= features
.height
;
697 //------------------------------------------------------------------------------
698 // Advance decoding API
700 int WebPInitDecoderConfigInternal(WebPDecoderConfig
* config
,
702 if (WEBP_ABI_IS_INCOMPATIBLE(version
, WEBP_DECODER_ABI_VERSION
)) {
703 return 0; // version mismatch
705 if (config
== NULL
) {
708 memset(config
, 0, sizeof(*config
));
709 DefaultFeatures(&config
->input
);
710 WebPInitDecBuffer(&config
->output
);
714 VP8StatusCode
WebPGetFeaturesInternal(const uint8_t* data
, size_t data_size
,
715 WebPBitstreamFeatures
* features
,
717 if (WEBP_ABI_IS_INCOMPATIBLE(version
, WEBP_DECODER_ABI_VERSION
)) {
718 return VP8_STATUS_INVALID_PARAM
; // version mismatch
720 if (features
== NULL
) {
721 return VP8_STATUS_INVALID_PARAM
;
723 return GetFeatures(data
, data_size
, features
);
726 VP8StatusCode
WebPDecode(const uint8_t* data
, size_t data_size
,
727 WebPDecoderConfig
* config
) {
728 WebPDecParams params
;
729 VP8StatusCode status
;
731 if (config
== NULL
) {
732 return VP8_STATUS_INVALID_PARAM
;
735 status
= GetFeatures(data
, data_size
, &config
->input
);
736 if (status
!= VP8_STATUS_OK
) {
737 if (status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
738 return VP8_STATUS_BITSTREAM_ERROR
; // Not-enough-data treated as error.
743 WebPResetDecParams(¶ms
);
744 params
.output
= &config
->output
;
745 params
.options
= &config
->options
;
746 status
= DecodeInto(data
, data_size
, ¶ms
);
751 //------------------------------------------------------------------------------
752 // Cropping and rescaling.
754 int WebPIoInitFromOptions(const WebPDecoderOptions
* const options
,
755 VP8Io
* const io
, WEBP_CSP_MODE src_colorspace
) {
756 const int W
= io
->width
;
757 const int H
= io
->height
;
758 int x
= 0, y
= 0, w
= W
, h
= H
;
761 io
->use_cropping
= (options
!= NULL
) && (options
->use_cropping
> 0);
762 if (io
->use_cropping
) {
763 w
= options
->crop_width
;
764 h
= options
->crop_height
;
765 x
= options
->crop_left
;
766 y
= options
->crop_top
;
767 if (!WebPIsRGBMode(src_colorspace
)) { // only snap for YUV420 or YUV422
769 y
&= ~1; // TODO(later): only for YUV420, not YUV422.
771 if (x
< 0 || y
< 0 || w
<= 0 || h
<= 0 || x
+ w
> W
|| y
+ h
> H
) {
772 return 0; // out of frame boundary error
777 io
->crop_right
= x
+ w
;
778 io
->crop_bottom
= y
+ h
;
783 io
->use_scaling
= (options
!= NULL
) && (options
->use_scaling
> 0);
784 if (io
->use_scaling
) {
785 if (options
->scaled_width
<= 0 || options
->scaled_height
<= 0) {
788 io
->scaled_width
= options
->scaled_width
;
789 io
->scaled_height
= options
->scaled_height
;
793 io
->bypass_filtering
= options
&& options
->bypass_filtering
;
796 #ifdef FANCY_UPSAMPLING
797 io
->fancy_upsampling
= (options
== NULL
) || (!options
->no_fancy_upsampling
);
800 if (io
->use_scaling
) {
801 // disable filter (only for large downscaling ratio).
802 io
->bypass_filtering
= (io
->scaled_width
< W
* 3 / 4) &&
803 (io
->scaled_height
< H
* 3 / 4);
804 io
->fancy_upsampling
= 0;
809 //------------------------------------------------------------------------------