| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkCodec_libpng.h" | 8 #include "SkCodec_libpng.h" |
| 9 #include "SkCodecPriv.h" | 9 #include "SkCodecPriv.h" |
| 10 #include "SkColorPriv.h" | 10 #include "SkColorPriv.h" |
| (...skipping 448 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 459 return false; | 459 return false; |
| 460 } | 460 } |
| 461 | 461 |
| 462 fPng_ptr = png_ptr; | 462 fPng_ptr = png_ptr; |
| 463 fInfo_ptr = info_ptr; | 463 fInfo_ptr = info_ptr; |
| 464 return true; | 464 return true; |
| 465 } | 465 } |
| 466 | 466 |
| 467 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
dst, | 467 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
dst, |
| 468 size_t dstRowBytes, const Options& optio
ns, | 468 size_t dstRowBytes, const Options& optio
ns, |
| 469 SkPMColor ctable[], int* ctableCount) { | 469 SkPMColor ctable[], int* ctableCount, |
| 470 int* rowsDecoded) { |
| 470 if (!conversion_possible(requestedInfo, this->getInfo())) { | 471 if (!conversion_possible(requestedInfo, this->getInfo())) { |
| 471 return kInvalidConversion; | 472 return kInvalidConversion; |
| 472 } | 473 } |
| 473 if (options.fSubset) { | 474 if (options.fSubset) { |
| 474 // Subsets are not supported. | 475 // Subsets are not supported. |
| 475 return kUnimplemented; | 476 return kUnimplemented; |
| 476 } | 477 } |
| 477 if (requestedInfo.dimensions() != this->getInfo().dimensions()) { | 478 if (requestedInfo.dimensions() != this->getInfo().dimensions()) { |
| 478 return kInvalidScale; | 479 return kInvalidScale; |
| 479 } | 480 } |
| 480 | 481 |
| 481 // Note that ctable and ctableCount may be modified if there is a color tabl
e | 482 // Note that ctable and ctableCount may be modified if there is a color tabl
e |
| 482 const Result result = this->initializeSwizzler(requestedInfo, options, | 483 const Result result = this->initializeSwizzler(requestedInfo, options, |
| 483 ctable, ctableCount); | 484 ctable, ctableCount); |
| 484 if (result != kSuccess) { | 485 if (result != kSuccess) { |
| 485 return result; | 486 return result; |
| 486 } | 487 } |
| 487 // FIXME: Could we use the return value of setjmp to specify the type of | 488 // FIXME: Could we use the return value of setjmp to specify the type of |
| 488 // error? | 489 // error? |
| 490 int row = 0; |
| 489 if (setjmp(png_jmpbuf(fPng_ptr))) { | 491 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 490 SkCodecPrintf("setjmp long jump!\n"); | 492 // Assume that any error that occurs while reading rows is caused by an
incomplete input. |
| 491 return kInvalidInput; | 493 if (fNumberPasses > 1) { |
| 494 // FIXME (msarett): Handle incomplete interlaced pngs. |
| 495 return kInvalidInput; |
| 496 } |
| 497 // FIXME: We do a poor job on incomplete pngs compared to other decoders
(ex: Chromium, |
| 498 // Ubuntu Image Viewer). This is because we use the default buffer size
in libpng (8192 |
| 499 // bytes), and if we can't fill the buffer, we immediately fail. |
| 500 // For example, if we try to read 8192 bytes, and the image (incorrectly
) only contains |
| 501 // half that, which may have been enough to contain a non-zero number of
lines, we fail |
| 502 // when we could have decoded a few more lines and then failed. |
| 503 // The read function that we provide for libpng has no way of indicating
that we have |
| 504 // made a partial read. |
| 505 // Making our buffer size smaller improves our incomplete decodes, but w
hat impact does |
| 506 // it have on regular decode performance? Should we investigate using a
different API |
| 507 // instead of png_read_row(s)? Chromium uses png_process_data. |
| 508 *rowsDecoded = row; |
| 509 return kIncompleteInput; |
| 492 } | 510 } |
| 493 | 511 |
| 494 bool hasAlpha = false; | 512 bool hasAlpha = false; |
| 495 // FIXME: We could split these out based on subclass. | 513 // FIXME: We could split these out based on subclass. |
| 496 SkAutoMalloc storage; | 514 SkAutoMalloc storage; |
| 497 void* dstRow = dst; | 515 void* dstRow = dst; |
| 498 if (fNumberPasses > 1) { | 516 if (fNumberPasses > 1) { |
| 499 const int width = requestedInfo.width(); | 517 const int width = requestedInfo.width(); |
| 500 const int height = requestedInfo.height(); | 518 const int height = requestedInfo.height(); |
| 501 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); | 519 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 516 // Now swizzle it. | 534 // Now swizzle it. |
| 517 uint8_t* srcRow = base; | 535 uint8_t* srcRow = base; |
| 518 for (int y = 0; y < height; y++) { | 536 for (int y = 0; y < height; y++) { |
| 519 hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)
); | 537 hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)
); |
| 520 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | 538 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
| 521 srcRow += srcRowBytes; | 539 srcRow += srcRowBytes; |
| 522 } | 540 } |
| 523 } else { | 541 } else { |
| 524 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConf
ig)); | 542 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConf
ig)); |
| 525 uint8_t* srcRow = static_cast<uint8_t*>(storage.get()); | 543 uint8_t* srcRow = static_cast<uint8_t*>(storage.get()); |
| 526 for (int y = 0; y < requestedInfo.height(); y++) { | 544 for (; row < requestedInfo.height(); row++) { |
| 527 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1); | 545 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1); |
| 528 // FIXME: Only call IsOpaque once, outside the loop. Same for onGetS
canlines. | 546 // FIXME: Only call IsOpaque once, outside the loop. Same for onGetS
canlines. |
| 529 hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)
); | 547 hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)
); |
| 530 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | 548 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
| 531 } | 549 } |
| 532 } | 550 } |
| 533 | 551 |
| 534 if (hasAlpha) { | 552 if (hasAlpha) { |
| 535 fAlphaState = kHasAlpha_AlphaState; | 553 fAlphaState = kHasAlpha_AlphaState; |
| 536 } else { | 554 } else { |
| 537 fAlphaState = kOpaque_AlphaState; | 555 fAlphaState = kOpaque_AlphaState; |
| 538 } | 556 } |
| 539 | 557 |
| 540 // FIXME: do we need substituteTranspColor? Note that we cannot do it for | 558 // FIXME: do we need substituteTranspColor? Note that we cannot do it for |
| 541 // scanline decoding, but we could do it here. Alternatively, we could do | 559 // scanline decoding, but we could do it here. Alternatively, we could do |
| 542 // it as we go, instead of in post-processing like SkPNGImageDecoder. | 560 // it as we go, instead of in post-processing like SkPNGImageDecoder. |
| 543 | 561 |
| 544 if (setjmp(png_jmpbuf(fPng_ptr))) { | 562 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 545 // We've already read all the scanlines. This is a success. | 563 // We've already read all the scanlines. This is a success. |
| 546 return kSuccess; | 564 return kSuccess; |
| 547 } | 565 } |
| 548 | 566 |
| 549 // read rest of file, and get additional comment and time chunks in info_ptr | 567 // read rest of file, and get additional comment and time chunks in info_ptr |
| 550 png_read_end(fPng_ptr, fInfo_ptr); | 568 png_read_end(fPng_ptr, fInfo_ptr); |
| 551 | 569 |
| 552 return kSuccess; | 570 return kSuccess; |
| 553 } | 571 } |
| 554 | 572 |
| 573 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType
) const { |
| 574 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); |
| 575 if (colorPtr) { |
| 576 return get_color_table_fill_value(colorType, colorPtr, 0); |
| 577 } |
| 578 return INHERITED::onGetFillValue(colorType, alphaType); |
| 579 } |
| 580 |
| 555 bool SkPngCodec::onReallyHasAlpha() const { | 581 bool SkPngCodec::onReallyHasAlpha() const { |
| 556 switch (fAlphaState) { | 582 switch (fAlphaState) { |
| 557 case kOpaque_AlphaState: | 583 case kOpaque_AlphaState: |
| 558 return false; | 584 return false; |
| 559 case kUnknown_AlphaState: | 585 case kUnknown_AlphaState: |
| 560 // Maybe the subclass knows? | 586 // Maybe the subclass knows? |
| 561 return this->alphaInScanlineDecode() == kHasAlpha_AlphaState; | 587 return this->alphaInScanlineDecode() == kHasAlpha_AlphaState; |
| 562 case kHasAlpha_AlphaState: | 588 case kHasAlpha_AlphaState: |
| 563 switch (this->alphaInScanlineDecode()) { | 589 switch (this->alphaInScanlineDecode()) { |
| 564 case kUnknown_AlphaState: | 590 case kUnknown_AlphaState: |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 606 return result; | 632 return result; |
| 607 } | 633 } |
| 608 | 634 |
| 609 fAlphaState = kUnknown_AlphaState; | 635 fAlphaState = kUnknown_AlphaState; |
| 610 fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this-
>srcConfig())); | 636 fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this-
>srcConfig())); |
| 611 fSrcRow = static_cast<uint8_t*>(fStorage.get()); | 637 fSrcRow = static_cast<uint8_t*>(fStorage.get()); |
| 612 | 638 |
| 613 return kSuccess; | 639 return kSuccess; |
| 614 } | 640 } |
| 615 | 641 |
| 616 Result onGetScanlines(void* dst, int count, size_t rowBytes) override { | 642 uint32_t onGetScanlines(void* dst, int count, size_t rowBytes) override { |
| 643 // Assume that an error in libpng indicates an incomplete input. |
| 644 int row = 0; |
| 617 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 645 if (setjmp(png_jmpbuf(this->png_ptr()))) { |
| 618 SkCodecPrintf("setjmp long jump!\n"); | 646 SkCodecPrintf("setjmp long jump!\n"); |
| 619 return kInvalidInput; | 647 return row; |
| 620 } | 648 } |
| 621 | 649 |
| 622 void* dstRow = dst; | 650 void* dstRow = dst; |
| 623 bool hasAlpha = false; | 651 bool hasAlpha = false; |
| 624 for (int i = 0; i < count; i++) { | 652 for (; row < count; row++) { |
| 625 png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); | 653 png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); |
| 626 hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow,
fSrcRow)); | 654 hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow,
fSrcRow)); |
| 627 dstRow = SkTAddOffset<void>(dstRow, rowBytes); | 655 dstRow = SkTAddOffset<void>(dstRow, rowBytes); |
| 628 } | 656 } |
| 629 | 657 |
| 630 if (hasAlpha) { | 658 if (hasAlpha) { |
| 631 fAlphaState = kHasAlpha_AlphaState; | 659 fAlphaState = kHasAlpha_AlphaState; |
| 632 } else { | 660 } else { |
| 633 if (kUnknown_AlphaState == fAlphaState) { | 661 if (kUnknown_AlphaState == fAlphaState) { |
| 634 fAlphaState = kOpaque_AlphaState; | 662 fAlphaState = kOpaque_AlphaState; |
| 635 } | 663 } |
| 636 // Otherwise, the AlphaState is unchanged. | 664 // Otherwise, the AlphaState is unchanged. |
| 637 } | 665 } |
| 638 | 666 |
| 639 return kSuccess; | 667 return row; |
| 640 } | 668 } |
| 641 | 669 |
| 642 Result onSkipScanlines(int count) override { | 670 bool onSkipScanlines(int count) override { |
| 643 // FIXME: Could we use the return value of setjmp to specify the type of | 671 // Assume that an error in libpng indicates an incomplete input. |
| 644 // error? | |
| 645 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 672 if (setjmp(png_jmpbuf(this->png_ptr()))) { |
| 646 SkCodecPrintf("setjmp long jump!\n"); | 673 SkCodecPrintf("setjmp long jump!\n"); |
| 647 return kInvalidInput; | 674 return false; |
| 648 } | 675 } |
| 649 //there is a potential tradeoff of memory vs speed created by putting th
is in a loop. | 676 //there is a potential tradeoff of memory vs speed created by putting th
is in a loop. |
| 650 //calling png_read_rows in a loop is insignificantly slower than calling
it once with count | 677 //calling png_read_rows in a loop is insignificantly slower than calling
it once with count |
| 651 //as png_read_rows has it's own loop which calls png_read_row count time
s. | 678 //as png_read_rows has it's own loop which calls png_read_row count time
s. |
| 652 for (int i = 0; i < count; i++) { | 679 for (int row = 0; row < count; row++) { |
| 653 png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); | 680 png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); |
| 654 } | 681 } |
| 655 return SkCodec::kSuccess; | 682 return true; |
| 656 } | 683 } |
| 657 | 684 |
| 658 AlphaState alphaInScanlineDecode() const override { | 685 AlphaState alphaInScanlineDecode() const override { |
| 659 return fAlphaState; | 686 return fAlphaState; |
| 660 } | 687 } |
| 661 | 688 |
| 662 private: | 689 private: |
| 663 AlphaState fAlphaState; | 690 AlphaState fAlphaState; |
| 664 SkAutoMalloc fStorage; | 691 SkAutoMalloc fStorage; |
| 665 uint8_t* fSrcRow; | 692 uint8_t* fSrcRow; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 704 fHeight = dstInfo.height(); | 731 fHeight = dstInfo.height(); |
| 705 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. | 732 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. |
| 706 fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this-
>srcConfig()); | 733 fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this-
>srcConfig()); |
| 707 fGarbageRow.reset(fSrcRowBytes); | 734 fGarbageRow.reset(fSrcRowBytes); |
| 708 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); | 735 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); |
| 709 fCanSkipRewind = true; | 736 fCanSkipRewind = true; |
| 710 | 737 |
| 711 return SkCodec::kSuccess; | 738 return SkCodec::kSuccess; |
| 712 } | 739 } |
| 713 | 740 |
| 714 Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override { | 741 uint32_t onGetScanlines(void* dst, int count, size_t dstRowBytes) override { |
| 715 // rewind stream if have previously called onGetScanlines, | 742 // rewind stream if have previously called onGetScanlines, |
| 716 // since we need entire progressive image to get scanlines | 743 // since we need entire progressive image to get scanlines |
| 717 if (fCanSkipRewind) { | 744 if (fCanSkipRewind) { |
| 718 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. | 745 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. |
| 719 // Next time onGetScanlines is called, we will need to rewind. | 746 // Next time onGetScanlines is called, we will need to rewind. |
| 720 fCanSkipRewind = false; | 747 fCanSkipRewind = false; |
| 721 } else { | 748 } else { |
| 722 // rewindIfNeeded resets fCurrScanline, since it assumes that start | 749 // rewindIfNeeded resets fCurrScanline, since it assumes that start |
| 723 // needs to be called again before scanline decoding. PNG scanline | 750 // needs to be called again before scanline decoding. PNG scanline |
| 724 // decoding is the exception, since it needs to rewind between | 751 // decoding is the exception, since it needs to rewind between |
| 725 // calls to getScanlines. Keep track of fCurrScanline, to undo the | 752 // calls to getScanlines. Keep track of fCurrScanline, to undo the |
| 726 // reset. | 753 // reset. |
| 727 const int currScanline = this->onNextScanline(); | 754 const int currScanline = this->nextScanline(); |
| 728 // This method would never be called if currScanline is -1 | 755 // This method would never be called if currScanline is -1 |
| 729 SkASSERT(currScanline != -1); | 756 SkASSERT(currScanline != -1); |
| 730 | 757 |
| 731 if (!this->rewindIfNeeded()) { | 758 if (!this->rewindIfNeeded()) { |
| 732 return kCouldNotRewind; | 759 return kCouldNotRewind; |
| 733 } | 760 } |
| 734 this->updateNextScanline(currScanline); | 761 this->updateNextScanline(currScanline); |
| 735 } | 762 } |
| 736 | 763 |
| 737 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 764 if (setjmp(png_jmpbuf(this->png_ptr()))) { |
| 738 SkCodecPrintf("setjmp long jump!\n"); | 765 SkCodecPrintf("setjmp long jump!\n"); |
| 739 return kInvalidInput; | 766 // FIXME (msarett): Returning 0 is pessimistic. If we can complete
a single pass, |
| 767 // we may be able to report that all of the memory has been initiali
zed. Even if we |
| 768 // fail on the first pass, we can still report than some scanlines a
re initialized. |
| 769 return 0; |
| 740 } | 770 } |
| 741 SkAutoMalloc storage(count * fSrcRowBytes); | 771 SkAutoMalloc storage(count * fSrcRowBytes); |
| 742 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); | 772 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); |
| 743 uint8_t* srcRow; | 773 uint8_t* srcRow; |
| 744 const int startRow = this->onNextScanline(); | 774 const int startRow = this->nextScanline(); |
| 745 for (int i = 0; i < this->numberPasses(); i++) { | 775 for (int i = 0; i < this->numberPasses(); i++) { |
| 746 // read rows we planned to skip into garbage row | 776 // read rows we planned to skip into garbage row |
| 747 for (int y = 0; y < startRow; y++){ | 777 for (int y = 0; y < startRow; y++){ |
| 748 png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL,
1); | 778 png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL,
1); |
| 749 } | 779 } |
| 750 // read rows we care about into buffer | 780 // read rows we care about into buffer |
| 751 srcRow = storagePtr; | 781 srcRow = storagePtr; |
| 752 for (int y = 0; y < count; y++) { | 782 for (int y = 0; y < count; y++) { |
| 753 png_read_rows(this->png_ptr(), &srcRow, png_bytepp_NULL, 1); | 783 png_read_rows(this->png_ptr(), &srcRow, png_bytepp_NULL, 1); |
| 754 srcRow += fSrcRowBytes; | 784 srcRow += fSrcRowBytes; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 770 | 800 |
| 771 if (hasAlpha) { | 801 if (hasAlpha) { |
| 772 fAlphaState = kHasAlpha_AlphaState; | 802 fAlphaState = kHasAlpha_AlphaState; |
| 773 } else { | 803 } else { |
| 774 if (kUnknown_AlphaState == fAlphaState) { | 804 if (kUnknown_AlphaState == fAlphaState) { |
| 775 fAlphaState = kOpaque_AlphaState; | 805 fAlphaState = kOpaque_AlphaState; |
| 776 } | 806 } |
| 777 // Otherwise, the AlphaState is unchanged. | 807 // Otherwise, the AlphaState is unchanged. |
| 778 } | 808 } |
| 779 | 809 |
| 780 return kSuccess; | 810 return count; |
| 781 } | 811 } |
| 782 | 812 |
| 783 SkCodec::Result onSkipScanlines(int count) override { | 813 bool onSkipScanlines(int count) override { |
| 784 // The non-virtual version will update fCurrScanline. | 814 // The non-virtual version will update fCurrScanline. |
| 785 return SkCodec::kSuccess; | 815 return true; |
| 786 } | 816 } |
| 787 | 817 |
| 788 AlphaState alphaInScanlineDecode() const override { | 818 AlphaState alphaInScanlineDecode() const override { |
| 789 return fAlphaState; | 819 return fAlphaState; |
| 790 } | 820 } |
| 791 | 821 |
| 792 SkScanlineOrder onGetScanlineOrder() const override { | 822 SkScanlineOrder onGetScanlineOrder() const override { |
| 793 return kNone_SkScanlineOrder; | 823 return kNone_SkScanlineOrder; |
| 794 } | 824 } |
| 795 | 825 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 826 | 856 |
| 827 if (1 == numberPasses) { | 857 if (1 == numberPasses) { |
| 828 return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), png_p
tr, info_ptr, | 858 return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), png_p
tr, info_ptr, |
| 829 bitDepth); | 859 bitDepth); |
| 830 } | 860 } |
| 831 | 861 |
| 832 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(),
png_ptr, | 862 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(),
png_ptr, |
| 833 info_ptr, bitDepth, numberPasses); | 863 info_ptr, bitDepth, numberPasses); |
| 834 } | 864 } |
| 835 | 865 |
| OLD | NEW |