webp_dec.c (33243B)
1 // Copyright 2010 Google Inc. All Rights Reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the COPYING file in the root of the source 5 // tree. An additional intellectual property rights grant can be found 6 // in the file PATENTS. All contributing project authors may 7 // be found in the AUTHORS file in the root of the source tree. 8 // ----------------------------------------------------------------------------- 9 // 10 // Main decoding functions for WEBP images. 11 // 12 // Author: Skal (pascal.massimino@gmail.com) 13 14 #include <assert.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include "src/dec/common_dec.h" 19 #include "src/dec/vp8_dec.h" 20 #include "src/dec/vp8i_dec.h" 21 #include "src/dec/vp8li_dec.h" 22 #include "src/dec/webpi_dec.h" 23 #include "src/utils/rescaler_utils.h" 24 #include "src/utils/utils.h" 25 #include "src/webp/decode.h" 26 #include "src/webp/format_constants.h" 27 #include "src/webp/mux_types.h" // ALPHA_FLAG 28 #include "src/webp/types.h" 29 30 //------------------------------------------------------------------------------ 31 // RIFF layout is: 32 // Offset tag 33 // 0...3 "RIFF" 4-byte tag 34 // 4...7 size of image data (including metadata) starting at offset 8 35 // 8...11 "WEBP" our form-type signature 36 // The RIFF container (12 bytes) is followed by appropriate chunks: 37 // 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format 38 // 16..19 size of the raw VP8 image data, starting at offset 20 39 // 20.... the VP8 bytes 40 // Or, 41 // 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format 42 // 16..19 size of the raw VP8L image data, starting at offset 20 43 // 20.... the VP8L bytes 44 // Or, 45 // 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk. 46 // 16..19 size of the VP8X chunk starting at offset 20. 47 // 20..23 VP8X flags bit-map corresponding to the chunk-types present. 48 // 24..26 Width of the Canvas Image. 49 // 27..29 Height of the Canvas Image. 50 // There can be extra chunks after the "VP8X" chunk (ICCP, ANMF, VP8, VP8L, 51 // XMP, EXIF ...) 52 // All sizes are in little-endian order. 53 // Note: chunk data size must be padded to multiple of 2 when written. 54 55 // Validates the RIFF container (if detected) and skips over it. 56 // If a RIFF container is detected, returns: 57 // VP8_STATUS_BITSTREAM_ERROR for invalid header, 58 // VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true, 59 // and 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, int have_all_data, 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 = GetLE32(*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 if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { 83 return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. 84 } 85 // We have a RIFF container. Skip it. 86 *riff_size = size; 87 *data += RIFF_HEADER_SIZE; 88 *data_size -= RIFF_HEADER_SIZE; 89 } 90 } 91 return VP8_STATUS_OK; 92 } 93 94 // Validates the VP8X header and skips over it. 95 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header, 96 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and 97 // VP8_STATUS_OK otherwise. 98 // If a VP8X chunk is found, found_vp8x is set to true and *width_ptr, 99 // *height_ptr and *flags_ptr are set to the corresponding values extracted 100 // from the VP8X chunk. 101 static VP8StatusCode ParseVP8X(const uint8_t** const data, 102 size_t* const data_size, 103 int* const found_vp8x, 104 int* const width_ptr, int* const height_ptr, 105 uint32_t* const flags_ptr) { 106 const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; 107 assert(data != NULL); 108 assert(data_size != NULL); 109 assert(found_vp8x != NULL); 110 111 *found_vp8x = 0; 112 113 if (*data_size < CHUNK_HEADER_SIZE) { 114 return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. 115 } 116 117 if (!memcmp(*data, "VP8X", TAG_SIZE)) { 118 int width, height; 119 uint32_t flags; 120 const uint32_t chunk_size = GetLE32(*data + TAG_SIZE); 121 if (chunk_size != VP8X_CHUNK_SIZE) { 122 return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size. 123 } 124 125 // Verify if enough data is available to validate the VP8X chunk. 126 if (*data_size < vp8x_size) { 127 return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. 128 } 129 flags = GetLE32(*data + 8); 130 width = 1 + GetLE24(*data + 12); 131 height = 1 + GetLE24(*data + 15); 132 if (width * (uint64_t)height >= MAX_IMAGE_AREA) { 133 return VP8_STATUS_BITSTREAM_ERROR; // image is too large 134 } 135 136 if (flags_ptr != NULL) *flags_ptr = flags; 137 if (width_ptr != NULL) *width_ptr = width; 138 if (height_ptr != NULL) *height_ptr = height; 139 // Skip over VP8X header bytes. 140 *data += vp8x_size; 141 *data_size -= vp8x_size; 142 *found_vp8x = 1; 143 } 144 return VP8_STATUS_OK; 145 } 146 147 // Skips to the next VP8/VP8L chunk header in the data given the size of the 148 // RIFF chunk 'riff_size'. 149 // Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered, 150 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and 151 // VP8_STATUS_OK otherwise. 152 // If an alpha chunk is found, *alpha_data and *alpha_size are set 153 // appropriately. 154 static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, 155 size_t* const data_size, 156 size_t const riff_size, 157 const uint8_t** const alpha_data, 158 size_t* const alpha_size) { 159 const uint8_t* buf; 160 size_t buf_size; 161 uint32_t total_size = TAG_SIZE + // "WEBP". 162 CHUNK_HEADER_SIZE + // "VP8Xnnnn". 163 VP8X_CHUNK_SIZE; // data. 164 assert(data != NULL); 165 assert(data_size != NULL); 166 buf = *data; 167 buf_size = *data_size; 168 169 assert(alpha_data != NULL); 170 assert(alpha_size != NULL); 171 *alpha_data = NULL; 172 *alpha_size = 0; 173 174 while (1) { 175 uint32_t chunk_size; 176 uint32_t disk_chunk_size; // chunk_size with padding 177 178 *data = buf; 179 *data_size = buf_size; 180 181 if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data. 182 return VP8_STATUS_NOT_ENOUGH_DATA; 183 } 184 185 chunk_size = GetLE32(buf + TAG_SIZE); 186 if (chunk_size > MAX_CHUNK_PAYLOAD) { 187 return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. 188 } 189 // For odd-sized chunk-payload, there's one byte padding at the end. 190 disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1u; 191 total_size += disk_chunk_size; 192 193 // Check that total bytes skipped so far does not exceed riff_size. 194 if (riff_size > 0 && (total_size > riff_size)) { 195 return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. 196 } 197 198 // Start of a (possibly incomplete) VP8/VP8L chunk implies that we have 199 // parsed all the optional chunks. 200 // Note: This check must occur before the check 'buf_size < disk_chunk_size' 201 // below to allow incomplete VP8/VP8L chunks. 202 if (!memcmp(buf, "VP8 ", TAG_SIZE) || 203 !memcmp(buf, "VP8L", TAG_SIZE)) { 204 return VP8_STATUS_OK; 205 } 206 207 if (buf_size < disk_chunk_size) { // Insufficient data. 208 return VP8_STATUS_NOT_ENOUGH_DATA; 209 } 210 211 if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header. 212 *alpha_data = buf + CHUNK_HEADER_SIZE; 213 *alpha_size = chunk_size; 214 } 215 216 // We have a full and valid chunk; skip it. 217 buf += disk_chunk_size; 218 buf_size -= disk_chunk_size; 219 } 220 } 221 222 // Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it. 223 // Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than 224 // riff_size) VP8/VP8L header, 225 // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and 226 // VP8_STATUS_OK otherwise. 227 // If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes 228 // extracted from the VP8/VP8L chunk header. 229 // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data. 230 static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, 231 size_t* const data_size, int have_all_data, 232 size_t riff_size, size_t* const chunk_size, 233 int* const is_lossless) { 234 const uint8_t* const data = *data_ptr; 235 const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE); 236 const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE); 237 const uint32_t minimal_size = 238 TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR 239 // "WEBP" + "VP8Lnnnn" 240 assert(data != NULL); 241 assert(data_size != NULL); 242 assert(chunk_size != NULL); 243 assert(is_lossless != NULL); 244 245 if (*data_size < CHUNK_HEADER_SIZE) { 246 return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. 247 } 248 249 if (is_vp8 || is_vp8l) { 250 // Bitstream contains VP8/VP8L header. 251 const uint32_t size = GetLE32(data + TAG_SIZE); 252 if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) { 253 return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. 254 } 255 if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { 256 return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. 257 } 258 // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header. 259 *chunk_size = size; 260 *data_ptr += CHUNK_HEADER_SIZE; 261 *data_size -= CHUNK_HEADER_SIZE; 262 *is_lossless = is_vp8l; 263 } else { 264 // Raw VP8/VP8L bitstream (no header). 265 *is_lossless = VP8LCheckSignature(data, *data_size); 266 *chunk_size = *data_size; 267 } 268 269 return VP8_STATUS_OK; 270 } 271 272 //------------------------------------------------------------------------------ 273 274 // Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on 275 // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the 276 // minimal amount will be read to fetch the remaining parameters. 277 // If 'headers' is non-NULL this function will attempt to locate both alpha 278 // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L). 279 // Note: The following chunk sequences (before the raw VP8/VP8L data) are 280 // considered valid by this function: 281 // RIFF + VP8(L) 282 // RIFF + VP8X + (optional chunks) + VP8(L) 283 // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. 284 // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. 285 static VP8StatusCode ParseHeadersInternal(const uint8_t* data, 286 size_t data_size, 287 int* const width, 288 int* const height, 289 int* const has_alpha, 290 int* const has_animation, 291 int* const format, 292 WebPHeaderStructure* const headers) { 293 int canvas_width = 0; 294 int canvas_height = 0; 295 int image_width = 0; 296 int image_height = 0; 297 int found_riff = 0; 298 int found_vp8x = 0; 299 int animation_present = 0; 300 const int have_all_data = (headers != NULL) ? headers->have_all_data : 0; 301 302 VP8StatusCode status; 303 WebPHeaderStructure hdrs; 304 305 if (data == NULL || data_size < RIFF_HEADER_SIZE) { 306 return VP8_STATUS_NOT_ENOUGH_DATA; 307 } 308 memset(&hdrs, 0, sizeof(hdrs)); 309 hdrs.data = data; 310 hdrs.data_size = data_size; 311 312 // Skip over RIFF header. 313 status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size); 314 if (status != VP8_STATUS_OK) { 315 return status; // Wrong RIFF header / insufficient data. 316 } 317 found_riff = (hdrs.riff_size > 0); 318 319 // Skip over VP8X. 320 { 321 uint32_t flags = 0; 322 status = ParseVP8X(&data, &data_size, &found_vp8x, 323 &canvas_width, &canvas_height, &flags); 324 if (status != VP8_STATUS_OK) { 325 return status; // Wrong VP8X / insufficient data. 326 } 327 animation_present = !!(flags & ANIMATION_FLAG); 328 if (!found_riff && found_vp8x) { 329 // Note: This restriction may be removed in the future, if it becomes 330 // necessary to send VP8X chunk to the decoder. 331 return VP8_STATUS_BITSTREAM_ERROR; 332 } 333 if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG); 334 if (has_animation != NULL) *has_animation = animation_present; 335 if (format != NULL) *format = 0; // default = undefined 336 337 image_width = canvas_width; 338 image_height = canvas_height; 339 if (found_vp8x && animation_present && headers == NULL) { 340 status = VP8_STATUS_OK; 341 goto ReturnWidthHeight; // Just return features from VP8X header. 342 } 343 } 344 345 if (data_size < TAG_SIZE) { 346 status = VP8_STATUS_NOT_ENOUGH_DATA; 347 goto ReturnWidthHeight; 348 } 349 350 // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". 351 if ((found_riff && found_vp8x) || 352 (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { 353 status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, 354 &hdrs.alpha_data, &hdrs.alpha_data_size); 355 if (status != VP8_STATUS_OK) { 356 goto ReturnWidthHeight; // Invalid chunk size / insufficient data. 357 } 358 } 359 360 // Skip over VP8/VP8L header. 361 status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size, 362 &hdrs.compressed_size, &hdrs.is_lossless); 363 if (status != VP8_STATUS_OK) { 364 goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data. 365 } 366 if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { 367 return VP8_STATUS_BITSTREAM_ERROR; 368 } 369 370 if (format != NULL && !animation_present) { 371 *format = hdrs.is_lossless ? 2 : 1; 372 } 373 374 if (!hdrs.is_lossless) { 375 if (data_size < VP8_FRAME_HEADER_SIZE) { 376 status = VP8_STATUS_NOT_ENOUGH_DATA; 377 goto ReturnWidthHeight; 378 } 379 // Validates raw VP8 data. 380 if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size, 381 &image_width, &image_height)) { 382 return VP8_STATUS_BITSTREAM_ERROR; 383 } 384 } else { 385 if (data_size < VP8L_FRAME_HEADER_SIZE) { 386 status = VP8_STATUS_NOT_ENOUGH_DATA; 387 goto ReturnWidthHeight; 388 } 389 // Validates raw VP8L data. 390 if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) { 391 return VP8_STATUS_BITSTREAM_ERROR; 392 } 393 } 394 // Validates image size coherency. 395 if (found_vp8x) { 396 if (canvas_width != image_width || canvas_height != image_height) { 397 return VP8_STATUS_BITSTREAM_ERROR; 398 } 399 } 400 if (headers != NULL) { 401 *headers = hdrs; 402 headers->offset = data - headers->data; 403 assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); 404 assert(headers->offset == headers->data_size - data_size); 405 } 406 ReturnWidthHeight: 407 if (status == VP8_STATUS_OK || 408 (status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) { 409 if (has_alpha != NULL) { 410 // If the data did not contain a VP8X/VP8L chunk the only definitive way 411 // to set this is by looking for alpha data (from an ALPH chunk). 412 *has_alpha |= (hdrs.alpha_data != NULL); 413 } 414 if (width != NULL) *width = image_width; 415 if (height != NULL) *height = image_height; 416 return VP8_STATUS_OK; 417 } else { 418 return status; 419 } 420 } 421 422 VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) { 423 // status is marked volatile as a workaround for a clang-3.8 (aarch64) bug 424 volatile VP8StatusCode status; 425 int has_animation = 0; 426 assert(headers != NULL); 427 // fill out headers, ignore width/height/has_alpha. 428 status = ParseHeadersInternal(headers->data, headers->data_size, 429 NULL, NULL, NULL, &has_animation, 430 NULL, headers); 431 if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) { 432 // The WebPDemux API + libwebp can be used to decode individual 433 // uncomposited frames or the WebPAnimDecoder can be used to fully 434 // reconstruct them (see webp/demux.h). 435 if (has_animation) { 436 status = VP8_STATUS_UNSUPPORTED_FEATURE; 437 } 438 } 439 return status; 440 } 441 442 //------------------------------------------------------------------------------ 443 // WebPDecParams 444 445 void WebPResetDecParams(WebPDecParams* const params) { 446 if (params != NULL) { 447 memset(params, 0, sizeof(*params)); 448 } 449 } 450 451 //------------------------------------------------------------------------------ 452 // "Into" decoding variants 453 454 // Main flow 455 WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data, 456 size_t data_size, 457 WebPDecParams* const params) { 458 VP8StatusCode status; 459 VP8Io io; 460 WebPHeaderStructure headers; 461 462 headers.data = data; 463 headers.data_size = data_size; 464 headers.have_all_data = 1; 465 status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks. 466 if (status != VP8_STATUS_OK) { 467 return status; 468 } 469 470 assert(params != NULL); 471 if (!VP8InitIo(&io)) { 472 return VP8_STATUS_INVALID_PARAM; 473 } 474 io.data = headers.data + headers.offset; 475 io.data_size = headers.data_size - headers.offset; 476 WebPInitCustomIo(params, &io); // Plug the I/O functions. 477 478 if (!headers.is_lossless) { 479 VP8Decoder* const dec = VP8New(); 480 if (dec == NULL) { 481 return VP8_STATUS_OUT_OF_MEMORY; 482 } 483 dec->alpha_data = headers.alpha_data; 484 dec->alpha_data_size = headers.alpha_data_size; 485 486 // Decode bitstream header, update io->width/io->height. 487 if (!VP8GetHeaders(dec, &io)) { 488 status = dec->status; // An error occurred. Grab error status. 489 } else { 490 // Allocate/check output buffers. 491 status = WebPAllocateDecBuffer(io.width, io.height, params->options, 492 params->output); 493 if (status == VP8_STATUS_OK) { // Decode 494 // This change must be done before calling VP8Decode() 495 dec->mt_method = VP8GetThreadMethod(params->options, &headers, 496 io.width, io.height); 497 VP8InitDithering(params->options, dec); 498 if (!VP8Decode(dec, &io)) { 499 status = dec->status; 500 } 501 } 502 } 503 VP8Delete(dec); 504 } else { 505 VP8LDecoder* const dec = VP8LNew(); 506 if (dec == NULL) { 507 return VP8_STATUS_OUT_OF_MEMORY; 508 } 509 if (!VP8LDecodeHeader(dec, &io)) { 510 status = dec->status; // An error occurred. Grab error status. 511 } else { 512 // Allocate/check output buffers. 513 status = WebPAllocateDecBuffer(io.width, io.height, params->options, 514 params->output); 515 if (status == VP8_STATUS_OK) { // Decode 516 if (!VP8LDecodeImage(dec)) { 517 status = dec->status; 518 } 519 } 520 } 521 VP8LDelete(dec); 522 } 523 524 if (status != VP8_STATUS_OK) { 525 WebPFreeDecBuffer(params->output); 526 } else { 527 if (params->options != NULL && params->options->flip) { 528 // This restores the original stride values if options->flip was used 529 // during the call to WebPAllocateDecBuffer above. 530 status = WebPFlipBuffer(params->output); 531 } 532 } 533 return status; 534 } 535 536 // Helpers 537 WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace, 538 const uint8_t* const data, 539 size_t data_size, 540 uint8_t* const rgba, 541 int stride, size_t size) { 542 WebPDecParams params; 543 WebPDecBuffer buf; 544 if (rgba == NULL || !WebPInitDecBuffer(&buf)) { 545 return NULL; 546 } 547 WebPResetDecParams(¶ms); 548 params.output = &buf; 549 buf.colorspace = colorspace; 550 buf.u.RGBA.rgba = rgba; 551 buf.u.RGBA.stride = stride; 552 buf.u.RGBA.size = size; 553 buf.is_external_memory = 1; 554 if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { 555 return NULL; 556 } 557 return rgba; 558 } 559 560 uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size, 561 uint8_t* output, size_t size, int stride) { 562 return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size); 563 } 564 565 uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size, 566 uint8_t* output, size_t size, int stride) { 567 return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size); 568 } 569 570 uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size, 571 uint8_t* output, size_t size, int stride) { 572 return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size); 573 } 574 575 uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size, 576 uint8_t* output, size_t size, int stride) { 577 return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size); 578 } 579 580 uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size, 581 uint8_t* output, size_t size, int stride) { 582 return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size); 583 } 584 585 uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size, 586 uint8_t* luma, size_t luma_size, int luma_stride, 587 uint8_t* u, size_t u_size, int u_stride, 588 uint8_t* v, size_t v_size, int v_stride) { 589 WebPDecParams params; 590 WebPDecBuffer output; 591 if (luma == NULL || !WebPInitDecBuffer(&output)) return NULL; 592 WebPResetDecParams(¶ms); 593 params.output = &output; 594 output.colorspace = MODE_YUV; 595 output.u.YUVA.y = luma; 596 output.u.YUVA.y_stride = luma_stride; 597 output.u.YUVA.y_size = luma_size; 598 output.u.YUVA.u = u; 599 output.u.YUVA.u_stride = u_stride; 600 output.u.YUVA.u_size = u_size; 601 output.u.YUVA.v = v; 602 output.u.YUVA.v_stride = v_stride; 603 output.u.YUVA.v_size = v_size; 604 output.is_external_memory = 1; 605 if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { 606 return NULL; 607 } 608 return luma; 609 } 610 611 //------------------------------------------------------------------------------ 612 613 WEBP_NODISCARD static uint8_t* Decode(WEBP_CSP_MODE mode, 614 const uint8_t* const data, 615 size_t data_size, int* const width, 616 int* const height, 617 WebPDecBuffer* const keep_info) { 618 WebPDecParams params; 619 WebPDecBuffer output; 620 621 if (!WebPInitDecBuffer(&output)) { 622 return NULL; 623 } 624 WebPResetDecParams(¶ms); 625 params.output = &output; 626 output.colorspace = mode; 627 628 // Retrieve (and report back) the required dimensions from bitstream. 629 if (!WebPGetInfo(data, data_size, &output.width, &output.height)) { 630 return NULL; 631 } 632 if (width != NULL) *width = output.width; 633 if (height != NULL) *height = output.height; 634 635 // Decode 636 if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { 637 return NULL; 638 } 639 if (keep_info != NULL) { // keep track of the side-info 640 WebPCopyDecBuffer(&output, keep_info); 641 } 642 // return decoded samples (don't clear 'output'!) 643 return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y; 644 } 645 646 uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size, 647 int* width, int* height) { 648 return Decode(MODE_RGB, data, data_size, width, height, NULL); 649 } 650 651 uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size, 652 int* width, int* height) { 653 return Decode(MODE_RGBA, data, data_size, width, height, NULL); 654 } 655 656 uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size, 657 int* width, int* height) { 658 return Decode(MODE_ARGB, data, data_size, width, height, NULL); 659 } 660 661 uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, 662 int* width, int* height) { 663 return Decode(MODE_BGR, data, data_size, width, height, NULL); 664 } 665 666 uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, 667 int* width, int* height) { 668 return Decode(MODE_BGRA, data, data_size, width, height, NULL); 669 } 670 671 uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size, 672 int* width, int* height, uint8_t** u, uint8_t** v, 673 int* stride, int* uv_stride) { 674 // data, width and height are checked by Decode(). 675 if (u == NULL || v == NULL || stride == NULL || uv_stride == NULL) { 676 return NULL; 677 } 678 679 { 680 WebPDecBuffer output; // only to preserve the side-infos 681 uint8_t* const out = Decode(MODE_YUV, data, data_size, 682 width, height, &output); 683 684 if (out != NULL) { 685 const WebPYUVABuffer* const buf = &output.u.YUVA; 686 *u = buf->u; 687 *v = buf->v; 688 *stride = buf->y_stride; 689 *uv_stride = buf->u_stride; 690 assert(buf->u_stride == buf->v_stride); 691 } 692 return out; 693 } 694 } 695 696 static void DefaultFeatures(WebPBitstreamFeatures* const features) { 697 assert(features != NULL); 698 memset(features, 0, sizeof(*features)); 699 } 700 701 static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size, 702 WebPBitstreamFeatures* const features) { 703 if (features == NULL || data == NULL) { 704 return VP8_STATUS_INVALID_PARAM; 705 } 706 DefaultFeatures(features); 707 708 // Only parse enough of the data to retrieve the features. 709 return ParseHeadersInternal(data, data_size, 710 &features->width, &features->height, 711 &features->has_alpha, &features->has_animation, 712 &features->format, NULL); 713 } 714 715 //------------------------------------------------------------------------------ 716 // WebPGetInfo() 717 718 int WebPGetInfo(const uint8_t* data, size_t data_size, 719 int* width, int* height) { 720 WebPBitstreamFeatures features; 721 722 if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) { 723 return 0; 724 } 725 726 if (width != NULL) { 727 *width = features.width; 728 } 729 if (height != NULL) { 730 *height = features.height; 731 } 732 733 return 1; 734 } 735 736 //------------------------------------------------------------------------------ 737 // Advance decoding API 738 739 int WebPInitDecoderConfigInternal(WebPDecoderConfig* config, 740 int version) { 741 if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { 742 return 0; // version mismatch 743 } 744 if (config == NULL) { 745 return 0; 746 } 747 memset(config, 0, sizeof(*config)); 748 DefaultFeatures(&config->input); 749 if (!WebPInitDecBuffer(&config->output)) { 750 return 0; 751 } 752 return 1; 753 } 754 755 static int WebPCheckCropDimensionsBasic(int x, int y, int w, int h) { 756 return !(x < 0 || y < 0 || w <= 0 || h <= 0); 757 } 758 759 int WebPValidateDecoderConfig(const WebPDecoderConfig* config) { 760 const WebPDecoderOptions* options; 761 if (config == NULL) return 0; 762 if (!IsValidColorspace(config->output.colorspace)) { 763 return 0; 764 } 765 766 options = &config->options; 767 // bypass_filtering, no_fancy_upsampling, use_cropping, use_scaling, 768 // use_threads, flip can be any integer and are interpreted as boolean. 769 770 // Check for cropping. 771 if (options->use_cropping && !WebPCheckCropDimensionsBasic( 772 options->crop_left, options->crop_top, 773 options->crop_width, options->crop_height)) { 774 return 0; 775 } 776 // Check for scaling. 777 if (options->use_scaling && 778 (options->scaled_width < 0 || options->scaled_height < 0 || 779 (options->scaled_width == 0 && options->scaled_height == 0))) { 780 return 0; 781 } 782 783 // In case the WebPBitstreamFeatures has been filled in, check further. 784 if (config->input.width > 0 || config->input.height > 0) { 785 int scaled_width = options->scaled_width; 786 int scaled_height = options->scaled_height; 787 if (options->use_cropping && 788 !WebPCheckCropDimensions(config->input.width, config->input.height, 789 options->crop_left, options->crop_top, 790 options->crop_width, options->crop_height)) { 791 return 0; 792 } 793 if (options->use_scaling && !WebPRescalerGetScaledDimensions( 794 config->input.width, config->input.height, 795 &scaled_width, &scaled_height)) { 796 return 0; 797 } 798 } 799 800 // Check for dithering. 801 if (options->dithering_strength < 0 || options->dithering_strength > 100 || 802 options->alpha_dithering_strength < 0 || 803 options->alpha_dithering_strength > 100) { 804 return 0; 805 } 806 807 return 1; 808 } 809 810 VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size, 811 WebPBitstreamFeatures* features, 812 int version) { 813 if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { 814 return VP8_STATUS_INVALID_PARAM; // version mismatch 815 } 816 if (features == NULL) { 817 return VP8_STATUS_INVALID_PARAM; 818 } 819 return GetFeatures(data, data_size, features); 820 } 821 822 VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, 823 WebPDecoderConfig* config) { 824 WebPDecParams params; 825 VP8StatusCode status; 826 827 if (config == NULL) { 828 return VP8_STATUS_INVALID_PARAM; 829 } 830 831 status = GetFeatures(data, data_size, &config->input); 832 if (status != VP8_STATUS_OK) { 833 if (status == VP8_STATUS_NOT_ENOUGH_DATA) { 834 return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error. 835 } 836 return status; 837 } 838 839 WebPResetDecParams(¶ms); 840 params.options = &config->options; 841 params.output = &config->output; 842 if (WebPAvoidSlowMemory(params.output, &config->input)) { 843 // decoding to slow memory: use a temporary in-mem buffer to decode into. 844 WebPDecBuffer in_mem_buffer; 845 if (!WebPInitDecBuffer(&in_mem_buffer)) { 846 return VP8_STATUS_INVALID_PARAM; 847 } 848 in_mem_buffer.colorspace = config->output.colorspace; 849 in_mem_buffer.width = config->input.width; 850 in_mem_buffer.height = config->input.height; 851 params.output = &in_mem_buffer; 852 status = DecodeInto(data, data_size, ¶ms); 853 if (status == VP8_STATUS_OK) { // do the slow-copy 854 status = WebPCopyDecBufferPixels(&in_mem_buffer, &config->output); 855 } 856 WebPFreeDecBuffer(&in_mem_buffer); 857 } else { 858 status = DecodeInto(data, data_size, ¶ms); 859 } 860 861 return status; 862 } 863 864 //------------------------------------------------------------------------------ 865 // Cropping and rescaling. 866 867 int WebPCheckCropDimensions(int image_width, int image_height, 868 int x, int y, int w, int h) { 869 return WebPCheckCropDimensionsBasic(x, y, w, h) && 870 !(x >= image_width || w > image_width || w > image_width - x || 871 y >= image_height || h > image_height || h > image_height - y); 872 } 873 874 int WebPIoInitFromOptions(const WebPDecoderOptions* const options, 875 VP8Io* const io, WEBP_CSP_MODE src_colorspace) { 876 const int W = io->width; 877 const int H = io->height; 878 int x = 0, y = 0, w = W, h = H; 879 880 // Cropping 881 io->use_cropping = (options != NULL) && options->use_cropping; 882 if (io->use_cropping) { 883 w = options->crop_width; 884 h = options->crop_height; 885 x = options->crop_left; 886 y = options->crop_top; 887 if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 888 x &= ~1; 889 y &= ~1; 890 } 891 if (!WebPCheckCropDimensions(W, H, x, y, w, h)) { 892 return 0; // out of frame boundary error 893 } 894 } 895 io->crop_left = x; 896 io->crop_top = y; 897 io->crop_right = x + w; 898 io->crop_bottom = y + h; 899 io->mb_w = w; 900 io->mb_h = h; 901 902 // Scaling 903 io->use_scaling = (options != NULL) && options->use_scaling; 904 if (io->use_scaling) { 905 int scaled_width = options->scaled_width; 906 int scaled_height = options->scaled_height; 907 if (!WebPRescalerGetScaledDimensions(w, h, &scaled_width, &scaled_height)) { 908 return 0; 909 } 910 io->scaled_width = scaled_width; 911 io->scaled_height = scaled_height; 912 } 913 914 // Filter 915 io->bypass_filtering = (options != NULL) && options->bypass_filtering; 916 917 // Fancy upsampler 918 #ifdef FANCY_UPSAMPLING 919 io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling); 920 #endif 921 922 if (io->use_scaling) { 923 // disable filter (only for large downscaling ratio). 924 io->bypass_filtering |= (io->scaled_width < W * 3 / 4) && 925 (io->scaled_height < H * 3 / 4); 926 io->fancy_upsampling = 0; 927 } 928 return 1; 929 } 930 931 //------------------------------------------------------------------------------