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