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(¶ms);
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, ¶ms) != 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(¶ms);
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, ¶ms) != 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(¶ms);
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, ¶ms) != 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(¶ms);
349 params.output = &config->output;
350 params.options = &config->options;
351 status = DecodeInto(data, data_size, ¶ms);
352
353 return status;
354 }
355
356 #if defined(__cplusplus) || defined(c_plusplus)
357 } // extern "C"
358 #endif
359