Index: src/codec/SkCodec_libpng.cpp |
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp |
index 121b74ff0b3a97defc00a75ed5c2c1cabda5b2e4..250d815f6bf2aecd3204d9eb3dbc070db19166aa 100644 |
--- a/src/codec/SkCodec_libpng.cpp |
+++ b/src/codec/SkCodec_libpng.cpp |
@@ -15,6 +15,7 @@ |
#include "SkSize.h" |
#include "SkStream.h" |
#include "SkSwizzler.h" |
+#include "SkUtils.h" |
/////////////////////////////////////////////////////////////////////////////// |
// Helper macros |
@@ -119,7 +120,7 @@ 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. |
-bool SkPngCodec::decodePalette(bool premultiply) { |
+bool SkPngCodec::decodePalette(bool premultiply, int bitDepth, int* ctableCount) { |
int numPalette; |
png_colorp palette; |
png_bytep trans; |
@@ -128,14 +129,7 @@ bool SkPngCodec::decodePalette(bool premultiply) { |
return false; |
} |
- /* BUGGY IMAGE WORKAROUND |
- |
- We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count |
- which is a problem since we use the byte as an index. To work around this we grow |
- the colortable by 1 (if its < 256) and duplicate the last color into that slot. |
- */ |
- const int colorCount = numPalette + (numPalette < 256); |
- // Note: These are not necessarily SkPMColors. |
+ // Note: These are not necessarily SkPMColors |
SkPMColor colorStorage[256]; // worst-case storage |
SkPMColor* colorPtr = colorStorage; |
@@ -175,9 +169,24 @@ bool SkPngCodec::decodePalette(bool premultiply) { |
palette++; |
} |
- // see BUGGY IMAGE WORKAROUND comment above |
- if (numPalette < 256) { |
- *colorPtr = colorPtr[-1]; |
+ /* BUGGY IMAGE WORKAROUND |
+ Invalid images could contain pixel values that are greater than the number of palette |
+ entries. Since we use pixel values as indices into the palette this could result in reading |
+ beyond the end of the palette which could leak the contents of uninitialized memory. To |
+ ensure this doesn't happen, we grow the colortable to the maximum size that can be |
+ addressed by the bitdepth of the image and fill it with the last palette color or black if |
+ the palette is empty (really broken image). |
+ */ |
+ int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8)); |
+ SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0); |
+ for (; index < colorCount; index++) { |
+ *colorPtr++ = lastColor; |
+ } |
+ |
+ // Set the new color count |
+ if (ctableCount != NULL) { |
+ SkASSERT(256 == *ctableCount); |
+ *ctableCount = colorCount; |
} |
fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorStorage, colorCount))); |
@@ -276,10 +285,7 @@ static bool read_header(SkStream* stream, png_structp* png_ptrp, |
SkAlphaType skAlphaType; |
switch (colorType) { |
case PNG_COLOR_TYPE_PALETTE: |
- // Technically, this is true of the data, but I don't think we want |
- // to support it. |
- // skColorType = kIndex8_SkColorType; |
- skColorType = kN32_SkColorType; |
+ skColorType = kIndex_8_SkColorType; |
skAlphaType = has_transparency_in_palette(png_ptr, info_ptr) ? |
kUnpremul_SkAlphaType : kOpaque_SkAlphaType; |
break; |
@@ -387,22 +393,43 @@ void SkPngCodec::destroyReadStruct() { |
static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) { |
// TODO: Support other conversions |
- if (dst.colorType() != src.colorType()) { |
- return false; |
- } |
if (dst.profileType() != src.profileType()) { |
return false; |
} |
- if (dst.alphaType() == src.alphaType()) { |
- return true; |
+ |
+ // Check for supported alpha types |
+ if (src.alphaType() != dst.alphaType()) { |
+ if (kOpaque_SkAlphaType == src.alphaType()) { |
+ // If the source is opaque, we must decode to opaque |
+ return false; |
+ } |
+ |
+ // The source is not opaque |
+ switch (dst.alphaType()) { |
+ case kPremul_SkAlphaType: |
+ case kUnpremul_SkAlphaType: |
+ // The source is not opaque, so either of these is okay |
+ break; |
+ default: |
+ // We cannot decode a non-opaque image to opaque (or unknown) |
+ return false; |
+ } |
+ } |
+ |
+ // Check for supported color types |
+ switch (dst.colorType()) { |
+ // Allow output to kN32 from any type of input |
+ case kN32_SkColorType: |
+ return true; |
+ default: |
+ return dst.colorType() == src.colorType(); |
} |
- return kPremul_SkAlphaType == dst.alphaType() && |
- kUnpremul_SkAlphaType == src.alphaType(); |
} |
SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
void* dst, size_t rowBytes, |
- const Options& options) { |
+ const Options& options, |
+ int* ctableCount) { |
// FIXME: Could we use the return value of setjmp to specify the type of |
// error? |
if (setjmp(png_jmpbuf(fPng_ptr))) { |
@@ -423,7 +450,8 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
fReallyHasAlpha = false; |
if (PNG_COLOR_TYPE_PALETTE == pngColorType) { |
fSrcConfig = SkSwizzler::kIndex; |
- if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType())) { |
+ if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(), bitDepth, |
+ ctableCount)) { |
return kInvalidInput; |
} |
} else if (kAlpha_8_SkColorType == requestedInfo.colorType()) { |
@@ -492,8 +520,18 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
return kInvalidConversion; |
} |
+ // Note that ctableCount will be modified if there is a color table |
const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes, |
- options); |
+ options, ctableCount); |
+ |
+ // Copy the color table to the client if necessary |
+ if (kIndex_8_SkColorType == requestedInfo.colorType()) { |
+ SkASSERT(NULL != ctable); |
+ SkASSERT(NULL != ctableCount); |
+ SkASSERT(NULL != fColorTable.get()); |
+ sk_memcpy32(ctable, fColorTable->readColors(), *ctableCount); |
+ } |
+ |
if (result != kSuccess) { |
return result; |
} |
@@ -631,7 +669,9 @@ SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo) |
Options opts; |
// FIXME: Pass this in to getScanlineDecoder? |
opts.fZeroInitialized = kNo_ZeroInitialized; |
- if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), opts) != kSuccess) { |
+ // FIXME: onGetScanlineDecoder does not currently have a way to get color table information |
+ // for a kIndex8 decoder. |
+ if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), opts, NULL) != kSuccess) { |
SkCodecPrintf("failed to initialize the swizzler.\n"); |
return NULL; |
} |