• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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  
get_le32(const uint8_t * const data)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.
WebPCheckRIFFHeader(const uint8_t ** data_ptr,uint32_t * data_size_ptr)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  
WebPResetDecParams(WebPDecParams * const params)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
DecodeInto(const uint8_t * data,uint32_t data_size,WebPDecParams * const params)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
DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,const uint8_t * data,uint32_t data_size,uint8_t * rgba,int stride,int size)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  
WebPDecodeRGBInto(const uint8_t * data,uint32_t data_size,uint8_t * output,int size,int stride)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  
WebPDecodeRGBAInto(const uint8_t * data,uint32_t data_size,uint8_t * output,int size,int stride)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  
WebPDecodeARGBInto(const uint8_t * data,uint32_t data_size,uint8_t * output,int size,int stride)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  
WebPDecodeBGRInto(const uint8_t * data,uint32_t data_size,uint8_t * output,int size,int stride)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  
WebPDecodeBGRAInto(const uint8_t * data,uint32_t data_size,uint8_t * output,int size,int stride)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  
WebPDecodeYUVInto(const uint8_t * data,uint32_t data_size,uint8_t * luma,int luma_size,int luma_stride,uint8_t * u,int u_size,int u_stride,uint8_t * v,int v_size,int v_stride)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  
Decode(WEBP_CSP_MODE mode,const uint8_t * data,uint32_t data_size,int * width,int * height,WebPDecBuffer * keep_info)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  
WebPDecodeRGB(const uint8_t * data,uint32_t data_size,int * width,int * height)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  
WebPDecodeRGBA(const uint8_t * data,uint32_t data_size,int * width,int * height)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  
WebPDecodeARGB(const uint8_t * data,uint32_t data_size,int * width,int * height)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  
WebPDecodeBGR(const uint8_t * data,uint32_t data_size,int * width,int * height)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  
WebPDecodeBGRA(const uint8_t * data,uint32_t data_size,int * width,int * height)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  
WebPDecodeYUV(const uint8_t * data,uint32_t data_size,int * width,int * height,uint8_t ** u,uint8_t ** v,int * stride,int * uv_stride)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  
WebPGetInfo(const uint8_t * data,uint32_t data_size,int * width,int * height)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  
DefaultFeatures(WebPBitstreamFeatures * const features)278  static void DefaultFeatures(WebPBitstreamFeatures* const features) {
279    assert(features);
280    memset(features, 0, sizeof(*features));
281    features->bitstream_version = 0;
282  }
283  
GetFeatures(const uint8_t ** data,uint32_t * data_size,WebPBitstreamFeatures * const features)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  
WebPInitDecoderConfigInternal(WebPDecoderConfig * const config,int version)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  
WebPGetFeaturesInternal(const uint8_t * data,uint32_t data_size,WebPBitstreamFeatures * const features,int version)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  
WebPDecode(const uint8_t * data,uint32_t data_size,WebPDecoderConfig * const config)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