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) 17extern "C" { 18#endif 19 20#define FANCY_UPSCALING // undefined to remove fancy upscaling support 21 22//----------------------------------------------------------------------------- 23// RIFF layout is: 24// 0ffset tag 25// 0...3 "RIFF" 4-byte tag 26// 4...7 size of image data (including metadata) starting at offset 8 27// 8...11 "WEBP" our form-type signature 28// 12..15 "VP8 ": 4-bytes tags, describing the raw video format used 29// 16..19 size of the raw VP8 image data, starting at offset 20 30// 20.... the VP8 bytes 31// There can be extra chunks after the "VP8 " chunk (ICMT, ICOP, ...) 32// All 32-bits sizes are in little-endian order. 33// Note: chunk data must be padded to multiple of 2 in size 34 35static inline uint32_t get_le32(const uint8_t* const data) { 36 return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); 37} 38 39// If a RIFF container is detected, validate it and skip over it. 40static uint32_t CheckRIFFHeader(const uint8_t** data_ptr, 41 uint32_t *data_size_ptr) { 42 uint32_t chunk_size = 0xffffffffu; 43 if (*data_size_ptr >= 10 + 20 && !memcmp(*data_ptr, "RIFF", 4)) { 44 if (memcmp(*data_ptr + 8, "WEBP", 4)) { 45 return 0; // wrong image file signature 46 } else { 47 const uint32_t riff_size = get_le32(*data_ptr + 4); 48 if (riff_size < 12) { 49 return 0; // we should have at least one chunk 50 } 51 if (memcmp(*data_ptr + 12, "VP8 ", 4)) { 52 return 0; // invalid compression format 53 } 54 chunk_size = get_le32(*data_ptr + 16); 55 if (chunk_size > riff_size - 12) { 56 return 0; // inconsistent size information. 57 } 58 // We have a RIFF container. Skip it. 59 *data_ptr += 20; 60 *data_size_ptr -= 20; 61 // Note: we don't report error for odd-sized chunks. 62 } 63 return chunk_size; 64 } 65 return *data_size_ptr; 66} 67 68//----------------------------------------------------------------------------- 69// Fancy upscaling 70 71typedef enum { MODE_RGB = 0, MODE_RGBA = 1, 72 MODE_BGR = 2, MODE_BGRA = 3, 73 MODE_YUV = 4 } CSP_MODE; 74 75#ifdef FANCY_UPSCALING 76 77// Given samples laid out in a square as: 78// [a b] 79// [c d] 80// we interpolate u/v as: 81// ([9*a + 3*b + 3*c + d 3*a + 9*b + 3*c + d] + [8 8]) / 16 82// ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16 83 84// We process u and v together stashed into 32bit (16bit each). 85#define LOAD_UV(u,v) ((u) | ((v) << 16)) 86 87#define UPSCALE_FUNC(FUNC_NAME, FUNC, XSTEP) \ 88static inline void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \ 89 const uint8_t* top_u, const uint8_t* top_v, \ 90 const uint8_t* cur_u, const uint8_t* cur_v, \ 91 uint8_t* top_dst, uint8_t* bottom_dst, int len) { \ 92 int x; \ 93 const int last_pixel_pair = (len - 1) >> 1; \ 94 uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]); /* top-left sample */ \ 95 uint32_t l_uv = LOAD_UV(cur_u[0], cur_v[0]); /* left-sample */ \ 96 if (top_y) { \ 97 const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \ 98 FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \ 99 } \ 100 if (bottom_y) { \ 101 const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \ 102 FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \ 103 } \ 104 for (x = 1; x <= last_pixel_pair; ++x) { \ 105 const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]); /* top sample */ \ 106 const uint32_t uv = LOAD_UV(cur_u[x], cur_v[x]); /* sample */ \ 107 /* precompute invariant values associated with first and second diagonals*/\ 108 const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u; \ 109 const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3; \ 110 const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3; \ 111 if (top_y) { \ 112 const uint32_t uv0 = (diag_12 + tl_uv) >> 1; \ 113 const uint32_t uv1 = (diag_03 + t_uv) >> 1; \ 114 FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \ 115 top_dst + (2 * x - 1) * XSTEP); \ 116 FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \ 117 top_dst + (2 * x - 0) * XSTEP); \ 118 } \ 119 if (bottom_y) { \ 120 const uint32_t uv0 = (diag_03 + l_uv) >> 1; \ 121 const uint32_t uv1 = (diag_12 + uv) >> 1; \ 122 FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \ 123 bottom_dst + (2 * x - 1) * XSTEP); \ 124 FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16), \ 125 bottom_dst + (2 * x + 0) * XSTEP); \ 126 } \ 127 tl_uv = t_uv; \ 128 l_uv = uv; \ 129 } \ 130 if (!(len & 1)) { \ 131 if (top_y) { \ 132 const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \ 133 FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16), \ 134 top_dst + (len - 1) * XSTEP); \ 135 } \ 136 if (bottom_y) { \ 137 const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \ 138 FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16), \ 139 bottom_dst + (len - 1) * XSTEP); \ 140 } \ 141 } \ 142} 143 144// All variants implemented. 145UPSCALE_FUNC(UpscaleRgbLinePair, VP8YuvToRgb, 3) 146UPSCALE_FUNC(UpscaleBgrLinePair, VP8YuvToBgr, 3) 147UPSCALE_FUNC(UpscaleRgbaLinePair, VP8YuvToRgba, 4) 148UPSCALE_FUNC(UpscaleBgraLinePair, VP8YuvToBgra, 4) 149 150// Main driver function. 151static inline 152void UpscaleLinePair(const uint8_t* top_y, const uint8_t* bottom_y, 153 const uint8_t* top_u, const uint8_t* top_v, 154 const uint8_t* cur_u, const uint8_t* cur_v, 155 uint8_t* top_dst, uint8_t* bottom_dst, int len, 156 CSP_MODE mode) { 157 if (mode == MODE_RGB) { 158 UpscaleRgbLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v, 159 top_dst, bottom_dst, len); 160 } else if (mode == MODE_BGR) { 161 UpscaleBgrLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v, 162 top_dst, bottom_dst, len); 163 } else if (mode == MODE_RGBA) { 164 UpscaleRgbaLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v, 165 top_dst, bottom_dst, len); 166 } else { 167 assert(mode == MODE_BGRA); 168 UpscaleBgraLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v, 169 top_dst, bottom_dst, len); 170 } 171} 172 173#undef LOAD_UV 174#undef UPSCALE_FUNC 175 176#endif // FANCY_UPSCALING 177 178//----------------------------------------------------------------------------- 179// Main conversion driver. 180 181typedef struct { 182 uint8_t* output; // rgb(a) or luma 183 uint8_t *u, *v; 184 uint8_t *top_y, *top_u, *top_v; 185 int stride; // rgb(a) stride or luma stride 186 int u_stride; 187 int v_stride; 188 CSP_MODE mode; 189} Params; 190 191static int CustomPut(const VP8Io* io) { 192 Params *p = (Params*)io->opaque; 193 const int w = io->width; 194 const int mb_h = io->mb_h; 195 const int uv_w = (w + 1) / 2; 196 assert(!(io->mb_y & 1)); 197 198 if (w <= 0 || mb_h <= 0) { 199 return 0; 200 } 201 202 if (p->mode == MODE_YUV) { 203 uint8_t* const y_dst = p->output + io->mb_y * p->stride; 204 uint8_t* const u_dst = p->u + (io->mb_y >> 1) * p->u_stride; 205 uint8_t* const v_dst = p->v + (io->mb_y >> 1) * p->v_stride; 206 int j; 207 for (j = 0; j < mb_h; ++j) { 208 memcpy(y_dst + j * p->stride, io->y + j * io->y_stride, w); 209 } 210 for (j = 0; j < (mb_h + 1) / 2; ++j) { 211 memcpy(u_dst + j * p->u_stride, io->u + j * io->uv_stride, uv_w); 212 memcpy(v_dst + j * p->v_stride, io->v + j * io->uv_stride, uv_w); 213 } 214 } else { 215 uint8_t* dst = p->output + io->mb_y * p->stride; 216 if (io->fancy_upscaling) { 217#ifdef FANCY_UPSCALING 218 const uint8_t* cur_y = io->y; 219 const uint8_t* cur_u = io->u; 220 const uint8_t* cur_v = io->v; 221 const uint8_t* top_u = p->top_u; 222 const uint8_t* top_v = p->top_v; 223 int y = io->mb_y; 224 int y_end = io->mb_y + io->mb_h; 225 if (y == 0) { 226 // First line is special cased. We mirror the u/v samples at boundary. 227 UpscaleLinePair(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, 228 NULL, dst, w, p->mode); 229 } else { 230 // We can finish the left-over line from previous call 231 UpscaleLinePair(p->top_y, cur_y, top_u, top_v, cur_u, cur_v, 232 dst - p->stride, dst, w, p->mode); 233 } 234 // Loop over each output pairs of row. 235 for (; y + 2 < y_end; y += 2) { 236 top_u = cur_u; 237 top_v = cur_v; 238 cur_u += io->uv_stride; 239 cur_v += io->uv_stride; 240 dst += 2 * p->stride; 241 cur_y += 2 * io->y_stride; 242 UpscaleLinePair(cur_y - io->y_stride, cur_y, 243 top_u, top_v, cur_u, cur_v, 244 dst - p->stride, dst, w, p->mode); 245 } 246 // move to last row 247 cur_y += io->y_stride; 248 if (y_end != io->height) { 249 // Save the unfinished samples for next call (as we're not done yet). 250 memcpy(p->top_y, cur_y, w * sizeof(*p->top_y)); 251 memcpy(p->top_u, cur_u, uv_w * sizeof(*p->top_u)); 252 memcpy(p->top_v, cur_v, uv_w * sizeof(*p->top_v)); 253 } else { 254 // Process the very last row of even-sized picture 255 if (!(y_end & 1)) { 256 UpscaleLinePair(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, 257 dst + p->stride, NULL, w, p->mode); 258 } 259 } 260#else 261 assert(0); // shouldn't happen. 262#endif 263 } else { 264 // Point-sampling U/V upscaler. 265 int j; 266 for (j = 0; j < mb_h; ++j) { 267 const uint8_t* y_src = io->y + j * io->y_stride; 268 int i; 269 for (i = 0; i < w; ++i) { 270 const int y = y_src[i]; 271 const int u = io->u[(j / 2) * io->uv_stride + (i / 2)]; 272 const int v = io->v[(j / 2) * io->uv_stride + (i / 2)]; 273 if (p->mode == MODE_RGB) { 274 VP8YuvToRgb(y, u, v, dst + i * 3); 275 } else if (p->mode == MODE_BGR) { 276 VP8YuvToBgr(y, u, v, dst + i * 3); 277 } else if (p->mode == MODE_RGBA) { 278 VP8YuvToRgba(y, u, v, dst + i * 4); 279 } else { 280 VP8YuvToBgra(y, u, v, dst + i * 4); 281 } 282 } 283 dst += p->stride; 284 } 285 } 286 } 287 return 1; 288} 289 290//----------------------------------------------------------------------------- 291 292static int CustomSetup(VP8Io* io) { 293#ifdef FANCY_UPSCALING 294 Params *p = (Params*)io->opaque; 295 p->top_y = p->top_u = p->top_v = NULL; 296 if (p->mode != MODE_YUV) { 297 const int uv_width = (io->width + 1) >> 1; 298 p->top_y = (uint8_t*)malloc(io->width + 2 * uv_width); 299 if (p->top_y == NULL) { 300 return 0; // memory error. 301 } 302 p->top_u = p->top_y + io->width; 303 p->top_v = p->top_u + uv_width; 304 io->fancy_upscaling = 1; // activate fancy upscaling 305 } 306#endif 307 return 1; 308} 309 310static void CustomTeardown(const VP8Io* io) { 311#ifdef FANCY_UPSCALING 312 Params *p = (Params*)io->opaque; 313 if (p->top_y) { 314 free(p->top_y); 315 p->top_y = p->top_u = p->top_v = NULL; 316 } 317#endif 318} 319 320//----------------------------------------------------------------------------- 321// "Into" variants 322 323static uint8_t* DecodeInto(CSP_MODE mode, 324 const uint8_t* data, uint32_t data_size, 325 Params* params, int output_size, 326 int output_u_size, int output_v_size) { 327 VP8Decoder* dec = VP8New(); 328 VP8Io io; 329 int ok = 1; 330 331 if (dec == NULL) { 332 return NULL; 333 } 334 335 VP8InitIo(&io); 336 io.data = data; 337 io.data_size = data_size; 338 339 params->mode = mode; 340 io.opaque = params; 341 io.put = CustomPut; 342 io.setup = CustomSetup; 343 io.teardown = CustomTeardown; 344 345 if (!VP8GetHeaders(dec, &io)) { 346 VP8Delete(dec); 347 return NULL; 348 } 349 // check output buffers 350 351 ok &= (params->stride * io.height <= output_size); 352 if (mode == MODE_RGB || mode == MODE_BGR) { 353 ok &= (params->stride >= io.width * 3); 354 } else if (mode == MODE_RGBA || mode == MODE_BGRA) { 355 ok &= (params->stride >= io.width * 4); 356 } else { 357 // some extra checks for U/V 358 const int u_size = params->u_stride * ((io.height + 1) / 2); 359 const int v_size = params->v_stride * ((io.height + 1) / 2); 360 ok &= (params->stride >= io.width); 361 ok &= (params->u_stride >= (io.width + 1) / 2) && 362 (params->v_stride >= (io.width + 1) / 2); 363 ok &= (u_size <= output_u_size && v_size <= output_v_size); 364 } 365 if (!ok) { 366 VP8Delete(dec); 367 return NULL; 368 } 369 370 if (mode != MODE_YUV) { 371 VP8YUVInit(); 372 } 373 374 ok = VP8Decode(dec, &io); 375 VP8Delete(dec); 376 return ok ? params->output : NULL; 377} 378 379uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size, 380 uint8_t* output, int output_size, 381 int output_stride) { 382 Params params; 383 384 if (output == NULL) { 385 return NULL; 386 } 387 388 params.output = output; 389 params.stride = output_stride; 390 return DecodeInto(MODE_RGB, data, data_size, ¶ms, output_size, 0, 0); 391} 392 393uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size, 394 uint8_t* output, int output_size, 395 int output_stride) { 396 Params params; 397 398 if (output == NULL) { 399 return NULL; 400 } 401 402 params.output = output; 403 params.stride = output_stride; 404 return DecodeInto(MODE_RGBA, data, data_size, ¶ms, output_size, 0, 0); 405} 406 407uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size, 408 uint8_t* output, int output_size, 409 int output_stride) { 410 Params params; 411 412 if (output == NULL) { 413 return NULL; 414 } 415 416 params.output = output; 417 params.stride = output_stride; 418 return DecodeInto(MODE_BGR, data, data_size, ¶ms, output_size, 0, 0); 419} 420 421uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size, 422 uint8_t* output, int output_size, 423 int output_stride) { 424 Params params; 425 426 if (output == NULL) { 427 return NULL; 428 } 429 430 params.output = output; 431 params.stride = output_stride; 432 return DecodeInto(MODE_BGRA, data, data_size, ¶ms, output_size, 0, 0); 433} 434 435uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, 436 uint8_t* luma, int luma_size, int luma_stride, 437 uint8_t* u, int u_size, int u_stride, 438 uint8_t* v, int v_size, int v_stride) { 439 Params params; 440 441 if (luma == NULL) { 442 return NULL; 443 } 444 445 params.output = luma; 446 params.stride = luma_stride; 447 params.u = u; 448 params.u_stride = u_stride; 449 params.v = v; 450 params.v_stride = v_stride; 451 return DecodeInto(MODE_YUV, data, data_size, ¶ms, 452 luma_size, u_size, v_size); 453} 454 455//----------------------------------------------------------------------------- 456 457static uint8_t* Decode(CSP_MODE mode, const uint8_t* data, uint32_t data_size, 458 int* width, int* height, Params* params_out) { 459 int w, h, stride; 460 int uv_size = 0; 461 int uv_stride = 0; 462 int size; 463 uint8_t* output; 464 Params params = { 0 }; 465 466 if (!WebPGetInfo(data, data_size, &w, &h)) { 467 return NULL; 468 } 469 if (width) *width = w; 470 if (height) *height = h; 471 472 // initialize output buffer, now that dimensions are known. 473 stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w 474 : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w 475 : w; 476 size = stride * h; 477 478 if (mode == MODE_YUV) { 479 uv_stride = (w + 1) / 2; 480 uv_size = uv_stride * ((h + 1) / 2); 481 } 482 483 output = (uint8_t*)malloc(size + 2 * uv_size); 484 if (!output) { 485 return NULL; 486 } 487 488 params.output = output; 489 params.stride = stride; 490 if (mode == MODE_YUV) { 491 params.u = output + size; 492 params.u_stride = uv_stride; 493 params.v = output + size + uv_size; 494 params.v_stride = uv_stride; 495 } 496 if (params_out) *params_out = params; 497 return DecodeInto(mode, data, data_size, ¶ms, size, uv_size, uv_size); 498} 499 500uint8_t* WebPDecodeRGB(const uint8_t* data, uint32_t data_size, 501 int *width, int *height) { 502 return Decode(MODE_RGB, data, data_size, width, height, NULL); 503} 504 505uint8_t* WebPDecodeRGBA(const uint8_t* data, uint32_t data_size, 506 int *width, int *height) { 507 return Decode(MODE_RGBA, data, data_size, width, height, NULL); 508} 509 510uint8_t* WebPDecodeBGR(const uint8_t* data, uint32_t data_size, 511 int *width, int *height) { 512 return Decode(MODE_BGR, data, data_size, width, height, NULL); 513} 514 515uint8_t* WebPDecodeBGRA(const uint8_t* data, uint32_t data_size, 516 int *width, int *height) { 517 return Decode(MODE_BGRA, data, data_size, width, height, NULL); 518} 519 520uint8_t* WebPDecodeYUV(const uint8_t* data, uint32_t data_size, 521 int *width, int *height, uint8_t** u, uint8_t** v, 522 int *stride, int* uv_stride) { 523 Params params; 524 uint8_t* const out = Decode(MODE_YUV, data, data_size, 525 width, height, ¶ms); 526 527 if (out) { 528 *u = params.u; 529 *v = params.v; 530 *stride = params.stride; 531 *uv_stride = params.u_stride; 532 assert(params.u_stride == params.v_stride); 533 } 534 return out; 535} 536 537//----------------------------------------------------------------------------- 538// WebPGetInfo() 539 540int WebPGetInfo(const uint8_t* data, uint32_t data_size, 541 int *width, int *height) { 542 const uint32_t chunk_size = CheckRIFFHeader(&data, &data_size); 543 if (!chunk_size) { 544 return 0; // unsupported RIFF header 545 } 546 // Validate raw video data 547 if (data_size < 10) { 548 return 0; // not enough data 549 } 550 // check signature 551 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) { 552 return 0; // Wrong signature. 553 } else { 554 const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16); 555 const int key_frame = !(bits & 1); 556 const int w = ((data[7] << 8) | data[6]) & 0x3fff; 557 const int h = ((data[9] << 8) | data[8]) & 0x3fff; 558 559 if (!key_frame) { // Not a keyframe. 560 return 0; 561 } 562 563 if (((bits >> 1) & 7) > 3) { 564 return 0; // unknown profile 565 } 566 if (!((bits >> 4) & 1)) { 567 return 0; // first frame is invisible! 568 } 569 if (((bits >> 5)) >= chunk_size) { // partition_length 570 return 0; // inconsistent size information. 571 } 572 573 if (width) { 574 *width = w; 575 } 576 if (height) { 577 *height = h; 578 } 579 580 return 1; 581 } 582} 583 584#if defined(__cplusplus) || defined(c_plusplus) 585} // extern "C" 586#endif 587