1 // Copyright 2010 Google Inc. All Rights Reserved. 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 14 #include "./vp8i.h" 15 #include "./vp8li.h" 16 #include "./webpi.h" 17 #include "webp/format_constants.h" 18 19 #if defined(__cplusplus) || defined(c_plusplus) 20 extern "C" { 21 #endif 22 23 //------------------------------------------------------------------------------ 24 // RIFF layout is: 25 // Offset tag 26 // 0...3 "RIFF" 4-byte tag 27 // 4...7 size of image data (including metadata) starting at offset 8 28 // 8...11 "WEBP" our form-type signature 29 // The RIFF container (12 bytes) is followed by appropriate chunks: 30 // 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format 31 // 16..19 size of the raw VP8 image data, starting at offset 20 32 // 20.... the VP8 bytes 33 // Or, 34 // 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format 35 // 16..19 size of the raw VP8L image data, starting at offset 20 36 // 20.... the VP8L bytes 37 // Or, 38 // 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk. 39 // 16..19 size of the VP8X chunk starting at offset 20. 40 // 20..23 VP8X flags bit-map corresponding to the chunk-types present. 41 // 24..26 Width of the Canvas Image. 42 // 27..29 Height of the Canvas Image. 43 // There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8, 44 // VP8L, XMP, EXIF ...) 45 // All sizes are in little-endian order. 46 // Note: chunk data size must be padded to multiple of 2 when written. 47 48 static WEBP_INLINE uint32_t get_le24(const uint8_t* const data) { 49 return data[0] | (data[1] << 8) | (data[2] << 16); 50 } 51 52 static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) { 53 return (uint32_t)get_le24(data) | (data[3] << 24); 54 } 55 56 // Validates the RIFF container (if detected) and skips over it. 57 // If a RIFF container is detected, 58 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and 59 // VP8_STATUS_OK otherwise. 60 // In case there are not enough bytes (partial RIFF container), return 0 for 61 // *riff_size. Else return the RIFF size extracted from the header. 62 static VP8StatusCode ParseRIFF(const uint8_t** const data, 63 size_t* const data_size, 64 size_t* const riff_size) { 65 assert(data != NULL); 66 assert(data_size != NULL); 67 assert(riff_size != NULL); 68 69 *riff_size = 0; // Default: no RIFF present. 70 if (*data_size >= RIFF_HEADER_SIZE && !memcmp(*data, "RIFF", TAG_SIZE)) { 71 if (memcmp(*data + 8, "WEBP", TAG_SIZE)) { 72 return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature. 73 } else { 74 const uint32_t size = get_le32(*data + TAG_SIZE); 75 // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn"). 76 if (size < TAG_SIZE + CHUNK_HEADER_SIZE) { 77 return VP8_STATUS_BITSTREAM_ERROR; 78 } 79 if (size > MAX_CHUNK_PAYLOAD) { 80 return VP8_STATUS_BITSTREAM_ERROR; 81 } 82 // We have a RIFF container. Skip it. 83 *riff_size = size; 84 *data += RIFF_HEADER_SIZE; 85 *data_size -= RIFF_HEADER_SIZE; 86 } 87 } 88 return VP8_STATUS_OK; 89 } 90 91 // Validates the VP8X header and skips over it. 92 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header, 93 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and 94 // VP8_STATUS_OK otherwise. 95 // If a VP8X chunk is found, found_vp8x is set to true and *width_ptr, 96 // *height_ptr and *flags_ptr are set to the corresponding values extracted 97 // from the VP8X chunk. 98 static VP8StatusCode ParseVP8X(const uint8_t** const data, 99 size_t* const data_size, 100 int* const found_vp8x, 101 int* const width_ptr, int* const height_ptr, 102 uint32_t* const flags_ptr) { 103 const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; 104 assert(data != NULL); 105 assert(data_size != NULL); 106 assert(found_vp8x != NULL); 107 108 *found_vp8x = 0; 109 110 if (*data_size < CHUNK_HEADER_SIZE) { 111 return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. 112 } 113 114 if (!memcmp(*data, "VP8X", TAG_SIZE)) { 115 int width, height; 116 uint32_t flags; 117 const uint32_t chunk_size = get_le32(*data + TAG_SIZE); 118 if (chunk_size != VP8X_CHUNK_SIZE) { 119 return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size. 120 } 121 122 // Verify if enough data is available to validate the VP8X chunk. 123 if (*data_size < vp8x_size) { 124 return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. 125 } 126 flags = get_le32(*data + 8); 127 width = 1 + get_le24(*data + 12); 128 height = 1 + get_le24(*data + 15); 129 if (width * (uint64_t)height >= MAX_IMAGE_AREA) { 130 return VP8_STATUS_BITSTREAM_ERROR; // image is too large 131 } 132 133 if (flags_ptr != NULL) *flags_ptr = flags; 134 if (width_ptr != NULL) *width_ptr = width; 135 if (height_ptr != NULL) *height_ptr = height; 136 // Skip over VP8X header bytes. 137 *data += vp8x_size; 138 *data_size -= vp8x_size; 139 *found_vp8x = 1; 140 } 141 return VP8_STATUS_OK; 142 } 143 144 // Skips to the next VP8/VP8L chunk header in the data given the size of the 145 // RIFF chunk 'riff_size'. 146 // Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered, 147 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and 148 // VP8_STATUS_OK otherwise. 149 // If an alpha chunk is found, *alpha_data and *alpha_size are set 150 // appropriately. 151 static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, 152 size_t* const data_size, 153 size_t const riff_size, 154 const uint8_t** const alpha_data, 155 size_t* const alpha_size) { 156 const uint8_t* buf; 157 size_t buf_size; 158 uint32_t total_size = TAG_SIZE + // "WEBP". 159 CHUNK_HEADER_SIZE + // "VP8Xnnnn". 160 VP8X_CHUNK_SIZE; // data. 161 assert(data != NULL); 162 assert(data_size != NULL); 163 buf = *data; 164 buf_size = *data_size; 165 166 assert(alpha_data != NULL); 167 assert(alpha_size != NULL); 168 *alpha_data = NULL; 169 *alpha_size = 0; 170 171 while (1) { 172 uint32_t chunk_size; 173 uint32_t disk_chunk_size; // chunk_size with padding 174 175 *data = buf; 176 *data_size = buf_size; 177 178 if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data. 179 return VP8_STATUS_NOT_ENOUGH_DATA; 180 } 181 182 chunk_size = get_le32(buf + TAG_SIZE); 183 if (chunk_size > MAX_CHUNK_PAYLOAD) { 184 return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. 185 } 186 // For odd-sized chunk-payload, there's one byte padding at the end. 187 disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1; 188 total_size += disk_chunk_size; 189 190 // Check that total bytes skipped so far does not exceed riff_size. 191 if (riff_size > 0 && (total_size > riff_size)) { 192 return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. 193 } 194 195 if (buf_size < disk_chunk_size) { // Insufficient data. 196 return VP8_STATUS_NOT_ENOUGH_DATA; 197 } 198 199 if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header. 200 *alpha_data = buf + CHUNK_HEADER_SIZE; 201 *alpha_size = chunk_size; 202 } else if (!memcmp(buf, "VP8 ", TAG_SIZE) || 203 !memcmp(buf, "VP8L", TAG_SIZE)) { // A valid VP8/VP8L header. 204 return VP8_STATUS_OK; // Found. 205 } 206 207 // We have a full and valid chunk; skip it. 208 buf += disk_chunk_size; 209 buf_size -= disk_chunk_size; 210 } 211 } 212 213 // Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it. 214 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than 215 // riff_size) VP8/VP8L header, 216 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and 217 // VP8_STATUS_OK otherwise. 218 // If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes 219 // extracted from the VP8/VP8L chunk header. 220 // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data. 221 static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, 222 size_t* const data_size, 223 size_t riff_size, 224 size_t* const chunk_size, 225 int* const is_lossless) { 226 const uint8_t* const data = *data_ptr; 227 const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE); 228 const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE); 229 const uint32_t minimal_size = 230 TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR 231 // "WEBP" + "VP8Lnnnn" 232 assert(data != NULL); 233 assert(data_size != NULL); 234 assert(chunk_size != NULL); 235 assert(is_lossless != NULL); 236 237 if (*data_size < CHUNK_HEADER_SIZE) { 238 return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. 239 } 240 241 if (is_vp8 || is_vp8l) { 242 // Bitstream contains VP8/VP8L header. 243 const uint32_t size = get_le32(data + TAG_SIZE); 244 if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) { 245 return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. 246 } 247 // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header. 248 *chunk_size = size; 249 *data_ptr += CHUNK_HEADER_SIZE; 250 *data_size -= CHUNK_HEADER_SIZE; 251 *is_lossless = is_vp8l; 252 } else { 253 // Raw VP8/VP8L bitstream (no header). 254 *is_lossless = VP8LCheckSignature(data, *data_size); 255 *chunk_size = *data_size; 256 } 257 258 return VP8_STATUS_OK; 259 } 260 261 //------------------------------------------------------------------------------ 262 263 // Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on 264 // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the 265 // minimal amount will be read to fetch the remaining parameters. 266 // If 'headers' is non-NULL this function will attempt to locate both alpha 267 // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L). 268 // Note: The following chunk sequences (before the raw VP8/VP8L data) are 269 // considered valid by this function: 270 // RIFF + VP8(L) 271 // RIFF + VP8X + (optional chunks) + VP8(L) 272 // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. 273 // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. 274 static VP8StatusCode ParseHeadersInternal(const uint8_t* data, 275 size_t data_size, 276 int* const width, 277 int* const height, 278 int* const has_alpha, 279 WebPHeaderStructure* const headers) { 280 int found_riff = 0; 281 int found_vp8x = 0; 282 VP8StatusCode status; 283 WebPHeaderStructure hdrs; 284 285 if (data == NULL || data_size < RIFF_HEADER_SIZE) { 286 return VP8_STATUS_NOT_ENOUGH_DATA; 287 } 288 memset(&hdrs, 0, sizeof(hdrs)); 289 hdrs.data = data; 290 hdrs.data_size = data_size; 291 292 // Skip over RIFF header. 293 status = ParseRIFF(&data, &data_size, &hdrs.riff_size); 294 if (status != VP8_STATUS_OK) { 295 return status; // Wrong RIFF header / insufficient data. 296 } 297 found_riff = (hdrs.riff_size > 0); 298 299 // Skip over VP8X. 300 { 301 uint32_t flags = 0; 302 status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags); 303 if (status != VP8_STATUS_OK) { 304 return status; // Wrong VP8X / insufficient data. 305 } 306 if (!found_riff && found_vp8x) { 307 // Note: This restriction may be removed in the future, if it becomes 308 // necessary to send VP8X chunk to the decoder. 309 return VP8_STATUS_BITSTREAM_ERROR; 310 } 311 if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG); 312 if (found_vp8x && headers == NULL) { 313 return VP8_STATUS_OK; // Return features from VP8X header. 314 } 315 } 316 317 if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA; 318 319 // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". 320 if ((found_riff && found_vp8x) || 321 (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { 322 status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, 323 &hdrs.alpha_data, &hdrs.alpha_data_size); 324 if (status != VP8_STATUS_OK) { 325 return status; // Found an invalid chunk size / insufficient data. 326 } 327 } 328 329 // Skip over VP8/VP8L header. 330 status = ParseVP8Header(&data, &data_size, hdrs.riff_size, 331 &hdrs.compressed_size, &hdrs.is_lossless); 332 if (status != VP8_STATUS_OK) { 333 return status; // Wrong VP8/VP8L chunk-header / insufficient data. 334 } 335 if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { 336 return VP8_STATUS_BITSTREAM_ERROR; 337 } 338 339 if (!hdrs.is_lossless) { 340 if (data_size < VP8_FRAME_HEADER_SIZE) { 341 return VP8_STATUS_NOT_ENOUGH_DATA; 342 } 343 // Validates raw VP8 data. 344 if (!VP8GetInfo(data, data_size, 345 (uint32_t)hdrs.compressed_size, width, height)) { 346 return VP8_STATUS_BITSTREAM_ERROR; 347 } 348 } else { 349 if (data_size < VP8L_FRAME_HEADER_SIZE) { 350 return VP8_STATUS_NOT_ENOUGH_DATA; 351 } 352 // Validates raw VP8L data. 353 if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) { 354 return VP8_STATUS_BITSTREAM_ERROR; 355 } 356 } 357 358 if (has_alpha != NULL) { 359 // If the data did not contain a VP8X/VP8L chunk the only definitive way 360 // to set this is by looking for alpha data (from an ALPH chunk). 361 *has_alpha |= (hdrs.alpha_data != NULL); 362 } 363 if (headers != NULL) { 364 *headers = hdrs; 365 headers->offset = data - headers->data; 366 assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); 367 assert(headers->offset == headers->data_size - data_size); 368 } 369 return VP8_STATUS_OK; // Return features from VP8 header. 370 } 371 372 VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) { 373 assert(headers != NULL); 374 // fill out headers, ignore width/height/has_alpha. 375 return ParseHeadersInternal(headers->data, headers->data_size, 376 NULL, NULL, NULL, headers); 377 } 378 379 //------------------------------------------------------------------------------ 380 // WebPDecParams 381 382 void WebPResetDecParams(WebPDecParams* const params) { 383 if (params) { 384 memset(params, 0, sizeof(*params)); 385 } 386 } 387 388 //------------------------------------------------------------------------------ 389 // "Into" decoding variants 390 391 // Main flow 392 static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, 393 WebPDecParams* const params) { 394 VP8StatusCode status; 395 VP8Io io; 396 WebPHeaderStructure headers; 397 398 headers.data = data; 399 headers.data_size = data_size; 400 status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks. 401 if (status != VP8_STATUS_OK) { 402 return status; 403 } 404 405 assert(params != NULL); 406 VP8InitIo(&io); 407 io.data = headers.data + headers.offset; 408 io.data_size = headers.data_size - headers.offset; 409 WebPInitCustomIo(params, &io); // Plug the I/O functions. 410 411 if (!headers.is_lossless) { 412 VP8Decoder* const dec = VP8New(); 413 if (dec == NULL) { 414 return VP8_STATUS_OUT_OF_MEMORY; 415 } 416 #ifdef WEBP_USE_THREAD 417 dec->use_threads_ = params->options && (params->options->use_threads > 0); 418 #else 419 dec->use_threads_ = 0; 420 #endif 421 dec->alpha_data_ = headers.alpha_data; 422 dec->alpha_data_size_ = headers.alpha_data_size; 423 424 // Decode bitstream header, update io->width/io->height. 425 if (!VP8GetHeaders(dec, &io)) { 426 status = dec->status_; // An error occurred. Grab error status. 427 } else { 428 // Allocate/check output buffers. 429 status = WebPAllocateDecBuffer(io.width, io.height, params->options, 430 params->output); 431 if (status == VP8_STATUS_OK) { // Decode 432 if (!VP8Decode(dec, &io)) { 433 status = dec->status_; 434 } 435 } 436 } 437 VP8Delete(dec); 438 } else { 439 VP8LDecoder* const dec = VP8LNew(); 440 if (dec == NULL) { 441 return VP8_STATUS_OUT_OF_MEMORY; 442 } 443 if (!VP8LDecodeHeader(dec, &io)) { 444 status = dec->status_; // An error occurred. Grab error status. 445 } else { 446 // Allocate/check output buffers. 447 status = WebPAllocateDecBuffer(io.width, io.height, params->options, 448 params->output); 449 if (status == VP8_STATUS_OK) { // Decode 450 if (!VP8LDecodeImage(dec)) { 451 status = dec->status_; 452 } 453 } 454 } 455 VP8LDelete(dec); 456 } 457 458 if (status != VP8_STATUS_OK) { 459 WebPFreeDecBuffer(params->output); 460 } 461 return status; 462 } 463 464 // Helpers 465 static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace, 466 const uint8_t* const data, 467 size_t data_size, 468 uint8_t* const rgba, 469 int stride, size_t size) { 470 WebPDecParams params; 471 WebPDecBuffer buf; 472 if (rgba == NULL) { 473 return NULL; 474 } 475 WebPInitDecBuffer(&buf); 476 WebPResetDecParams(¶ms); 477 params.output = &buf; 478 buf.colorspace = colorspace; 479 buf.u.RGBA.rgba = rgba; 480 buf.u.RGBA.stride = stride; 481 buf.u.RGBA.size = size; 482 buf.is_external_memory = 1; 483 if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { 484 return NULL; 485 } 486 return rgba; 487 } 488 489 uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size, 490 uint8_t* output, size_t size, int stride) { 491 return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size); 492 } 493 494 uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size, 495 uint8_t* output, size_t size, int stride) { 496 return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size); 497 } 498 499 uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size, 500 uint8_t* output, size_t size, int stride) { 501 return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size); 502 } 503 504 uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size, 505 uint8_t* output, size_t size, int stride) { 506 return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size); 507 } 508 509 uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size, 510 uint8_t* output, size_t size, int stride) { 511 return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size); 512 } 513 514 uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size, 515 uint8_t* luma, size_t luma_size, int luma_stride, 516 uint8_t* u, size_t u_size, int u_stride, 517 uint8_t* v, size_t v_size, int v_stride) { 518 WebPDecParams params; 519 WebPDecBuffer output; 520 if (luma == NULL) return NULL; 521 WebPInitDecBuffer(&output); 522 WebPResetDecParams(¶ms); 523 params.output = &output; 524 output.colorspace = MODE_YUV; 525 output.u.YUVA.y = luma; 526 output.u.YUVA.y_stride = luma_stride; 527 output.u.YUVA.y_size = luma_size; 528 output.u.YUVA.u = u; 529 output.u.YUVA.u_stride = u_stride; 530 output.u.YUVA.u_size = u_size; 531 output.u.YUVA.v = v; 532 output.u.YUVA.v_stride = v_stride; 533 output.u.YUVA.v_size = v_size; 534 output.is_external_memory = 1; 535 if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { 536 return NULL; 537 } 538 return luma; 539 } 540 541 //------------------------------------------------------------------------------ 542 543 static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data, 544 size_t data_size, int* const width, int* const height, 545 WebPDecBuffer* const keep_info) { 546 WebPDecParams params; 547 WebPDecBuffer output; 548 549 WebPInitDecBuffer(&output); 550 WebPResetDecParams(¶ms); 551 params.output = &output; 552 output.colorspace = mode; 553 554 // Retrieve (and report back) the required dimensions from bitstream. 555 if (!WebPGetInfo(data, data_size, &output.width, &output.height)) { 556 return NULL; 557 } 558 if (width != NULL) *width = output.width; 559 if (height != NULL) *height = output.height; 560 561 // Decode 562 if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { 563 return NULL; 564 } 565 if (keep_info != NULL) { // keep track of the side-info 566 WebPCopyDecBuffer(&output, keep_info); 567 } 568 // return decoded samples (don't clear 'output'!) 569 return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y; 570 } 571 572 uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size, 573 int* width, int* height) { 574 return Decode(MODE_RGB, data, data_size, width, height, NULL); 575 } 576 577 uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size, 578 int* width, int* height) { 579 return Decode(MODE_RGBA, data, data_size, width, height, NULL); 580 } 581 582 uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size, 583 int* width, int* height) { 584 return Decode(MODE_ARGB, data, data_size, width, height, NULL); 585 } 586 587 uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, 588 int* width, int* height) { 589 return Decode(MODE_BGR, data, data_size, width, height, NULL); 590 } 591 592 uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, 593 int* width, int* height) { 594 return Decode(MODE_BGRA, data, data_size, width, height, NULL); 595 } 596 597 uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size, 598 int* width, int* height, uint8_t** u, uint8_t** v, 599 int* stride, int* uv_stride) { 600 WebPDecBuffer output; // only to preserve the side-infos 601 uint8_t* const out = Decode(MODE_YUV, data, data_size, 602 width, height, &output); 603 604 if (out != NULL) { 605 const WebPYUVABuffer* const buf = &output.u.YUVA; 606 *u = buf->u; 607 *v = buf->v; 608 *stride = buf->y_stride; 609 *uv_stride = buf->u_stride; 610 assert(buf->u_stride == buf->v_stride); 611 } 612 return out; 613 } 614 615 static void DefaultFeatures(WebPBitstreamFeatures* const features) { 616 assert(features != NULL); 617 memset(features, 0, sizeof(*features)); 618 features->bitstream_version = 0; 619 } 620 621 static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size, 622 WebPBitstreamFeatures* const features) { 623 if (features == NULL || data == NULL) { 624 return VP8_STATUS_INVALID_PARAM; 625 } 626 DefaultFeatures(features); 627 628 // Only parse enough of the data to retrieve width/height/has_alpha. 629 return ParseHeadersInternal(data, data_size, 630 &features->width, &features->height, 631 &features->has_alpha, NULL); 632 } 633 634 //------------------------------------------------------------------------------ 635 // WebPGetInfo() 636 637 int WebPGetInfo(const uint8_t* data, size_t data_size, 638 int* width, int* height) { 639 WebPBitstreamFeatures features; 640 641 if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) { 642 return 0; 643 } 644 645 if (width != NULL) { 646 *width = features.width; 647 } 648 if (height != NULL) { 649 *height = features.height; 650 } 651 652 return 1; 653 } 654 655 //------------------------------------------------------------------------------ 656 // Advance decoding API 657 658 int WebPInitDecoderConfigInternal(WebPDecoderConfig* config, 659 int version) { 660 if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { 661 return 0; // version mismatch 662 } 663 if (config == NULL) { 664 return 0; 665 } 666 memset(config, 0, sizeof(*config)); 667 DefaultFeatures(&config->input); 668 WebPInitDecBuffer(&config->output); 669 return 1; 670 } 671 672 VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size, 673 WebPBitstreamFeatures* features, 674 int version) { 675 if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { 676 return VP8_STATUS_INVALID_PARAM; // version mismatch 677 } 678 if (features == NULL) { 679 return VP8_STATUS_INVALID_PARAM; 680 } 681 return GetFeatures(data, data_size, features); 682 } 683 684 VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, 685 WebPDecoderConfig* config) { 686 WebPDecParams params; 687 VP8StatusCode status; 688 689 if (config == NULL) { 690 return VP8_STATUS_INVALID_PARAM; 691 } 692 693 status = GetFeatures(data, data_size, &config->input); 694 if (status != VP8_STATUS_OK) { 695 if (status == VP8_STATUS_NOT_ENOUGH_DATA) { 696 return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error. 697 } 698 return status; 699 } 700 701 WebPResetDecParams(¶ms); 702 params.output = &config->output; 703 params.options = &config->options; 704 status = DecodeInto(data, data_size, ¶ms); 705 706 return status; 707 } 708 709 //------------------------------------------------------------------------------ 710 // Cropping and rescaling. 711 712 int WebPIoInitFromOptions(const WebPDecoderOptions* const options, 713 VP8Io* const io, WEBP_CSP_MODE src_colorspace) { 714 const int W = io->width; 715 const int H = io->height; 716 int x = 0, y = 0, w = W, h = H; 717 718 // Cropping 719 io->use_cropping = (options != NULL) && (options->use_cropping > 0); 720 if (io->use_cropping) { 721 w = options->crop_width; 722 h = options->crop_height; 723 x = options->crop_left; 724 y = options->crop_top; 725 if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 or YUV422 726 x &= ~1; 727 y &= ~1; // TODO(later): only for YUV420, not YUV422. 728 } 729 if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) { 730 return 0; // out of frame boundary error 731 } 732 } 733 io->crop_left = x; 734 io->crop_top = y; 735 io->crop_right = x + w; 736 io->crop_bottom = y + h; 737 io->mb_w = w; 738 io->mb_h = h; 739 740 // Scaling 741 io->use_scaling = (options != NULL) && (options->use_scaling > 0); 742 if (io->use_scaling) { 743 if (options->scaled_width <= 0 || options->scaled_height <= 0) { 744 return 0; 745 } 746 io->scaled_width = options->scaled_width; 747 io->scaled_height = options->scaled_height; 748 } 749 750 // Filter 751 io->bypass_filtering = options && options->bypass_filtering; 752 753 // Fancy upsampler 754 #ifdef FANCY_UPSAMPLING 755 io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling); 756 #endif 757 758 if (io->use_scaling) { 759 // disable filter (only for large downscaling ratio). 760 io->bypass_filtering = (io->scaled_width < W * 3 / 4) && 761 (io->scaled_height < H * 3 / 4); 762 io->fancy_upsampling = 0; 763 } 764 return 1; 765 } 766 767 //------------------------------------------------------------------------------ 768 769 #if defined(__cplusplus) || defined(c_plusplus) 770 } // extern "C" 771 #endif 772