Index: src/codec/SkCodec_libpng.cpp |
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp |
index 33111cee67841938335fd6f7b0862d5f9ec0a9c6..819ade55df079b0d1f05de936640330f6200b3ec 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; |
@@ -401,7 +407,8 @@ static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) |
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))) { |
@@ -422,7 +429,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()) { |
@@ -491,8 +499,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); |
msarett
2015/04/03 18:01:32
Is this better than using regular memcpy?
scroggo
2015/04/06 14:55:11
We have MemoryBench which compares the two, and it
msarett
2015/04/06 18:10:55
Acknowledged.
|
+ } |
+ |
if (result != kSuccess) { |
return result; |
} |
@@ -630,7 +648,7 @@ 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) { |
+ if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), opts, NULL) != kSuccess) { |
scroggo
2015/04/06 14:55:11
Maybe add a FIXME acknowledging that getScanlineDe
msarett
2015/04/06 18:10:55
Done.
|
SkCodecPrintf("failed to initialize the swizzler.\n"); |
return NULL; |
} |