Index: src/codec/SkCodec_libpng.cpp |
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp |
index 2ba2d5bb9f2bed42209d9c2ca5b0ce4ce127bba0..faba79f6a2faffcc9fe87c50862df599be940594 100644 |
--- a/src/codec/SkCodec_libpng.cpp |
+++ b/src/codec/SkCodec_libpng.cpp |
@@ -12,7 +12,6 @@ |
#include "SkBitmap.h" |
#include "SkMath.h" |
#include "SkScaledCodec.h" |
-#include "SkScanlineDecoder.h" |
#include "SkSize.h" |
#include "SkStream.h" |
#include "SkSwizzler.h" |
@@ -163,7 +162,10 @@ bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) { |
palette++; |
} |
- fReallyHasAlpha = transLessThanFF < 0; |
+ if (transLessThanFF >= 0) { |
+ // No transparent colors were found. |
+ fAlphaState = kOpaque_AlphaState; |
+ } |
for (; index < numPalette; index++) { |
*colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); |
@@ -216,7 +218,8 @@ bool SkPngCodec::IsPng(SkStream* stream) { |
// png_destroy_read_struct. If it returns false, the passed in fields (except |
// stream) are unchanged. |
static bool read_header(SkStream* stream, png_structp* png_ptrp, |
- png_infop* info_ptrp, SkImageInfo* imageInfo, int* bitDepthPtr) { |
+ png_infop* info_ptrp, SkImageInfo* imageInfo, |
+ int* bitDepthPtr, int* numberPassesPtr) { |
// The image is known to be a PNG. Decode enough to know the SkImageInfo. |
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, |
sk_error_fn, sk_warning_fn); |
@@ -333,6 +336,11 @@ static bool read_header(SkStream* stream, png_structp* png_ptrp, |
SkASSERT(false); |
} |
+ int numberPasses = png_set_interlace_handling(png_ptr); |
+ if (numberPassesPtr) { |
+ *numberPassesPtr = numberPasses; |
+ } |
+ |
// FIXME: Also need to check for sRGB (skbug.com/3471). |
if (imageInfo) { |
@@ -349,29 +357,21 @@ static bool read_header(SkStream* stream, png_structp* png_ptrp, |
return true; |
} |
-SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { |
- SkAutoTDelete<SkStream> streamDeleter(stream); |
- png_structp png_ptr; |
- png_infop info_ptr; |
- SkImageInfo imageInfo; |
- int bitDepth; |
- if (read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth)) { |
- return new SkPngCodec(imageInfo, streamDeleter.detach(), png_ptr, info_ptr, bitDepth); |
- } |
- return nullptr; |
-} |
- |
-#define INVALID_NUMBER_PASSES -1 |
SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, |
- png_structp png_ptr, png_infop info_ptr, int bitDepth) |
+ png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses) |
: INHERITED(info, stream) |
, fPng_ptr(png_ptr) |
, fInfo_ptr(info_ptr) |
, fSrcConfig(SkSwizzler::kUnknown) |
- , fNumberPasses(INVALID_NUMBER_PASSES) |
- , fReallyHasAlpha(false) |
+ , fNumberPasses(numberPasses) |
, fBitDepth(bitDepth) |
-{} |
+{ |
+ if (info.alphaType() == kOpaque_SkAlphaType) { |
+ fAlphaState = kOpaque_AlphaState; |
+ } else { |
+ fAlphaState = kUnknown_AlphaState; |
+ } |
+} |
SkPngCodec::~SkPngCodec() { |
this->destroyReadStruct(); |
@@ -401,13 +401,9 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
SkCodecPrintf("setjmp long jump!\n"); |
return kInvalidInput; |
} |
- fNumberPasses = png_set_interlace_handling(fPng_ptr); |
png_read_update_info(fPng_ptr, fInfo_ptr); |
- // Set to the default before calling decodePalette, which may change it. |
- fReallyHasAlpha = false; |
- |
- //srcColorType was determined in readHeader() which determined png color type |
+ //srcColorType was determined in read_header() which determined png color type |
const SkColorType srcColorType = this->getInfo().colorType(); |
switch (srcColorType) { |
@@ -459,7 +455,7 @@ bool SkPngCodec::onRewind() { |
png_structp png_ptr; |
png_infop info_ptr; |
- if (!read_header(this->stream(), &png_ptr, &info_ptr, nullptr, nullptr)) { |
+ if (!read_header(this->stream(), &png_ptr, &info_ptr, nullptr, nullptr, nullptr)) { |
return false; |
} |
@@ -498,7 +494,8 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
return kInvalidInput; |
} |
- SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES); |
+ bool hasAlpha = false; |
+ // FIXME: We could split these out based on subclass. |
SkAutoMalloc storage; |
void* dstRow = dst; |
if (fNumberPasses > 1) { |
@@ -522,7 +519,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
// Now swizzle it. |
uint8_t* srcRow = base; |
for (int y = 0; y < height; y++) { |
- fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)); |
+ hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)); |
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
srcRow += srcRowBytes; |
} |
@@ -531,11 +528,18 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
uint8_t* srcRow = static_cast<uint8_t*>(storage.get()); |
for (int y = 0; y < requestedInfo.height(); y++) { |
png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1); |
- fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)); |
+ // FIXME: Only call IsOpaque once, outside the loop. Same for onGetScanlines. |
+ hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)); |
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
} |
} |
+ if (hasAlpha) { |
+ fAlphaState = kHasAlpha_AlphaState; |
+ } else { |
+ fAlphaState = kOpaque_AlphaState; |
+ } |
+ |
// FIXME: do we need substituteTranspColor? Note that we cannot do it for |
// scanline decoding, but we could do it here. Alternatively, we could do |
// it as we go, instead of in post-processing like SkPNGImageDecoder. |
@@ -547,136 +551,170 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
// read rest of file, and get additional comment and time chunks in info_ptr |
png_read_end(fPng_ptr, fInfo_ptr); |
+ |
return kSuccess; |
} |
-class SkPngScanlineDecoder : public SkScanlineDecoder { |
+bool SkPngCodec::onReallyHasAlpha() const { |
+ switch (fAlphaState) { |
+ case kOpaque_AlphaState: |
+ return false; |
+ case kUnknown_AlphaState: |
+ // Maybe the subclass knows? |
+ return this->alphaInScanlineDecode() == kHasAlpha_AlphaState; |
+ case kHasAlpha_AlphaState: |
+ switch (this->alphaInScanlineDecode()) { |
+ case kUnknown_AlphaState: |
+ // Scanline decoder must not have been used. Return our knowledge. |
+ return true; |
+ case kOpaque_AlphaState: |
+ // Scanline decoder was used, and did not find alpha in its subset. |
+ return false; |
+ case kHasAlpha_AlphaState: |
+ return true; |
+ } |
+ } |
+ |
+ // All valid AlphaStates have been covered, so this should not be reached. |
+ SkASSERT(false); |
+ return true; |
+} |
+ |
+// Subclass of SkPngCodec which supports scanline decoding |
+class SkPngScanlineDecoder : public SkPngCodec { |
public: |
- SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec) |
- : INHERITED(srcInfo) |
- , fCodec(codec) |
- , fHasAlpha(false) |
+ SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, |
+ png_structp png_ptr, png_infop info_ptr, int bitDepth) |
+ : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, 1) |
+ , fSrcRow(nullptr) |
+ , fAlphaState(kUnknown_AlphaState) |
{} |
- SkCodec::Result onStart(const SkImageInfo& dstInfo, |
- const SkCodec::Options& options, |
- SkPMColor ctable[], int* ctableCount) override { |
- if (!fCodec->rewindIfNeeded()) { |
- return SkCodec::kCouldNotRewind; |
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, |
+ SkPMColor ctable[], int* ctableCount) override { |
+ if (!this->rewindIfNeeded()) { |
+ return kCouldNotRewind; |
} |
if (!conversion_possible(dstInfo, this->getInfo())) { |
- return SkCodec::kInvalidConversion; |
+ return kInvalidConversion; |
} |
// Check to see if scaling was requested. |
if (dstInfo.dimensions() != this->getInfo().dimensions()) { |
if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { |
- return SkCodec::kInvalidScale; |
+ return kInvalidScale; |
} |
} |
- const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable, |
- ctableCount); |
- if (result != SkCodec::kSuccess) { |
+ const Result result = this->initializeSwizzler(dstInfo, options, ctable, |
+ ctableCount); |
+ if (result != kSuccess) { |
return result; |
} |
- fHasAlpha = false; |
- fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig)); |
+ fAlphaState = kUnknown_AlphaState; |
+ fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig())); |
fSrcRow = static_cast<uint8_t*>(fStorage.get()); |
- return SkCodec::kSuccess; |
+ return kSuccess; |
} |
- SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override { |
- if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { |
+ Result onGetScanlines(void* dst, int count, size_t rowBytes) override { |
+ if (setjmp(png_jmpbuf(this->png_ptr()))) { |
SkCodecPrintf("setjmp long jump!\n"); |
- return SkCodec::kInvalidInput; |
+ return kInvalidInput; |
} |
void* dstRow = dst; |
+ bool hasAlpha = false; |
for (int i = 0; i < count; i++) { |
- png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1); |
- fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, fSrcRow)); |
+ png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); |
+ hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, fSrcRow)); |
dstRow = SkTAddOffset<void>(dstRow, rowBytes); |
} |
- return SkCodec::kSuccess; |
+ |
+ if (hasAlpha) { |
+ fAlphaState = kHasAlpha_AlphaState; |
+ } else { |
+ if (kUnknown_AlphaState == fAlphaState) { |
+ fAlphaState = kOpaque_AlphaState; |
+ } |
+ // Otherwise, the AlphaState is unchanged. |
+ } |
+ |
+ return kSuccess; |
} |
- SkCodec::Result onSkipScanlines(int count) override { |
+ Result onSkipScanlines(int count) override { |
// FIXME: Could we use the return value of setjmp to specify the type of |
// error? |
- if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { |
+ if (setjmp(png_jmpbuf(this->png_ptr()))) { |
SkCodecPrintf("setjmp long jump!\n"); |
- return SkCodec::kInvalidInput; |
+ return kInvalidInput; |
} |
//there is a potential tradeoff of memory vs speed created by putting this in a loop. |
//calling png_read_rows in a loop is insignificantly slower than calling it once with count |
//as png_read_rows has it's own loop which calls png_read_row count times. |
for (int i = 0; i < count; i++) { |
- png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1); |
+ png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); |
} |
return SkCodec::kSuccess; |
} |
- bool onReallyHasAlpha() const override { return fHasAlpha; } |
- |
- SkEncodedFormat onGetEncodedFormat() const override { |
- return kPNG_SkEncodedFormat; |
+ AlphaState alphaInScanlineDecode() const override { |
+ return fAlphaState; |
} |
- |
private: |
- SkAutoTDelete<SkPngCodec> fCodec; |
- bool fHasAlpha; |
+ AlphaState fAlphaState; |
SkAutoMalloc fStorage; |
uint8_t* fSrcRow; |
- typedef SkScanlineDecoder INHERITED; |
+ typedef SkPngCodec INHERITED; |
}; |
-class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder { |
+class SkPngInterlacedScanlineDecoder : public SkPngCodec { |
public: |
- SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec) |
- : INHERITED(srcInfo) |
- , fCodec(codec) |
- , fHasAlpha(false) |
- , fCurrentRow(0) |
- , fHeight(srcInfo.height()) |
+ SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, |
+ png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses) |
+ : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, numberPasses) |
+ , fAlphaState(kUnknown_AlphaState) |
+ , fHeight(-1) |
, fCanSkipRewind(false) |
- {} |
+ { |
+ SkASSERT(numberPasses != 1); |
+ } |
- SkCodec::Result onStart(const SkImageInfo& dstInfo, |
- const SkCodec::Options& options, |
- SkPMColor ctable[], int* ctableCount) override |
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, |
+ SkPMColor ctable[], int* ctableCount) override |
{ |
- if (!fCodec->rewindIfNeeded()) { |
- return SkCodec::kCouldNotRewind; |
+ if (!this->rewindIfNeeded()) { |
+ return kCouldNotRewind; |
} |
if (!conversion_possible(dstInfo, this->getInfo())) { |
- return SkCodec::kInvalidConversion; |
+ return kInvalidConversion; |
} |
// Check to see if scaling was requested. |
if (dstInfo.dimensions() != this->getInfo().dimensions()) { |
if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { |
- return SkCodec::kInvalidScale; |
+ return kInvalidScale; |
} |
} |
- const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable, |
- ctableCount); |
- if (result != SkCodec::kSuccess) { |
+ const Result result = this->initializeSwizzler(dstInfo, options, ctable, |
+ ctableCount); |
+ if (result != kSuccess) { |
return result; |
} |
- fHasAlpha = false; |
- fCurrentRow = 0; |
+ fAlphaState = kUnknown_AlphaState; |
fHeight = dstInfo.height(); |
- fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig); |
+ // FIXME: This need not be called on a second call to onStartScanlineDecode. |
+ fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig()); |
fGarbageRow.reset(fSrcRowBytes); |
fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); |
fCanSkipRewind = true; |
@@ -684,73 +722,90 @@ public: |
return SkCodec::kSuccess; |
} |
- SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override { |
+ Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override { |
// rewind stream if have previously called onGetScanlines, |
// since we need entire progressive image to get scanlines |
if (fCanSkipRewind) { |
- // We already rewound in onStart, so there is no reason to rewind. |
+ // We already rewound in onStartScanlineDecode, so there is no reason to rewind. |
// Next time onGetScanlines is called, we will need to rewind. |
fCanSkipRewind = false; |
- } else if (!fCodec->rewindIfNeeded()) { |
- return SkCodec::kCouldNotRewind; |
+ } else { |
+ // rewindIfNeeded resets fCurrScanline, since it assumes that start |
+ // needs to be called again before scanline decoding. PNG scanline |
+ // decoding is the exception, since it needs to rewind between |
+ // calls to getScanlines. Keep track of fCurrScanline, to undo the |
+ // reset. |
+ const int currScanline = this->onNextScanline(); |
+ // This method would never be called if currScanline is -1 |
+ SkASSERT(currScanline != -1); |
+ |
+ if (!this->rewindIfNeeded()) { |
+ return kCouldNotRewind; |
+ } |
+ this->updateNextScanline(currScanline); |
} |
- if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { |
+ if (setjmp(png_jmpbuf(this->png_ptr()))) { |
SkCodecPrintf("setjmp long jump!\n"); |
- return SkCodec::kInvalidInput; |
+ return kInvalidInput; |
} |
- const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr); |
SkAutoMalloc storage(count * fSrcRowBytes); |
uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); |
uint8_t* srcRow; |
- for (int i = 0; i < number_passes; i++) { |
- //read rows we planned to skip into garbage row |
- for (int y = 0; y < fCurrentRow; y++){ |
- png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1); |
+ const int startRow = this->onNextScanline(); |
+ for (int i = 0; i < this->numberPasses(); i++) { |
+ // read rows we planned to skip into garbage row |
+ for (int y = 0; y < startRow; y++){ |
+ png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1); |
} |
- //read rows we care about into buffer |
+ // read rows we care about into buffer |
srcRow = storagePtr; |
for (int y = 0; y < count; y++) { |
- png_read_rows(fCodec->fPng_ptr, &srcRow, png_bytepp_NULL, 1); |
+ png_read_rows(this->png_ptr(), &srcRow, png_bytepp_NULL, 1); |
srcRow += fSrcRowBytes; |
} |
- //read rows we don't want into garbage buffer |
- for (int y = 0; y < fHeight - fCurrentRow - count; y++) { |
- png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1); |
+ // read rows we don't want into garbage buffer |
+ for (int y = 0; y < fHeight - startRow - count; y++) { |
+ png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1); |
} |
} |
//swizzle the rows we care about |
srcRow = storagePtr; |
void* dstRow = dst; |
+ bool hasAlpha = false; |
for (int y = 0; y < count; y++) { |
- fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, srcRow)); |
+ hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, srcRow)); |
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
srcRow += fSrcRowBytes; |
} |
- fCurrentRow += count; |
- return SkCodec::kSuccess; |
+ |
+ if (hasAlpha) { |
+ fAlphaState = kHasAlpha_AlphaState; |
+ } else { |
+ if (kUnknown_AlphaState == fAlphaState) { |
+ fAlphaState = kOpaque_AlphaState; |
+ } |
+ // Otherwise, the AlphaState is unchanged. |
+ } |
+ |
+ return kSuccess; |
} |
SkCodec::Result onSkipScanlines(int count) override { |
- //when ongetScanlines is called it will skip to fCurrentRow |
- fCurrentRow += count; |
+ // The non-virtual version will update fCurrScanline. |
return SkCodec::kSuccess; |
} |
- bool onReallyHasAlpha() const override { return fHasAlpha; } |
+ AlphaState alphaInScanlineDecode() const override { |
+ return fAlphaState; |
+ } |
SkScanlineOrder onGetScanlineOrder() const override { |
return kNone_SkScanlineOrder; |
} |
- SkEncodedFormat onGetEncodedFormat() const override { |
- return kPNG_SkEncodedFormat; |
- } |
- |
private: |
- SkAutoTDelete<SkPngCodec> fCodec; |
- bool fHasAlpha; |
- int fCurrentRow; |
+ AlphaState fAlphaState; |
int fHeight; |
size_t fSrcRowBytes; |
SkAutoMalloc fGarbageRow; |
@@ -759,30 +814,33 @@ private: |
// is called whenever some action is taken that reads the stream and |
// therefore the next call will require a rewind. So it modifies a boolean |
// to note that the *next* time it is called a rewind is needed. |
- // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling onStart |
- // followed by onGetScanlines does *not* require a rewind. Since |
- // rewindIfNeeded does not have this flexibility, we need to add another |
- // layer. |
+ // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling |
+ // onStartScanlineDecode followed by onGetScanlines does *not* require a |
+ // rewind. Since rewindIfNeeded does not have this flexibility, we need to |
+ // add another layer. |
bool fCanSkipRewind; |
- typedef SkScanlineDecoder INHERITED; |
+ typedef SkPngCodec INHERITED; |
}; |
-SkScanlineDecoder* SkPngCodec::NewSDFromStream(SkStream* stream) { |
- SkAutoTDelete<SkPngCodec> codec (static_cast<SkPngCodec*>(SkPngCodec::NewFromStream(stream))); |
- if (!codec) { |
+SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { |
+ SkAutoTDelete<SkStream> streamDeleter(stream); |
+ png_structp png_ptr; |
+ png_infop info_ptr; |
+ SkImageInfo imageInfo; |
+ int bitDepth; |
+ int numberPasses; |
+ |
+ if (!read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth, &numberPasses)) { |
return nullptr; |
} |
- codec->fNumberPasses = png_set_interlace_handling(codec->fPng_ptr); |
- SkASSERT(codec->fNumberPasses != INVALID_NUMBER_PASSES); |
- |
- const SkImageInfo& srcInfo = codec->getInfo(); |
- if (codec->fNumberPasses > 1) { |
- // interlaced image |
- return new SkPngInterlacedScanlineDecoder(srcInfo, codec.detach()); |
+ if (1 == numberPasses) { |
+ return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr, info_ptr, |
+ bitDepth); |
} |
- return new SkPngScanlineDecoder(srcInfo, codec.detach()); |
+ return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr, |
+ info_ptr, bitDepth, numberPasses); |
} |