Chromium Code Reviews| Index: src/codec/SkCodec_libpng.cpp |
| diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp |
| index 8e7ee33a9554bce341cf8681b2db86d334bfae1b..186cd945247d771009ffe03281e9063d790ecb3e 100644 |
| --- a/src/codec/SkCodec_libpng.cpp |
| +++ b/src/codec/SkCodec_libpng.cpp |
| @@ -10,6 +10,7 @@ |
| #include "SkColorTable.h" |
| #include "SkBitmap.h" |
| #include "SkMath.h" |
| +#include "SkScanlineDecoder.h" |
| #include "SkSize.h" |
| #include "SkStream.h" |
| #include "SkSwizzler.h" |
| @@ -113,15 +114,13 @@ typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); |
| // Note: SkColorTable claims to store SkPMColors, which is not necessarily |
| // the case here. |
| -SkColorTable* decode_palette(png_structp png_ptr, png_infop info_ptr, |
| - bool premultiply, SkAlphaType* outAlphaType) { |
| - SkASSERT(outAlphaType != NULL); |
| +bool SkPngCodec::decodePalette(bool premultiply) { |
| int numPalette; |
| png_colorp palette; |
| png_bytep trans; |
| - if (!png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette)) { |
| - return NULL; |
| + if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) { |
| + return false; |
| } |
| /* BUGGY IMAGE WORKAROUND |
| @@ -136,8 +135,8 @@ SkColorTable* decode_palette(png_structp png_ptr, png_infop info_ptr, |
| SkPMColor* colorPtr = colorStorage; |
| int numTrans; |
| - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { |
| - png_get_tRNS(png_ptr, info_ptr, &trans, &numTrans, NULL); |
| + if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { |
| + png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, NULL); |
| } else { |
| numTrans = 0; |
| } |
| @@ -164,11 +163,7 @@ SkColorTable* decode_palette(png_structp png_ptr, png_infop info_ptr, |
| palette++; |
| } |
| - if (transLessThanFF < 0) { |
| - *outAlphaType = premultiply ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; |
| - } else { |
| - *outAlphaType = kOpaque_SkAlphaType; |
| - } |
| + fReallyHasAlpha = transLessThanFF < 0; |
| for (; index < numPalette; index++) { |
| *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); |
| @@ -180,7 +175,8 @@ SkColorTable* decode_palette(png_structp png_ptr, png_infop info_ptr, |
| *colorPtr = colorPtr[-1]; |
| } |
| - return SkNEW_ARGS(SkColorTable, (colorStorage, colorCount)); |
| + fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorStorage, colorCount))); |
| + return true; |
| } |
| /////////////////////////////////////////////////////////////////////////////// |
| @@ -332,11 +328,16 @@ SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { |
| return codec; |
| } |
| +#define INVALID_NUMBER_PASSES -1 |
| SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, |
| png_structp png_ptr, png_infop info_ptr) |
| : INHERITED(info, stream) |
| , fPng_ptr(png_ptr) |
| - , fInfo_ptr(info_ptr) {} |
| + , fInfo_ptr(info_ptr) |
| + , fSc(SkSwizzler::kUnknown) |
| + , fNumberPasses(INVALID_NUMBER_PASSES) |
| + , fReallyHasAlpha(false) |
| +{} |
| SkPngCodec::~SkPngCodec() { |
| png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL); |
| @@ -365,29 +366,8 @@ static bool conversion_possible(const SkImageInfo& A, const SkImageInfo& B) { |
| || premul_and_unpremul(B.alphaType(), A.alphaType()); |
| } |
| -SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, |
| - size_t rowBytes, SkPMColor ctable[], |
| - int* ctableCount) { |
| - if (!this->rewindIfNeeded()) { |
| - return kCouldNotRewind; |
| - } |
| - if (requestedInfo.dimensions() != this->getOriginalInfo().dimensions()) { |
| - return kInvalidScale; |
| - } |
| - if (!conversion_possible(requestedInfo, this->getOriginalInfo())) { |
| - return kInvalidConversion; |
| - } |
| - |
| - SkBitmap decodedBitmap; |
| - // If installPixels would have failed, getPixels should have failed before |
| - // calling onGetPixels. |
| - SkAssertResult(decodedBitmap.installPixels(requestedInfo, dst, rowBytes)); |
| - |
| - // Initialize all non-trivial objects before setjmp. |
| - SkAutoTUnref<SkColorTable> colorTable; |
| - SkAutoTDelete<SkSwizzler> swizzler; |
| - SkAutoMalloc storage; // Scratch memory for pre-swizzled rows. |
| - |
| +SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, void* dst, |
| + size_t rowBytes) { |
| // FIXME: Could we use the return value of setjmp to specify the type of |
| // error? |
| if (setjmp(png_jmpbuf(fPng_ptr))) { |
| @@ -401,42 +381,31 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
| png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth, |
| &pngColorType, &interlaceType, int_p_NULL, int_p_NULL); |
| - const int numberPasses = (interlaceType != PNG_INTERLACE_NONE) ? |
| + fNumberPasses = (interlaceType != PNG_INTERLACE_NONE) ? |
| png_set_interlace_handling(fPng_ptr) : 1; |
| - SkSwizzler::SrcConfig sc; |
| - bool reallyHasAlpha = false; |
| + // Set to the default before calling decodePalette, which may change it. |
| + fReallyHasAlpha = false; |
| if (PNG_COLOR_TYPE_PALETTE == pngColorType) { |
| - sc = SkSwizzler::kIndex; |
| - SkAlphaType at = requestedInfo.alphaType(); |
| - colorTable.reset(decode_palette(fPng_ptr, fInfo_ptr, |
| - kPremul_SkAlphaType == at, |
| - &at)); |
| - if (!colorTable) { |
| + fSc = SkSwizzler::kIndex; |
| + if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType())) { |
| return kInvalidInput; |
| } |
| - |
| - reallyHasAlpha = (at != kOpaque_SkAlphaType); |
| - |
| - if (at != requestedInfo.alphaType()) { |
| - // It turns out the image is opaque. |
| - SkASSERT(kOpaque_SkAlphaType == at); |
| - } |
| } else if (kAlpha_8_SkColorType == requestedInfo.colorType()) { |
| // Note: we check the destination, since otherwise we would have |
| // told png to upscale. |
| SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType); |
| - sc = SkSwizzler::kGray; |
| + fSc = SkSwizzler::kGray; |
| } else if (this->getOriginalInfo().alphaType() == kOpaque_SkAlphaType) { |
| - sc = SkSwizzler::kRGBX; |
| + fSc = SkSwizzler::kRGBX; |
| } else { |
| - sc = SkSwizzler::kRGBA; |
| + fSc = SkSwizzler::kRGBA; |
| } |
| - const SkPMColor* colors = colorTable ? colorTable->readColors() : NULL; |
| + const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL; |
| // TODO: Support skipZeroes. |
| - swizzler.reset(SkSwizzler::CreateSwizzler(sc, colors, requestedInfo, |
| - dst, rowBytes, false)); |
| - if (!swizzler) { |
| + fSwizzler.reset(SkSwizzler::CreateSwizzler(fSc, colors, requestedInfo, |
| + dst, rowBytes, false)); |
| + if (!fSwizzler) { |
| // FIXME: CreateSwizzler could fail for another reason. |
| return kUnimplemented; |
| } |
| @@ -445,16 +414,46 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
| // made in the factory. |
| png_read_update_info(fPng_ptr, fInfo_ptr); |
| - if (numberPasses > 1) { |
| + return kSuccess; |
| +} |
| + |
| +SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, |
| + size_t rowBytes, SkPMColor ctable[], |
| + int* ctableCount) { |
| + if (!this->rewindIfNeeded()) { |
| + return kCouldNotRewind; |
| + } |
| + if (requestedInfo.dimensions() != this->getOriginalInfo().dimensions()) { |
| + return kInvalidScale; |
| + } |
| + if (!conversion_possible(requestedInfo, this->getOriginalInfo())) { |
| + return kInvalidConversion; |
| + } |
| + |
| + const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes); |
| + if (result != kSuccess) { |
| + return result; |
| + } |
| + |
| + // FIXME: Could we use the return value of setjmp to specify the type of |
| + // error? |
| + if (setjmp(png_jmpbuf(fPng_ptr))) { |
| + SkDebugf("setjmp long jump!\n"); |
| + return kInvalidInput; |
| + } |
| + |
| + SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES); |
| + SkAutoMalloc storage; |
| + if (fNumberPasses > 1) { |
| const int width = requestedInfo.width(); |
| const int height = requestedInfo.height(); |
| - const int bpp = SkSwizzler::BytesPerPixel(sc); |
| + const int bpp = SkSwizzler::BytesPerPixel(fSc); |
| const size_t rowBytes = width * bpp; |
| storage.reset(width * height * bpp); |
| uint8_t* const base = static_cast<uint8_t*>(storage.get()); |
| - for (int i = 0; i < numberPasses; i++) { |
| + for (int i = 0; i < fNumberPasses; i++) { |
| uint8_t* row = base; |
| for (int y = 0; y < height; y++) { |
| uint8_t* bmRow = row; |
| @@ -466,27 +465,111 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
| // Now swizzle it. |
| uint8_t* row = base; |
| for (int y = 0; y < height; y++) { |
| - reallyHasAlpha |= swizzler->next(row); |
| + fReallyHasAlpha |= fSwizzler->next(row); |
| row += rowBytes; |
| } |
| } else { |
| - storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(sc)); |
| + storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSc)); |
| 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); |
| - reallyHasAlpha |= swizzler->next(srcRow); |
| + fReallyHasAlpha |= fSwizzler->next(srcRow); |
| } |
| } |
| + // 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. |
| + |
| + return this->finish(); |
| +} |
| + |
| +SkImageGenerator::Result SkPngCodec::finish() { |
| + // FIXME: Maybe these should return incomplete? |
| + if (setjmp(png_jmpbuf(fPng_ptr))) { |
| + SkDebugf("setjmp long jump!\n"); |
| + return kInvalidInput; |
| + } |
| /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ |
| png_read_end(fPng_ptr, fInfo_ptr); |
| - // FIXME: do we need substituteTranspColor? |
| + return kSuccess; |
| +} |
| + |
| +class SkPngScanlineDecoder : public SkScanlineDecoder { |
| +public: |
| + // FIXME: Need to support subsets... |
| + SkPngScanlineDecoder(const SkImageInfo& origInfo, const SkImageInfo& dstInfo, |
| + SkPngCodec* codec) |
| + // FIXME: Does SkScanlineDecoder need origInfo? Maybe for subsets? |
| + : INHERITED(origInfo, dstInfo) |
| + , fDstInfo(dstInfo) |
| + , fCodec(codec) |
| + , fHasAlpha(false) |
| + { |
| + fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSc)); |
| + fSrcRow = static_cast<uint8_t*>(fStorage.get()); |
| + } |
| + |
| + SkImageGenerator::Result onGetNextScanline(void* dst) SK_OVERRIDE { |
|
reed1
2015/03/19 15:31:39
Is it easy to override the N-scanlines method here
scroggo
2015/03/19 17:59:19
Yes.
|
| + // FIXME: Could we use the return value of setjmp to specify the type of |
| + // error? |
| + if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { |
| + SkDebugf("setjmp long jump!\n"); |
| + return SkImageGenerator::kInvalidInput; |
| + } |
| - if (reallyHasAlpha && requestedInfo.alphaType() != kOpaque_SkAlphaType) { |
| - // FIXME: We want to alert the caller. Is this the right way? |
| - SkImageInfo* modInfo = const_cast<SkImageInfo*>(&requestedInfo); |
| - *modInfo = requestedInfo.makeAlphaType(kOpaque_SkAlphaType); |
| + png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1); |
| + fCodec->fSwizzler->setDstRow(dst); |
| + fHasAlpha |= fCodec->fSwizzler->next(fSrcRow); |
| + return SkImageGenerator::kSuccess; |
| } |
| - return kSuccess; |
| + |
| + SkImageGenerator::Result onFinish() SK_OVERRIDE { |
| + return fCodec->finish(); |
| + } |
| + |
| + bool onReallyHasAlpha() const SK_OVERRIDE { return fHasAlpha; } |
| + |
| +private: |
| + const SkImageInfo& fDstInfo; |
| + SkPngCodec* fCodec; // Unowned. |
| + bool fHasAlpha; |
| + SkAutoMalloc fStorage; |
| + uint8_t* fSrcRow; |
| + |
| + typedef SkScanlineDecoder INHERITED; |
| +}; |
| + |
| +SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, |
| + const SkIRect& origSubset) { |
| + // Check to see if scaling was requested. |
| + if (dstInfo.dimensions() != origSubset.size()) { |
| + SkDebugf("Scaling not supported: dimensions: %d %d\toriginal: %d %d\n", |
| + dstInfo.width(), dstInfo.height(), |
| + origSubset.width(), origSubset.height()); |
| + return NULL; |
| + } |
| + |
| + if (!conversion_possible(dstInfo, this->getOriginalInfo())) { |
| + SkDebugf("no conversion possible\n"); |
| + return NULL; |
| + } |
| + |
| + // Note: We set dst to NULL since we do not know it yet. rowBytes is not needed, |
| + // since we'll be manually updating the dstRow, but the SkSwizzler requires it to |
| + // be at least dstInfo.minRowBytes. |
| + if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes()) != kSuccess) { |
| + SkDebugf("failed to initialize the swizzler.\n"); |
| + return NULL; |
| + } |
| + |
| + SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES); |
| + if (fNumberPasses > 1) { |
| + // We cannot efficiently do scanline decoding. |
| + return NULL; |
| + } |
| + |
| + return SkNEW_ARGS(SkPngScanlineDecoder, (this->getOriginalInfo(), dstInfo, this)); |
| } |
| + |