Chromium Code Reviews| Index: src/images/SkImageDecoder_libgif.cpp | 
| diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp | 
| index 08f37efced9fc8bdee36b286dc52ad44f06ac028..71469f975927ea73f89f3c8110e084b16e5821b0 100644 | 
| --- a/src/images/SkImageDecoder_libgif.cpp | 
| +++ b/src/images/SkImageDecoder_libgif.cpp | 
| @@ -5,11 +5,11 @@ | 
| * 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" | 
| @@ -36,6 +36,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 | 
| @@ -147,12 +153,40 @@ static int find_transpIndex(const SavedImage& image, int colorCount) { | 
| 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 | 
| + 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(GifFileType* gif, const SkBitmap& bm, | 
| 
 
scroggo
2013/10/09 20:12:03
gif is never used.
(Same in error_return)
 
hal.canary
2013/10/10 16:22:11
Good catch.
 
 | 
| + 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()); | 
| + } | 
| +} | 
| + | 
| +/** | 
| + * Used several times in SkGIFImageDecoder::onDecode() to determine | 
| + * the index value to use when the image is truncated or doesn't fill | 
| + * the bitmap. */ | 
| +static int get_fill_index(int transpIndex, int colorCount, GifFileType & gif) { | 
| 
 
scroggo
2013/10/09 20:12:03
Can't gif be const?
 
hal.canary
2013/10/10 16:22:11
good catch.
 
 | 
| + int fill = 0; | 
| + if (transpIndex >= 0) { | 
| + fill = transpIndex; | 
| + } else { | 
| + fill = gif.SBackGroundColor; | 
| + } | 
| + // check for valid fill index/color | 
| + if (static_cast<unsigned>(fill) >= | 
| + static_cast<unsigned>(colorCount)) { | 
| + return 0; | 
| + } | 
| + return fill; | 
| +} | 
| + | 
| /** | 
| * Skip rows in the source gif image. | 
| @@ -213,10 +247,46 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { | 
| width = gif->SWidth; | 
| height = gif->SHeight; | 
| - if (width <= 0 || height <= 0) { | 
| + // if (width <= 0 || height <= 0) { | 
| 
 
scroggo
2013/10/09 20:12:03
Any reason not to remove this commented out code?
 
hal.canary
2013/10/10 16:22:11
Good catch
 
 | 
| + // 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(gif, *bm, "invalid dimensions"); | 
| } | 
| + // check for valid descriptor | 
| + if (innerWidth > width) { | 
| + gif_warning(gif, *bm, "image too wide, expanding output to size"); | 
| + width = innerWidth; | 
| + imageLeft = 0; | 
| + } else if (imageLeft + innerWidth > width) { | 
| + gif_warning(gif, *bm, "shifting image left to fit"); | 
| + imageLeft = width - innerWidth; | 
| + } else if (imageLeft < 0) { | 
| + gif_warning(gif, *bm, "shifting image right to fit"); | 
| + imageLeft = 0; | 
| + } | 
| + | 
| + | 
| + if (innerHeight > height) { | 
| + gif_warning(gif, *bm, "image too tall, expanding output to size"); | 
| + height = innerHeight; | 
| + imageTop = 0; | 
| + } else if (imageTop + innerHeight > height) { | 
| + gif_warning(gif, *bm, "shifting image up to fit"); | 
| + imageTop = height - innerHeight; | 
| + } else if (imageTop < 0) { | 
| + gif_warning(gif, *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"); | 
| @@ -231,50 +301,45 @@ 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; | 
| { | 
| + SkAutoTMalloc<SkPMColor> colorStorage; // declare here for scope. | 
| 
 
scroggo
2013/10/09 20:12:03
Mike's patch (https://codereview.chromium.org/2657
 
hal.canary
2013/10/10 16:22:11
That's a good idea.  1kb on the stack is a lot che
 
 | 
| const ColorMapObject* cmap = find_colormap(gif); | 
| - if (NULL == cmap) { | 
| - return error_return(gif, *bm, "null cmap"); | 
| - } | 
| - | 
| - colorCount = cmap->ColorCount; | 
| - SkAutoTMalloc<SkPMColor> colorStorage(colorCount); | 
| - SkPMColor* colorPtr = colorStorage.get(); | 
| - 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; | 
| + colorStorage.reset(colorCount); | 
| + for (int index = 0; index < colorCount; index++) { | 
| + colorStorage[index] = SkPackARGB32(0xFF, | 
| + cmap->Colors[index].Red, | 
| + cmap->Colors[index].Green, | 
| + cmap->Colors[index].Blue); | 
| + } | 
| + } else { | 
| + // find_colormap() returned NULL. Some GIFs don't | 
| + // have a color table, so we force one. | 
| + gif_warning(gif, *bm, "missing colormap"); | 
| + colorCount = 256; | 
| + colorStorage.reset(colorCount); | 
| + for (int i = 0; i < colorCount; ++i) { | 
| + colorStorage[i] = SK_ColorWHITE; | 
| 
 
scroggo
2013/10/09 20:12:03
Why did you pick white? I thought you said this ha
 
hal.canary
2013/10/10 16:22:11
In every example I saw, the only index used was th
 
 | 
| + } | 
| } | 
| - | 
| transpIndex = find_transpIndex(temp_save, colorCount); | 
| bool reallyHasAlpha = transpIndex >= 0; | 
| if (reallyHasAlpha) { | 
| - colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a transparent SkPMColor | 
| + // ram in a transparent SkPMColor | 
| + colorStorage[transpIndex] = SK_ColorTRANSPARENT; | 
| } | 
| - | 
| - SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, (colorPtr, colorCount))); | 
| + SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, | 
| + (colorStorage.get(), colorCount))); | 
| ctable->setIsOpaque(!reallyHasAlpha); | 
| if (!this->allocPixelRef(bm, ctable)) { | 
| return error_return(gif, *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"); | 
| @@ -291,25 +356,15 @@ 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; | 
| - } | 
| + int fill = get_fill_index(transpIndex, colorCount, *gif); | 
| // Fill the background. | 
| memset(bm->getPixels(), fill, 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)) { | 
| @@ -335,20 +390,36 @@ 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(gif, *bm, "interlace DGifGetLine"); | 
| + int fill = get_fill_index(transpIndex, colorCount, *gif); | 
| 
 
scroggo
2013/10/09 20:12:03
Maybe this suggestion introduces complexity withou
 
hal.canary
2013/10/10 16:22:11
But we aren't guaranteed to have done that.
 
 | 
| + for (; y < innerHeight; y++) { | 
| + memset(scanline, fill, innerWidth); | 
| 
 
scroggo
2013/10/09 20:12:03
This can be done once outside the loop. (sampleInt
 
hal.canary
2013/10/10 16:22:11
good catch.
 
 | 
| + sampler.sampleInterlaced(scanline, iter.currY()); | 
| + iter.next(); | 
| + } | 
| + return true; | 
| 
 
scroggo
2013/10/09 20:12:03
Should we say 'goto DONE;' to fit with the surroun
 
hal.canary
2013/10/10 16:22:11
Since there is no cleanup code, let's just get rid
 
 | 
| } | 
| sampler.sampleInterlaced(scanline, iter.currY()); | 
| iter.next(); | 
| } | 
| } else { | 
| // easy, non-interlace case | 
| + bool getLineErrorState = false; | 
| + int fill = 0; | 
| const int outHeight = workingBitmap->height(); | 
| 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"); | 
| + if (!getLineErrorState) { | 
| 
 
scroggo
2013/10/09 20:12:03
This is a little complicated to follow. Why not fo
 
hal.canary
2013/10/10 16:22:11
I'll change it.
 
 | 
| + if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { | 
| + getLineErrorState = true; | 
| + gif_warning(gif, *bm, "DGifGetLine"); | 
| + fill = get_fill_index(transpIndex, colorCount, *gif); | 
| + memset(scanline, fill, innerWidth); | 
| + } | 
| + } else { | 
| + memset(scanline, fill, innerWidth); | 
| 
 
scroggo
2013/10/09 20:12:03
This step is unnecessary for each line past the fi
 
hal.canary
2013/10/10 16:22:11
I'll change it.
 
 | 
| } | 
| // scanline now contains the raw data. Sample it. | 
| sampler.next(scanline); |