aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--dm/DMSrcSink.cpp67
-rw-r--r--src/codec/SkCodec_libpng.cpp96
-rw-r--r--src/codec/SkCodec_libpng.h1
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;
};