• 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