aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gyp/tests.gyp1
-rw-r--r--src/images/SkImageDecoder_libgif.cpp183
-rw-r--r--tests/GifTest.cpp210
3 files changed, 324 insertions, 70 deletions
diff --git a/gyp/tests.gyp b/gyp/tests.gyp
index 32f3abf7ef..daa6a8b736 100644
--- a/gyp/tests.gyp
+++ b/gyp/tests.gyp
@@ -64,6 +64,7 @@
'../tests/FontNamesTest.cpp',
'../tests/FrontBufferedStreamTest.cpp',
'../tests/GeometryTest.cpp',
+ '../tests/GifTest.cpp',
'../tests/GLInterfaceValidation.cpp',
'../tests/GLProgramsTest.cpp',
'../tests/GpuBitmapCopyTest.cpp',
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
index ab0fbdaf3f..f484441c8c 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;
}
diff --git a/tests/GifTest.cpp b/tests/GifTest.cpp
new file mode 100644
index 0000000000..5919b44d68
--- /dev/null
+++ b/tests/GifTest.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This tests out GIF decoder (SkImageDecoder_libgif.cpp)
+// It is not used on these platforms:
+#if (!defined(SK_BUILD_FOR_WIN32)) && \
+ (!defined(SK_BUILD_FOR_IOS)) && \
+ (!defined(SK_BUILD_FOR_MAC))
+
+#include "SkBitmap.h"
+#include "SkData.h"
+#include "SkForceLinking.h"
+#include "SkImageDecoder.h"
+#include "SkImage.h"
+#include "SkStream.h"
+#include "Test.h"
+
+__SK_FORCE_IMAGE_DECODER_LINKING;
+
+namespace {
+ unsigned char gifData[] = {
+ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x03, 0x00,
+ 0x03, 0x00, 0xe3, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00,
+ 0xff, 0x80, 0x80, 0x80, 0x00, 0xff, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x04,
+ 0x07, 0x50, 0x1c, 0x43, 0x40, 0x41, 0x23, 0x44,
+ 0x00, 0x3b};
+ unsigned char gifDataNoColormap[] = {
+ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x21, 0xf9, 0x04,
+ 0x01, 0x0a, 0x00, 0x01, 0x00, 0x2c, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02,
+ 0x02, 0x4c, 0x01, 0x00, 0x3b};
+ unsigned char interlacedGif[] = {
+ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x09, 0x00,
+ 0x09, 0x00, 0xe3, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00,
+ 0xff, 0x80, 0x80, 0x80, 0x00, 0xff, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00,
+ 0x00, 0x00, 0x09, 0x00, 0x09, 0x00, 0x40, 0x04,
+ 0x1b, 0x50, 0x1c, 0x23, 0xe9, 0x44, 0x23, 0x60,
+ 0x9d, 0x09, 0x28, 0x1e, 0xf8, 0x6d, 0x64, 0x56,
+ 0x9d, 0x53, 0xa8, 0x7e, 0xa8, 0x65, 0x94, 0x5c,
+ 0xb0, 0x8a, 0x45, 0x04, 0x00, 0x3b};
+}; // namespace
+
+static void test_gif_data_no_colormap(skiatest::Reporter* r,
+ void* data, size_t size) {
+ SkBitmap bm;
+ bool imageDecodeSuccess = SkImageDecoder::DecodeMemory(
+ data, size, &bm);
+ REPORTER_ASSERT(r, imageDecodeSuccess);
+ REPORTER_ASSERT(r, bm.width() == 1);
+ REPORTER_ASSERT(r, bm.height() == 1);
+ REPORTER_ASSERT(r, !(bm.empty()));
+ if (!(bm.empty())) {
+ REPORTER_ASSERT(r, bm.getColor(0, 0) == 0x00000000);
+ }
+}
+static void test_gif_data(skiatest::Reporter* r, void* data, size_t size) {
+ SkBitmap bm;
+ bool imageDecodeSuccess = SkImageDecoder::DecodeMemory(
+ data, size, &bm);
+ REPORTER_ASSERT(r, imageDecodeSuccess);
+ REPORTER_ASSERT(r, bm.width() == 3);
+ REPORTER_ASSERT(r, bm.height() == 3);
+ REPORTER_ASSERT(r, !(bm.empty()));
+ if (!(bm.empty())) {
+ REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000);
+ REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00);
+ REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff);
+ REPORTER_ASSERT(r, bm.getColor(0, 1) == 0xff808080);
+ REPORTER_ASSERT(r, bm.getColor(1, 1) == 0xff000000);
+ REPORTER_ASSERT(r, bm.getColor(2, 1) == 0xff00ff00);
+ REPORTER_ASSERT(r, bm.getColor(0, 2) == 0xffffffff);
+ REPORTER_ASSERT(r, bm.getColor(1, 2) == 0xffff00ff);
+ REPORTER_ASSERT(r, bm.getColor(2, 2) == 0xff0000ff);
+ }
+}
+static void test_interlaced_gif_data(skiatest::Reporter* r,
+ void* data,
+ size_t size) {
+ SkBitmap bm;
+ bool imageDecodeSuccess = SkImageDecoder::DecodeMemory(
+ data, size, &bm);
+ REPORTER_ASSERT(r, imageDecodeSuccess);
+ REPORTER_ASSERT(r, bm.width() == 9);
+ REPORTER_ASSERT(r, bm.height() == 9);
+ REPORTER_ASSERT(r, !(bm.empty()));
+ if (!(bm.empty())) {
+ REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000);
+ REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00);
+ REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff);
+
+ REPORTER_ASSERT(r, bm.getColor(0, 2) == 0xffffffff);
+ REPORTER_ASSERT(r, bm.getColor(1, 2) == 0xffff00ff);
+ REPORTER_ASSERT(r, bm.getColor(2, 2) == 0xff0000ff);
+
+ REPORTER_ASSERT(r, bm.getColor(0, 4) == 0xff808080);
+ REPORTER_ASSERT(r, bm.getColor(1, 4) == 0xff000000);
+ REPORTER_ASSERT(r, bm.getColor(2, 4) == 0xff00ff00);
+
+ REPORTER_ASSERT(r, bm.getColor(0, 6) == 0xffff0000);
+ REPORTER_ASSERT(r, bm.getColor(1, 6) == 0xffffff00);
+ REPORTER_ASSERT(r, bm.getColor(2, 6) == 0xff00ffff);
+
+ REPORTER_ASSERT(r, bm.getColor(0, 8) == 0xffffffff);
+ REPORTER_ASSERT(r, bm.getColor(1, 8) == 0xffff00ff);
+ REPORTER_ASSERT(r, bm.getColor(2, 8) == 0xff0000ff);
+ }
+}
+
+static void test_gif_data_short(skiatest::Reporter* r,
+ void* data,
+ size_t size) {
+ SkBitmap bm;
+ bool imageDecodeSuccess = SkImageDecoder::DecodeMemory(
+ data, size, &bm);
+ REPORTER_ASSERT(r, imageDecodeSuccess);
+ REPORTER_ASSERT(r, bm.width() == 3);
+ REPORTER_ASSERT(r, bm.height() == 3);
+ REPORTER_ASSERT(r, !(bm.empty()));
+ if (!(bm.empty())) {
+ REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000);
+ REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00);
+ REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff);
+ REPORTER_ASSERT(r, bm.getColor(0, 1) == 0xff808080);
+ REPORTER_ASSERT(r, bm.getColor(1, 1) == 0xff000000);
+ REPORTER_ASSERT(r, bm.getColor(2, 1) == 0xff00ff00);
+ }
+}
+
+/**
+ This test will test the ability of the SkImageDecoder to deal with
+ GIF files which have been mangled somehow. We want to display as
+ much of the GIF as possible.
+*/
+static void TestGif(skiatest::Reporter* reporter) {
+ // test perfectly good images.
+ test_gif_data(reporter, static_cast<void *>(gifData), sizeof(gifData));
+ test_interlaced_gif_data(reporter, static_cast<void *>(interlacedGif),
+ sizeof(interlacedGif));
+
+ unsigned char badData[sizeof(gifData)];
+
+ /* If you set the environment variable
+ skia_images_gif_suppressDecoderWarnings to 'false', you will
+ see warnings on stderr. This is a feature. */
+
+ memcpy(badData, gifData, sizeof(gifData));
+ badData[6] = 0x01; // image too wide
+ test_gif_data(reporter, static_cast<void *>(badData), sizeof(gifData));
+ // "libgif warning [image too wide, expanding output to size]"
+
+ memcpy(badData, gifData, sizeof(gifData));
+ badData[8] = 0x01; // image too tall
+ test_gif_data(reporter, static_cast<void *>(badData), sizeof(gifData));
+ // "libgif warning [image too tall, expanding output to size]"
+
+ memcpy(badData, gifData, sizeof(gifData));
+ badData[62] = 0x01; // image shifted right
+ test_gif_data(reporter, static_cast<void *>(badData), sizeof(gifData));
+ // "libgif warning [shifting image left to fit]"
+
+ memcpy(badData, gifData, sizeof(gifData));
+ badData[64] = 0x01; // image shifted down
+ test_gif_data(reporter, static_cast<void *>(badData), sizeof(gifData));
+ // "libgif warning [shifting image up to fit]"
+
+ memcpy(badData, gifData, sizeof(gifData));
+ badData[62] = 0xff; // image shifted left
+ badData[63] = 0xff; // 2's complement -1 short
+ test_gif_data(reporter, static_cast<void *>(badData), sizeof(gifData));
+ // "libgif warning [shifting image left to fit]"
+
+ memcpy(badData, gifData, sizeof(gifData));
+ badData[64] = 0xff; // image shifted up
+ badData[65] = 0xff; // 2's complement -1 short
+ test_gif_data(reporter, static_cast<void *>(badData), sizeof(gifData));
+ // "libgif warning [shifting image up to fit]"
+
+ test_gif_data_no_colormap(reporter, static_cast<void *>(gifDataNoColormap),
+ sizeof(gifDataNoColormap));
+ // "libgif warning [missing colormap]"
+
+ // test short Gif. 80 is missing a few bytes.
+ test_gif_data_short(reporter, static_cast<void *>(gifData), 80);
+ // "libgif warning [DGifGetLine]"
+
+ test_interlaced_gif_data(reporter, static_cast<void *>(interlacedGif),
+ 100); // 100 is missing a few bytes
+ // "libgif warning [interlace DGifGetLine]"
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("GifTest", GifTestClass, TestGif)
+
+#endif // !(SK_BUILD_FOR_WIN32||SK_BUILD_FOR_IOS||SK_BUILD_FOR_MAC)