| Index: src/images/SkImageDecoder_libgif.cpp
|
| diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
|
| index ab0fbdaf3f777855d2b8621eed7a1c1c4abe7325..f484441c8c3e340d938582eff80c481b538cbf18 100644
|
| --- a/src/images/SkImageDecoder_libgif.cpp
|
| +++ b/src/images/SkImageDecoder_libgif.cpp
|
| @@ -5,14 +5,15 @@
|
| * found in the LICENSE file.
|
| */
|
|
|
| -
|
| #include "SkColor.h"
|
| #include "SkColorPriv.h"
|
| #include "SkColorTable.h"
|
| #include "SkImageDecoder.h"
|
| +#include "SkRTConf.h"
|
| #include "SkScaledBitmapSampler.h"
|
| #include "SkStream.h"
|
| #include "SkTemplates.h"
|
| +#include "SkUtils.h"
|
|
|
| #include "gif_lib.h"
|
|
|
| @@ -36,6 +37,12 @@ static const uint8_t gDeltaIterlaceYValue[] = {
|
| 8, 8, 4, 2
|
| };
|
|
|
| +SK_CONF_DECLARE(bool, c_suppressGIFImageDecoderWarnings,
|
| + "images.gif.suppressDecoderWarnings", true,
|
| + "Suppress GIF warnings and errors when calling image decode "
|
| + "functions.");
|
| +
|
| +
|
| /* Implement the GIF interlace algorithm in an iterator.
|
| 1) grab every 8th line beginning at 0
|
| 2) grab every 8th line beginning at 4
|
| @@ -145,14 +152,21 @@ static int find_transpIndex(const SavedImage& image, int colorCount) {
|
| return transpIndex;
|
| }
|
|
|
| -static bool error_return(GifFileType* gif, const SkBitmap& bm,
|
| - const char msg[]) {
|
| -#if 0
|
| - SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
|
| - msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
|
| -#endif
|
| +static bool error_return(const SkBitmap& bm, const char msg[]) {
|
| + if (!c_suppressGIFImageDecoderWarnings) {
|
| + SkDebugf("libgif error [%s] bitmap [%d %d] pixels %p colortable %p\n",
|
| + msg, bm.width(), bm.height(), bm.getPixels(),
|
| + bm.getColorTable());
|
| + }
|
| return false;
|
| }
|
| +static void gif_warning(const SkBitmap& bm, const char msg[]) {
|
| + if (!c_suppressGIFImageDecoderWarnings) {
|
| + SkDebugf("libgif warning [%s] bitmap [%d %d] pixels %p colortable %p\n",
|
| + msg, bm.width(), bm.height(), bm.getPixels(),
|
| + bm.getColorTable());
|
| + }
|
| +}
|
|
|
| /**
|
| * Skip rows in the source gif image.
|
| @@ -178,7 +192,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
|
| #endif
|
| if (NULL == gif) {
|
| - return error_return(gif, *bm, "DGifOpen");
|
| + return error_return(*bm, "DGifOpen");
|
| }
|
|
|
| SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
|
| @@ -195,31 +209,66 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| int extFunction;
|
| #endif
|
| int transpIndex = -1; // -1 means we don't have it (yet)
|
| + int fillIndex = gif->SBackGroundColor;
|
|
|
| do {
|
| if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
|
| - return error_return(gif, *bm, "DGifGetRecordType");
|
| + return error_return(*bm, "DGifGetRecordType");
|
| }
|
|
|
| switch (recType) {
|
| case IMAGE_DESC_RECORD_TYPE: {
|
| if (DGifGetImageDesc(gif) == GIF_ERROR) {
|
| - return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
|
| + return error_return(*bm, "IMAGE_DESC_RECORD_TYPE");
|
| }
|
|
|
| if (gif->ImageCount < 1) { // sanity check
|
| - return error_return(gif, *bm, "ImageCount < 1");
|
| + return error_return(*bm, "ImageCount < 1");
|
| }
|
|
|
| width = gif->SWidth;
|
| height = gif->SHeight;
|
| - if (width <= 0 || height <= 0) {
|
| - return error_return(gif, *bm, "invalid dimensions");
|
| +
|
| + SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
|
| + const GifImageDesc& desc = image->ImageDesc;
|
| +
|
| + int imageLeft = desc.Left;
|
| + int imageTop = desc.Top;
|
| + const int innerWidth = desc.Width;
|
| + const int innerHeight = desc.Height;
|
| + if (innerWidth <= 0 || innerHeight <= 0) {
|
| + return error_return(*bm, "invalid dimensions");
|
| + }
|
| +
|
| + // check for valid descriptor
|
| + if (innerWidth > width) {
|
| + gif_warning(*bm, "image too wide, expanding output to size");
|
| + width = innerWidth;
|
| + imageLeft = 0;
|
| + } else if (imageLeft + innerWidth > width) {
|
| + gif_warning(*bm, "shifting image left to fit");
|
| + imageLeft = width - innerWidth;
|
| + } else if (imageLeft < 0) {
|
| + gif_warning(*bm, "shifting image right to fit");
|
| + imageLeft = 0;
|
| + }
|
| +
|
| +
|
| + if (innerHeight > height) {
|
| + gif_warning(*bm, "image too tall, expanding output to size");
|
| + height = innerHeight;
|
| + imageTop = 0;
|
| + } else if (imageTop + innerHeight > height) {
|
| + gif_warning(*bm, "shifting image up to fit");
|
| + imageTop = height - innerHeight;
|
| + } else if (imageTop < 0) {
|
| + gif_warning(*bm, "shifting image down to fit");
|
| + imageTop = 0;
|
| }
|
|
|
| // FIXME: We could give the caller a choice of images or configs.
|
| if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, height)) {
|
| - return error_return(gif, *bm, "chooseFromOneChoice");
|
| + return error_return(*bm, "chooseFromOneChoice");
|
| }
|
|
|
| SkScaledBitmapSampler sampler(width, height, this->getSampleSize());
|
| @@ -231,58 +280,53 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| return true;
|
| }
|
|
|
| - SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
|
| - const GifImageDesc& desc = image->ImageDesc;
|
| -
|
| - // check for valid descriptor
|
| - if ( (desc.Top | desc.Left) < 0 ||
|
| - desc.Left + desc.Width > width ||
|
| - desc.Top + desc.Height > height) {
|
| - return error_return(gif, *bm, "TopLeft");
|
| - }
|
|
|
| // now we decode the colortable
|
| int colorCount = 0;
|
| {
|
| - const ColorMapObject* cmap = find_colormap(gif);
|
| - if (NULL == cmap) {
|
| - return error_return(gif, *bm, "null cmap");
|
| - }
|
| - colorCount = cmap->ColorCount;
|
| - if (colorCount > 256) {
|
| - colorCount = 256; // our kIndex8 can't support more
|
| - }
|
| -
|
| + // Declare colorPtr here for scope.
|
| SkPMColor colorPtr[256]; // storage for worst-case
|
| + const ColorMapObject* cmap = find_colormap(gif);
|
| SkAlphaType alphaType = kOpaque_SkAlphaType;
|
| - for (int index = 0; index < colorCount; index++) {
|
| - colorPtr[index] = SkPackARGB32(0xFF,
|
| - cmap->Colors[index].Red,
|
| - cmap->Colors[index].Green,
|
| - cmap->Colors[index].Blue);
|
| + if (cmap != NULL) {
|
| + colorCount = cmap->ColorCount;
|
| + if (colorCount > 256) {
|
| + colorCount = 256; // our kIndex8 can't support more
|
| + }
|
| + for (int index = 0; index < colorCount; index++) {
|
| + colorPtr[index] = SkPackARGB32(0xFF,
|
| + cmap->Colors[index].Red,
|
| + cmap->Colors[index].Green,
|
| + cmap->Colors[index].Blue);
|
| + }
|
| + } else {
|
| + // find_colormap() returned NULL. Some (rare, broken)
|
| + // GIFs don't have a color table, so we force one.
|
| + gif_warning(*bm, "missing colormap");
|
| + colorCount = 256;
|
| + sk_memset32(colorPtr, SK_ColorWHITE, colorCount);
|
| }
|
| -
|
| transpIndex = find_transpIndex(temp_save, colorCount);
|
| - bool reallyHasAlpha = transpIndex >= 0;
|
| - if (reallyHasAlpha) {
|
| + if (transpIndex >= 0) {
|
| colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a transparent SkPMColor
|
| alphaType = kPremul_SkAlphaType;
|
| + fillIndex = transpIndex;
|
| + } else if (fillIndex >= colorCount) {
|
| + // gif->SBackGroundColor should be less than colorCount.
|
| + fillIndex = 0; // If not, fix it.
|
| }
|
|
|
| SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable,
|
| (colorPtr, colorCount,
|
| alphaType)));
|
| if (!this->allocPixelRef(bm, ctable)) {
|
| - return error_return(gif, *bm, "allocPixelRef");
|
| + return error_return(*bm, "allocPixelRef");
|
| }
|
| }
|
|
|
| - const int innerWidth = desc.Width;
|
| - const int innerHeight = desc.Height;
|
| -
|
| // abort if either inner dimension is <= 0
|
| if (innerWidth <= 0 || innerHeight <= 0) {
|
| - return error_return(gif, *bm, "non-pos inner width/height");
|
| + return error_return(*bm, "non-pos inner width/height");
|
| }
|
|
|
| SkAutoLockPixels alp(*bm);
|
| @@ -296,29 +340,18 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| SkBitmap subset;
|
| SkBitmap* workingBitmap;
|
| // are we only a subset of the total bounds?
|
| - if ((desc.Top | desc.Left) > 0 ||
|
| + if ((imageTop | imageLeft) > 0 ||
|
| innerWidth < width || innerHeight < height) {
|
| - int fill;
|
| - if (transpIndex >= 0) {
|
| - fill = transpIndex;
|
| - } else {
|
| - fill = gif->SBackGroundColor;
|
| - }
|
| - // check for valid fill index/color
|
| - if (static_cast<unsigned>(fill) >=
|
| - static_cast<unsigned>(colorCount)) {
|
| - fill = 0;
|
| - }
|
| // Fill the background.
|
| - memset(bm->getPixels(), fill, bm->getSize());
|
| + memset(bm->getPixels(), fillIndex, bm->getSize());
|
|
|
| // Create a subset of the bitmap.
|
| - SkIRect subsetRect(SkIRect::MakeXYWH(desc.Left / sampler.srcDX(),
|
| - desc.Top / sampler.srcDY(),
|
| + SkIRect subsetRect(SkIRect::MakeXYWH(imageLeft / sampler.srcDX(),
|
| + imageTop / sampler.srcDY(),
|
| innerWidth / sampler.srcDX(),
|
| innerHeight / sampler.srcDY()));
|
| if (!bm->extractSubset(&subset, subsetRect)) {
|
| - return error_return(gif, *bm, "Extract failed.");
|
| + return error_return(*bm, "Extract failed.");
|
| }
|
| // Update the sampler. We'll now be only sampling into the subset.
|
| sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize());
|
| @@ -332,7 +365,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| SkAutoLockPixels alpWorking(*workingBitmap);
|
|
|
| if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) {
|
| - return error_return(gif, *bm, "Sampler failed to begin.");
|
| + return error_return(*bm, "Sampler failed to begin.");
|
| }
|
|
|
| // now decode each scanline
|
| @@ -340,9 +373,15 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| // Iterate over the height of the source data. The sampler will
|
| // take care of skipping unneeded rows.
|
| GifInterlaceIter iter(innerHeight);
|
| - for (int y = 0; y < innerHeight; y++){
|
| + for (int y = 0; y < innerHeight; y++) {
|
| if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
|
| - return error_return(gif, *bm, "interlace DGifGetLine");
|
| + gif_warning(*bm, "interlace DGifGetLine");
|
| + memset(scanline, fillIndex, innerWidth);
|
| + for (; y < innerHeight; y++) {
|
| + sampler.sampleInterlaced(scanline, iter.currY());
|
| + iter.next();
|
| + }
|
| + return true;
|
| }
|
| sampler.sampleInterlaced(scanline, iter.currY());
|
| iter.next();
|
| @@ -353,7 +392,12 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| skip_src_rows(gif, scanline, innerWidth, sampler.srcY0());
|
| for (int y = 0; y < outHeight; y++) {
|
| if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
|
| - return error_return(gif, *bm, "DGifGetLine");
|
| + gif_warning(*bm, "DGifGetLine");
|
| + memset(scanline, fillIndex, innerWidth);
|
| + for (; y < outHeight; y++) {
|
| + sampler.next(scanline);
|
| + }
|
| + return true;
|
| }
|
| // scanline now contains the raw data. Sample it.
|
| sampler.next(scanline);
|
| @@ -366,7 +410,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| SkASSERT(read <= innerHeight);
|
| skip_src_rows(gif, scanline, innerWidth, innerHeight - read);
|
| }
|
| - goto DONE;
|
| + return true;
|
| } break;
|
|
|
| case EXTENSION_RECORD_TYPE:
|
| @@ -376,7 +420,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| #else
|
| if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
|
| #endif
|
| - return error_return(gif, *bm, "DGifGetExtension");
|
| + return error_return(*bm, "DGifGetExtension");
|
| }
|
|
|
| while (extData != NULL) {
|
| @@ -391,10 +435,10 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| extData[0],
|
| &extData[1]) == GIF_ERROR) {
|
| #endif
|
| - return error_return(gif, *bm, "AddExtensionBlock");
|
| + return error_return(*bm, "AddExtensionBlock");
|
| }
|
| if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
|
| - return error_return(gif, *bm, "DGifGetExtensionNext");
|
| + return error_return(*bm, "DGifGetExtensionNext");
|
| }
|
| #if GIFLIB_MAJOR < 5
|
| temp_save.Function = 0;
|
| @@ -410,7 +454,6 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
| }
|
| } while (recType != TERMINATE_RECORD_TYPE);
|
|
|
| -DONE:
|
| return true;
|
| }
|
|
|
|
|