diff options
-rw-r--r-- | dm/DMSrcSink.cpp | 67 | ||||
-rw-r--r-- | src/codec/SkCodec_libpng.cpp | 96 | ||||
-rw-r--r-- | src/codec/SkCodec_libpng.h | 1 |
3 files changed, 135 insertions, 29 deletions
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index 98f3ba4620..51c79dac74 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -140,7 +140,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const { } switch (fMode) { - case kNormal_Mode: + case kNormal_Mode: { switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), NULL, colorPtr, colorCountPtr)) { case SkImageGenerator::kSuccess: @@ -156,23 +156,22 @@ Error CodecSrc::draw(SkCanvas* canvas) const { } canvas->drawBitmap(bitmap, 0, 0); break; + } case kScanline_Mode: { SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder(decodeInfo, NULL, colorPtr, colorCountPtr); if (NULL == scanlineDecoder) { return Error::Nonfatal("Cannot use scanline decoder for all images"); } - for (int y = 0; y < decodeInfo.height(); ++y) { - const SkImageGenerator::Result result = scanlineDecoder->getScanlines( - bitmap.getAddr(0, y), 1, 0); - switch (result) { - case SkImageGenerator::kSuccess: - case SkImageGenerator::kIncompleteInput: - break; - default: - return SkStringPrintf("%s failed after %d scanlines with error message %d", - fPath.c_str(), y-1, (int) result); - } + const SkImageGenerator::Result result = scanlineDecoder->getScanlines( + bitmap.getAddr(0, 0), decodeInfo.height(), bitmap.rowBytes()); + switch (result) { + case SkImageGenerator::kSuccess: + case SkImageGenerator::kIncompleteInput: + break; + default: + return SkStringPrintf("%s failed with error message %d", + fPath.c_str(), (int) result); } canvas->drawBitmap(bitmap, 0, 0); break; @@ -207,8 +206,9 @@ Error CodecSrc::draw(SkCanvas* canvas) const { return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(), largestSubsetDecodeInfo.width(), largestSubsetDecodeInfo.height()); } - char* line = SkNEW_ARRAY(char, decodeInfo.minRowBytes()); - SkAutoTDeleteArray<char> lineDeleter(line); + const size_t rowBytes = decodeInfo.minRowBytes(); + char* buffer = SkNEW_ARRAY(char, largestSubsetDecodeInfo.height() * rowBytes); + SkAutoTDeleteArray<char> lineDeleter(buffer); for (int col = 0; col < divisor; col++) { //currentSubsetWidth may be larger than subsetWidth for rightmost subsets const int currentSubsetWidth = (col + 1 == divisor) ? @@ -247,21 +247,33 @@ Error CodecSrc::draw(SkCanvas* canvas) const { bounds.setXYWH(0, 0, currentSubsetWidth, currentSubsetHeight); SkAssertResult(largestSubsetBm.extractSubset(&subsetBm, bounds)); SkAutoLockPixels autlockSubsetBm(subsetBm, true); + const SkImageGenerator::Result subsetResult = + subsetScanlineDecoder->getScanlines(buffer, currentSubsetHeight, rowBytes); + switch (subsetResult) { + case SkImageGenerator::kSuccess: + case SkImageGenerator::kIncompleteInput: + break; + default: + return SkStringPrintf("%s failed with error message %d", + fPath.c_str(), (int) subsetResult); + } + const size_t bpp = decodeInfo.bytesPerPixel(); + /* + * we copy all the lines at once becuase when calling getScanlines for + * interlaced pngs the entire image must be read regardless of the number + * of lines requested. Reading an interlaced png in a loop, line-by-line, would + * decode the entire image height times, which is very slow + * it is aknowledged that copying each line as you read it in a loop + * may be faster for other types of images. Since this is a correctness test + * that's okay. + */ + char* bufferRow = buffer; for (int subsetY = 0; subsetY < currentSubsetHeight; ++subsetY) { - const SkImageGenerator::Result subsetResult = - subsetScanlineDecoder->getScanlines(line, 1, 0); - const size_t bpp = decodeInfo.bytesPerPixel(); - //copy section of line based on x value - memcpy(subsetBm.getAddr(0, subsetY), line + x*bpp, currentSubsetWidth*bpp); - switch (subsetResult) { - case SkImageGenerator::kSuccess: - case SkImageGenerator::kIncompleteInput: - break; - default: - return SkStringPrintf("%s failed after %d scanlines with error" - "message %d", fPath.c_str(), y-1, (int) subsetResult); - } + memcpy(subsetBm.getAddr(0, subsetY), bufferRow + x*bpp, + currentSubsetWidth*bpp); + bufferRow += rowBytes; } + canvas->drawBitmap(subsetBm, SkIntToScalar(x), SkIntToScalar(y)); } } @@ -341,6 +353,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const { } } canvas->drawBitmap(bitmap, 0, 0); + break; } } return ""; diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp index c2a032e84e..444e2ad87e 100644 --- a/src/codec/SkCodec_libpng.cpp +++ b/src/codec/SkCodec_libpng.cpp @@ -648,6 +648,98 @@ private: typedef SkScanlineDecoder INHERITED; }; + +class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder { +public: + SkPngInterlacedScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec) + : INHERITED(dstInfo) + , fCodec(codec) + , fHasAlpha(false) + , fCurrentRow(0) + , fHeight(dstInfo.height()) + , fSrcRowBytes(dstInfo.minRowBytes()) + , fRewindNeeded(false) + { + fGarbageRow.reset(fSrcRowBytes); + fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); + } + + SkImageGenerator::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 (fRewindNeeded) { + if(false == fCodec->handleRewind()) { + return SkImageGenerator::kCouldNotRewind; + } + } else { + fRewindNeeded = true; + } + if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { + SkCodecPrintf("setjmp long jump!\n"); + return SkImageGenerator::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); + } + //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); + 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); + } + } + //swizzle the rows we care about + srcRow = storagePtr; + for (int y = 0; y < count; y++) { + fCodec->fSwizzler->setDstRow(dst); + fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(srcRow)); + dst = SkTAddOffset<void>(dst, dstRowBytes); + srcRow += fSrcRowBytes; + } + fCurrentRow += count; + return SkImageGenerator::kSuccess; + } + + SkImageGenerator::Result onSkipScanlines(int count) override { + //when ongetScanlines is called it will skip to fCurrentRow + fCurrentRow += count; + return SkImageGenerator::kSuccess; + } + + void onFinish() override { + fCodec->finish(); + } + + bool onReallyHasAlpha() const override { return fHasAlpha; } + +private: + SkPngCodec* fCodec; // Unowned. + bool fHasAlpha; + int fCurrentRow; + int fHeight; + size_t fSrcRowBytes; + bool fRewindNeeded; + SkAutoMalloc fGarbageRow; + uint8_t* fGarbageRowPtr; + + + + + + typedef SkScanlineDecoder INHERITED; +}; + + SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, const Options& options, SkPMColor ctable[], int* ctableCount) { if (!this->handleRewind()) { @@ -675,8 +767,8 @@ SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES); if (fNumberPasses > 1) { - // We cannot efficiently do scanline decoding. - return NULL; + // interlaced image + return SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (dstInfo, this)); } return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, this)); diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h index 3146331764..a105c3c5e3 100644 --- a/src/codec/SkCodec_libpng.h +++ b/src/codec/SkCodec_libpng.h @@ -58,6 +58,7 @@ private: void destroyReadStruct(); friend class SkPngScanlineDecoder; + friend class SkPngInterlacedScanlineDecoder; typedef SkCodec INHERITED; }; |