New comit of SDL2
[supertux.git] / src / SDL2 / external / libwebp-0.3.0 / src / dec / webp.c
1 // Copyright 2010 Google Inc. All Rights Reserved.
2 //
3 // This code is licensed under the same terms as WebM:
4 //  Software License Agreement:  http://www.webmproject.org/license/software/
5 //  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
6 // -----------------------------------------------------------------------------
7 //
8 // Main decoding functions for WEBP images.
9 //
10 // Author: Skal (pascal.massimino@gmail.com)
11
12 #include <stdlib.h>
13
14 #include "./vp8i.h"
15 #include "./vp8li.h"
16 #include "./webpi.h"
17 #include "../webp/mux_types.h"  // ALPHA_FLAG
18
19 #if defined(__cplusplus) || defined(c_plusplus)
20 extern "C" {
21 #endif
22
23 //------------------------------------------------------------------------------
24 // RIFF layout is:
25 //   Offset  tag
26 //   0...3   "RIFF" 4-byte tag
27 //   4...7   size of image data (including metadata) starting at offset 8
28 //   8...11  "WEBP"   our form-type signature
29 // The RIFF container (12 bytes) is followed by appropriate chunks:
30 //   12..15  "VP8 ": 4-bytes tags, signaling the use of VP8 video format
31 //   16..19  size of the raw VP8 image data, starting at offset 20
32 //   20....  the VP8 bytes
33 // Or,
34 //   12..15  "VP8L": 4-bytes tags, signaling the use of VP8L lossless format
35 //   16..19  size of the raw VP8L image data, starting at offset 20
36 //   20....  the VP8L bytes
37 // Or,
38 //   12..15  "VP8X": 4-bytes tags, describing the extended-VP8 chunk.
39 //   16..19  size of the VP8X chunk starting at offset 20.
40 //   20..23  VP8X flags bit-map corresponding to the chunk-types present.
41 //   24..26  Width of the Canvas Image.
42 //   27..29  Height of the Canvas Image.
43 // There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8,
44 // VP8L, XMP, EXIF  ...)
45 // All sizes are in little-endian order.
46 // Note: chunk data size must be padded to multiple of 2 when written.
47
48 static WEBP_INLINE uint32_t get_le24(const uint8_t* const data) {
49   return data[0] | (data[1] << 8) | (data[2] << 16);
50 }
51
52 static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) {
53   return (uint32_t)get_le24(data) | (data[3] << 24);
54 }
55
56 // Validates the RIFF container (if detected) and skips over it.
57 // If a RIFF container is detected,
58 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and
59 //         VP8_STATUS_OK otherwise.
60 // In case there are not enough bytes (partial RIFF container), return 0 for
61 // *riff_size. Else return the RIFF size extracted from the header.
62 static VP8StatusCode ParseRIFF(const uint8_t** const data,
63                                size_t* const data_size,
64                                size_t* const riff_size) {
65   assert(data != NULL);
66   assert(data_size != NULL);
67   assert(riff_size != NULL);
68
69   *riff_size = 0;  // Default: no RIFF present.
70   if (*data_size >= RIFF_HEADER_SIZE && !memcmp(*data, "RIFF", TAG_SIZE)) {
71     if (memcmp(*data + 8, "WEBP", TAG_SIZE)) {
72       return VP8_STATUS_BITSTREAM_ERROR;  // Wrong image file signature.
73     } else {
74       const uint32_t size = get_le32(*data + TAG_SIZE);
75       // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
76       if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
77         return VP8_STATUS_BITSTREAM_ERROR;
78       }
79       if (size > MAX_CHUNK_PAYLOAD) {
80         return VP8_STATUS_BITSTREAM_ERROR;
81       }
82       // We have a RIFF container. Skip it.
83       *riff_size = size;
84       *data += RIFF_HEADER_SIZE;
85       *data_size -= RIFF_HEADER_SIZE;
86     }
87   }
88   return VP8_STATUS_OK;
89 }
90
91 // Validates the VP8X header and skips over it.
92 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header,
93 //         VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
94 //         VP8_STATUS_OK otherwise.
95 // If a VP8X chunk is found, found_vp8x is set to true and *width_ptr,
96 // *height_ptr and *flags_ptr are set to the corresponding values extracted
97 // from the VP8X chunk.
98 static VP8StatusCode ParseVP8X(const uint8_t** const data,
99                                size_t* const data_size,
100                                int* const found_vp8x,
101                                int* const width_ptr, int* const height_ptr,
102                                uint32_t* const flags_ptr) {
103   const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
104   assert(data != NULL);
105   assert(data_size != NULL);
106   assert(found_vp8x != NULL);
107
108   *found_vp8x = 0;
109
110   if (*data_size < CHUNK_HEADER_SIZE) {
111     return VP8_STATUS_NOT_ENOUGH_DATA;  // Insufficient data.
112   }
113
114   if (!memcmp(*data, "VP8X", TAG_SIZE)) {
115     int width, height;
116     uint32_t flags;
117     const uint32_t chunk_size = get_le32(*data + TAG_SIZE);
118     if (chunk_size != VP8X_CHUNK_SIZE) {
119       return VP8_STATUS_BITSTREAM_ERROR;  // Wrong chunk size.
120     }
121
122     // Verify if enough data is available to validate the VP8X chunk.
123     if (*data_size < vp8x_size) {
124       return VP8_STATUS_NOT_ENOUGH_DATA;  // Insufficient data.
125     }
126     flags = get_le32(*data + 8);
127     width = 1 + get_le24(*data + 12);
128     height = 1 + get_le24(*data + 15);
129     if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
130       return VP8_STATUS_BITSTREAM_ERROR;  // image is too large
131     }
132
133     if (flags_ptr != NULL) *flags_ptr = flags;
134     if (width_ptr != NULL) *width_ptr = width;
135     if (height_ptr != NULL) *height_ptr = height;
136     // Skip over VP8X header bytes.
137     *data += vp8x_size;
138     *data_size -= vp8x_size;
139     *found_vp8x = 1;
140   }
141   return VP8_STATUS_OK;
142 }
143
144 // Skips to the next VP8/VP8L chunk header in the data given the size of the
145 // RIFF chunk 'riff_size'.
146 // Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered,
147 //         VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
148 //         VP8_STATUS_OK otherwise.
149 // If an alpha chunk is found, *alpha_data and *alpha_size are set
150 // appropriately.
151 static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
152                                          size_t* const data_size,
153                                          size_t const riff_size,
154                                          const uint8_t** const alpha_data,
155                                          size_t* const alpha_size) {
156   const uint8_t* buf;
157   size_t buf_size;
158   uint32_t total_size = TAG_SIZE +           // "WEBP".
159                         CHUNK_HEADER_SIZE +  // "VP8Xnnnn".
160                         VP8X_CHUNK_SIZE;     // data.
161   assert(data != NULL);
162   assert(data_size != NULL);
163   buf = *data;
164   buf_size = *data_size;
165
166   assert(alpha_data != NULL);
167   assert(alpha_size != NULL);
168   *alpha_data = NULL;
169   *alpha_size = 0;
170
171   while (1) {
172     uint32_t chunk_size;
173     uint32_t disk_chunk_size;   // chunk_size with padding
174
175     *data = buf;
176     *data_size = buf_size;
177
178     if (buf_size < CHUNK_HEADER_SIZE) {  // Insufficient data.
179       return VP8_STATUS_NOT_ENOUGH_DATA;
180     }
181
182     chunk_size = get_le32(buf + TAG_SIZE);
183     if (chunk_size > MAX_CHUNK_PAYLOAD) {
184       return VP8_STATUS_BITSTREAM_ERROR;          // Not a valid chunk size.
185     }
186     // For odd-sized chunk-payload, there's one byte padding at the end.
187     disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1;
188     total_size += disk_chunk_size;
189
190     // Check that total bytes skipped so far does not exceed riff_size.
191     if (riff_size > 0 && (total_size > riff_size)) {
192       return VP8_STATUS_BITSTREAM_ERROR;          // Not a valid chunk size.
193     }
194
195     // Start of a (possibly incomplete) VP8/VP8L chunk implies that we have
196     // parsed all the optional chunks.
197     // Note: This check must occur before the check 'buf_size < disk_chunk_size'
198     // below to allow incomplete VP8/VP8L chunks.
199     if (!memcmp(buf, "VP8 ", TAG_SIZE) ||
200         !memcmp(buf, "VP8L", TAG_SIZE)) {
201       return VP8_STATUS_OK;
202     }
203
204     if (buf_size < disk_chunk_size) {             // Insufficient data.
205       return VP8_STATUS_NOT_ENOUGH_DATA;
206     }
207
208     if (!memcmp(buf, "ALPH", TAG_SIZE)) {         // A valid ALPH header.
209       *alpha_data = buf + CHUNK_HEADER_SIZE;
210       *alpha_size = chunk_size;
211     }
212
213     // We have a full and valid chunk; skip it.
214     buf += disk_chunk_size;
215     buf_size -= disk_chunk_size;
216   }
217 }
218
219 // Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it.
220 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than
221 //         riff_size) VP8/VP8L header,
222 //         VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
223 //         VP8_STATUS_OK otherwise.
224 // If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes
225 // extracted from the VP8/VP8L chunk header.
226 // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
227 static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
228                                     size_t* const data_size,
229                                     size_t riff_size,
230                                     size_t* const chunk_size,
231                                     int* const is_lossless) {
232   const uint8_t* const data = *data_ptr;
233   const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE);
234   const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE);
235   const uint32_t minimal_size =
236       TAG_SIZE + CHUNK_HEADER_SIZE;  // "WEBP" + "VP8 nnnn" OR
237                                      // "WEBP" + "VP8Lnnnn"
238   assert(data != NULL);
239   assert(data_size != NULL);
240   assert(chunk_size != NULL);
241   assert(is_lossless != NULL);
242
243   if (*data_size < CHUNK_HEADER_SIZE) {
244     return VP8_STATUS_NOT_ENOUGH_DATA;  // Insufficient data.
245   }
246
247   if (is_vp8 || is_vp8l) {
248     // Bitstream contains VP8/VP8L header.
249     const uint32_t size = get_le32(data + TAG_SIZE);
250     if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
251       return VP8_STATUS_BITSTREAM_ERROR;  // Inconsistent size information.
252     }
253     // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
254     *chunk_size = size;
255     *data_ptr += CHUNK_HEADER_SIZE;
256     *data_size -= CHUNK_HEADER_SIZE;
257     *is_lossless = is_vp8l;
258   } else {
259     // Raw VP8/VP8L bitstream (no header).
260     *is_lossless = VP8LCheckSignature(data, *data_size);
261     *chunk_size = *data_size;
262   }
263
264   return VP8_STATUS_OK;
265 }
266
267 //------------------------------------------------------------------------------
268
269 // Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
270 // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
271 // minimal amount will be read to fetch the remaining parameters.
272 // If 'headers' is non-NULL this function will attempt to locate both alpha
273 // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
274 // Note: The following chunk sequences (before the raw VP8/VP8L data) are
275 // considered valid by this function:
276 // RIFF + VP8(L)
277 // RIFF + VP8X + (optional chunks) + VP8(L)
278 // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
279 // VP8(L)     <-- Not a valid WebP format: only allowed for internal purpose.
280 static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
281                                           size_t data_size,
282                                           int* const width,
283                                           int* const height,
284                                           int* const has_alpha,
285                                           int* const has_animation,
286                                           WebPHeaderStructure* const headers) {
287   int found_riff = 0;
288   int found_vp8x = 0;
289   VP8StatusCode status;
290   WebPHeaderStructure hdrs;
291
292   if (data == NULL || data_size < RIFF_HEADER_SIZE) {
293     return VP8_STATUS_NOT_ENOUGH_DATA;
294   }
295   memset(&hdrs, 0, sizeof(hdrs));
296   hdrs.data = data;
297   hdrs.data_size = data_size;
298
299   // Skip over RIFF header.
300   status = ParseRIFF(&data, &data_size, &hdrs.riff_size);
301   if (status != VP8_STATUS_OK) {
302     return status;   // Wrong RIFF header / insufficient data.
303   }
304   found_riff = (hdrs.riff_size > 0);
305
306   // Skip over VP8X.
307   {
308     uint32_t flags = 0;
309     status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags);
310     if (status != VP8_STATUS_OK) {
311       return status;  // Wrong VP8X / insufficient data.
312     }
313     if (!found_riff && found_vp8x) {
314       // Note: This restriction may be removed in the future, if it becomes
315       // necessary to send VP8X chunk to the decoder.
316       return VP8_STATUS_BITSTREAM_ERROR;
317     }
318     if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
319     if (has_animation != NULL) *has_animation = !!(flags & ANIMATION_FLAG);
320     if (found_vp8x && headers == NULL) {
321       return VP8_STATUS_OK;  // Return features from VP8X header.
322     }
323   }
324
325   if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA;
326
327   // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
328   if ((found_riff && found_vp8x) ||
329       (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) {
330     status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
331                                  &hdrs.alpha_data, &hdrs.alpha_data_size);
332     if (status != VP8_STATUS_OK) {
333       return status;  // Found an invalid chunk size / insufficient data.
334     }
335   }
336
337   // Skip over VP8/VP8L header.
338   status = ParseVP8Header(&data, &data_size, hdrs.riff_size,
339                           &hdrs.compressed_size, &hdrs.is_lossless);
340   if (status != VP8_STATUS_OK) {
341     return status;  // Wrong VP8/VP8L chunk-header / insufficient data.
342   }
343   if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
344     return VP8_STATUS_BITSTREAM_ERROR;
345   }
346
347   if (!hdrs.is_lossless) {
348     if (data_size < VP8_FRAME_HEADER_SIZE) {
349       return VP8_STATUS_NOT_ENOUGH_DATA;
350     }
351     // Validates raw VP8 data.
352     if (!VP8GetInfo(data, data_size,
353                     (uint32_t)hdrs.compressed_size, width, height)) {
354       return VP8_STATUS_BITSTREAM_ERROR;
355     }
356   } else {
357     if (data_size < VP8L_FRAME_HEADER_SIZE) {
358       return VP8_STATUS_NOT_ENOUGH_DATA;
359     }
360     // Validates raw VP8L data.
361     if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) {
362       return VP8_STATUS_BITSTREAM_ERROR;
363     }
364   }
365
366   if (has_alpha != NULL) {
367     // If the data did not contain a VP8X/VP8L chunk the only definitive way
368     // to set this is by looking for alpha data (from an ALPH chunk).
369     *has_alpha |= (hdrs.alpha_data != NULL);
370   }
371   if (headers != NULL) {
372     *headers = hdrs;
373     headers->offset = data - headers->data;
374     assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
375     assert(headers->offset == headers->data_size - data_size);
376   }
377   return VP8_STATUS_OK;  // Return features from VP8 header.
378 }
379
380 VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
381   VP8StatusCode status;
382   int has_animation = 0;
383   assert(headers != NULL);
384   // fill out headers, ignore width/height/has_alpha.
385   status = ParseHeadersInternal(headers->data, headers->data_size,
386                                 NULL, NULL, NULL, &has_animation, headers);
387   if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) {
388     // TODO(jzern): full support of animation frames will require API additions.
389     if (has_animation) {
390       status = VP8_STATUS_UNSUPPORTED_FEATURE;
391     }
392   }
393   return status;
394 }
395
396 //------------------------------------------------------------------------------
397 // WebPDecParams
398
399 void WebPResetDecParams(WebPDecParams* const params) {
400   if (params != NULL) {
401     memset(params, 0, sizeof(*params));
402   }
403 }
404
405 //------------------------------------------------------------------------------
406 // "Into" decoding variants
407
408 // Main flow
409 static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
410                                 WebPDecParams* const params) {
411   VP8StatusCode status;
412   VP8Io io;
413   WebPHeaderStructure headers;
414
415   headers.data = data;
416   headers.data_size = data_size;
417   status = WebPParseHeaders(&headers);   // Process Pre-VP8 chunks.
418   if (status != VP8_STATUS_OK) {
419     return status;
420   }
421
422   assert(params != NULL);
423   VP8InitIo(&io);
424   io.data = headers.data + headers.offset;
425   io.data_size = headers.data_size - headers.offset;
426   WebPInitCustomIo(params, &io);  // Plug the I/O functions.
427
428   if (!headers.is_lossless) {
429     VP8Decoder* const dec = VP8New();
430     if (dec == NULL) {
431       return VP8_STATUS_OUT_OF_MEMORY;
432     }
433 #ifdef WEBP_USE_THREAD
434     dec->use_threads_ = params->options && (params->options->use_threads > 0);
435 #else
436     dec->use_threads_ = 0;
437 #endif
438     dec->alpha_data_ = headers.alpha_data;
439     dec->alpha_data_size_ = headers.alpha_data_size;
440
441     // Decode bitstream header, update io->width/io->height.
442     if (!VP8GetHeaders(dec, &io)) {
443       status = dec->status_;   // An error occurred. Grab error status.
444     } else {
445       // Allocate/check output buffers.
446       status = WebPAllocateDecBuffer(io.width, io.height, params->options,
447                                      params->output);
448       if (status == VP8_STATUS_OK) {  // Decode
449         if (!VP8Decode(dec, &io)) {
450           status = dec->status_;
451         }
452       }
453     }
454     VP8Delete(dec);
455   } else {
456     VP8LDecoder* const dec = VP8LNew();
457     if (dec == NULL) {
458       return VP8_STATUS_OUT_OF_MEMORY;
459     }
460     if (!VP8LDecodeHeader(dec, &io)) {
461       status = dec->status_;   // An error occurred. Grab error status.
462     } else {
463       // Allocate/check output buffers.
464       status = WebPAllocateDecBuffer(io.width, io.height, params->options,
465                                      params->output);
466       if (status == VP8_STATUS_OK) {  // Decode
467         if (!VP8LDecodeImage(dec)) {
468           status = dec->status_;
469         }
470       }
471     }
472     VP8LDelete(dec);
473   }
474
475   if (status != VP8_STATUS_OK) {
476     WebPFreeDecBuffer(params->output);
477   }
478   return status;
479 }
480
481 // Helpers
482 static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
483                                      const uint8_t* const data,
484                                      size_t data_size,
485                                      uint8_t* const rgba,
486                                      int stride, size_t size) {
487   WebPDecParams params;
488   WebPDecBuffer buf;
489   if (rgba == NULL) {
490     return NULL;
491   }
492   WebPInitDecBuffer(&buf);
493   WebPResetDecParams(&params);
494   params.output = &buf;
495   buf.colorspace    = colorspace;
496   buf.u.RGBA.rgba   = rgba;
497   buf.u.RGBA.stride = stride;
498   buf.u.RGBA.size   = size;
499   buf.is_external_memory = 1;
500   if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
501     return NULL;
502   }
503   return rgba;
504 }
505
506 uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size,
507                            uint8_t* output, size_t size, int stride) {
508   return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size);
509 }
510
511 uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size,
512                             uint8_t* output, size_t size, int stride) {
513   return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size);
514 }
515
516 uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size,
517                             uint8_t* output, size_t size, int stride) {
518   return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size);
519 }
520
521 uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size,
522                            uint8_t* output, size_t size, int stride) {
523   return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size);
524 }
525
526 uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size,
527                             uint8_t* output, size_t size, int stride) {
528   return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size);
529 }
530
531 uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
532                            uint8_t* luma, size_t luma_size, int luma_stride,
533                            uint8_t* u, size_t u_size, int u_stride,
534                            uint8_t* v, size_t v_size, int v_stride) {
535   WebPDecParams params;
536   WebPDecBuffer output;
537   if (luma == NULL) return NULL;
538   WebPInitDecBuffer(&output);
539   WebPResetDecParams(&params);
540   params.output = &output;
541   output.colorspace      = MODE_YUV;
542   output.u.YUVA.y        = luma;
543   output.u.YUVA.y_stride = luma_stride;
544   output.u.YUVA.y_size   = luma_size;
545   output.u.YUVA.u        = u;
546   output.u.YUVA.u_stride = u_stride;
547   output.u.YUVA.u_size   = u_size;
548   output.u.YUVA.v        = v;
549   output.u.YUVA.v_stride = v_stride;
550   output.u.YUVA.v_size   = v_size;
551   output.is_external_memory = 1;
552   if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
553     return NULL;
554   }
555   return luma;
556 }
557
558 //------------------------------------------------------------------------------
559
560 static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data,
561                        size_t data_size, int* const width, int* const height,
562                        WebPDecBuffer* const keep_info) {
563   WebPDecParams params;
564   WebPDecBuffer output;
565
566   WebPInitDecBuffer(&output);
567   WebPResetDecParams(&params);
568   params.output = &output;
569   output.colorspace = mode;
570
571   // Retrieve (and report back) the required dimensions from bitstream.
572   if (!WebPGetInfo(data, data_size, &output.width, &output.height)) {
573     return NULL;
574   }
575   if (width != NULL) *width = output.width;
576   if (height != NULL) *height = output.height;
577
578   // Decode
579   if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
580     return NULL;
581   }
582   if (keep_info != NULL) {    // keep track of the side-info
583     WebPCopyDecBuffer(&output, keep_info);
584   }
585   // return decoded samples (don't clear 'output'!)
586   return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y;
587 }
588
589 uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
590                        int* width, int* height) {
591   return Decode(MODE_RGB, data, data_size, width, height, NULL);
592 }
593
594 uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
595                         int* width, int* height) {
596   return Decode(MODE_RGBA, data, data_size, width, height, NULL);
597 }
598
599 uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
600                         int* width, int* height) {
601   return Decode(MODE_ARGB, data, data_size, width, height, NULL);
602 }
603
604 uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
605                        int* width, int* height) {
606   return Decode(MODE_BGR, data, data_size, width, height, NULL);
607 }
608
609 uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
610                         int* width, int* height) {
611   return Decode(MODE_BGRA, data, data_size, width, height, NULL);
612 }
613
614 uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
615                        int* width, int* height, uint8_t** u, uint8_t** v,
616                        int* stride, int* uv_stride) {
617   WebPDecBuffer output;   // only to preserve the side-infos
618   uint8_t* const out = Decode(MODE_YUV, data, data_size,
619                               width, height, &output);
620
621   if (out != NULL) {
622     const WebPYUVABuffer* const buf = &output.u.YUVA;
623     *u = buf->u;
624     *v = buf->v;
625     *stride = buf->y_stride;
626     *uv_stride = buf->u_stride;
627     assert(buf->u_stride == buf->v_stride);
628   }
629   return out;
630 }
631
632 static void DefaultFeatures(WebPBitstreamFeatures* const features) {
633   assert(features != NULL);
634   memset(features, 0, sizeof(*features));
635   features->bitstream_version = 0;
636 }
637
638 static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
639                                  WebPBitstreamFeatures* const features) {
640   if (features == NULL || data == NULL) {
641     return VP8_STATUS_INVALID_PARAM;
642   }
643   DefaultFeatures(features);
644
645   // Only parse enough of the data to retrieve the features.
646   return ParseHeadersInternal(data, data_size,
647                               &features->width, &features->height,
648                               &features->has_alpha, &features->has_animation,
649                               NULL);
650 }
651
652 //------------------------------------------------------------------------------
653 // WebPGetInfo()
654
655 int WebPGetInfo(const uint8_t* data, size_t data_size,
656                 int* width, int* height) {
657   WebPBitstreamFeatures features;
658
659   if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) {
660     return 0;
661   }
662
663   if (width != NULL) {
664     *width  = features.width;
665   }
666   if (height != NULL) {
667     *height = features.height;
668   }
669
670   return 1;
671 }
672
673 //------------------------------------------------------------------------------
674 // Advance decoding API
675
676 int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
677                                   int version) {
678   if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
679     return 0;   // version mismatch
680   }
681   if (config == NULL) {
682     return 0;
683   }
684   memset(config, 0, sizeof(*config));
685   DefaultFeatures(&config->input);
686   WebPInitDecBuffer(&config->output);
687   return 1;
688 }
689
690 VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size,
691                                       WebPBitstreamFeatures* features,
692                                       int version) {
693   if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
694     return VP8_STATUS_INVALID_PARAM;   // version mismatch
695   }
696   if (features == NULL) {
697     return VP8_STATUS_INVALID_PARAM;
698   }
699   return GetFeatures(data, data_size, features);
700 }
701
702 VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
703                          WebPDecoderConfig* config) {
704   WebPDecParams params;
705   VP8StatusCode status;
706
707   if (config == NULL) {
708     return VP8_STATUS_INVALID_PARAM;
709   }
710
711   status = GetFeatures(data, data_size, &config->input);
712   if (status != VP8_STATUS_OK) {
713     if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
714       return VP8_STATUS_BITSTREAM_ERROR;  // Not-enough-data treated as error.
715     }
716     return status;
717   }
718
719   WebPResetDecParams(&params);
720   params.output = &config->output;
721   params.options = &config->options;
722   status = DecodeInto(data, data_size, &params);
723
724   return status;
725 }
726
727 //------------------------------------------------------------------------------
728 // Cropping and rescaling.
729
730 int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
731                           VP8Io* const io, WEBP_CSP_MODE src_colorspace) {
732   const int W = io->width;
733   const int H = io->height;
734   int x = 0, y = 0, w = W, h = H;
735
736   // Cropping
737   io->use_cropping = (options != NULL) && (options->use_cropping > 0);
738   if (io->use_cropping) {
739     w = options->crop_width;
740     h = options->crop_height;
741     x = options->crop_left;
742     y = options->crop_top;
743     if (!WebPIsRGBMode(src_colorspace)) {   // only snap for YUV420 or YUV422
744       x &= ~1;
745       y &= ~1;    // TODO(later): only for YUV420, not YUV422.
746     }
747     if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
748       return 0;  // out of frame boundary error
749     }
750   }
751   io->crop_left   = x;
752   io->crop_top    = y;
753   io->crop_right  = x + w;
754   io->crop_bottom = y + h;
755   io->mb_w = w;
756   io->mb_h = h;
757
758   // Scaling
759   io->use_scaling = (options != NULL) && (options->use_scaling > 0);
760   if (io->use_scaling) {
761     if (options->scaled_width <= 0 || options->scaled_height <= 0) {
762       return 0;
763     }
764     io->scaled_width = options->scaled_width;
765     io->scaled_height = options->scaled_height;
766   }
767
768   // Filter
769   io->bypass_filtering = options && options->bypass_filtering;
770
771   // Fancy upsampler
772 #ifdef FANCY_UPSAMPLING
773   io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling);
774 #endif
775
776   if (io->use_scaling) {
777     // disable filter (only for large downscaling ratio).
778     io->bypass_filtering = (io->scaled_width < W * 3 / 4) &&
779                            (io->scaled_height < H * 3 / 4);
780     io->fancy_upsampling = 0;
781   }
782   return 1;
783 }
784
785 //------------------------------------------------------------------------------
786
787 #if defined(__cplusplus) || defined(c_plusplus)
788 }    // extern "C"
789 #endif