Merge branch '2.4'
[profile/ivi/opencv.git] / 3rdparty / libwebp / 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     if (buf_size < disk_chunk_size) {             // Insufficient data.
196       return VP8_STATUS_NOT_ENOUGH_DATA;
197     }
198
199     if (!memcmp(buf, "ALPH", TAG_SIZE)) {         // A valid ALPH header.
200       *alpha_data = buf + CHUNK_HEADER_SIZE;
201       *alpha_size = chunk_size;
202     } else if (!memcmp(buf, "VP8 ", TAG_SIZE) ||
203                !memcmp(buf, "VP8L", TAG_SIZE)) {  // A valid VP8/VP8L header.
204       return VP8_STATUS_OK;  // Found.
205     }
206
207     // We have a full and valid chunk; skip it.
208     buf += disk_chunk_size;
209     buf_size -= disk_chunk_size;
210   }
211 }
212
213 // Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it.
214 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than
215 //         riff_size) VP8/VP8L header,
216 //         VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
217 //         VP8_STATUS_OK otherwise.
218 // If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes
219 // extracted from the VP8/VP8L chunk header.
220 // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
221 static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
222                                     size_t* const data_size,
223                                     size_t riff_size,
224                                     size_t* const chunk_size,
225                                     int* const is_lossless) {
226   const uint8_t* const data = *data_ptr;
227   const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE);
228   const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE);
229   const uint32_t minimal_size =
230       TAG_SIZE + CHUNK_HEADER_SIZE;  // "WEBP" + "VP8 nnnn" OR
231                                      // "WEBP" + "VP8Lnnnn"
232   assert(data != NULL);
233   assert(data_size != NULL);
234   assert(chunk_size != NULL);
235   assert(is_lossless != NULL);
236
237   if (*data_size < CHUNK_HEADER_SIZE) {
238     return VP8_STATUS_NOT_ENOUGH_DATA;  // Insufficient data.
239   }
240
241   if (is_vp8 || is_vp8l) {
242     // Bitstream contains VP8/VP8L header.
243     const uint32_t size = get_le32(data + TAG_SIZE);
244     if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
245       return VP8_STATUS_BITSTREAM_ERROR;  // Inconsistent size information.
246     }
247     // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
248     *chunk_size = size;
249     *data_ptr += CHUNK_HEADER_SIZE;
250     *data_size -= CHUNK_HEADER_SIZE;
251     *is_lossless = is_vp8l;
252   } else {
253     // Raw VP8/VP8L bitstream (no header).
254     *is_lossless = VP8LCheckSignature(data, *data_size);
255     *chunk_size = *data_size;
256   }
257
258   return VP8_STATUS_OK;
259 }
260
261 //------------------------------------------------------------------------------
262
263 // Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
264 // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
265 // minimal amount will be read to fetch the remaining parameters.
266 // If 'headers' is non-NULL this function will attempt to locate both alpha
267 // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
268 // Note: The following chunk sequences (before the raw VP8/VP8L data) are
269 // considered valid by this function:
270 // RIFF + VP8(L)
271 // RIFF + VP8X + (optional chunks) + VP8(L)
272 // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
273 // VP8(L)     <-- Not a valid WebP format: only allowed for internal purpose.
274 static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
275                                           size_t data_size,
276                                           int* const width,
277                                           int* const height,
278                                           int* const has_alpha,
279                                           int* const has_animation,
280                                           WebPHeaderStructure* const headers) {
281   int found_riff = 0;
282   int found_vp8x = 0;
283   VP8StatusCode status;
284   WebPHeaderStructure hdrs;
285
286   if (data == NULL || data_size < RIFF_HEADER_SIZE) {
287     return VP8_STATUS_NOT_ENOUGH_DATA;
288   }
289   memset(&hdrs, 0, sizeof(hdrs));
290   hdrs.data = data;
291   hdrs.data_size = data_size;
292
293   // Skip over RIFF header.
294   status = ParseRIFF(&data, &data_size, &hdrs.riff_size);
295   if (status != VP8_STATUS_OK) {
296     return status;   // Wrong RIFF header / insufficient data.
297   }
298   found_riff = (hdrs.riff_size > 0);
299
300   // Skip over VP8X.
301   {
302     uint32_t flags = 0;
303     status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags);
304     if (status != VP8_STATUS_OK) {
305       return status;  // Wrong VP8X / insufficient data.
306     }
307     if (!found_riff && found_vp8x) {
308       // Note: This restriction may be removed in the future, if it becomes
309       // necessary to send VP8X chunk to the decoder.
310       return VP8_STATUS_BITSTREAM_ERROR;
311     }
312     if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
313     if (has_animation != NULL) *has_animation = !!(flags & ANIMATION_FLAG);
314     if (found_vp8x && headers == NULL) {
315       return VP8_STATUS_OK;  // Return features from VP8X header.
316     }
317   }
318
319   if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA;
320
321   // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
322   if ((found_riff && found_vp8x) ||
323       (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) {
324     status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
325                                  &hdrs.alpha_data, &hdrs.alpha_data_size);
326     if (status != VP8_STATUS_OK) {
327       return status;  // Found an invalid chunk size / insufficient data.
328     }
329   }
330
331   // Skip over VP8/VP8L header.
332   status = ParseVP8Header(&data, &data_size, hdrs.riff_size,
333                           &hdrs.compressed_size, &hdrs.is_lossless);
334   if (status != VP8_STATUS_OK) {
335     return status;  // Wrong VP8/VP8L chunk-header / insufficient data.
336   }
337   if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
338     return VP8_STATUS_BITSTREAM_ERROR;
339   }
340
341   if (!hdrs.is_lossless) {
342     if (data_size < VP8_FRAME_HEADER_SIZE) {
343       return VP8_STATUS_NOT_ENOUGH_DATA;
344     }
345     // Validates raw VP8 data.
346     if (!VP8GetInfo(data, data_size,
347                     (uint32_t)hdrs.compressed_size, width, height)) {
348       return VP8_STATUS_BITSTREAM_ERROR;
349     }
350   } else {
351     if (data_size < VP8L_FRAME_HEADER_SIZE) {
352       return VP8_STATUS_NOT_ENOUGH_DATA;
353     }
354     // Validates raw VP8L data.
355     if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) {
356       return VP8_STATUS_BITSTREAM_ERROR;
357     }
358   }
359
360   if (has_alpha != NULL) {
361     // If the data did not contain a VP8X/VP8L chunk the only definitive way
362     // to set this is by looking for alpha data (from an ALPH chunk).
363     *has_alpha |= (hdrs.alpha_data != NULL);
364   }
365   if (headers != NULL) {
366     *headers = hdrs;
367     headers->offset = data - headers->data;
368     assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
369     assert(headers->offset == headers->data_size - data_size);
370   }
371   return VP8_STATUS_OK;  // Return features from VP8 header.
372 }
373
374 VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
375   VP8StatusCode status;
376   int has_animation = 0;
377   assert(headers != NULL);
378   // fill out headers, ignore width/height/has_alpha.
379   status = ParseHeadersInternal(headers->data, headers->data_size,
380                                 NULL, NULL, NULL, &has_animation, headers);
381   if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) {
382     // TODO(jzern): full support of animation frames will require API additions.
383     if (has_animation) {
384       status = VP8_STATUS_UNSUPPORTED_FEATURE;
385     }
386   }
387   return status;
388 }
389
390 //------------------------------------------------------------------------------
391 // WebPDecParams
392
393 void WebPResetDecParams(WebPDecParams* const params) {
394   if (params) {
395     memset(params, 0, sizeof(*params));
396   }
397 }
398
399 //------------------------------------------------------------------------------
400 // "Into" decoding variants
401
402 // Main flow
403 static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
404                                 WebPDecParams* const params) {
405   VP8StatusCode status;
406   VP8Io io;
407   WebPHeaderStructure headers;
408
409   headers.data = data;
410   headers.data_size = data_size;
411   status = WebPParseHeaders(&headers);   // Process Pre-VP8 chunks.
412   if (status != VP8_STATUS_OK) {
413     return status;
414   }
415
416   assert(params != NULL);
417   VP8InitIo(&io);
418   io.data = headers.data + headers.offset;
419   io.data_size = headers.data_size - headers.offset;
420   WebPInitCustomIo(params, &io);  // Plug the I/O functions.
421
422   if (!headers.is_lossless) {
423     VP8Decoder* const dec = VP8New();
424     if (dec == NULL) {
425       return VP8_STATUS_OUT_OF_MEMORY;
426     }
427 #ifdef WEBP_USE_THREAD
428     dec->use_threads_ = params->options && (params->options->use_threads > 0);
429 #else
430     dec->use_threads_ = 0;
431 #endif
432     dec->alpha_data_ = headers.alpha_data;
433     dec->alpha_data_size_ = headers.alpha_data_size;
434
435     // Decode bitstream header, update io->width/io->height.
436     if (!VP8GetHeaders(dec, &io)) {
437       status = dec->status_;   // An error occurred. Grab error status.
438     } else {
439       // Allocate/check output buffers.
440       status = WebPAllocateDecBuffer(io.width, io.height, params->options,
441                                      params->output);
442       if (status == VP8_STATUS_OK) {  // Decode
443         if (!VP8Decode(dec, &io)) {
444           status = dec->status_;
445         }
446       }
447     }
448     VP8Delete(dec);
449   } else {
450     VP8LDecoder* const dec = VP8LNew();
451     if (dec == NULL) {
452       return VP8_STATUS_OUT_OF_MEMORY;
453     }
454     if (!VP8LDecodeHeader(dec, &io)) {
455       status = dec->status_;   // An error occurred. Grab error status.
456     } else {
457       // Allocate/check output buffers.
458       status = WebPAllocateDecBuffer(io.width, io.height, params->options,
459                                      params->output);
460       if (status == VP8_STATUS_OK) {  // Decode
461         if (!VP8LDecodeImage(dec)) {
462           status = dec->status_;
463         }
464       }
465     }
466     VP8LDelete(dec);
467   }
468
469   if (status != VP8_STATUS_OK) {
470     WebPFreeDecBuffer(params->output);
471   }
472   return status;
473 }
474
475 // Helpers
476 static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
477                                      const uint8_t* const data,
478                                      size_t data_size,
479                                      uint8_t* const rgba,
480                                      int stride, size_t size) {
481   WebPDecParams params;
482   WebPDecBuffer buf;
483   if (rgba == NULL) {
484     return NULL;
485   }
486   WebPInitDecBuffer(&buf);
487   WebPResetDecParams(&params);
488   params.output = &buf;
489   buf.colorspace    = colorspace;
490   buf.u.RGBA.rgba   = rgba;
491   buf.u.RGBA.stride = stride;
492   buf.u.RGBA.size   = size;
493   buf.is_external_memory = 1;
494   if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
495     return NULL;
496   }
497   return rgba;
498 }
499
500 uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size,
501                            uint8_t* output, size_t size, int stride) {
502   return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size);
503 }
504
505 uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size,
506                             uint8_t* output, size_t size, int stride) {
507   return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size);
508 }
509
510 uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size,
511                             uint8_t* output, size_t size, int stride) {
512   return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size);
513 }
514
515 uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size,
516                            uint8_t* output, size_t size, int stride) {
517   return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size);
518 }
519
520 uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size,
521                             uint8_t* output, size_t size, int stride) {
522   return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size);
523 }
524
525 uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
526                            uint8_t* luma, size_t luma_size, int luma_stride,
527                            uint8_t* u, size_t u_size, int u_stride,
528                            uint8_t* v, size_t v_size, int v_stride) {
529   WebPDecParams params;
530   WebPDecBuffer output;
531   if (luma == NULL) return NULL;
532   WebPInitDecBuffer(&output);
533   WebPResetDecParams(&params);
534   params.output = &output;
535   output.colorspace      = MODE_YUV;
536   output.u.YUVA.y        = luma;
537   output.u.YUVA.y_stride = luma_stride;
538   output.u.YUVA.y_size   = luma_size;
539   output.u.YUVA.u        = u;
540   output.u.YUVA.u_stride = u_stride;
541   output.u.YUVA.u_size   = u_size;
542   output.u.YUVA.v        = v;
543   output.u.YUVA.v_stride = v_stride;
544   output.u.YUVA.v_size   = v_size;
545   output.is_external_memory = 1;
546   if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
547     return NULL;
548   }
549   return luma;
550 }
551
552 //------------------------------------------------------------------------------
553
554 static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data,
555                        size_t data_size, int* const width, int* const height,
556                        WebPDecBuffer* const keep_info) {
557   WebPDecParams params;
558   WebPDecBuffer output;
559
560   WebPInitDecBuffer(&output);
561   WebPResetDecParams(&params);
562   params.output = &output;
563   output.colorspace = mode;
564
565   // Retrieve (and report back) the required dimensions from bitstream.
566   if (!WebPGetInfo(data, data_size, &output.width, &output.height)) {
567     return NULL;
568   }
569   if (width != NULL) *width = output.width;
570   if (height != NULL) *height = output.height;
571
572   // Decode
573   if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
574     return NULL;
575   }
576   if (keep_info != NULL) {    // keep track of the side-info
577     WebPCopyDecBuffer(&output, keep_info);
578   }
579   // return decoded samples (don't clear 'output'!)
580   return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y;
581 }
582
583 uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
584                        int* width, int* height) {
585   return Decode(MODE_RGB, data, data_size, width, height, NULL);
586 }
587
588 uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
589                         int* width, int* height) {
590   return Decode(MODE_RGBA, data, data_size, width, height, NULL);
591 }
592
593 uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
594                         int* width, int* height) {
595   return Decode(MODE_ARGB, data, data_size, width, height, NULL);
596 }
597
598 uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
599                        int* width, int* height) {
600   return Decode(MODE_BGR, data, data_size, width, height, NULL);
601 }
602
603 uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
604                         int* width, int* height) {
605   return Decode(MODE_BGRA, data, data_size, width, height, NULL);
606 }
607
608 uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
609                        int* width, int* height, uint8_t** u, uint8_t** v,
610                        int* stride, int* uv_stride) {
611   WebPDecBuffer output;   // only to preserve the side-infos
612   uint8_t* const out = Decode(MODE_YUV, data, data_size,
613                               width, height, &output);
614
615   if (out != NULL) {
616     const WebPYUVABuffer* const buf = &output.u.YUVA;
617     *u = buf->u;
618     *v = buf->v;
619     *stride = buf->y_stride;
620     *uv_stride = buf->u_stride;
621     assert(buf->u_stride == buf->v_stride);
622   }
623   return out;
624 }
625
626 static void DefaultFeatures(WebPBitstreamFeatures* const features) {
627   assert(features != NULL);
628   memset(features, 0, sizeof(*features));
629   features->bitstream_version = 0;
630 }
631
632 static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
633                                  WebPBitstreamFeatures* const features) {
634   if (features == NULL || data == NULL) {
635     return VP8_STATUS_INVALID_PARAM;
636   }
637   DefaultFeatures(features);
638
639   // Only parse enough of the data to retrieve the features.
640   return ParseHeadersInternal(data, data_size,
641                               &features->width, &features->height,
642                               &features->has_alpha, &features->has_animation,
643                               NULL);
644 }
645
646 //------------------------------------------------------------------------------
647 // WebPGetInfo()
648
649 int WebPGetInfo(const uint8_t* data, size_t data_size,
650                 int* width, int* height) {
651   WebPBitstreamFeatures features;
652
653   if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) {
654     return 0;
655   }
656
657   if (width != NULL) {
658     *width  = features.width;
659   }
660   if (height != NULL) {
661     *height = features.height;
662   }
663
664   return 1;
665 }
666
667 //------------------------------------------------------------------------------
668 // Advance decoding API
669
670 int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
671                                   int version) {
672   if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
673     return 0;   // version mismatch
674   }
675   if (config == NULL) {
676     return 0;
677   }
678   memset(config, 0, sizeof(*config));
679   DefaultFeatures(&config->input);
680   WebPInitDecBuffer(&config->output);
681   return 1;
682 }
683
684 VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size,
685                                       WebPBitstreamFeatures* features,
686                                       int version) {
687   if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
688     return VP8_STATUS_INVALID_PARAM;   // version mismatch
689   }
690   if (features == NULL) {
691     return VP8_STATUS_INVALID_PARAM;
692   }
693   return GetFeatures(data, data_size, features);
694 }
695
696 VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
697                          WebPDecoderConfig* config) {
698   WebPDecParams params;
699   VP8StatusCode status;
700
701   if (config == NULL) {
702     return VP8_STATUS_INVALID_PARAM;
703   }
704
705   status = GetFeatures(data, data_size, &config->input);
706   if (status != VP8_STATUS_OK) {
707     if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
708       return VP8_STATUS_BITSTREAM_ERROR;  // Not-enough-data treated as error.
709     }
710     return status;
711   }
712
713   WebPResetDecParams(&params);
714   params.output = &config->output;
715   params.options = &config->options;
716   status = DecodeInto(data, data_size, &params);
717
718   return status;
719 }
720
721 //------------------------------------------------------------------------------
722 // Cropping and rescaling.
723
724 int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
725                           VP8Io* const io, WEBP_CSP_MODE src_colorspace) {
726   const int W = io->width;
727   const int H = io->height;
728   int x = 0, y = 0, w = W, h = H;
729
730   // Cropping
731   io->use_cropping = (options != NULL) && (options->use_cropping > 0);
732   if (io->use_cropping) {
733     w = options->crop_width;
734     h = options->crop_height;
735     x = options->crop_left;
736     y = options->crop_top;
737     if (!WebPIsRGBMode(src_colorspace)) {   // only snap for YUV420 or YUV422
738       x &= ~1;
739       y &= ~1;    // TODO(later): only for YUV420, not YUV422.
740     }
741     if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
742       return 0;  // out of frame boundary error
743     }
744   }
745   io->crop_left   = x;
746   io->crop_top    = y;
747   io->crop_right  = x + w;
748   io->crop_bottom = y + h;
749   io->mb_w = w;
750   io->mb_h = h;
751
752   // Scaling
753   io->use_scaling = (options != NULL) && (options->use_scaling > 0);
754   if (io->use_scaling) {
755     if (options->scaled_width <= 0 || options->scaled_height <= 0) {
756       return 0;
757     }
758     io->scaled_width = options->scaled_width;
759     io->scaled_height = options->scaled_height;
760   }
761
762   // Filter
763   io->bypass_filtering = options && options->bypass_filtering;
764
765   // Fancy upsampler
766 #ifdef FANCY_UPSAMPLING
767   io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling);
768 #endif
769
770   if (io->use_scaling) {
771     // disable filter (only for large downscaling ratio).
772     io->bypass_filtering = (io->scaled_width < W * 3 / 4) &&
773                            (io->scaled_height < H * 3 / 4);
774     io->fancy_upsampling = 0;
775   }
776   return 1;
777 }
778
779 //------------------------------------------------------------------------------
780
781 #if defined(__cplusplus) || defined(c_plusplus)
782 }    // extern "C"
783 #endif