aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--dm/DMSrcSink.cpp11
-rw-r--r--src/codec/SkGifCodec.cpp36
-rw-r--r--tests/CodecPartialTest.cpp74
-rw-r--r--tests/GifTest.cpp42
4 files changed, 148 insertions, 15 deletions
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 31a3bc7059..197dcc1ec8 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -453,9 +453,14 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
} else {
options.fHasPriorFrame = false;
}
- const SkCodec::Result result = codec->getPixels(decodeInfo, pixels.get(),
- rowBytes, &options,
- colorPtr, &colorCount);
+ SkCodec::Result result = codec->getPixels(decodeInfo, pixels.get(),
+ rowBytes, &options,
+ colorPtr, &colorCount);
+ if (SkCodec::kInvalidInput == result && i > 0) {
+ // Some of our test images have truncated later frames. Treat that
+ // the same as incomplete.
+ result = SkCodec::kIncompleteInput;
+ }
switch (result) {
case SkCodec::kSuccess:
case SkCodec::kIncompleteInput: {
diff --git a/src/codec/SkGifCodec.cpp b/src/codec/SkGifCodec.cpp
index d13f97ae72..9a5948b2d0 100644
--- a/src/codec/SkGifCodec.cpp
+++ b/src/codec/SkGifCodec.cpp
@@ -239,6 +239,14 @@ SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColo
return gif_error("frame index out of range!\n", kIncompleteInput);
}
+ auto& localMap = fReader->frameContext(frameIndex)->localColorMap();
+ if (localMap.numColors() && !localMap.isDefined()) {
+ // We have parsed enough to know that there is a color map, but cannot
+ // parse the map itself yet. Exit now, so we do not build an incorrect
+ // table.
+ return gif_error("color map not available yet\n", kIncompleteInput);
+ }
+
fTmpBuffer.reset(new uint8_t[dstInfo.minRowBytes()]);
this->initializeColorTable(dstInfo, frameIndex);
@@ -296,8 +304,19 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
int* inputColorCount,
int* rowsDecoded) {
Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, opts);
- if (kSuccess != result) {
- return result;
+ switch (result) {
+ case kSuccess:
+ break;
+ case kIncompleteInput:
+ // onStartIncrementalDecode treats this as incomplete, since it may
+ // provide more data later, but in this case, no more data will be
+ // provided, and there is nothing to draw. We also cannot return
+ // kIncompleteInput, which will make SkCodec attempt to fill
+ // remaining rows, but that requires an SkSwizzler, which we have
+ // not created.
+ return kInvalidInput;
+ default:
+ return result;
}
if (dstInfo.dimensions() != this->getInfo().dimensions()) {
@@ -426,6 +445,11 @@ SkCodec::Result SkGifCodec::decodeFrame(bool firstAttempt, const Options& opts,
}
}
+ if (!fCurrColorTableIsReal) {
+ // Nothing to draw this frame.
+ return kSuccess;
+ }
+
// Note: there is a difference between the following call to SkGifImageReader::decode
// returning false and leaving frameDecoded false:
// - If the method returns false, there was an error in the stream. We still treat this as
@@ -560,11 +584,9 @@ bool SkGifCodec::haveDecodedRow(size_t frameIndex, const unsigned char* rowBegin
fRowsDecoded++;
}
- if (!fCurrColorTableIsReal) {
- // No color table, so nothing to draw this frame.
- // FIXME: We can abort even earlier - no need to decode this frame.
- return true;
- }
+ // decodeFrame will early exit if this is false, so this method will not be
+ // called.
+ SkASSERT(fCurrColorTableIsReal);
// The swizzler takes care of offsetting into the dst width-wise.
void* dstLine = SkTAddOffset<void>(fDst, dstRow * fDstRowBytes);
diff --git a/tests/CodecPartialTest.cpp b/tests/CodecPartialTest.cpp
index 9611d0982e..e29037dd14 100644
--- a/tests/CodecPartialTest.cpp
+++ b/tests/CodecPartialTest.cpp
@@ -209,7 +209,7 @@ DEF_TEST(Codec_partialAnim, r) {
const SkCodec::Result result = fullCodec->getPixels(info, frame.getPixels(),
frame.rowBytes(), &opts, nullptr, nullptr);
- if (result == SkCodec::kIncompleteInput) {
+ if (result == SkCodec::kIncompleteInput || result == SkCodec::kInvalidInput) {
frameByteCounts.push_back(stream->getPosition() - lastOffset);
// We need to distinguish between a partial frame and no more frames.
@@ -320,3 +320,75 @@ DEF_TEST(Codec_rewind, r) {
test_interleaved(r, "plane_interlaced.png");
test_interleaved(r, "box.gif");
}
+
+// Modified version of the giflib logo, from
+// http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html
+// The global color map has been replaced with a local color map.
+static unsigned char gNoGlobalColorMap[] = {
+ // Header
+ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
+
+ // Logical screen descriptor
+ 0x0A, 0x00, 0x0A, 0x00, 0x11, 0x00, 0x00,
+
+ // Image descriptor
+ 0x2C, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x81,
+
+ // Local color table
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
+
+ // Image data
+ 0x02, 0x16, 0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33, 0xA0, 0x02, 0x75,
+ 0xEC, 0x95, 0xFA, 0xA8, 0xDE, 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01, 0x00,
+
+ // Trailer
+ 0x3B,
+};
+
+// Test that a gif file truncated before its local color map behaves as expected.
+DEF_TEST(Codec_GifPreMap, r) {
+ sk_sp<SkData> data = SkData::MakeWithoutCopy(gNoGlobalColorMap, sizeof(gNoGlobalColorMap));
+ std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+ if (!codec) {
+ ERRORF(r, "failed to create codec");
+ return;
+ }
+
+ SkBitmap truth;
+ auto info = standardize_info(codec.get());
+ truth.allocPixels(info);
+
+ auto result = codec->getPixels(info, truth.getPixels(), truth.rowBytes());
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+ // Truncate to 23 bytes, just before the color map. This should fail to decode.
+ codec.reset(SkCodec::NewFromData(SkData::MakeWithoutCopy(gNoGlobalColorMap, 23)));
+ REPORTER_ASSERT(r, codec);
+ if (codec) {
+ SkBitmap bm;
+ bm.allocPixels(info);
+ result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
+ REPORTER_ASSERT(r, result == SkCodec::kInvalidInput);
+ }
+
+ // Again, truncate to 23 bytes, this time for an incremental decode. We
+ // cannot start an incremental decode until we have more data. If we did,
+ // we would be using the wrong color table.
+ HaltingStream* stream = new HaltingStream(data, 23);
+ codec.reset(SkCodec::NewFromStream(stream));
+ REPORTER_ASSERT(r, codec);
+ if (codec) {
+ SkBitmap bm;
+ bm.allocPixels(info);
+ result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
+ REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
+
+ stream->addNewData(data->size());
+ result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+ result = codec->incrementalDecode();
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ compare_bitmaps(r, truth, bm);
+ }
+}
diff --git a/tests/GifTest.cpp b/tests/GifTest.cpp
index 17dce92186..12bfb5348f 100644
--- a/tests/GifTest.cpp
+++ b/tests/GifTest.cpp
@@ -26,9 +26,18 @@ static unsigned char gGIFData[] = {
};
static unsigned char gGIFDataNoColormap[] = {
- 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
+ // Header
+ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
+ // Screen descriptor
+ 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ // Graphics control extension
+ 0x21, 0xf9, 0x04, 0x01, 0x0a, 0x00, 0x01, 0x00,
+ // Image descriptor
+ 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ // Image data
+ 0x02, 0x02, 0x4c, 0x01, 0x00,
+ // Trailer
+ 0x3b
};
static unsigned char gInterlacedGIF[] = {
@@ -177,7 +186,32 @@ DEF_TEST(Gif, reporter) {
test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap),
sizeof(gGIFDataNoColormap));
- // "libgif warning [missing colormap]"
+
+ // Since there is no color map, we do not even need to parse the image data
+ // to know that we should draw transparent. Truncate the file before the
+ // data. This should still succeed.
+ test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap), 31);
+
+ // Likewise, incremental decoding should succeed here.
+ {
+ sk_sp<SkData> data = SkData::MakeWithoutCopy(gGIFDataNoColormap, 31);
+ std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+ REPORTER_ASSERT(reporter, codec);
+ if (codec) {
+ auto info = codec->getInfo().makeColorType(kN32_SkColorType);
+ SkBitmap bm;
+ bm.allocPixels(info);
+ REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->startIncrementalDecode(
+ info, bm.getPixels(), bm.rowBytes()));
+ REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->incrementalDecode());
+ REPORTER_ASSERT(reporter, bm.width() == 1);
+ REPORTER_ASSERT(reporter, bm.height() == 1);
+ REPORTER_ASSERT(reporter, !(bm.empty()));
+ if (!(bm.empty())) {
+ REPORTER_ASSERT(reporter, bm.getColor(0, 0) == 0x00000000);
+ }
+ }
+ }
// test short Gif. 80 is missing a few bytes.
test_gif_data_short(reporter, static_cast<void *>(gGIFData), 80);