Home | History | Annotate | Download | only in dec
      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 (at) 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 
     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                                           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 
    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 
    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
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    615 static void DefaultFeatures(WebPBitstreamFeatures* const features) {
    616   assert(features != NULL);
    617   memset(features, 0, sizeof(*features));
    618   features->bitstream_version = 0;
    619 }
    620 
    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 
    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 
    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 
    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 
    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 
    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