OLD | NEW |
(Empty) | |
| 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 "yuv.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 static int CheckRIFFHeader(const uint8_t** data_ptr, uint32_t *data_size_ptr) { |
| 39 uint32_t chunk_size = 0xffffffffu; |
| 40 if (*data_size_ptr >= 10 + 20 && !memcmp(*data_ptr, "RIFF", 4)) { |
| 41 if (memcmp(*data_ptr + 8, "WEBP", 4)) { |
| 42 return 0; // wrong image file signature |
| 43 } |
| 44 const uint32_t riff_size = get_le32(*data_ptr + 4); |
| 45 if (memcmp(*data_ptr + 12, "VP8 ", 4)) { |
| 46 return 0; // invalid compression format |
| 47 } |
| 48 chunk_size = get_le32(*data_ptr + 16); |
| 49 if ((chunk_size > riff_size + 8) || (chunk_size & 1)) { |
| 50 return 0; // inconsistent size information. |
| 51 } |
| 52 // We have a IFF container. Skip it. |
| 53 *data_ptr += 20; |
| 54 *data_size_ptr -= 20; |
| 55 } |
| 56 return chunk_size; |
| 57 } |
| 58 |
| 59 //----------------------------------------------------------------------------- |
| 60 |
| 61 typedef enum { MODE_RGB = 0, MODE_RGBA = 1, |
| 62 MODE_BGR = 2, MODE_BGRA = 3, |
| 63 MODE_YUV = 4 } CSP_MODE; |
| 64 |
| 65 typedef struct { |
| 66 uint8_t* output; // rgb(a) or luma |
| 67 uint8_t *u, *v; |
| 68 int stride; // rgb(a) stride or luma stride |
| 69 int u_stride; |
| 70 int v_stride; |
| 71 CSP_MODE mode; |
| 72 } Params; |
| 73 |
| 74 static void CustomPut(const VP8Io* io) { |
| 75 Params *p = (Params*)io->opaque; |
| 76 const int mb_w = io->mb_w; |
| 77 const int mb_h = io->mb_h; |
| 78 if (p->mode == MODE_YUV) { |
| 79 uint8_t* const y_dst = p->output + io->mb_x + io->mb_y * p->stride; |
| 80 for (int j = 0; j < mb_h; ++j) { |
| 81 memcpy(y_dst + j * p->stride, io->y + j * io->y_stride, mb_w); |
| 82 } |
| 83 uint8_t* const u_dst = p->u + (io->mb_x / 2) + (io->mb_y / 2) * p->u_stride; |
| 84 uint8_t* const v_dst = p->v + (io->mb_x / 2) + (io->mb_y / 2) * p->v_stride; |
| 85 const int uv_w = (mb_w + 1) / 2; |
| 86 for (int j = 0; j < (mb_h + 1) / 2; ++j) { |
| 87 memcpy(u_dst + j * p->u_stride, io->u + j * io->uv_stride, uv_w); |
| 88 memcpy(v_dst + j * p->v_stride, io->v + j * io->uv_stride, uv_w); |
| 89 } |
| 90 } else { |
| 91 const int psize = (p->mode == MODE_RGB || p->mode == MODE_BGR) ? 3 : 4; |
| 92 uint8_t* dst = p->output + psize * io->mb_x + io->mb_y * p->stride; |
| 93 for (int j = 0; j < mb_h; ++j) { |
| 94 const uint8_t* y_src = io->y + j * io->y_stride; |
| 95 for (int i = 0; i < mb_w; ++i) { |
| 96 const int y = y_src[i]; |
| 97 const int u = io->u[(j / 2) * io->uv_stride + (i / 2)]; |
| 98 const int v = io->v[(j / 2) * io->uv_stride + (i / 2)]; |
| 99 if (p->mode == MODE_RGB) { |
| 100 VP8YuvToRgb(y, u, v, dst + i * 3); |
| 101 } else if (p->mode == MODE_BGR) { |
| 102 VP8YuvToBgr(y, u, v, dst + i * 3); |
| 103 } else if (p->mode == MODE_RGBA) { |
| 104 VP8YuvToRgba(y, u, v, dst + i * 4); |
| 105 } else { |
| 106 VP8YuvToBgra(y, u, v, dst + i * 4); |
| 107 } |
| 108 } |
| 109 dst += p->stride; |
| 110 } |
| 111 } |
| 112 } |
| 113 |
| 114 |
| 115 //----------------------------------------------------------------------------- |
| 116 // "Into" variants |
| 117 |
| 118 static uint8_t* DecodeInto(CSP_MODE mode, |
| 119 const uint8_t* data, uint32_t data_size, |
| 120 Params* params, int output_size, |
| 121 int output_u_size, int output_v_size) { |
| 122 VP8Decoder* dec = VP8New(); |
| 123 if (dec == NULL) { |
| 124 return NULL; |
| 125 } |
| 126 |
| 127 VP8Io io; |
| 128 VP8InitIo(&io); |
| 129 io.data = data; |
| 130 io.data_size = data_size; |
| 131 |
| 132 params->mode = mode; |
| 133 io.opaque = params; |
| 134 io.put = CustomPut; |
| 135 |
| 136 if (!VP8GetHeaders(dec, &io)) { |
| 137 VP8Delete(dec); |
| 138 return NULL; |
| 139 } |
| 140 // check output buffers |
| 141 int ok = 1; |
| 142 ok &= (params->stride * io.height <= output_size); |
| 143 if (mode == MODE_RGB || mode == MODE_BGR) { |
| 144 ok &= (params->stride >= io.width * 3); |
| 145 } else if (mode == MODE_RGBA || mode == MODE_BGRA) { |
| 146 ok &= (params->stride >= io.width * 4); |
| 147 } else { |
| 148 ok &= (params->stride >= io.width); |
| 149 // some extra checks for U/V |
| 150 const int u_size = params->u_stride * ((io.height + 1) / 2); |
| 151 const int v_size = params->v_stride * ((io.height + 1) / 2); |
| 152 ok &= (params->u_stride >= (io.width + 1) / 2) && |
| 153 (params->v_stride >= (io.width + 1) / 2); |
| 154 ok &= (u_size <= output_u_size && v_size <= output_v_size); |
| 155 } |
| 156 if (!ok) { |
| 157 VP8Delete(dec); |
| 158 return NULL; |
| 159 } |
| 160 |
| 161 if (mode != MODE_YUV) { |
| 162 VP8YUVInit(); |
| 163 } |
| 164 |
| 165 ok = VP8Decode(dec, &io); |
| 166 VP8Delete(dec); |
| 167 return ok ? params->output : NULL; |
| 168 } |
| 169 |
| 170 uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size, |
| 171 uint8_t* output, int output_size, |
| 172 int output_stride) { |
| 173 if (output == NULL) { |
| 174 return NULL; |
| 175 } |
| 176 Params params; |
| 177 params.output = output; |
| 178 params.stride = output_stride; |
| 179 return DecodeInto(MODE_RGB, data, data_size, ¶ms, output_size, 0, 0); |
| 180 } |
| 181 |
| 182 uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size, |
| 183 uint8_t* output, int output_size, |
| 184 int output_stride) { |
| 185 if (output == NULL) { |
| 186 return NULL; |
| 187 } |
| 188 Params params; |
| 189 params.output = output; |
| 190 params.stride = output_stride; |
| 191 return DecodeInto(MODE_RGBA, data, data_size, ¶ms, output_size, 0, 0); |
| 192 } |
| 193 |
| 194 uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size, |
| 195 uint8_t* output, int output_size, |
| 196 int output_stride) { |
| 197 if (output == NULL) { |
| 198 return NULL; |
| 199 } |
| 200 Params params; |
| 201 params.output = output; |
| 202 params.stride = output_stride; |
| 203 return DecodeInto(MODE_BGR, data, data_size, ¶ms, output_size, 0, 0); |
| 204 } |
| 205 |
| 206 uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size, |
| 207 uint8_t* output, int output_size, |
| 208 int output_stride) { |
| 209 if (output == NULL) { |
| 210 return NULL; |
| 211 } |
| 212 Params params; |
| 213 params.output = output; |
| 214 params.stride = output_stride; |
| 215 return DecodeInto(MODE_BGRA, data, data_size, ¶ms, output_size, 0, 0); |
| 216 } |
| 217 |
| 218 uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, |
| 219 uint8_t* luma, int luma_size, int luma_stride, |
| 220 uint8_t* u, int u_size, int u_stride, |
| 221 uint8_t* v, int v_size, int v_stride) { |
| 222 if (luma == NULL) { |
| 223 return NULL; |
| 224 } |
| 225 Params params; |
| 226 params.output = luma; |
| 227 params.stride = luma_stride; |
| 228 params.u = u; |
| 229 params.u_stride = u_stride; |
| 230 params.v = v; |
| 231 params.v_stride = v_stride; |
| 232 return DecodeInto(MODE_YUV, data, data_size, ¶ms, |
| 233 luma_size, u_size, v_size); |
| 234 } |
| 235 |
| 236 //----------------------------------------------------------------------------- |
| 237 |
| 238 static uint8_t* Decode(CSP_MODE mode, const uint8_t* data, uint32_t data_size, |
| 239 int* width, int* height, Params* params_out) { |
| 240 int w, h; |
| 241 if (!WebPGetInfo(data, data_size, &w, &h)) { |
| 242 return NULL; |
| 243 } |
| 244 if (width) *width = w; |
| 245 if (height) *height = h; |
| 246 |
| 247 // initialize output buffer, now that dimensions are known. |
| 248 int stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w |
| 249 : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w |
| 250 : w; |
| 251 const int size = stride * h; |
| 252 int uv_size = 0; |
| 253 int uv_stride = 0; |
| 254 if (mode == MODE_YUV) { |
| 255 uv_stride = (w + 1) / 2; |
| 256 uv_size = uv_stride * ((h + 1) / 2); |
| 257 } |
| 258 uint8_t* const output = (uint8_t*)malloc(size + 2 * uv_size); |
| 259 if (!output) { |
| 260 return NULL; |
| 261 } |
| 262 Params params = { 0 }; |
| 263 params.output = output; |
| 264 params.stride = stride; |
| 265 if (mode == MODE_YUV) { |
| 266 params.u = output + size; |
| 267 params.u_stride = uv_stride; |
| 268 params.v = output + size + uv_size; |
| 269 params.v_stride = uv_stride; |
| 270 } |
| 271 if (params_out) *params_out = params; |
| 272 return DecodeInto(mode, data, data_size, ¶ms, size, uv_size, uv_size); |
| 273 } |
| 274 |
| 275 uint8_t* WebPDecodeRGB(const uint8_t* data, uint32_t data_size, |
| 276 int *width, int *height) { |
| 277 return Decode(MODE_RGB, data, data_size, width, height, NULL); |
| 278 } |
| 279 |
| 280 uint8_t* WebPDecodeRGBA(const uint8_t* data, uint32_t data_size, |
| 281 int *width, int *height) { |
| 282 return Decode(MODE_RGBA, data, data_size, width, height, NULL); |
| 283 } |
| 284 |
| 285 uint8_t* WebPDecodeBGR(const uint8_t* data, uint32_t data_size, |
| 286 int *width, int *height) { |
| 287 return Decode(MODE_BGR, data, data_size, width, height, NULL); |
| 288 } |
| 289 |
| 290 uint8_t* WebPDecodeBGRA(const uint8_t* data, uint32_t data_size, |
| 291 int *width, int *height) { |
| 292 return Decode(MODE_BGRA, data, data_size, width, height, NULL); |
| 293 } |
| 294 |
| 295 uint8_t* WebPDecodeYUV(const uint8_t* data, uint32_t data_size, |
| 296 int *width, int *height, uint8_t** u, uint8_t** v, |
| 297 int *stride, int* uv_stride) { |
| 298 Params params; |
| 299 uint8_t* const out = Decode(MODE_YUV, data, data_size, |
| 300 width, height, ¶ms); |
| 301 |
| 302 if (out) { |
| 303 *u = params.u; |
| 304 *v = params.v; |
| 305 *stride = params.stride; |
| 306 *uv_stride = params.u_stride; |
| 307 assert(params.u_stride == params.v_stride); |
| 308 } |
| 309 return out; |
| 310 } |
| 311 |
| 312 //----------------------------------------------------------------------------- |
| 313 // WebPGetInfo() |
| 314 |
| 315 int WebPGetInfo(const uint8_t* data, uint32_t data_size, |
| 316 int *width, int *height) { |
| 317 const uint32_t chunk_size = CheckRIFFHeader(&data, &data_size); |
| 318 if (!chunk_size) { |
| 319 return 0; // unsupported RIFF header |
| 320 } |
| 321 // Validate raw video data |
| 322 if (data_size < 10) { |
| 323 return 0; // not enough data |
| 324 } |
| 325 // check signature |
| 326 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) { |
| 327 return 0; // Wrong signature. |
| 328 } |
| 329 const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16); |
| 330 const int key_frame = !(bits & 1); |
| 331 if (!key_frame) { // Not a keyframe. |
| 332 return 0; |
| 333 } |
| 334 const int profile = (bits >> 1) & 7; |
| 335 const int show_frame = (bits >> 4) & 1; |
| 336 const uint32_t partition_length = (bits >> 5); |
| 337 if (profile > 3) { |
| 338 return 0; // unknown profile |
| 339 } |
| 340 if (!show_frame) { |
| 341 return 0; // first frame is invisible! |
| 342 } |
| 343 if (partition_length >= chunk_size) { |
| 344 return 0; // inconsistent size information. |
| 345 } |
| 346 |
| 347 const int w = ((data[7] << 8) | data[6]) & 0x3fff; |
| 348 const int h = ((data[9] << 8) | data[8]) & 0x3fff; |
| 349 if (width) { |
| 350 *width = w; |
| 351 } |
| 352 if (height) { |
| 353 *height = h; |
| 354 } |
| 355 |
| 356 return 1; |
| 357 } |
| 358 |
| 359 #if defined(__cplusplus) || defined(c_plusplus) |
| 360 } // extern "C" |
| 361 #endif |
OLD | NEW |