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(¶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 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(¶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 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 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(¶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