aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/codec/SkCodec_libgif.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/codec/SkCodec_libgif.cpp')
-rw-r--r--src/codec/SkCodec_libgif.cpp167
1 files changed, 110 insertions, 57 deletions
diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp
index 9356a6973b..efb3a90ea4 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",