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