Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(599)

Side by Side Diff: src/codec/SkCodec_libpng.cpp

Issue 1332053002: Fill incomplete images in SkCodec parent class (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Rebase on merged SkCodec and SkScanlineDecoder Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698