| Index: src/codec/SkPngCodec.cpp
|
| diff --git a/src/codec/SkPngCodec.cpp b/src/codec/SkPngCodec.cpp
|
| index a5ff9fcc96655b9cd36987b89ba5409e4ae83e7b..1ad7f8006ee85a0a8016e129dd15104993fe8108 100644
|
| --- a/src/codec/SkPngCodec.cpp
|
| +++ b/src/codec/SkPngCodec.cpp
|
| @@ -261,6 +261,206 @@ sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
|
| return nullptr;
|
| }
|
|
|
| +static int bytes_per_pixel(int bitsPerPixel) {
|
| + // Note that we will have to change this implementation if we start
|
| + // supporting outputs from libpng that are less than 8-bits per component.
|
| + return bitsPerPixel / 8;
|
| +}
|
| +
|
| +// Subclass of SkPngCodec which supports scanline decoding
|
| +class SkPngScanlineDecoder : public SkPngCodec {
|
| +public:
|
| + SkPngScanlineDecoder(int width, int height, const SkEncodedInfo& info, SkStream* stream,
|
| + SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_ptr, int bitDepth,
|
| + sk_sp<SkColorSpace> colorSpace)
|
| + : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1,
|
| + colorSpace)
|
| + , fSrcRow(nullptr)
|
| + {}
|
| +
|
| + Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
|
| + SkPMColor ctable[], int* ctableCount) override {
|
| + if (!conversion_possible(dstInfo, this->getInfo())) {
|
| + return kInvalidConversion;
|
| + }
|
| +
|
| + const Result result = this->initializeSwizzler(dstInfo, options, ctable,
|
| + ctableCount);
|
| + if (result != kSuccess) {
|
| + return result;
|
| + }
|
| +
|
| + fStorage.reset(this->getInfo().width() *
|
| + (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel())));
|
| + fSrcRow = fStorage.get();
|
| +
|
| + return kSuccess;
|
| + }
|
| +
|
| + int onGetScanlines(void* dst, int count, size_t rowBytes) override {
|
| + // Assume that an error in libpng indicates an incomplete input.
|
| + int row = 0;
|
| + if (setjmp(png_jmpbuf(this->png_ptr()))) {
|
| + SkCodecPrintf("setjmp long jump!\n");
|
| + return row;
|
| + }
|
| +
|
| + void* dstRow = dst;
|
| + for (; row < count; row++) {
|
| + png_read_row(this->png_ptr(), fSrcRow, nullptr);
|
| + this->swizzler()->swizzle(dstRow, fSrcRow);
|
| + dstRow = SkTAddOffset<void>(dstRow, rowBytes);
|
| + }
|
| +
|
| + return row;
|
| + }
|
| +
|
| + bool onSkipScanlines(int count) override {
|
| + // Assume that an error in libpng indicates an incomplete input.
|
| + if (setjmp(png_jmpbuf(this->png_ptr()))) {
|
| + SkCodecPrintf("setjmp long jump!\n");
|
| + return false;
|
| + }
|
| +
|
| + for (int row = 0; row < count; row++) {
|
| + png_read_row(this->png_ptr(), fSrcRow, nullptr);
|
| + }
|
| + return true;
|
| + }
|
| +
|
| +private:
|
| + SkAutoTMalloc<uint8_t> fStorage;
|
| + uint8_t* fSrcRow;
|
| +
|
| + typedef SkPngCodec INHERITED;
|
| +};
|
| +
|
| +
|
| +class SkPngInterlacedScanlineDecoder : public SkPngCodec {
|
| +public:
|
| + SkPngInterlacedScanlineDecoder(int width, int height, const SkEncodedInfo& info,
|
| + SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr,
|
| + png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpace> colorSpace)
|
| + : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr, bitDepth,
|
| + numberPasses, colorSpace)
|
| + , fHeight(-1)
|
| + , fCanSkipRewind(false)
|
| + {
|
| + SkASSERT(numberPasses != 1);
|
| + }
|
| +
|
| + Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
|
| + SkPMColor ctable[], int* ctableCount) override {
|
| + if (!conversion_possible(dstInfo, this->getInfo())) {
|
| + return kInvalidConversion;
|
| + }
|
| +
|
| + const Result result = this->initializeSwizzler(dstInfo, options, ctable,
|
| + ctableCount);
|
| + if (result != kSuccess) {
|
| + return result;
|
| + }
|
| +
|
| + fHeight = dstInfo.height();
|
| + // FIXME: This need not be called on a second call to onStartScanlineDecode.
|
| + fSrcRowBytes = this->getInfo().width() *
|
| + (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()));
|
| + fGarbageRow.reset(fSrcRowBytes);
|
| + fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
|
| + fCanSkipRewind = true;
|
| +
|
| + return SkCodec::kSuccess;
|
| + }
|
| +
|
| + int 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 onStartScanlineDecode, so there is no reason to rewind.
|
| + // Next time onGetScanlines is called, we will need to rewind.
|
| + fCanSkipRewind = false;
|
| + } 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->nextScanline();
|
| + // This method would never be called if currScanline is -1
|
| + SkASSERT(currScanline != -1);
|
| +
|
| + if (!this->rewindIfNeeded()) {
|
| + return kCouldNotRewind;
|
| + }
|
| + this->updateCurrScanline(currScanline);
|
| + }
|
| +
|
| + if (setjmp(png_jmpbuf(this->png_ptr()))) {
|
| + SkCodecPrintf("setjmp long jump!\n");
|
| + // FIXME (msarett): Returning 0 is pessimistic. If we can complete a single pass,
|
| + // we may be able to report that all of the memory has been initialized. Even if we
|
| + // fail on the first pass, we can still report than some scanlines are initialized.
|
| + return 0;
|
| + }
|
| + SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes);
|
| + uint8_t* storagePtr = storage.get();
|
| + uint8_t* srcRow;
|
| + const int startRow = this->nextScanline();
|
| + 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_row(this->png_ptr(), fGarbageRowPtr, nullptr);
|
| + }
|
| + // read rows we care about into buffer
|
| + srcRow = storagePtr;
|
| + for (int y = 0; y < count; y++) {
|
| + png_read_row(this->png_ptr(), srcRow, nullptr);
|
| + srcRow += fSrcRowBytes;
|
| + }
|
| + // read rows we don't want into garbage buffer
|
| + for (int y = 0; y < fHeight - startRow - count; y++) {
|
| + png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr);
|
| + }
|
| + }
|
| + //swizzle the rows we care about
|
| + srcRow = storagePtr;
|
| + void* dstRow = dst;
|
| + for (int y = 0; y < count; y++) {
|
| + this->swizzler()->swizzle(dstRow, srcRow);
|
| + dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
|
| + srcRow += fSrcRowBytes;
|
| + }
|
| +
|
| + return count;
|
| + }
|
| +
|
| + bool onSkipScanlines(int count) override {
|
| + // The non-virtual version will update fCurrScanline.
|
| + return true;
|
| + }
|
| +
|
| + SkScanlineOrder onGetScanlineOrder() const override {
|
| + return kNone_SkScanlineOrder;
|
| + }
|
| +
|
| +private:
|
| + int fHeight;
|
| + size_t fSrcRowBytes;
|
| + SkAutoMalloc fGarbageRow;
|
| + uint8_t* fGarbageRowPtr;
|
| + // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function
|
| + // 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
|
| + // 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 SkPngCodec INHERITED;
|
| +};
|
| +
|
| // Reads the header and initializes the output fields, if not NULL.
|
| //
|
| // @param stream Input data. Will be read to get enough information to properly
|
| @@ -268,23 +468,17 @@ sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
|
| // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL.
|
| // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is
|
| // expected to continue to own it for the lifetime of the png_ptr.
|
| +// @param outCodec Optional output variable. If non-NULL, will be set to a new
|
| +// SkPngCodec on success.
|
| // @param png_ptrp Optional output variable. If non-NULL, will be set to a new
|
| // png_structp on success.
|
| // @param info_ptrp Optional output variable. If non-NULL, will be set to a new
|
| // png_infop on success;
|
| -// @param info Optional output variable. If non-NULL, will be set to
|
| -// reflect the properties of the encoded image on success.
|
| -// @param bitDepthPtr Optional output variable. If non-NULL, will be set to the
|
| -// bit depth of the encoded image on success.
|
| -// @param numberPassesPtr Optional output variable. If non-NULL, will be set to
|
| -// the number_passes of the encoded image on success.
|
| // @return true on success, in which case the caller is responsible for calling
|
| // png_destroy_read_struct(png_ptrp, info_ptrp).
|
| // If it returns false, the passed in fields (except stream) are unchanged.
|
| -static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader,
|
| - png_structp* png_ptrp, png_infop* info_ptrp,
|
| - int* width, int* height, SkEncodedInfo* info, int* bitDepthPtr,
|
| - int* numberPassesPtr) {
|
| +static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec** outCodec,
|
| + png_structp* png_ptrp, png_infop* info_ptrp) {
|
| // 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);
|
| @@ -327,10 +521,6 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader,
|
| png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
| &encodedColorType, nullptr, nullptr, nullptr);
|
|
|
| - if (bitDepthPtr) {
|
| - *bitDepthPtr = bitDepth;
|
| - }
|
| -
|
| // Tell libpng to strip 16 bit/color files down to 8 bits/color.
|
| // TODO: Should we handle this in SkSwizzler? Could this also benefit
|
| // RAW decodes?
|
| @@ -401,19 +591,7 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader,
|
| }
|
|
|
| int numberPasses = png_set_interlace_handling(png_ptr);
|
| - if (numberPassesPtr) {
|
| - *numberPassesPtr = numberPasses;
|
| - }
|
|
|
| - if (info) {
|
| - *info = SkEncodedInfo::Make(color, alpha, 8);
|
| - }
|
| - if (width) {
|
| - *width = origWidth;
|
| - }
|
| - if (height) {
|
| - *height = origHeight;
|
| - }
|
| autoClean.release();
|
| if (png_ptrp) {
|
| *png_ptrp = png_ptr;
|
| @@ -422,6 +600,19 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader,
|
| *info_ptrp = info_ptr;
|
| }
|
|
|
| + if (outCodec) {
|
| + sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr);
|
| + SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
|
| +
|
| + if (1 == numberPasses) {
|
| + *outCodec = new SkPngScanlineDecoder(origWidth, origHeight, info, stream,
|
| + chunkReader, png_ptr, info_ptr, bitDepth, colorSpace);
|
| + } else {
|
| + *outCodec = new SkPngInterlacedScanlineDecoder(origWidth, origHeight, info, stream,
|
| + chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colorSpace);
|
| + }
|
| + }
|
| +
|
| return true;
|
| }
|
|
|
| @@ -496,8 +687,7 @@ bool SkPngCodec::onRewind() {
|
|
|
| png_structp png_ptr;
|
| png_infop info_ptr;
|
| - if (!read_header(this->stream(), fPngChunkReader.get(), &png_ptr, &info_ptr,
|
| - nullptr, nullptr, nullptr, nullptr, nullptr)) {
|
| + if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, &info_ptr)) {
|
| return false;
|
| }
|
|
|
| @@ -506,12 +696,6 @@ bool SkPngCodec::onRewind() {
|
| return true;
|
| }
|
|
|
| -static int bytes_per_pixel(int bitsPerPixel) {
|
| - // Note that we will have to change this implementation if we start
|
| - // supporting outputs from libpng that are less than 8-bits per component.
|
| - return bitsPerPixel / 8;
|
| -}
|
| -
|
| SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
|
| size_t dstRowBytes, const Options& options,
|
| SkPMColor ctable[], int* ctableCount,
|
| @@ -606,222 +790,16 @@ uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const {
|
| return INHERITED::onGetFillValue(colorType);
|
| }
|
|
|
| -// Subclass of SkPngCodec which supports scanline decoding
|
| -class SkPngScanlineDecoder : public SkPngCodec {
|
| -public:
|
| - SkPngScanlineDecoder(int width, int height, const SkEncodedInfo& info, SkStream* stream,
|
| - SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_ptr, int bitDepth,
|
| - sk_sp<SkColorSpace> colorSpace)
|
| - : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1,
|
| - colorSpace)
|
| - , fSrcRow(nullptr)
|
| - {}
|
| -
|
| - Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
|
| - SkPMColor ctable[], int* ctableCount) override {
|
| - if (!conversion_possible(dstInfo, this->getInfo())) {
|
| - return kInvalidConversion;
|
| - }
|
| -
|
| - const Result result = this->initializeSwizzler(dstInfo, options, ctable,
|
| - ctableCount);
|
| - if (result != kSuccess) {
|
| - return result;
|
| - }
|
| -
|
| - fStorage.reset(this->getInfo().width() *
|
| - (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel())));
|
| - fSrcRow = fStorage.get();
|
| -
|
| - return kSuccess;
|
| - }
|
| -
|
| - int onGetScanlines(void* dst, int count, size_t rowBytes) override {
|
| - // Assume that an error in libpng indicates an incomplete input.
|
| - int row = 0;
|
| - if (setjmp(png_jmpbuf(this->png_ptr()))) {
|
| - SkCodecPrintf("setjmp long jump!\n");
|
| - return row;
|
| - }
|
| -
|
| - void* dstRow = dst;
|
| - for (; row < count; row++) {
|
| - png_read_row(this->png_ptr(), fSrcRow, nullptr);
|
| - this->swizzler()->swizzle(dstRow, fSrcRow);
|
| - dstRow = SkTAddOffset<void>(dstRow, rowBytes);
|
| - }
|
| -
|
| - return row;
|
| - }
|
| -
|
| - bool onSkipScanlines(int count) override {
|
| - // Assume that an error in libpng indicates an incomplete input.
|
| - if (setjmp(png_jmpbuf(this->png_ptr()))) {
|
| - SkCodecPrintf("setjmp long jump!\n");
|
| - return false;
|
| - }
|
| -
|
| - for (int row = 0; row < count; row++) {
|
| - png_read_row(this->png_ptr(), fSrcRow, nullptr);
|
| - }
|
| - return true;
|
| - }
|
| -
|
| -private:
|
| - SkAutoTMalloc<uint8_t> fStorage;
|
| - uint8_t* fSrcRow;
|
| -
|
| - typedef SkPngCodec INHERITED;
|
| -};
|
| -
|
| -
|
| -class SkPngInterlacedScanlineDecoder : public SkPngCodec {
|
| -public:
|
| - SkPngInterlacedScanlineDecoder(int width, int height, const SkEncodedInfo& info,
|
| - SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr,
|
| - png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpace> colorSpace)
|
| - : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr, bitDepth,
|
| - numberPasses, colorSpace)
|
| - , fHeight(-1)
|
| - , fCanSkipRewind(false)
|
| - {
|
| - SkASSERT(numberPasses != 1);
|
| - }
|
| -
|
| - Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
|
| - SkPMColor ctable[], int* ctableCount) override {
|
| - if (!conversion_possible(dstInfo, this->getInfo())) {
|
| - return kInvalidConversion;
|
| - }
|
| -
|
| - const Result result = this->initializeSwizzler(dstInfo, options, ctable,
|
| - ctableCount);
|
| - if (result != kSuccess) {
|
| - return result;
|
| - }
|
| -
|
| - fHeight = dstInfo.height();
|
| - // FIXME: This need not be called on a second call to onStartScanlineDecode.
|
| - fSrcRowBytes = this->getInfo().width() *
|
| - (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()));
|
| - fGarbageRow.reset(fSrcRowBytes);
|
| - fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
|
| - fCanSkipRewind = true;
|
| -
|
| - return SkCodec::kSuccess;
|
| - }
|
| -
|
| - int 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 onStartScanlineDecode, so there is no reason to rewind.
|
| - // Next time onGetScanlines is called, we will need to rewind.
|
| - fCanSkipRewind = false;
|
| - } 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->nextScanline();
|
| - // This method would never be called if currScanline is -1
|
| - SkASSERT(currScanline != -1);
|
| -
|
| - if (!this->rewindIfNeeded()) {
|
| - return kCouldNotRewind;
|
| - }
|
| - this->updateCurrScanline(currScanline);
|
| - }
|
| -
|
| - if (setjmp(png_jmpbuf(this->png_ptr()))) {
|
| - SkCodecPrintf("setjmp long jump!\n");
|
| - // FIXME (msarett): Returning 0 is pessimistic. If we can complete a single pass,
|
| - // we may be able to report that all of the memory has been initialized. Even if we
|
| - // fail on the first pass, we can still report than some scanlines are initialized.
|
| - return 0;
|
| - }
|
| - SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes);
|
| - uint8_t* storagePtr = storage.get();
|
| - uint8_t* srcRow;
|
| - const int startRow = this->nextScanline();
|
| - 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_row(this->png_ptr(), fGarbageRowPtr, nullptr);
|
| - }
|
| - // read rows we care about into buffer
|
| - srcRow = storagePtr;
|
| - for (int y = 0; y < count; y++) {
|
| - png_read_row(this->png_ptr(), srcRow, nullptr);
|
| - srcRow += fSrcRowBytes;
|
| - }
|
| - // read rows we don't want into garbage buffer
|
| - for (int y = 0; y < fHeight - startRow - count; y++) {
|
| - png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr);
|
| - }
|
| - }
|
| - //swizzle the rows we care about
|
| - srcRow = storagePtr;
|
| - void* dstRow = dst;
|
| - for (int y = 0; y < count; y++) {
|
| - this->swizzler()->swizzle(dstRow, srcRow);
|
| - dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
|
| - srcRow += fSrcRowBytes;
|
| - }
|
| -
|
| - return count;
|
| - }
|
| -
|
| - bool onSkipScanlines(int count) override {
|
| - // The non-virtual version will update fCurrScanline.
|
| - return true;
|
| - }
|
| -
|
| - SkScanlineOrder onGetScanlineOrder() const override {
|
| - return kNone_SkScanlineOrder;
|
| - }
|
| -
|
| -private:
|
| - int fHeight;
|
| - size_t fSrcRowBytes;
|
| - SkAutoMalloc fGarbageRow;
|
| - uint8_t* fGarbageRowPtr;
|
| - // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function
|
| - // 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
|
| - // 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 SkPngCodec INHERITED;
|
| -};
|
| -
|
| SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) {
|
| SkAutoTDelete<SkStream> streamDeleter(stream);
|
| - png_structp png_ptr;
|
| - png_infop info_ptr;
|
| - int width, height;
|
| - SkEncodedInfo imageInfo;
|
| - int bitDepth;
|
| - int numberPasses;
|
| -
|
| - if (!read_header(stream, chunkReader, &png_ptr, &info_ptr, &width, &height, &imageInfo,
|
| - &bitDepth, &numberPasses)) {
|
| - return nullptr;
|
| - }
|
| -
|
| - auto colorSpace = read_color_space(png_ptr, info_ptr);
|
|
|
| - if (1 == numberPasses) {
|
| - return new SkPngScanlineDecoder(width, height, imageInfo, streamDeleter.release(),
|
| - chunkReader, png_ptr, info_ptr, bitDepth, colorSpace);
|
| + SkCodec* outCodec;
|
| + if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) {
|
| + // Codec has taken ownership of the stream.
|
| + SkASSERT(outCodec);
|
| + streamDeleter.release();
|
| + return outCodec;
|
| }
|
|
|
| - return new SkPngInterlacedScanlineDecoder(width, height, imageInfo, streamDeleter.release(),
|
| - chunkReader, png_ptr, info_ptr, bitDepth,
|
| - numberPasses, colorSpace);
|
| + return nullptr;
|
| }
|
|
|