Index: src/codec/SkCodec_libgif.cpp |
diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp |
index 9356a6973bcba5995b1e3b330c4f6c981bd87f3e..efb3a90ea4e1911e89e6fb8eed33fd379bb483ba 100644 |
--- a/src/codec/SkCodec_libgif.cpp |
+++ b/src/codec/SkCodec_libgif.cpp |
@@ -70,11 +70,11 @@ static GifFileType* open_gif(SkStream* stream) { |
* This function cleans up the gif object after the decode completes |
* It is used in a SkAutoTCallIProc template |
*/ |
-int32_t SkGifCodec::CloseGif(GifFileType* gif) { |
+void SkGifCodec::CloseGif(GifFileType* gif) { |
#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0) |
- return DGifCloseFile(gif); |
+ DGifCloseFile(gif); |
#else |
- return DGifCloseFile(gif, NULL); |
+ DGifCloseFile(gif, NULL); |
#endif |
} |
@@ -131,42 +131,77 @@ static uint32_t find_trans_index(const SavedImage& image) { |
} |
/* |
- * Assumes IsGif was called and returned true |
- * Creates a gif decoder |
- * Reads enough of the stream to determine the image format |
+ * Read enough of the stream to initialize the SkGifCodec. |
+ * Returns a bool representing success or failure. |
+ * |
+ * @param codecOut |
+ * If it returned true, and codecOut was not NULL, |
+ * codecOut will be set to a new SkGifCodec. |
+ * |
+ * @param gifOut |
+ * If it returned true, and codecOut was NULL, |
+ * gifOut must be non-NULL and gifOut will be set to a new |
+ * GifFileType pointer. |
+ * |
+ * @param stream |
+ * Deleted on failure. |
+ * codecOut will take ownership of it in the case where we created a codec. |
+ * Ownership is unchanged when we returned a gifOut. |
+ * |
*/ |
-SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { |
+bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) { |
SkAutoTDelete<SkStream> streamDeleter(stream); |
+ |
// Read gif header, logical screen descriptor, and global color table |
- SkAutoTCallIProc<GifFileType, CloseGif> gif(open_gif(stream)); |
+ SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); |
if (NULL == gif) { |
gif_error("DGifOpen failed.\n"); |
- return NULL; |
+ return false; |
} |
- // Get fields from header |
- const int32_t width = gif->SWidth; |
- const int32_t height = gif->SHeight; |
- if (width <= 0 || height <= 0) { |
- gif_error("Invalid dimensions.\n"); |
- return NULL; |
+ if (NULL != codecOut) { |
+ // Get fields from header |
+ const int32_t width = gif->SWidth; |
+ const int32_t height = gif->SHeight; |
+ if (width <= 0 || height <= 0) { |
+ gif_error("Invalid dimensions.\n"); |
+ return false; |
+ } |
+ |
+ // Return the codec |
+ // kIndex is the most natural color type for gifs, so we set this as |
+ // the default. |
+ // Many gifs specify a color table index for transparent pixels. Every |
+ // other pixel is guaranteed to be opaque. Despite this, because of the |
+ // possiblity of transparent pixels, we cannot assume that the image is |
+ // opaque. We have the option to set the alpha type as kPremul or |
+ // kUnpremul. Both are valid since the alpha component will always be |
+ // 0xFF or the entire 32-bit pixel will be set to zero. We prefer |
+ // kPremul because we support kPremul, and it is more efficient to |
+ // use kPremul directly even when kUnpremul is supported. |
+ const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, |
+ kIndex_8_SkColorType, kPremul_SkAlphaType); |
+ *codecOut = SkNEW_ARGS(SkGifCodec, (imageInfo, streamDeleter.detach(), gif.detach())); |
+ } else { |
+ SkASSERT(NULL != gifOut); |
+ streamDeleter.detach(); |
+ *gifOut = gif.detach(); |
} |
+ return true; |
+} |
- // Return the codec |
- // kIndex is the most natural color type for gifs, so we set this as |
- // the default. |
- // Many gifs specify a color table index for transparent pixels. Every |
- // other pixel is guaranteed to be opaque. Despite this, because of the |
- // possiblity of transparent pixels, we cannot assume that the image is |
- // opaque. We have the option to set the alpha type as kPremul or |
- // kUnpremul. Both are valid since the alpha component will always be |
- // 0xFF or the entire 32-bit pixel will be set to zero. We prefer |
- // kPremul because we support kPremul, and it is more efficient to |
- // use kPremul directly even when kUnpremul is supported. |
- const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, |
- kIndex_8_SkColorType, kPremul_SkAlphaType); |
- return SkNEW_ARGS(SkGifCodec, (imageInfo, streamDeleter.detach(), gif.detach())); |
+/* |
+ * Assumes IsGif was called and returned true |
+ * Creates a gif decoder |
+ * Reads enough of the stream to determine the image format |
+ */ |
+SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { |
+ SkCodec* codec = NULL; |
+ if (ReadHeader(stream, &codec, NULL)) { |
+ return codec; |
+ } |
+ return NULL; |
} |
SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, |
@@ -191,6 +226,9 @@ static bool conversion_possible(const SkImageInfo& dst, |
case kN32_SkColorType: |
return kPremul_SkAlphaType == dst.alphaType() || |
kUnpremul_SkAlphaType == dst.alphaType(); |
+ case kIndex_8_SkColorType: |
+ return kPremul_SkAlphaType == dst.alphaType() || |
+ kUnpremul_SkAlphaType == dst.alphaType(); |
default: |
return false; |
} |
@@ -201,11 +239,24 @@ static bool conversion_possible(const SkImageInfo& dst, |
*/ |
SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
void* dst, size_t dstRowBytes, |
- const Options& opts, SkPMColor*, int*) { |
- // Check for valid input parameters |
- if (!this->rewindIfNeeded()) { |
+ const Options& opts, |
+ SkPMColor* inputColorPtr, |
+ int* inputColorCount) { |
+ // Rewind if necessary |
+ SkCodec::RewindState rewindState = this->rewindIfNeeded(); |
+ if (rewindState == kCouldNotRewind_RewindState) { |
return kCouldNotRewind; |
+ } else if (rewindState == kRewound_RewindState) { |
+ GifFileType* gifOut = NULL; |
+ if (!ReadHeader(this->stream(), NULL, &gifOut)) { |
+ return kCouldNotRewind; |
+ } else { |
+ SkASSERT(NULL != gifOut); |
+ fGif.reset(gifOut); |
+ } |
} |
+ |
+ // Check for valid input parameters |
if (dstInfo.dimensions() != this->getInfo().dimensions()) { |
return gif_error("Scaling not supported.\n", kInvalidScale); |
} |
@@ -285,11 +336,23 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
imageTop = 0; |
} |
+ // Create a color table to store colors the giflib colorMap |
+ SkPMColor alternateColorPtr[256]; |
+ SkPMColor* colorTable; |
+ SkColorType dstColorType = dstInfo.colorType(); |
+ if (kIndex_8_SkColorType == dstColorType) { |
+ SkASSERT(NULL != inputColorPtr); |
+ SkASSERT(NULL != inputColorCount); |
+ SkASSERT(256 == *inputColorCount); |
+ colorTable = inputColorPtr; |
+ } else { |
+ colorTable = alternateColorPtr; |
+ } |
+ |
// Set up the color table |
uint32_t colorCount = 0; |
// Allocate maximum storage to deal with invalid indices safely |
const uint32_t maxColors = 256; |
- SkPMColor colorTable[maxColors]; |
ColorMapObject* colorMap = fGif->Image.ColorMap; |
// If there is no local color table, use the global color table |
if (NULL == colorMap) { |
@@ -310,7 +373,6 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
// This is used to fill unspecified pixels in the image data. |
uint32_t fillIndex = fGif->SBackGroundColor; |
- bool fillBackground = true; |
ZeroInitialized zeroInit = opts.fZeroInitialized; |
// Gifs have the option to specify the color at a single |
@@ -324,14 +386,11 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
// is out of range. |
uint32_t transIndex = find_trans_index(saveExt); |
- // If the background is already zeroed and we have a valid |
- // transparent index, we do not need to fill the background. |
if (transIndex < colorCount) { |
colorTable[transIndex] = SK_ColorTRANSPARENT; |
// If there is a transparent index, we also use this as |
// the fill index. |
fillIndex = transIndex; |
- fillBackground = (kYes_ZeroInitialized != zeroInit); |
} else if (fillIndex >= colorCount) { |
// If the fill index is invalid, we default to 0. This |
// behavior is unspecified but matches SkImageDecoder. |
@@ -339,6 +398,14 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
} |
} |
+ // Check if we can skip filling the background of the image. We |
+ // may be able to if the memory is zero initialized. |
+ bool skipBackground = |
+ ((kN32_SkColorType == dstColorType && colorTable[fillIndex] == 0) || |
+ (kIndex_8_SkColorType == dstColorType && fillIndex == 0)) && |
+ kYes_ZeroInitialized == zeroInit; |
+ |
+ |
// Fill in the color table for indices greater than color count. |
// This allows for predictable, safe behavior. |
for (uint32_t i = colorCount; i < maxColors; i++) { |
@@ -357,19 +424,8 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
// FIXME: This may not be the behavior that we want for |
// animated gifs where we draw on top of the |
// previous frame. |
- SkColorType dstColorType = dstInfo.colorType(); |
- if (fillBackground) { |
- switch (dstColorType) { |
- case kN32_SkColorType: |
- sk_memset32((SkPMColor*) dst, |
- colorTable[fillIndex], |
- ((int) dstRowBytes) * height |
- / sizeof(SkPMColor)); |
- break; |
- default: |
- SkASSERT(false); |
- break; |
- } |
+ if (!skipBackground) { |
+ SkSwizzler::Fill(dst, dstInfo, dstRowBytes, 0, fillIndex, colorTable); |
} |
// Modify the dst pointer |
@@ -404,7 +460,7 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), |
innerWidth)) { |
// Recover from error by filling remainder of image |
- if (fillBackground) { |
+ if (!skipBackground) { |
memset(buffer.get(), fillIndex, innerWidth); |
for (; y < innerHeight; y++) { |
swizzler->next(buffer.get(), iter.nextY()); |
@@ -421,12 +477,9 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
for (int32_t y = 0; y < innerHeight; y++) { |
if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), |
innerWidth)) { |
- if (fillBackground) { |
- SkPMColor* dstPtr = (SkPMColor*) SkTAddOffset |
- <void*>(dst, y * dstRowBytes); |
- sk_memset32(dstPtr, colorTable[fillIndex], |
- (height - y) * ((int) dstRowBytes) |
- / sizeof(SkPMColor)); |
+ if (!skipBackground) { |
+ SkSwizzler::Fill(dst, dstInfo, dstRowBytes, y, fillIndex, |
+ colorTable); |
} |
return gif_error(SkStringPrintf( |
"Could not decode line %d of %d.\n", |