Index: src/codec/SkPngCodec.cpp |
diff --git a/src/codec/SkPngCodec.cpp b/src/codec/SkPngCodec.cpp |
index 232373c04407c63c2b391c148a7053db9e9d62e9..e7ddc7eb7a7c808b2656c3435f3d90b6a0435aa3 100644 |
--- a/src/codec/SkPngCodec.cpp |
+++ b/src/codec/SkPngCodec.cpp |
@@ -373,10 +373,16 @@ void SkPngCodec::destroyReadStruct() { |
// Getting the pixels |
/////////////////////////////////////////////////////////////////////////////// |
-SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
- const Options& options, |
- SkPMColor ctable[], |
- int* ctableCount) { |
+void SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, const Options& options) { |
+ const SkPMColor* colors = get_color_ptr(fColorTable.get()); |
+ fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo, options)); |
+ SkASSERT(fSwizzler); |
+} |
+ |
+SkCodec::Result SkPngCodec::prepareToDecode(const SkImageInfo& requestedInfo, |
+ const Options& options, |
+ SkPMColor ctable[], |
+ int* ctableCount) { |
// FIXME: Could we use the return value of setjmp to specify the type of |
// error? |
if (setjmp(png_jmpbuf(fPng_ptr))) { |
@@ -388,10 +394,16 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
// suggestedColorType was determined in read_header() based on the encodedColorType |
const SkColorType suggestedColorType = this->getInfo().colorType(); |
+ // If the conversion provided by the swizzler would be a no-op, we may be able |
+ // to skip the swizzle step. |
+ bool skipSwizzle = false; |
+ |
switch (suggestedColorType) { |
case kIndex_8_SkColorType: |
- //decode palette to Skia format |
fSrcConfig = SkSwizzler::kIndex; |
+ skipSwizzle = (kIndex_8_SkColorType == requestedInfo.colorType()); |
+ |
+ // Decode palette to Skia format |
if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(), |
ctableCount)) { |
return kInvalidInput; |
@@ -399,6 +411,7 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
break; |
case kGray_8_SkColorType: |
fSrcConfig = SkSwizzler::kGray; |
+ skipSwizzle = (kGray_8_SkColorType == requestedInfo.colorType()); |
break; |
case kN32_SkColorType: { |
const uint8_t encodedColorType = png_get_color_type(fPng_ptr, fInfo_ptr); |
@@ -417,6 +430,9 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
fSrcConfig = SkSwizzler::kRGB; |
} else { |
fSrcConfig = SkSwizzler::kRGBA; |
+#ifdef SK_PMCOLOR_IS_RGBA |
+ skipSwizzle = (kUnpremul_SkAlphaType == this->getInfo().alphaType()); |
+#endif |
} |
} |
break; |
@@ -429,14 +445,27 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
// Copy the color table to the client if they request kIndex8 mode |
copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); |
+ // If this is a subset decode, we will need a swizzler. |
+ if (options.fSubset) { |
+ skipSwizzle = false; |
+ } |
+ |
// Create the swizzler. SkPngCodec retains ownership of the color table. |
- const SkPMColor* colors = get_color_ptr(fColorTable.get()); |
- fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo, options)); |
- SkASSERT(fSwizzler); |
+ if (!skipSwizzle) { |
+ this->initializeSwizzler(requestedInfo, options); |
+ } |
return kSuccess; |
} |
+SkSampler* SkPngCodec::getSampler(bool createIfNecessary) { |
+ if (!createIfNecessary || fSwizzler) { |
+ return fSwizzler; |
+ } |
+ |
+ this->initializeSwizzler(this->dstInfo(), this->options()); |
+ return fSwizzler; |
+} |
bool SkPngCodec::onRewind() { |
// This sets fPng_ptr and fInfo_ptr to nullptr. If read_header |
@@ -471,10 +500,15 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
} |
// Note that ctable and ctableCount may be modified if there is a color table |
- const Result result = this->initializeSwizzler(requestedInfo, options, ctable, ctableCount); |
+ const Result result = this->prepareToDecode(requestedInfo, options, ctable, ctableCount); |
if (result != kSuccess) { |
return result; |
} |
+ |
+ const int width = requestedInfo.width(); |
+ const int height = requestedInfo.height(); |
+ const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); |
+ |
// FIXME: Could we use the return value of setjmp to specify the type of |
// error? |
int row = 0; |
@@ -484,7 +518,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
// Assume that any error that occurs while reading rows is caused by an incomplete input. |
if (fNumberPasses > 1) { |
// FIXME (msarett): Handle incomplete interlaced pngs. |
- return kInvalidInput; |
+ return row == height ? kSuccess : kInvalidInput; |
} |
// FIXME: We do a poor job on incomplete pngs compared to other decoders (ex: Chromium, |
// Ubuntu Image Viewer). This is because we use the default buffer size in libpng (8192 |
@@ -498,52 +532,51 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
// it have on regular decode performance? Should we investigate using a different API |
// instead of png_read_row(s)? Chromium uses png_process_data. |
*rowsDecoded = row; |
- return kIncompleteInput; |
+ return row == height ? kSuccess : kIncompleteInput; |
} |
// FIXME: We could split these out based on subclass. |
- void* dstRow = dst; |
if (fNumberPasses > 1) { |
- const int width = requestedInfo.width(); |
- const int height = requestedInfo.height(); |
- const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); |
const size_t srcRowBytes = width * bpp; |
- |
- storage.reset(width * height * bpp); |
- uint8_t* const base = storage.get(); |
- |
+ const size_t rowBytes = fSwizzler ? srcRowBytes : dstRowBytes; |
+ uint8_t* const base = fSwizzler ? storage.reset(width * height * bpp) : (uint8_t*) dst; |
for (int i = 0; i < fNumberPasses; i++) { |
- uint8_t* srcRow = base; |
+ uint8_t* rowPtr = base; |
for (int y = 0; y < height; y++) { |
- uint8_t* bmRow = srcRow; |
- png_read_rows(fPng_ptr, &bmRow, nullptr, 1); |
- srcRow += srcRowBytes; |
+ png_read_rows(fPng_ptr, &rowPtr, nullptr, 1); |
+ rowPtr += rowBytes; |
} |
} |
- // Now swizzle it. |
- uint8_t* srcRow = base; |
- for (int y = 0; y < height; y++) { |
- fSwizzler->swizzle(dstRow, srcRow); |
- dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
- srcRow += srcRowBytes; |
+ // Swizzle if necessary. |
+ if (fSwizzler) { |
+ uint8_t* srcRow = base; |
+ for (int y = 0; y < height; y++) { |
+ fSwizzler->swizzle(dst, srcRow); |
+ dst = SkTAddOffset<void>(dst, dstRowBytes); |
+ srcRow += srcRowBytes; |
+ } |
} |
} else { |
- storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig)); |
- uint8_t* srcRow = storage.get(); |
- for (; row < requestedInfo.height(); row++) { |
- png_read_rows(fPng_ptr, &srcRow, nullptr, 1); |
- // FIXME: Only call IsOpaque once, outside the loop. Same for onGetScanlines. |
- fSwizzler->swizzle(dstRow, srcRow); |
- dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
+ if (fSwizzler) { |
+ uint8_t* rowPtr = storage.reset(width * bpp); |
+ for (; row < requestedInfo.height(); row++) { |
+ png_read_rows(fPng_ptr, &rowPtr, nullptr, 1); |
+ fSwizzler->swizzle(dst, rowPtr); |
+ dst = SkTAddOffset<void>(dst, dstRowBytes); |
+ } |
+ } else { |
+ uint8_t* rowPtr = (uint8_t*) dst; |
+ SkAutoMalloc storage(sizeof(uint8_t*) * requestedInfo.height()); |
+ uint8_t** ptrs = (uint8_t**) storage.get(); |
+ for (int i = 0; i < requestedInfo.height(); i++) { |
+ ptrs[i] = rowPtr; |
+ rowPtr += dstRowBytes; |
+ } |
+ png_read_rows(fPng_ptr, ptrs, nullptr, requestedInfo.height()); |
} |
} |
- if (setjmp(png_jmpbuf(fPng_ptr))) { |
- // We've already read all the scanlines. This is a success. |
- return kSuccess; |
- } |
- |
// read rest of file, and get additional comment and time chunks in info_ptr |
png_read_end(fPng_ptr, fInfo_ptr); |
@@ -573,8 +606,7 @@ public: |
return kInvalidConversion; |
} |
- const Result result = this->initializeSwizzler(dstInfo, options, ctable, |
- ctableCount); |
+ const Result result = this->prepareToDecode(dstInfo, options, ctable, ctableCount); |
if (result != kSuccess) { |
return result; |
} |
@@ -593,11 +625,23 @@ public: |
return row; |
} |
- void* dstRow = dst; |
+ uint8_t* rowPtr; |
+ if (this->swizzler()) { |
+ rowPtr = fSrcRow; |
+ } else { |
+ // Write decoded pixels directly to dst. |
+ rowPtr = (uint8_t*) dst; |
+ } |
+ |
for (; row < count; row++) { |
- png_read_rows(this->png_ptr(), &fSrcRow, nullptr, 1); |
- this->swizzler()->swizzle(dstRow, fSrcRow); |
- dstRow = SkTAddOffset<void>(dstRow, rowBytes); |
+ png_read_rows(this->png_ptr(), &rowPtr, nullptr, 1); |
+ |
+ if (this->swizzler()) { |
+ this->swizzler()->swizzle(dst, rowPtr); |
+ dst = SkTAddOffset<void>(dst, rowBytes); |
+ } else { |
+ rowPtr += rowBytes; |
+ } |
} |
return row; |
@@ -644,8 +688,7 @@ public: |
return kInvalidConversion; |
} |
- const Result result = this->initializeSwizzler(dstInfo, options, ctable, |
- ctableCount); |
+ const Result result = this->prepareToDecode(dstInfo, options, ctable, ctableCount); |
if (result != kSuccess) { |
return result; |
} |
@@ -690,9 +733,10 @@ public: |
// 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; |
+ SkAutoTMalloc<uint8_t> storage; |
+ uint8_t* const base = this->swizzler() ? |
+ storage.reset(count * fSrcRowBytes) : (uint8_t*) dst; |
+ const size_t rowBytes = this->swizzler() ? fSrcRowBytes : dstRowBytes; |
const int startRow = this->nextScanline(); |
for (int i = 0; i < this->numberPasses(); i++) { |
// read rows we planned to skip into garbage row |
@@ -700,23 +744,26 @@ public: |
png_read_rows(this->png_ptr(), &fGarbageRowPtr, nullptr, 1); |
} |
// read rows we care about into buffer |
- srcRow = storagePtr; |
+ uint8_t* rowPtr = base; |
for (int y = 0; y < count; y++) { |
- png_read_rows(this->png_ptr(), &srcRow, nullptr, 1); |
- srcRow += fSrcRowBytes; |
+ png_read_rows(this->png_ptr(), &rowPtr, nullptr, 1); |
+ rowPtr += rowBytes; |
} |
// 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, nullptr, 1); |
} |
} |
- //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; |
+ |
+ // Swizzle if necessary |
+ if (this->swizzler()) { |
+ uint8_t* srcRow = storage.get(); |
+ void* dstRow = dst; |
+ for (int y = 0; y < count; y++) { |
+ this->swizzler()->swizzle(dstRow, srcRow); |
+ dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
+ srcRow += fSrcRowBytes; |
+ } |
} |
return count; |