Home | History | Annotate | Download | only in dec
      1 // Copyright 2010 Google Inc.
      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 #include "vp8i.h"
     14 #include "webpi.h"
     15 
     16 #if defined(__cplusplus) || defined(c_plusplus)
     17 extern "C" {
     18 #endif
     19 
     20 //-----------------------------------------------------------------------------
     21 // RIFF layout is:
     22 //   0ffset  tag
     23 //   0...3   "RIFF" 4-byte tag
     24 //   4...7   size of image data (including metadata) starting at offset 8
     25 //   8...11  "WEBP"   our form-type signature
     26 //   12..15  "VP8 ": 4-bytes tags, describing the raw video format used
     27 //   16..19  size of the raw VP8 image data, starting at offset 20
     28 //   20....  the VP8 bytes
     29 // There can be extra chunks after the "VP8 " chunk (ICMT, ICOP, ...)
     30 // All 32-bits sizes are in little-endian order.
     31 // Note: chunk data must be padded to multiple of 2 in size
     32 
     33 static inline uint32_t get_le32(const uint8_t* const data) {
     34   return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
     35 }
     36 
     37 // If a RIFF container is detected, validate it and skip over it.
     38 uint32_t WebPCheckRIFFHeader(const uint8_t** data_ptr,
     39                              uint32_t* data_size_ptr) {
     40   uint32_t chunk_size = 0xffffffffu;
     41   if (*data_size_ptr >= 10 + 20 && !memcmp(*data_ptr, "RIFF", 4)) {
     42     if (memcmp(*data_ptr + 8, "WEBP", 4)) {
     43       return 0;  // wrong image file signature
     44     } else {
     45       const uint32_t riff_size = get_le32(*data_ptr + 4);
     46       if (riff_size < 12) {
     47         return 0;   // we should have at least one chunk
     48       }
     49       if (memcmp(*data_ptr + 12, "VP8 ", 4)) {
     50         return 0;   // invalid compression format
     51       }
     52       chunk_size = get_le32(*data_ptr + 16);
     53       if (chunk_size > riff_size - 12) {
     54         return 0;  // inconsistent size information.
     55       }
     56       // We have a RIFF container. Skip it.
     57       *data_ptr += 20;
     58       *data_size_ptr -= 20;
     59       // Note: we don't report error for odd-sized chunks.
     60     }
     61     return chunk_size;
     62   }
     63   return *data_size_ptr;
     64 }
     65 
     66 //-----------------------------------------------------------------------------
     67 // WebPDecParams
     68 
     69 void WebPResetDecParams(WebPDecParams* const params) {
     70   if (params) {
     71     memset(params, 0, sizeof(*params));
     72   }
     73 }
     74 
     75 //-----------------------------------------------------------------------------
     76 // "Into" decoding variants
     77 
     78 // Main flow
     79 static VP8StatusCode DecodeInto(const uint8_t* data, uint32_t data_size,
     80                                 WebPDecParams* const params) {
     81   VP8Decoder* dec = VP8New();
     82   VP8StatusCode status = VP8_STATUS_OK;
     83   VP8Io io;
     84 
     85   assert(params);
     86   if (dec == NULL) {
     87     return VP8_STATUS_INVALID_PARAM;
     88   }
     89 
     90   VP8InitIo(&io);
     91   io.data = data;
     92   io.data_size = data_size;
     93   WebPInitCustomIo(params, &io);  // Plug the I/O functions.
     94 
     95   // Decode bitstream header, update io->width/io->height.
     96   if (!VP8GetHeaders(dec, &io)) {
     97     status = VP8_STATUS_BITSTREAM_ERROR;
     98   } else {
     99     // Allocate/check output buffers.
    100     status = WebPAllocateDecBuffer(io.width, io.height, params->options,
    101                                    params->output);
    102     if (status == VP8_STATUS_OK) {
    103       // Decode
    104       if (!VP8Decode(dec, &io)) {
    105         status = dec->status_;
    106       }
    107     }
    108   }
    109   VP8Delete(dec);
    110   if (status != VP8_STATUS_OK) {
    111     WebPFreeDecBuffer(params->output);
    112   }
    113   return status;
    114 }
    115 
    116 // Helpers
    117 static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
    118                                      const uint8_t* data, uint32_t data_size,
    119                                      uint8_t* rgba, int stride, int size) {
    120   WebPDecParams params;
    121   WebPDecBuffer buf;
    122   if (rgba == NULL) {
    123     return NULL;
    124   }
    125   WebPInitDecBuffer(&buf);
    126   WebPResetDecParams(&params);
    127   params.output = &buf;
    128   buf.colorspace    = colorspace;
    129   buf.u.RGBA.rgba   = rgba;
    130   buf.u.RGBA.stride = stride;
    131   buf.u.RGBA.size   = size;
    132   buf.is_external_memory = 1;
    133   if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
    134     return NULL;
    135   }
    136   return rgba;
    137 }
    138 
    139 uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size,
    140                            uint8_t* output, int size, int stride) {
    141   return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size);
    142 }
    143 
    144 uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size,
    145                             uint8_t* output, int size, int stride) {
    146   return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size);
    147 }
    148 
    149 uint8_t* WebPDecodeARGBInto(const uint8_t* data, uint32_t data_size,
    150                             uint8_t* output, int size, int stride) {
    151   return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size);
    152 }
    153 
    154 uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size,
    155                            uint8_t* output, int size, int stride) {
    156   return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size);
    157 }
    158 
    159 uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size,
    160                             uint8_t* output, int size, int stride) {
    161   return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size);
    162 }
    163 
    164 uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size,
    165                            uint8_t* luma, int luma_size, int luma_stride,
    166                            uint8_t* u, int u_size, int u_stride,
    167                            uint8_t* v, int v_size, int v_stride) {
    168   WebPDecParams params;
    169   WebPDecBuffer output;
    170   if (luma == NULL) return NULL;
    171   WebPInitDecBuffer(&output);
    172   WebPResetDecParams(&params);
    173   params.output = &output;
    174   output.colorspace      = MODE_YUV;
    175   output.u.YUVA.y        = luma;
    176   output.u.YUVA.y_stride = luma_stride;
    177   output.u.YUVA.y_size   = luma_size;
    178   output.u.YUVA.u        = u;
    179   output.u.YUVA.u_stride = u_stride;
    180   output.u.YUVA.u_size   = u_size;
    181   output.u.YUVA.v        = v;
    182   output.u.YUVA.v_stride = v_stride;
    183   output.u.YUVA.v_size   = v_size;
    184   output.is_external_memory = 1;
    185   if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
    186     return NULL;
    187   }
    188   return luma;
    189 }
    190 
    191 //-----------------------------------------------------------------------------
    192 
    193 static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data,
    194                        uint32_t data_size, int* width, int* height,
    195                        WebPDecBuffer* keep_info) {
    196   WebPDecParams params;
    197   WebPDecBuffer output;
    198 
    199   WebPInitDecBuffer(&output);
    200   WebPResetDecParams(&params);
    201   params.output = &output;
    202   output.colorspace = mode;
    203 
    204   // Retrieve (and report back) the required dimensions from bitstream.
    205   if (!WebPGetInfo(data, data_size, &output.width, &output.height)) {
    206     return NULL;
    207   }
    208   if (width) *width = output.width;
    209   if (height) *height = output.height;
    210 
    211   // Decode
    212   if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
    213     return NULL;
    214   }
    215   if (keep_info) {    // keep track of the side-info
    216     WebPCopyDecBuffer(&output, keep_info);
    217   }
    218   // return decoded samples (don't clear 'output'!)
    219   return (mode >= MODE_YUV) ? output.u.YUVA.y : output.u.RGBA.rgba;
    220 }
    221 
    222 uint8_t* WebPDecodeRGB(const uint8_t* data, uint32_t data_size,
    223                        int* width, int* height) {
    224   return Decode(MODE_RGB, data, data_size, width, height, NULL);
    225 }
    226 
    227 uint8_t* WebPDecodeRGBA(const uint8_t* data, uint32_t data_size,
    228                         int* width, int* height) {
    229   return Decode(MODE_RGBA, data, data_size, width, height, NULL);
    230 }
    231 
    232 uint8_t* WebPDecodeARGB(const uint8_t* data, uint32_t data_size,
    233                         int* width, int* height) {
    234   return Decode(MODE_ARGB, data, data_size, width, height, NULL);
    235 }
    236 
    237 uint8_t* WebPDecodeBGR(const uint8_t* data, uint32_t data_size,
    238                        int* width, int* height) {
    239   return Decode(MODE_BGR, data, data_size, width, height, NULL);
    240 }
    241 
    242 uint8_t* WebPDecodeBGRA(const uint8_t* data, uint32_t data_size,
    243                         int* width, int* height) {
    244   return Decode(MODE_BGRA, data, data_size, width, height, NULL);
    245 }
    246 
    247 uint8_t* WebPDecodeYUV(const uint8_t* data, uint32_t data_size,
    248                        int* width, int* height, uint8_t** u, uint8_t** v,
    249                        int* stride, int* uv_stride) {
    250   WebPDecBuffer output;   // only to preserve the side-infos
    251   uint8_t* const out = Decode(MODE_YUV, data, data_size,
    252                               width, height, &output);
    253 
    254   if (out) {
    255     const WebPYUVABuffer* const buf = &output.u.YUVA;
    256     *u = buf->u;
    257     *v = buf->v;
    258     *stride = buf->y_stride;
    259     *uv_stride = buf->u_stride;
    260     assert(buf->u_stride == buf->v_stride);
    261   }
    262   return out;
    263 }
    264 
    265 //-----------------------------------------------------------------------------
    266 // WebPGetInfo()
    267 
    268 int WebPGetInfo(const uint8_t* data, uint32_t data_size,
    269                 int* width, int* height) {
    270   const uint32_t chunk_size = WebPCheckRIFFHeader(&data, &data_size);
    271   if (!chunk_size) {
    272     return 0;         // unsupported RIFF header
    273   }
    274   // Validate raw video data
    275   return VP8GetInfo(data, data_size, chunk_size, width, height, NULL);
    276 }
    277 
    278 static void DefaultFeatures(WebPBitstreamFeatures* const features) {
    279   assert(features);
    280   memset(features, 0, sizeof(*features));
    281   features->bitstream_version = 0;
    282 }
    283 
    284 static VP8StatusCode GetFeatures(const uint8_t** data, uint32_t* data_size,
    285                                  WebPBitstreamFeatures* const features) {
    286   uint32_t chunk_size;
    287   if (features == NULL) {
    288     return VP8_STATUS_INVALID_PARAM;
    289   }
    290   DefaultFeatures(features);
    291   if (data == NULL || *data == NULL || data_size == 0) {
    292     return VP8_STATUS_INVALID_PARAM;
    293   }
    294   chunk_size = WebPCheckRIFFHeader(data, data_size);
    295   if (chunk_size == 0) {
    296     return VP8_STATUS_BITSTREAM_ERROR;   // unsupported RIFF header
    297   }
    298   if (!VP8GetInfo(*data, *data_size, chunk_size,
    299                   &features->width, &features->height, &features->has_alpha)) {
    300     return VP8_STATUS_BITSTREAM_ERROR;
    301   }
    302   return VP8_STATUS_OK;
    303 }
    304 
    305 //-----------------------------------------------------------------------------
    306 // Advance decoding API
    307 
    308 int WebPInitDecoderConfigInternal(WebPDecoderConfig* const config,
    309                                   int version) {
    310   if (version != WEBP_DECODER_ABI_VERSION) {
    311     return 0;   // version mismatch
    312   }
    313   if (config == NULL) {
    314     return 0;
    315   }
    316   memset(config, 0, sizeof(*config));
    317   DefaultFeatures(&config->input);
    318   WebPInitDecBuffer(&config->output);
    319   return 1;
    320 }
    321 
    322 VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, uint32_t data_size,
    323                                       WebPBitstreamFeatures* const features,
    324                             int version) {
    325   if (version != WEBP_DECODER_ABI_VERSION) {
    326     return VP8_STATUS_INVALID_PARAM;   // version mismatch
    327   }
    328   if (features == NULL) {
    329     return VP8_STATUS_INVALID_PARAM;
    330   }
    331   return GetFeatures(&data, &data_size, features);
    332 }
    333 
    334 VP8StatusCode WebPDecode(const uint8_t* data, uint32_t data_size,
    335                          WebPDecoderConfig* const config) {
    336   WebPDecParams params;
    337   VP8StatusCode status;
    338 
    339   if (!config) {
    340     return VP8_STATUS_INVALID_PARAM;
    341   }
    342 
    343   status = GetFeatures(&data, &data_size, &config->input);
    344   if (status != VP8_STATUS_OK) {
    345     return status;
    346   }
    347 
    348   WebPResetDecParams(&params);
    349   params.output = &config->output;
    350   params.options = &config->options;
    351   status = DecodeInto(data, data_size, &params);
    352 
    353   return status;
    354 }
    355 
    356 #if defined(__cplusplus) || defined(c_plusplus)
    357 }    // extern "C"
    358 #endif
    359