| 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 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 113 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); | 113 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); |
| 114 return num_trans > 0; | 114 return num_trans > 0; |
| 115 } | 115 } |
| 116 | 116 |
| 117 // Method for coverting to either an SkPMColor or a similarly packed | 117 // Method for coverting to either an SkPMColor or a similarly packed |
| 118 // unpremultiplied color. | 118 // unpremultiplied color. |
| 119 typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); | 119 typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); |
| 120 | 120 |
| 121 // Note: SkColorTable claims to store SkPMColors, which is not necessarily | 121 // Note: SkColorTable claims to store SkPMColors, which is not necessarily |
| 122 // the case here. | 122 // the case here. |
| 123 bool SkPngCodec::decodePalette(bool premultiply, int bitDepth, int* ctableCount)
{ | 123 bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) { |
| 124 int numPalette; | 124 int numPalette; |
| 125 png_colorp palette; | 125 png_colorp palette; |
| 126 png_bytep trans; | 126 png_bytep trans; |
| 127 | 127 |
| 128 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) { | 128 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) { |
| 129 return false; | 129 return false; |
| 130 } | 130 } |
| 131 | 131 |
| 132 // Note: These are not necessarily SkPMColors | 132 // Note: These are not necessarily SkPMColors |
| 133 SkPMColor colorStorage[256]; // worst-case storage | 133 SkPMColor colorStorage[256]; // worst-case storage |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 170 } | 170 } |
| 171 | 171 |
| 172 /* BUGGY IMAGE WORKAROUND | 172 /* BUGGY IMAGE WORKAROUND |
| 173 Invalid images could contain pixel values that are greater than the numb
er of palette | 173 Invalid images could contain pixel values that are greater than the numb
er of palette |
| 174 entries. Since we use pixel values as indices into the palette this coul
d result in reading | 174 entries. Since we use pixel values as indices into the palette this coul
d result in reading |
| 175 beyond the end of the palette which could leak the contents of uninitial
ized memory. To | 175 beyond the end of the palette which could leak the contents of uninitial
ized memory. To |
| 176 ensure this doesn't happen, we grow the colortable to the maximum size t
hat can be | 176 ensure this doesn't happen, we grow the colortable to the maximum size t
hat can be |
| 177 addressed by the bitdepth of the image and fill it with the last palette
color or black if | 177 addressed by the bitdepth of the image and fill it with the last palette
color or black if |
| 178 the palette is empty (really broken image). | 178 the palette is empty (really broken image). |
| 179 */ | 179 */ |
| 180 int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8)); | 180 int colorCount = SkTMax(numPalette, 1 << SkTMin(fBitDepth, 8)); |
| 181 SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0)
; | 181 SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0)
; |
| 182 for (; index < colorCount; index++) { | 182 for (; index < colorCount; index++) { |
| 183 *colorPtr++ = lastColor; | 183 *colorPtr++ = lastColor; |
| 184 } | 184 } |
| 185 | 185 |
| 186 // Set the new color count | 186 // Set the new color count |
| 187 if (ctableCount != NULL) { | 187 if (ctableCount != NULL) { |
| 188 *ctableCount = colorCount; | 188 *ctableCount = colorCount; |
| 189 } | 189 } |
| 190 | 190 |
| (...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 367 : INHERITED(info, stream) | 367 : INHERITED(info, stream) |
| 368 , fPng_ptr(png_ptr) | 368 , fPng_ptr(png_ptr) |
| 369 , fInfo_ptr(info_ptr) | 369 , fInfo_ptr(info_ptr) |
| 370 , fSrcConfig(SkSwizzler::kUnknown) | 370 , fSrcConfig(SkSwizzler::kUnknown) |
| 371 , fNumberPasses(INVALID_NUMBER_PASSES) | 371 , fNumberPasses(INVALID_NUMBER_PASSES) |
| 372 , fReallyHasAlpha(false) | 372 , fReallyHasAlpha(false) |
| 373 , fBitDepth(bitDepth) | 373 , fBitDepth(bitDepth) |
| 374 {} | 374 {} |
| 375 | 375 |
| 376 SkPngCodec::~SkPngCodec() { | 376 SkPngCodec::~SkPngCodec() { |
| 377 // First, ensure that the scanline decoder is left in a finished state. | |
| 378 SkAutoTDelete<SkScanlineDecoder> decoder(this->detachScanlineDecoder()); | |
| 379 if (NULL != decoder) { | |
| 380 this->finish(); | |
| 381 } | |
| 382 | |
| 383 this->destroyReadStruct(); | 377 this->destroyReadStruct(); |
| 384 } | 378 } |
| 385 | 379 |
| 386 void SkPngCodec::destroyReadStruct() { | 380 void SkPngCodec::destroyReadStruct() { |
| 387 if (fPng_ptr) { | 381 if (fPng_ptr) { |
| 388 // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr | 382 // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr |
| 389 SkASSERT(fInfo_ptr); | 383 SkASSERT(fInfo_ptr); |
| 390 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL); | 384 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL); |
| 391 fPng_ptr = NULL; | 385 fPng_ptr = NULL; |
| 392 fInfo_ptr = NULL; | 386 fInfo_ptr = NULL; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 447 // Set to the default before calling decodePalette, which may change it. | 441 // Set to the default before calling decodePalette, which may change it. |
| 448 fReallyHasAlpha = false; | 442 fReallyHasAlpha = false; |
| 449 | 443 |
| 450 //srcColorType was determined in readHeader() which determined png color typ
e | 444 //srcColorType was determined in readHeader() which determined png color typ
e |
| 451 const SkColorType srcColorType = this->getInfo().colorType(); | 445 const SkColorType srcColorType = this->getInfo().colorType(); |
| 452 | 446 |
| 453 switch (srcColorType) { | 447 switch (srcColorType) { |
| 454 case kIndex_8_SkColorType: | 448 case kIndex_8_SkColorType: |
| 455 //decode palette to Skia format | 449 //decode palette to Skia format |
| 456 fSrcConfig = SkSwizzler::kIndex; | 450 fSrcConfig = SkSwizzler::kIndex; |
| 457 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaT
ype(), fBitDepth, | 451 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaT
ype(), |
| 458 ctableCount)) { | 452 ctableCount)) { |
| 459 return kInvalidInput; | 453 return kInvalidInput; |
| 460 } | 454 } |
| 461 break; | 455 break; |
| 462 case kGray_8_SkColorType: | 456 case kGray_8_SkColorType: |
| 463 fSrcConfig = SkSwizzler::kGray; | 457 fSrcConfig = SkSwizzler::kGray; |
| 464 break; | 458 break; |
| 465 case kN32_SkColorType: | 459 case kN32_SkColorType: |
| 466 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) { | 460 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) { |
| 467 fSrcConfig = SkSwizzler::kRGBX; | 461 fSrcConfig = SkSwizzler::kRGBX; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 516 return false; | 510 return false; |
| 517 } | 511 } |
| 518 } | 512 } |
| 519 | 513 |
| 520 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
dst, | 514 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
dst, |
| 521 size_t rowBytes, const Options& options, | 515 size_t rowBytes, const Options& options, |
| 522 SkPMColor ctable[], int* ctableCount) { | 516 SkPMColor ctable[], int* ctableCount) { |
| 523 if (!conversion_possible(requestedInfo, this->getInfo())) { | 517 if (!conversion_possible(requestedInfo, this->getInfo())) { |
| 524 return kInvalidConversion; | 518 return kInvalidConversion; |
| 525 } | 519 } |
| 526 // Do not allow a regular decode if the caller has asked for a scanline deco
der | |
| 527 if (NULL != this->scanlineDecoder()) { | |
| 528 SkCodecPrintf("cannot getPixels() if a scanline decoder has been created
\n"); | |
| 529 return kInvalidParameters; | |
| 530 } | |
| 531 if (requestedInfo.dimensions() != this->getInfo().dimensions()) { | 520 if (requestedInfo.dimensions() != this->getInfo().dimensions()) { |
| 532 return kInvalidScale; | 521 return kInvalidScale; |
| 533 } | 522 } |
| 534 if (!this->handleRewind()) { | 523 if (!this->handleRewind()) { |
| 535 return kCouldNotRewind; | 524 return kCouldNotRewind; |
| 536 } | 525 } |
| 537 | 526 |
| 538 // Note that ctable and ctableCount may be modified if there is a color tabl
e | 527 // Note that ctable and ctableCount may be modified if there is a color tabl
e |
| 539 const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes, | 528 const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes, |
| 540 options, ctable, ctableCount)
; | 529 options, ctable, ctableCount)
; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 589 | 578 |
| 590 this->finish(); | 579 this->finish(); |
| 591 return kSuccess; | 580 return kSuccess; |
| 592 } | 581 } |
| 593 | 582 |
| 594 void SkPngCodec::finish() { | 583 void SkPngCodec::finish() { |
| 595 if (setjmp(png_jmpbuf(fPng_ptr))) { | 584 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 596 // We've already read all the scanlines. This is a success. | 585 // We've already read all the scanlines. This is a success. |
| 597 return; | 586 return; |
| 598 } | 587 } |
| 588 // FIXME: Is this necessary? |
| 599 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ | 589 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ |
| 600 png_read_end(fPng_ptr, fInfo_ptr); | 590 png_read_end(fPng_ptr, fInfo_ptr); |
| 601 } | 591 } |
| 602 | 592 |
| 603 class SkPngScanlineDecoder : public SkScanlineDecoder { | 593 class SkPngScanlineDecoder : public SkScanlineDecoder { |
| 604 public: | 594 public: |
| 605 SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec) | 595 SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec) |
| 606 : INHERITED(dstInfo) | 596 : INHERITED(dstInfo) |
| 607 , fCodec(codec) | 597 , fCodec(codec) |
| 608 , fHasAlpha(false) | 598 , fHasAlpha(false) |
| 609 { | 599 { |
| 610 fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcC
onfig)); | 600 fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcC
onfig)); |
| 611 fSrcRow = static_cast<uint8_t*>(fStorage.get()); | 601 fSrcRow = static_cast<uint8_t*>(fStorage.get()); |
| 612 } | 602 } |
| 613 | 603 |
| 604 ~SkPngScanlineDecoder() { |
| 605 fCodec->finish(); |
| 606 } |
| 607 |
| 614 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) overri
de { | 608 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) overri
de { |
| 615 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { | 609 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { |
| 616 SkCodecPrintf("setjmp long jump!\n"); | 610 SkCodecPrintf("setjmp long jump!\n"); |
| 617 return SkCodec::kInvalidInput; | 611 return SkCodec::kInvalidInput; |
| 618 } | 612 } |
| 619 | 613 |
| 620 for (int i = 0; i < count; i++) { | 614 for (int i = 0; i < count; i++) { |
| 621 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1); | 615 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1); |
| 622 fCodec->fSwizzler->setDstRow(dst); | 616 fCodec->fSwizzler->setDstRow(dst); |
| 623 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(fSrcRow))
; | 617 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(fSrcRow))
; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 638 //as png_read_rows has it's own loop which calls png_read_row count time
s. | 632 //as png_read_rows has it's own loop which calls png_read_row count time
s. |
| 639 for (int i = 0; i < count; i++) { | 633 for (int i = 0; i < count; i++) { |
| 640 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1); | 634 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1); |
| 641 } | 635 } |
| 642 return SkCodec::kSuccess; | 636 return SkCodec::kSuccess; |
| 643 } | 637 } |
| 644 | 638 |
| 645 bool onReallyHasAlpha() const override { return fHasAlpha; } | 639 bool onReallyHasAlpha() const override { return fHasAlpha; } |
| 646 | 640 |
| 647 private: | 641 private: |
| 648 SkPngCodec* fCodec; // Unowned. | 642 SkAutoTDelete<SkPngCodec> fCodec; |
| 649 bool fHasAlpha; | 643 bool fHasAlpha; |
| 650 SkAutoMalloc fStorage; | 644 SkAutoMalloc fStorage; |
| 651 uint8_t* fSrcRow; | 645 uint8_t* fSrcRow; |
| 652 | 646 |
| 653 typedef SkScanlineDecoder INHERITED; | 647 typedef SkScanlineDecoder INHERITED; |
| 654 }; | 648 }; |
| 655 | 649 |
| 656 | 650 |
| 657 class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder { | 651 class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder { |
| 658 public: | 652 public: |
| 659 SkPngInterlacedScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec
) | 653 SkPngInterlacedScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec
) |
| 660 : INHERITED(dstInfo) | 654 : INHERITED(dstInfo) |
| 661 , fCodec(codec) | 655 , fCodec(codec) |
| 662 , fHasAlpha(false) | 656 , fHasAlpha(false) |
| 663 , fCurrentRow(0) | 657 , fCurrentRow(0) |
| 664 , fHeight(dstInfo.height()) | 658 , fHeight(dstInfo.height()) |
| 665 , fRewindNeeded(false) | |
| 666 { | 659 { |
| 667 fSrcRowBytes = dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcC
onfig); | 660 fSrcRowBytes = dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcC
onfig); |
| 668 fGarbageRow.reset(fSrcRowBytes); | 661 fGarbageRow.reset(fSrcRowBytes); |
| 669 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); | 662 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); |
| 670 } | 663 } |
| 671 | 664 |
| 665 ~SkPngInterlacedScanlineDecoder() { |
| 666 fCodec->finish(); |
| 667 } |
| 668 |
| 672 SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) ove
rride { | 669 SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) ove
rride { |
| 673 //rewind stream if have previously called onGetScanlines, | 670 //rewind stream if have previously called onGetScanlines, |
| 674 //since we need entire progressive image to get scanlines | 671 //since we need entire progressive image to get scanlines |
| 675 if (fRewindNeeded) { | 672 if (!fCodec->handleRewind()) { |
| 676 if(false == fCodec->handleRewind()) { | 673 return SkCodec::kCouldNotRewind; |
| 677 return SkCodec::kCouldNotRewind; | |
| 678 } | |
| 679 } else { | |
| 680 fRewindNeeded = true; | |
| 681 } | 674 } |
| 682 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { | 675 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { |
| 683 SkCodecPrintf("setjmp long jump!\n"); | 676 SkCodecPrintf("setjmp long jump!\n"); |
| 684 return SkCodec::kInvalidInput; | 677 return SkCodec::kInvalidInput; |
| 685 } | 678 } |
| 686 const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr); | 679 const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr); |
| 687 SkAutoMalloc storage(count * fSrcRowBytes); | 680 SkAutoMalloc storage(count * fSrcRowBytes); |
| 688 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); | 681 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); |
| 689 uint8_t* srcRow; | 682 uint8_t* srcRow; |
| 690 for (int i = 0; i < number_passes; i++) { | 683 for (int i = 0; i < number_passes; i++) { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 717 | 710 |
| 718 SkCodec::Result onSkipScanlines(int count) override { | 711 SkCodec::Result onSkipScanlines(int count) override { |
| 719 //when ongetScanlines is called it will skip to fCurrentRow | 712 //when ongetScanlines is called it will skip to fCurrentRow |
| 720 fCurrentRow += count; | 713 fCurrentRow += count; |
| 721 return SkCodec::kSuccess; | 714 return SkCodec::kSuccess; |
| 722 } | 715 } |
| 723 | 716 |
| 724 bool onReallyHasAlpha() const override { return fHasAlpha; } | 717 bool onReallyHasAlpha() const override { return fHasAlpha; } |
| 725 | 718 |
| 726 private: | 719 private: |
| 727 SkPngCodec* fCodec; // Unowned. | 720 SkAutoTDelete<SkPngCodec> fCodec; |
| 728 bool fHasAlpha; | 721 bool fHasAlpha; |
| 729 int fCurrentRow; | 722 int fCurrentRow; |
| 730 int fHeight; | 723 int fHeight; |
| 731 size_t fSrcRowBytes; | 724 size_t fSrcRowBytes; |
| 732 bool fRewindNeeded; | 725 SkAutoMalloc fGarbageRow; |
| 733 SkAutoMalloc fGarbageRow; | 726 uint8_t* fGarbageRowPtr; |
| 734 uint8_t* fGarbageRowPtr; | |
| 735 | 727 |
| 736 typedef SkScanlineDecoder INHERITED; | 728 typedef SkScanlineDecoder INHERITED; |
| 737 }; | 729 }; |
| 738 | 730 |
| 739 | 731 |
| 740 SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, | 732 SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, |
| 741 const Options& options, SkPMColor ctable[], int* ctableCount) { | 733 const Options& options, SkPMColor ctable[], int* ctableCount) { |
| 742 if (!conversion_possible(dstInfo, this->getInfo())) { | 734 if (!conversion_possible(dstInfo, this->getInfo())) { |
| 743 SkCodecPrintf("no conversion possible\n"); | 735 SkCodecPrintf("no conversion possible\n"); |
| 744 return NULL; | 736 return NULL; |
| 745 } | 737 } |
| 746 // Check to see if scaling was requested. | 738 // Check to see if scaling was requested. |
| 747 if (dstInfo.dimensions() != this->getInfo().dimensions()) { | 739 if (dstInfo.dimensions() != this->getInfo().dimensions()) { |
| 748 return NULL; | 740 return NULL; |
| 749 } | 741 } |
| 750 if (!this->handleRewind()) { | 742 // Create a new SkPngCodec, to be owned by the scanline decoder. |
| 743 SkStream* stream = this->stream()->duplicate(); |
| 744 if (!stream) { |
| 745 return NULL; |
| 746 } |
| 747 SkAutoTDelete<SkPngCodec> codec (static_cast<SkPngCodec*>(SkPngCodec::NewFro
mStream(stream))); |
| 748 if (!codec) { |
| 751 return NULL; | 749 return NULL; |
| 752 } | 750 } |
| 753 | 751 |
| 754 // Note: We set dst to NULL since we do not know it yet. rowBytes is not nee
ded, | 752 // Note: We set dst to NULL since we do not know it yet. rowBytes is not nee
ded, |
| 755 // since we'll be manually updating the dstRow, but the SkSwizzler requires
it to | 753 // since we'll be manually updating the dstRow, but the SkSwizzler requires
it to |
| 756 // be at least dstInfo.minRowBytes. | 754 // be at least dstInfo.minRowBytes. |
| 757 if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options,
ctable, | 755 if (codec->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options,
ctable, |
| 758 ctableCount) != kSuccess) { | 756 ctableCount) != kSuccess) { |
| 759 SkCodecPrintf("failed to initialize the swizzler.\n"); | 757 SkCodecPrintf("failed to initialize the swizzler.\n"); |
| 760 return NULL; | 758 return NULL; |
| 761 } | 759 } |
| 762 | 760 |
| 763 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES); | 761 SkASSERT(codec->fNumberPasses != INVALID_NUMBER_PASSES); |
| 764 if (fNumberPasses > 1) { | 762 if (codec->fNumberPasses > 1) { |
| 765 // interlaced image | 763 // interlaced image |
| 766 return SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (dstInfo, this)); | 764 return SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (dstInfo, codec.detach
())); |
| 767 } | 765 } |
| 768 | 766 |
| 769 return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, this)); | 767 return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, codec.detach())); |
| 770 } | 768 } |
| 771 | 769 |
| OLD | NEW |