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

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

Powered by Google App Engine
This is Rietveld 408576698