| OLD | NEW |
| 1 // Copyright 2010 Google Inc. All Rights Reserved. | 1 // Copyright 2010 Google Inc. All Rights Reserved. |
| 2 // | 2 // |
| 3 // Use of this source code is governed by a BSD-style license | 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 | 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 | 5 // tree. An additional intellectual property rights grant can be found |
| 6 // in the file PATENTS. All contributing project authors may | 6 // in the file PATENTS. All contributing project authors may |
| 7 // be found in the AUTHORS file in the root of the source tree. | 7 // be found in the AUTHORS file in the root of the source tree. |
| 8 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
| 9 // | 9 // |
| 10 // Main decoding functions for WEBP images. | 10 // Main decoding functions for WEBP images. |
| 11 // | 11 // |
| 12 // Author: Skal (pascal.massimino@gmail.com) | 12 // Author: Skal (pascal.massimino@gmail.com) |
| 13 | 13 |
| 14 #include <stdlib.h> | 14 #include <stdlib.h> |
| 15 | 15 |
| 16 #include "./vp8i.h" | 16 #include "./vp8i.h" |
| 17 #include "./vp8li.h" | 17 #include "./vp8li.h" |
| 18 #include "./webpi.h" | 18 #include "./webpi.h" |
| 19 #include "../webp/mux_types.h" // ALPHA_FLAG | 19 #include "../webp/mux_types.h" // ALPHA_FLAG |
| 20 | 20 |
| 21 #if defined(__cplusplus) || defined(c_plusplus) | |
| 22 extern "C" { | |
| 23 #endif | |
| 24 | |
| 25 //------------------------------------------------------------------------------ | 21 //------------------------------------------------------------------------------ |
| 26 // RIFF layout is: | 22 // RIFF layout is: |
| 27 // Offset tag | 23 // Offset tag |
| 28 // 0...3 "RIFF" 4-byte tag | 24 // 0...3 "RIFF" 4-byte tag |
| 29 // 4...7 size of image data (including metadata) starting at offset 8 | 25 // 4...7 size of image data (including metadata) starting at offset 8 |
| 30 // 8...11 "WEBP" our form-type signature | 26 // 8...11 "WEBP" our form-type signature |
| 31 // The RIFF container (12 bytes) is followed by appropriate chunks: | 27 // The RIFF container (12 bytes) is followed by appropriate chunks: |
| 32 // 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format | 28 // 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format |
| 33 // 16..19 size of the raw VP8 image data, starting at offset 20 | 29 // 16..19 size of the raw VP8 image data, starting at offset 20 |
| 34 // 20.... the VP8 bytes | 30 // 20.... the VP8 bytes |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 278 // RIFF + VP8(L) | 274 // RIFF + VP8(L) |
| 279 // RIFF + VP8X + (optional chunks) + VP8(L) | 275 // RIFF + VP8X + (optional chunks) + VP8(L) |
| 280 // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. | 276 // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. |
| 281 // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. | 277 // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. |
| 282 static VP8StatusCode ParseHeadersInternal(const uint8_t* data, | 278 static VP8StatusCode ParseHeadersInternal(const uint8_t* data, |
| 283 size_t data_size, | 279 size_t data_size, |
| 284 int* const width, | 280 int* const width, |
| 285 int* const height, | 281 int* const height, |
| 286 int* const has_alpha, | 282 int* const has_alpha, |
| 287 int* const has_animation, | 283 int* const has_animation, |
| 284 int* const format, |
| 288 WebPHeaderStructure* const headers) { | 285 WebPHeaderStructure* const headers) { |
| 289 int canvas_width = 0; | 286 int canvas_width = 0; |
| 290 int canvas_height = 0; | 287 int canvas_height = 0; |
| 291 int image_width = 0; | 288 int image_width = 0; |
| 292 int image_height = 0; | 289 int image_height = 0; |
| 293 int found_riff = 0; | 290 int found_riff = 0; |
| 294 int found_vp8x = 0; | 291 int found_vp8x = 0; |
| 292 int animation_present = 0; |
| 293 int fragments_present = 0; |
| 294 |
| 295 VP8StatusCode status; | 295 VP8StatusCode status; |
| 296 WebPHeaderStructure hdrs; | 296 WebPHeaderStructure hdrs; |
| 297 | 297 |
| 298 if (data == NULL || data_size < RIFF_HEADER_SIZE) { | 298 if (data == NULL || data_size < RIFF_HEADER_SIZE) { |
| 299 return VP8_STATUS_NOT_ENOUGH_DATA; | 299 return VP8_STATUS_NOT_ENOUGH_DATA; |
| 300 } | 300 } |
| 301 memset(&hdrs, 0, sizeof(hdrs)); | 301 memset(&hdrs, 0, sizeof(hdrs)); |
| 302 hdrs.data = data; | 302 hdrs.data = data; |
| 303 hdrs.data_size = data_size; | 303 hdrs.data_size = data_size; |
| 304 | 304 |
| 305 // Skip over RIFF header. | 305 // Skip over RIFF header. |
| 306 status = ParseRIFF(&data, &data_size, &hdrs.riff_size); | 306 status = ParseRIFF(&data, &data_size, &hdrs.riff_size); |
| 307 if (status != VP8_STATUS_OK) { | 307 if (status != VP8_STATUS_OK) { |
| 308 return status; // Wrong RIFF header / insufficient data. | 308 return status; // Wrong RIFF header / insufficient data. |
| 309 } | 309 } |
| 310 found_riff = (hdrs.riff_size > 0); | 310 found_riff = (hdrs.riff_size > 0); |
| 311 | 311 |
| 312 // Skip over VP8X. | 312 // Skip over VP8X. |
| 313 { | 313 { |
| 314 uint32_t flags = 0; | 314 uint32_t flags = 0; |
| 315 int animation_present; | |
| 316 status = ParseVP8X(&data, &data_size, &found_vp8x, | 315 status = ParseVP8X(&data, &data_size, &found_vp8x, |
| 317 &canvas_width, &canvas_height, &flags); | 316 &canvas_width, &canvas_height, &flags); |
| 318 if (status != VP8_STATUS_OK) { | 317 if (status != VP8_STATUS_OK) { |
| 319 return status; // Wrong VP8X / insufficient data. | 318 return status; // Wrong VP8X / insufficient data. |
| 320 } | 319 } |
| 321 animation_present = !!(flags & ANIMATION_FLAG); | 320 animation_present = !!(flags & ANIMATION_FLAG); |
| 321 fragments_present = !!(flags & FRAGMENTS_FLAG); |
| 322 if (!found_riff && found_vp8x) { | 322 if (!found_riff && found_vp8x) { |
| 323 // Note: This restriction may be removed in the future, if it becomes | 323 // Note: This restriction may be removed in the future, if it becomes |
| 324 // necessary to send VP8X chunk to the decoder. | 324 // necessary to send VP8X chunk to the decoder. |
| 325 return VP8_STATUS_BITSTREAM_ERROR; | 325 return VP8_STATUS_BITSTREAM_ERROR; |
| 326 } | 326 } |
| 327 if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG); | 327 if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG); |
| 328 if (has_animation != NULL) *has_animation = animation_present; | 328 if (has_animation != NULL) *has_animation = animation_present; |
| 329 if (format != NULL) *format = 0; // default = undefined |
| 329 | 330 |
| 330 if (found_vp8x && animation_present && headers == NULL) { | 331 if (found_vp8x && (animation_present || fragments_present) && |
| 332 headers == NULL) { |
| 331 if (width != NULL) *width = canvas_width; | 333 if (width != NULL) *width = canvas_width; |
| 332 if (height != NULL) *height = canvas_height; | 334 if (height != NULL) *height = canvas_height; |
| 333 return VP8_STATUS_OK; // Just return features from VP8X header. | 335 return VP8_STATUS_OK; // Just return features from VP8X header. |
| 334 } | 336 } |
| 335 } | 337 } |
| 336 | 338 |
| 337 if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA; | 339 if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA; |
| 338 | 340 |
| 339 // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". | 341 // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". |
| 340 if ((found_riff && found_vp8x) || | 342 if ((found_riff && found_vp8x) || |
| 341 (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { | 343 (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { |
| 342 status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, | 344 status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, |
| 343 &hdrs.alpha_data, &hdrs.alpha_data_size); | 345 &hdrs.alpha_data, &hdrs.alpha_data_size); |
| 344 if (status != VP8_STATUS_OK) { | 346 if (status != VP8_STATUS_OK) { |
| 345 return status; // Found an invalid chunk size / insufficient data. | 347 return status; // Found an invalid chunk size / insufficient data. |
| 346 } | 348 } |
| 347 } | 349 } |
| 348 | 350 |
| 349 // Skip over VP8/VP8L header. | 351 // Skip over VP8/VP8L header. |
| 350 status = ParseVP8Header(&data, &data_size, hdrs.riff_size, | 352 status = ParseVP8Header(&data, &data_size, hdrs.riff_size, |
| 351 &hdrs.compressed_size, &hdrs.is_lossless); | 353 &hdrs.compressed_size, &hdrs.is_lossless); |
| 352 if (status != VP8_STATUS_OK) { | 354 if (status != VP8_STATUS_OK) { |
| 353 return status; // Wrong VP8/VP8L chunk-header / insufficient data. | 355 return status; // Wrong VP8/VP8L chunk-header / insufficient data. |
| 354 } | 356 } |
| 355 if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { | 357 if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { |
| 356 return VP8_STATUS_BITSTREAM_ERROR; | 358 return VP8_STATUS_BITSTREAM_ERROR; |
| 357 } | 359 } |
| 358 | 360 |
| 361 if (format != NULL && !(animation_present || fragments_present)) { |
| 362 *format = hdrs.is_lossless ? 2 : 1; |
| 363 } |
| 364 |
| 359 if (!hdrs.is_lossless) { | 365 if (!hdrs.is_lossless) { |
| 360 if (data_size < VP8_FRAME_HEADER_SIZE) { | 366 if (data_size < VP8_FRAME_HEADER_SIZE) { |
| 361 return VP8_STATUS_NOT_ENOUGH_DATA; | 367 return VP8_STATUS_NOT_ENOUGH_DATA; |
| 362 } | 368 } |
| 363 // Validates raw VP8 data. | 369 // Validates raw VP8 data. |
| 364 if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size, | 370 if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size, |
| 365 &image_width, &image_height)) { | 371 &image_width, &image_height)) { |
| 366 return VP8_STATUS_BITSTREAM_ERROR; | 372 return VP8_STATUS_BITSTREAM_ERROR; |
| 367 } | 373 } |
| 368 } else { | 374 } else { |
| 369 if (data_size < VP8L_FRAME_HEADER_SIZE) { | 375 if (data_size < VP8L_FRAME_HEADER_SIZE) { |
| 370 return VP8_STATUS_NOT_ENOUGH_DATA; | 376 return VP8_STATUS_NOT_ENOUGH_DATA; |
| 371 } | 377 } |
| 372 // Validates raw VP8L data. | 378 // Validates raw VP8L data. |
| 373 if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) { | 379 if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) { |
| 374 return VP8_STATUS_BITSTREAM_ERROR; | 380 return VP8_STATUS_BITSTREAM_ERROR; |
| 375 } | 381 } |
| 376 } | 382 } |
| 377 // Validates image size coherency. TODO(urvang): what about FRGM? | 383 // Validates image size coherency. |
| 378 if (found_vp8x) { | 384 if (found_vp8x) { |
| 379 if (canvas_width != image_width || canvas_height != image_height) { | 385 if (canvas_width != image_width || canvas_height != image_height) { |
| 380 return VP8_STATUS_BITSTREAM_ERROR; | 386 return VP8_STATUS_BITSTREAM_ERROR; |
| 381 } | 387 } |
| 382 } | 388 } |
| 383 if (width != NULL) *width = image_width; | 389 if (width != NULL) *width = image_width; |
| 384 if (height != NULL) *height = image_height; | 390 if (height != NULL) *height = image_height; |
| 385 if (has_alpha != NULL) { | 391 if (has_alpha != NULL) { |
| 386 // If the data did not contain a VP8X/VP8L chunk the only definitive way | 392 // If the data did not contain a VP8X/VP8L chunk the only definitive way |
| 387 // to set this is by looking for alpha data (from an ALPH chunk). | 393 // to set this is by looking for alpha data (from an ALPH chunk). |
| 388 *has_alpha |= (hdrs.alpha_data != NULL); | 394 *has_alpha |= (hdrs.alpha_data != NULL); |
| 389 } | 395 } |
| 390 if (headers != NULL) { | 396 if (headers != NULL) { |
| 391 *headers = hdrs; | 397 *headers = hdrs; |
| 392 headers->offset = data - headers->data; | 398 headers->offset = data - headers->data; |
| 393 assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); | 399 assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); |
| 394 assert(headers->offset == headers->data_size - data_size); | 400 assert(headers->offset == headers->data_size - data_size); |
| 395 } | 401 } |
| 396 return VP8_STATUS_OK; // Return features from VP8 header. | 402 return VP8_STATUS_OK; // Return features from VP8 header. |
| 397 } | 403 } |
| 398 | 404 |
| 399 VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) { | 405 VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) { |
| 400 VP8StatusCode status; | 406 VP8StatusCode status; |
| 401 int has_animation = 0; | 407 int has_animation = 0; |
| 402 assert(headers != NULL); | 408 assert(headers != NULL); |
| 403 // fill out headers, ignore width/height/has_alpha. | 409 // fill out headers, ignore width/height/has_alpha. |
| 404 status = ParseHeadersInternal(headers->data, headers->data_size, | 410 status = ParseHeadersInternal(headers->data, headers->data_size, |
| 405 NULL, NULL, NULL, &has_animation, headers); | 411 NULL, NULL, NULL, &has_animation, |
| 412 NULL, headers); |
| 406 if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) { | 413 if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) { |
| 407 // TODO(jzern): full support of animation frames will require API additions. | 414 // TODO(jzern): full support of animation frames will require API additions. |
| 408 if (has_animation) { | 415 if (has_animation) { |
| 409 status = VP8_STATUS_UNSUPPORTED_FEATURE; | 416 status = VP8_STATUS_UNSUPPORTED_FEATURE; |
| 410 } | 417 } |
| 411 } | 418 } |
| 412 return status; | 419 return status; |
| 413 } | 420 } |
| 414 | 421 |
| 415 //------------------------------------------------------------------------------ | 422 //------------------------------------------------------------------------------ |
| 416 // WebPDecParams | 423 // WebPDecParams |
| 417 | 424 |
| 418 void WebPResetDecParams(WebPDecParams* const params) { | 425 void WebPResetDecParams(WebPDecParams* const params) { |
| 419 if (params) { | 426 if (params != NULL) { |
| 420 memset(params, 0, sizeof(*params)); | 427 memset(params, 0, sizeof(*params)); |
| 421 } | 428 } |
| 422 } | 429 } |
| 423 | 430 |
| 424 //------------------------------------------------------------------------------ | 431 //------------------------------------------------------------------------------ |
| 425 // "Into" decoding variants | 432 // "Into" decoding variants |
| 426 | 433 |
| 427 // Main flow | 434 // Main flow |
| 428 static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, | 435 static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, |
| 429 WebPDecParams* const params) { | 436 WebPDecParams* const params) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 442 VP8InitIo(&io); | 449 VP8InitIo(&io); |
| 443 io.data = headers.data + headers.offset; | 450 io.data = headers.data + headers.offset; |
| 444 io.data_size = headers.data_size - headers.offset; | 451 io.data_size = headers.data_size - headers.offset; |
| 445 WebPInitCustomIo(params, &io); // Plug the I/O functions. | 452 WebPInitCustomIo(params, &io); // Plug the I/O functions. |
| 446 | 453 |
| 447 if (!headers.is_lossless) { | 454 if (!headers.is_lossless) { |
| 448 VP8Decoder* const dec = VP8New(); | 455 VP8Decoder* const dec = VP8New(); |
| 449 if (dec == NULL) { | 456 if (dec == NULL) { |
| 450 return VP8_STATUS_OUT_OF_MEMORY; | 457 return VP8_STATUS_OUT_OF_MEMORY; |
| 451 } | 458 } |
| 452 #ifdef WEBP_USE_THREAD | |
| 453 dec->use_threads_ = params->options && (params->options->use_threads > 0); | |
| 454 #else | |
| 455 dec->use_threads_ = 0; | |
| 456 #endif | |
| 457 dec->alpha_data_ = headers.alpha_data; | 459 dec->alpha_data_ = headers.alpha_data; |
| 458 dec->alpha_data_size_ = headers.alpha_data_size; | 460 dec->alpha_data_size_ = headers.alpha_data_size; |
| 459 | 461 |
| 460 // Decode bitstream header, update io->width/io->height. | 462 // Decode bitstream header, update io->width/io->height. |
| 461 if (!VP8GetHeaders(dec, &io)) { | 463 if (!VP8GetHeaders(dec, &io)) { |
| 462 status = dec->status_; // An error occurred. Grab error status. | 464 status = dec->status_; // An error occurred. Grab error status. |
| 463 } else { | 465 } else { |
| 464 // Allocate/check output buffers. | 466 // Allocate/check output buffers. |
| 465 status = WebPAllocateDecBuffer(io.width, io.height, params->options, | 467 status = WebPAllocateDecBuffer(io.width, io.height, params->options, |
| 466 params->output); | 468 params->output); |
| 467 if (status == VP8_STATUS_OK) { // Decode | 469 if (status == VP8_STATUS_OK) { // Decode |
| 470 // This change must be done before calling VP8Decode() |
| 471 dec->mt_method_ = VP8GetThreadMethod(params->options, &headers, |
| 472 io.width, io.height); |
| 473 VP8InitDithering(params->options, dec); |
| 468 if (!VP8Decode(dec, &io)) { | 474 if (!VP8Decode(dec, &io)) { |
| 469 status = dec->status_; | 475 status = dec->status_; |
| 470 } | 476 } |
| 471 } | 477 } |
| 472 } | 478 } |
| 473 VP8Delete(dec); | 479 VP8Delete(dec); |
| 474 } else { | 480 } else { |
| 475 VP8LDecoder* const dec = VP8LNew(); | 481 VP8LDecoder* const dec = VP8LNew(); |
| 476 if (dec == NULL) { | 482 if (dec == NULL) { |
| 477 return VP8_STATUS_OUT_OF_MEMORY; | 483 return VP8_STATUS_OUT_OF_MEMORY; |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 644 *stride = buf->y_stride; | 650 *stride = buf->y_stride; |
| 645 *uv_stride = buf->u_stride; | 651 *uv_stride = buf->u_stride; |
| 646 assert(buf->u_stride == buf->v_stride); | 652 assert(buf->u_stride == buf->v_stride); |
| 647 } | 653 } |
| 648 return out; | 654 return out; |
| 649 } | 655 } |
| 650 | 656 |
| 651 static void DefaultFeatures(WebPBitstreamFeatures* const features) { | 657 static void DefaultFeatures(WebPBitstreamFeatures* const features) { |
| 652 assert(features != NULL); | 658 assert(features != NULL); |
| 653 memset(features, 0, sizeof(*features)); | 659 memset(features, 0, sizeof(*features)); |
| 654 features->bitstream_version = 0; | |
| 655 } | 660 } |
| 656 | 661 |
| 657 static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size, | 662 static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size, |
| 658 WebPBitstreamFeatures* const features) { | 663 WebPBitstreamFeatures* const features) { |
| 659 if (features == NULL || data == NULL) { | 664 if (features == NULL || data == NULL) { |
| 660 return VP8_STATUS_INVALID_PARAM; | 665 return VP8_STATUS_INVALID_PARAM; |
| 661 } | 666 } |
| 662 DefaultFeatures(features); | 667 DefaultFeatures(features); |
| 663 | 668 |
| 664 // Only parse enough of the data to retrieve the features. | 669 // Only parse enough of the data to retrieve the features. |
| 665 return ParseHeadersInternal(data, data_size, | 670 return ParseHeadersInternal(data, data_size, |
| 666 &features->width, &features->height, | 671 &features->width, &features->height, |
| 667 &features->has_alpha, &features->has_animation, | 672 &features->has_alpha, &features->has_animation, |
| 668 NULL); | 673 &features->format, NULL); |
| 669 } | 674 } |
| 670 | 675 |
| 671 //------------------------------------------------------------------------------ | 676 //------------------------------------------------------------------------------ |
| 672 // WebPGetInfo() | 677 // WebPGetInfo() |
| 673 | 678 |
| 674 int WebPGetInfo(const uint8_t* data, size_t data_size, | 679 int WebPGetInfo(const uint8_t* data, size_t data_size, |
| 675 int* width, int* height) { | 680 int* width, int* height) { |
| 676 WebPBitstreamFeatures features; | 681 WebPBitstreamFeatures features; |
| 677 | 682 |
| 678 if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) { | 683 if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) { |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 796 // disable filter (only for large downscaling ratio). | 801 // disable filter (only for large downscaling ratio). |
| 797 io->bypass_filtering = (io->scaled_width < W * 3 / 4) && | 802 io->bypass_filtering = (io->scaled_width < W * 3 / 4) && |
| 798 (io->scaled_height < H * 3 / 4); | 803 (io->scaled_height < H * 3 / 4); |
| 799 io->fancy_upsampling = 0; | 804 io->fancy_upsampling = 0; |
| 800 } | 805 } |
| 801 return 1; | 806 return 1; |
| 802 } | 807 } |
| 803 | 808 |
| 804 //------------------------------------------------------------------------------ | 809 //------------------------------------------------------------------------------ |
| 805 | 810 |
| 806 #if defined(__cplusplus) || defined(c_plusplus) | |
| 807 } // extern "C" | |
| 808 #endif | |
| OLD | NEW |