Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(657)

Unified Diff: src/images/SkImageDecoder_libgif.cpp

Issue 26743002: GIF decode: optional error messages and fault tolerance. (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698