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 |