aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Leon Scroggins III <scroggo@google.com>2017-07-14 16:32:31 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-07-14 21:14:06 +0000
commit588fb040b3ad410cdb10c87f9a7884b6eb825e90 (patch)
tree954314604fe306c899e5d44417cc1019a0a078f5
parent0274b30feeacae0bcd12f03ae96cb4721c1393a2 (diff)
Report error on failure to create SkCodec
Update NewFromStream to report an error on failure to create an SkCodec, so that a client can distinguish between - not enough data - invalid data In Chromium, this will allow blink::ImageDecoder to call SetFailed if the stream is invalid early and we never create an SkCodec. Without this, ImageDecoder will keep trying to create an SkCodec when it receives more data. Change-Id: I4f505c56d91c982be36a828fd0f7db17b1596588 Reviewed-on: https://skia-review.googlesource.com/22642 Commit-Queue: Leon Scroggins <scroggo@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com> Reviewed-by: Chris Blume <cblume@chromium.org>
-rw-r--r--include/codec/SkCodec.h110
-rw-r--r--src/codec/SkAndroidCodec.cpp2
-rw-r--r--src/codec/SkBmpCodec.cpp83
-rw-r--r--src/codec/SkBmpCodec.h13
-rw-r--r--src/codec/SkCodec.cpp25
-rw-r--r--src/codec/SkGifCodec.cpp12
-rw-r--r--src/codec/SkGifCodec.h3
-rw-r--r--src/codec/SkIcoCodec.cpp25
-rw-r--r--src/codec/SkIcoCodec.h2
-rw-r--r--src/codec/SkJpegCodec.cpp31
-rw-r--r--src/codec/SkJpegCodec.h7
-rw-r--r--src/codec/SkPngCodec.cpp33
-rw-r--r--src/codec/SkPngCodec.h3
-rw-r--r--src/codec/SkRawCodec.cpp34
-rw-r--r--src/codec/SkRawCodec.h2
-rw-r--r--src/codec/SkWbmpCodec.cpp6
-rw-r--r--src/codec/SkWbmpCodec.h2
-rw-r--r--src/codec/SkWebpCodec.cpp47
-rw-r--r--src/codec/SkWebpCodec.h2
-rw-r--r--tests/CodecPartialTest.cpp35
-rw-r--r--third_party/gif/SkGifImageReader.cpp30
-rw-r--r--third_party/gif/SkGifImageReader.h4
22 files changed, 293 insertions, 218 deletions
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index b78ec69c74..bb780f0906 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -53,6 +53,59 @@ public:
static size_t MinBufferedBytesNeeded();
/**
+ * Error codes for various SkCodec methods.
+ */
+ enum Result {
+ /**
+ * General return value for success.
+ */
+ kSuccess,
+ /**
+ * The input is incomplete. A partial image was generated.
+ */
+ kIncompleteInput,
+ /**
+ * Like kIncompleteInput, except the input had an error.
+ *
+ * If returned from an incremental decode, decoding cannot continue,
+ * even with more data.
+ */
+ kErrorInInput,
+ /**
+ * The generator cannot convert to match the request, ignoring
+ * dimensions.
+ */
+ kInvalidConversion,
+ /**
+ * The generator cannot scale to requested size.
+ */
+ kInvalidScale,
+ /**
+ * Parameters (besides info) are invalid. e.g. NULL pixels, rowBytes
+ * too small, etc.
+ */
+ kInvalidParameters,
+ /**
+ * The input did not contain a valid image.
+ */
+ kInvalidInput,
+ /**
+ * Fulfilling this request requires rewinding the input, which is not
+ * supported for this input.
+ */
+ kCouldNotRewind,
+ /**
+ * An internal error, such as OOM.
+ */
+ kInternalError,
+ /**
+ * This method is not implemented by this codec.
+ * FIXME: Perhaps this should be kUnsupported?
+ */
+ kUnimplemented,
+ };
+
+ /**
* If this stream represents an encoded image that we know how to decode,
* return an SkCodec that can decode it. Otherwise return NULL.
*
@@ -65,6 +118,9 @@ public:
* returns zero bytes, this call will instead attempt to read(). This
* will require that the stream can be rewind()ed.
*
+ * If Result is not NULL, it will be set to either kSuccess if an SkCodec
+ * is returned or a reason for the failure if NULL is returned.
+ *
* If SkPngChunkReader is not NULL, take a ref and pass it to libpng if
* the image is a png.
*
@@ -83,7 +139,8 @@ public:
* If NULL is returned, the stream is deleted immediately. Otherwise, the
* SkCodec takes ownership of it, and will delete it when done with it.
*/
- static SkCodec* NewFromStream(SkStream*, SkPngChunkReader* = NULL);
+ static SkCodec* NewFromStream(SkStream*, Result* = nullptr,
+ SkPngChunkReader* = nullptr);
/**
* If this data represents an encoded image that we know how to decode,
@@ -181,57 +238,6 @@ public:
SkEncodedImageFormat getEncodedFormat() const { return this->onGetEncodedFormat(); }
/**
- * Used to describe the result of a call to getPixels().
- *
- * Result is the union of possible results from subclasses.
- */
- enum Result {
- /**
- * General return value for success.
- */
- kSuccess,
- /**
- * The input is incomplete. A partial image was generated.
- */
- kIncompleteInput,
- /**
- * Like kIncompleteInput, except the input had an error.
- *
- * If returned from an incremental decode, decoding cannot continue,
- * even with more data.
- */
- kErrorInInput,
- /**
- * The generator cannot convert to match the request, ignoring
- * dimensions.
- */
- kInvalidConversion,
- /**
- * The generator cannot scale to requested size.
- */
- kInvalidScale,
- /**
- * Parameters (besides info) are invalid. e.g. NULL pixels, rowBytes
- * too small, etc.
- */
- kInvalidParameters,
- /**
- * The input did not contain a valid image.
- */
- kInvalidInput,
- /**
- * Fulfilling this request requires rewinding the input, which is not
- * supported for this input.
- */
- kCouldNotRewind,
- /**
- * This method is not implemented by this codec.
- * FIXME: Perhaps this should be kUnsupported?
- */
- kUnimplemented,
- };
-
- /**
* Whether or not the memory passed to getPixels is zero initialized.
*/
enum ZeroInitialized {
diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp
index 046f0ae3c4..97be146d77 100644
--- a/src/codec/SkAndroidCodec.cpp
+++ b/src/codec/SkAndroidCodec.cpp
@@ -66,7 +66,7 @@ SkAndroidCodec::SkAndroidCodec(SkCodec* codec)
{}
SkAndroidCodec* SkAndroidCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) {
- std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream, chunkReader));
+ std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream, nullptr, chunkReader));
if (nullptr == codec) {
return nullptr;
}
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index 3e224c13a9..70397f656f 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -67,16 +67,16 @@ bool SkBmpCodec::IsBmp(const void* buffer, size_t bytesRead) {
* Creates a bmp decoder
* Reads enough of the stream to determine the image format
*/
-SkCodec* SkBmpCodec::NewFromStream(SkStream* stream) {
- return SkBmpCodec::NewFromStream(stream, false);
+SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, Result* result) {
+ return SkBmpCodec::NewFromStream(stream, result, false);
}
/*
* Creates a bmp decoder for a bmp embedded in ico
* Reads enough of the stream to determine the image format
*/
-SkCodec* SkBmpCodec::NewFromIco(SkStream* stream) {
- return SkBmpCodec::NewFromStream(stream, true);
+SkCodec* SkBmpCodec::NewFromIco(SkStream* stream, Result* result) {
+ return SkBmpCodec::NewFromStream(stream, result, true);
}
// Header size constants
@@ -132,14 +132,7 @@ static BmpHeaderType get_header_type(size_t infoBytes) {
}
}
-/*
- * Read enough of the stream to initialize the SkBmpCodec. Returns a bool
- * representing success or failure. If it returned true, and codecOut was
- * not nullptr, it will be set to a new SkBmpCodec.
- * If codecOut is set to a new SkCodec, it will take ownership of the stream.
- * Otherwise, the stream will not be deleted.
- */
-bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
+SkCodec::Result SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
std::unique_ptr<SkCodec>* codecOut) {
// The total bytes in the bmp file
// We only need to use this value for RLE decoding, so we will only
@@ -157,14 +150,14 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
if (stream->read(hBuffer, kBmpHeaderBytesPlusFour) !=
kBmpHeaderBytesPlusFour) {
SkCodecPrintf("Error: unable to read first bitmap header.\n");
- return false;
+ return kIncompleteInput;
}
totalBytes = get_int(hBuffer, 2);
offset = get_int(hBuffer, 10);
if (offset < kBmpHeaderBytes + kBmpOS2V1Bytes) {
SkCodecPrintf("Error: invalid starting location for pixel data\n");
- return false;
+ return kInvalidInput;
}
// The size of the second (info) header in bytes
@@ -173,7 +166,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
infoBytes = get_int(hBuffer, 14);
if (infoBytes < kBmpOS2V1Bytes) {
SkCodecPrintf("Error: invalid second header size.\n");
- return false;
+ return kInvalidInput;
}
} else {
// This value is only used by RLE compression. Bmp in Ico files do not
@@ -190,19 +183,19 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
uint8_t hBuffer[4];
if (stream->read(hBuffer, 4) != 4) {
SkCodecPrintf("Error: unable to read size of second bitmap header.\n");
- return false;
+ return kIncompleteInput;
}
infoBytes = get_int(hBuffer, 0);
if (infoBytes < kBmpOS2V1Bytes) {
SkCodecPrintf("Error: invalid second header size.\n");
- return false;
+ return kInvalidInput;
}
}
// Determine image information depending on second header format
const BmpHeaderType headerType = get_header_type(infoBytes);
if (kUnknown_BmpHeaderType == headerType) {
- return false;
+ return kInvalidInput;
}
// We already read the first four bytes of the info header to get the size
@@ -212,7 +205,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
std::unique_ptr<uint8_t[]> iBuffer(new uint8_t[infoBytesRemaining]);
if (stream->read(iBuffer.get(), infoBytesRemaining) != infoBytesRemaining) {
SkCodecPrintf("Error: unable to read second bitmap header.\n");
- return false;
+ return kIncompleteInput;
}
// The number of bits used per pixel in the pixel data
@@ -268,7 +261,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
case kUnknown_BmpHeaderType:
// We'll exit above in this case.
SkASSERT(false);
- return false;
+ return kInvalidInput;
}
// Check for valid dimensions from header
@@ -287,7 +280,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
constexpr int kMaxDim = 1 << 16;
if (width <= 0 || height <= 0 || width >= kMaxDim || height >= kMaxDim) {
SkCodecPrintf("Error: invalid bitmap dimensions.\n");
- return false;
+ return kInvalidInput;
}
// Create mask struct
@@ -337,7 +330,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
uint8_t buffer[kBmpMaskBytes];
if (stream->read(buffer, kBmpMaskBytes) != kBmpMaskBytes) {
SkCodecPrintf("Error: unable to read bit inputMasks.\n");
- return false;
+ return kIncompleteInput;
}
maskBytes = kBmpMaskBytes;
inputMasks.red = get_int(buffer, 0);
@@ -384,10 +377,10 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
// in chromium. I have not come across a test case
// that uses this format.
SkCodecPrintf("Error: huffman format unsupported.\n");
- return false;
+ return kUnimplemented;
default:
SkCodecPrintf("Error: invalid bmp bit masks header.\n");
- return false;
+ return kInvalidInput;
}
break;
case kJpeg_BmpCompressionMethod:
@@ -401,16 +394,16 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
// It is unsupported in the previous version and
// in chromium. I think it is used mostly for printers.
SkCodecPrintf("Error: compression format not supported.\n");
- return false;
+ return kUnimplemented;
case kCMYK_BmpCompressionMethod:
case kCMYK8BitRLE_BmpCompressionMethod:
case kCMYK4BitRLE_BmpCompressionMethod:
// TODO: Same as above.
SkCodecPrintf("Error: CMYK not supported for bitmap decoding.\n");
- return false;
+ return kUnimplemented;
default:
SkCodecPrintf("Error: invalid format for bitmap decoding.\n");
- return false;
+ return kInvalidInput;
}
iBuffer.reset();
@@ -421,7 +414,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
// Seems like we can just assume that the offset is zero and try to decode?
// Maybe we don't want to try to decode corrupt images?
SkCodecPrintf("Error: pixel data offset less than header size.\n");
- return false;
+ return kInvalidInput;
}
@@ -474,7 +467,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
break;
default:
SkCodecPrintf("Error: invalid input value for bits per pixel.\n");
- return false;
+ return kInvalidInput;
}
if (codecOut) {
@@ -486,16 +479,17 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
codecOut->reset(new SkBmpStandardCodec(width, height, info, stream, bitsPerPixel,
numColors, bytesPerColor, offset - bytesRead,
rowOrder, isOpaque, inIco));
- return static_cast<SkBmpStandardCodec*>(codecOut->get())->didCreateSrcBuffer();
+ return static_cast<SkBmpStandardCodec*>(codecOut->get())->didCreateSrcBuffer()
+ ? kSuccess : kInvalidInput;
}
- return true;
+ return kSuccess;
}
case kBitMask_BmpInputFormat: {
// Bmp-in-Ico must be standard mode
if (inIco) {
SkCodecPrintf("Error: Icos may not use bit mask format.\n");
- return false;
+ return kInvalidInput;
}
switch (bitsPerPixel) {
@@ -505,7 +499,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
break;
default:
SkCodecPrintf("Error: invalid input value for bits per pixel.\n");
- return false;
+ return kInvalidInput;
}
// Skip to the start of the pixel array.
@@ -513,7 +507,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
// in bit mask mode.
if (stream->skip(offset - bytesRead) != offset - bytesRead) {
SkCodecPrintf("Error: unable to skip to image data.\n");
- return false;
+ return kIncompleteInput;
}
if (codecOut) {
@@ -521,7 +515,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
std::unique_ptr<SkMasks> masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel));
if (nullptr == masks) {
SkCodecPrintf("Error: invalid input masks.\n");
- return false;
+ return kInvalidInput;
}
// Masked bmps are not a great fit for SkEncodedInfo, since they have
@@ -540,9 +534,10 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
const SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
codecOut->reset(new SkBmpMaskCodec(width, height, info, stream, bitsPerPixel,
masks.release(), rowOrder));
- return static_cast<SkBmpMaskCodec*>(codecOut->get())->didCreateSrcBuffer();
+ return static_cast<SkBmpMaskCodec*>(codecOut->get())->didCreateSrcBuffer()
+ ? kSuccess : kInvalidInput;
}
- return true;
+ return kSuccess;
}
case kRLE_BmpInputFormat: {
@@ -552,7 +547,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
// Check for a valid number of total bytes when in RLE mode
if (totalBytes <= offset) {
SkCodecPrintf("Error: RLE requires valid input size.\n");
- return false;
+ return kInvalidInput;
}
// Bmp-in-Ico must be standard mode
@@ -572,11 +567,11 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
numColors, bytesPerColor, offset - bytesRead,
rowOrder));
}
- return true;
+ return kSuccess;
}
default:
SkASSERT(false);
- return false;
+ return kInvalidInput;
}
}
@@ -584,16 +579,16 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
* Creates a bmp decoder
* Reads enough of the stream to determine the image format
*/
-SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool inIco) {
+SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, Result* result, bool inIco) {
std::unique_ptr<SkStream> streamDeleter(stream);
std::unique_ptr<SkCodec> codec;
- bool success = ReadHeader(stream, inIco, &codec);
+ *result = ReadHeader(stream, inIco, &codec);
if (codec) {
// codec has taken ownership of stream, so we do not need to
// delete it.
streamDeleter.release();
}
- return success ? codec.release() : nullptr;
+ return kSuccess == *result ? codec.release() : nullptr;
}
SkBmpCodec::SkBmpCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream,
@@ -606,7 +601,7 @@ SkBmpCodec::SkBmpCodec(int width, int height, const SkEncodedInfo& info, SkStrea
{}
bool SkBmpCodec::onRewind() {
- return SkBmpCodec::ReadHeader(this->stream(), this->inIco(), nullptr);
+ return SkBmpCodec::ReadHeader(this->stream(), this->inIco(), nullptr) == kSuccess;
}
int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const {
diff --git a/src/codec/SkBmpCodec.h b/src/codec/SkBmpCodec.h
index bf9727fa1c..d5f5a32eae 100644
--- a/src/codec/SkBmpCodec.h
+++ b/src/codec/SkBmpCodec.h
@@ -28,13 +28,13 @@ public:
* Creates a bmp decoder
* Reads enough of the stream to determine the image format
*/
- static SkCodec* NewFromStream(SkStream*);
+ static SkCodec* NewFromStream(SkStream*, Result*);
/*
* Creates a bmp decoder for a bmp embedded in ico
* Reads enough of the stream to determine the image format
*/
- static SkCodec* NewFromIco(SkStream*);
+ static SkCodec* NewFromIco(SkStream*, Result*);
protected:
@@ -44,12 +44,11 @@ protected:
SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kBMP; }
/*
- * Read enough of the stream to initialize the SkBmpCodec. Returns a bool
- * representing success or failure. If it returned true, and codecOut was
- * not nullptr, it will be set to a new SkBmpCodec.
+ * Read enough of the stream to initialize the SkBmpCodec.
+ * On kSuccess, if codecOut is not nullptr, it will be set to a new SkBmpCodec.
* If an SkCodec is created, it will take ownership of the SkStream.
*/
- static bool ReadHeader(SkStream*, bool inIco, std::unique_ptr<SkCodec>* codecOut);
+ static Result ReadHeader(SkStream*, bool inIco, std::unique_ptr<SkCodec>* codecOut);
bool onRewind() override;
@@ -113,7 +112,7 @@ private:
* Creates a bmp decoder
* Reads enough of the stream to determine the image format
*/
- static SkCodec* NewFromStream(SkStream*, bool inIco);
+ static SkCodec* NewFromStream(SkStream*, Result*, bool inIco);
/*
* Decodes the next dstInfo.height() lines.
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 4bd3917880..872fe2b689 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -26,7 +26,7 @@
struct DecoderProc {
bool (*IsFormat)(const void*, size_t);
- SkCodec* (*NewFromStream)(SkStream*);
+ SkCodec* (*NewFromStream)(SkStream*, SkCodec::Result*);
};
static const DecoderProc gDecoderProcs[] = {
@@ -49,8 +49,14 @@ size_t SkCodec::MinBufferedBytesNeeded() {
}
SkCodec* SkCodec::NewFromStream(SkStream* stream,
- SkPngChunkReader* chunkReader) {
+ Result* outResult, SkPngChunkReader* chunkReader) {
+ Result resultStorage;
+ if (!outResult) {
+ outResult = &resultStorage;
+ }
+
if (!stream) {
+ *outResult = kInvalidInput;
return nullptr;
}
@@ -81,6 +87,7 @@ SkCodec* SkCodec::NewFromStream(SkStream* stream,
bytesRead = stream->read(buffer, bytesToRead);
if (!stream->rewind()) {
SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n");
+ *outResult = kCouldNotRewind;
return nullptr;
}
}
@@ -89,22 +96,28 @@ SkCodec* SkCodec::NewFromStream(SkStream* stream,
// But this code follows the same pattern as the loop.
#ifdef SK_HAS_PNG_LIBRARY
if (SkPngCodec::IsPng(buffer, bytesRead)) {
- return SkPngCodec::NewFromStream(streamDeleter.release(), chunkReader);
+ return SkPngCodec::NewFromStream(streamDeleter.release(), outResult, chunkReader);
} else
#endif
{
for (DecoderProc proc : gDecoderProcs) {
if (proc.IsFormat(buffer, bytesRead)) {
- return proc.NewFromStream(streamDeleter.release());
+ return proc.NewFromStream(streamDeleter.release(), outResult);
}
}
#ifdef SK_CODEC_DECODES_RAW
// Try to treat the input as RAW if all the other checks failed.
- return SkRawCodec::NewFromStream(streamDeleter.release());
+ return SkRawCodec::NewFromStream(streamDeleter.release(), outResult);
#endif
}
+ if (bytesRead < bytesToRead) {
+ *outResult = kIncompleteInput;
+ } else {
+ *outResult = kUnimplemented;
+ }
+
return nullptr;
}
@@ -112,7 +125,7 @@ SkCodec* SkCodec::NewFromData(sk_sp<SkData> data, SkPngChunkReader* reader) {
if (!data) {
return nullptr;
}
- return NewFromStream(new SkMemoryStream(data), reader);
+ return NewFromStream(new SkMemoryStream(data), nullptr, reader);
}
SkCodec::SkCodec(int width, int height, const SkEncodedInfo& info,
diff --git a/src/codec/SkGifCodec.cpp b/src/codec/SkGifCodec.cpp
index 081f237fe1..879297d739 100644
--- a/src/codec/SkGifCodec.cpp
+++ b/src/codec/SkGifCodec.cpp
@@ -66,21 +66,17 @@ static SkCodec::Result gif_error(const char* msg, SkCodec::Result result = SkCod
return result;
}
-/*
- * Assumes IsGif was called and returned true
- * Creates a gif decoder
- * Reads enough of the stream to determine the image format
- */
-SkCodec* SkGifCodec::NewFromStream(SkStream* stream) {
+SkCodec* SkGifCodec::NewFromStream(SkStream* stream, Result* result) {
std::unique_ptr<SkGifImageReader> reader(new SkGifImageReader(stream));
- if (!reader->parse(SkGifImageReader::SkGIFSizeQuery)) {
- // Fatal error occurred.
+ *result = reader->parse(SkGifImageReader::SkGIFSizeQuery);
+ if (*result != kSuccess) {
return nullptr;
}
// If no images are in the data, or the first header is not yet defined, we cannot
// create a codec. In either case, the width and height are not yet known.
if (0 == reader->imagesCount() || !reader->frameContext(0)->isHeaderDefined()) {
+ *result = kInvalidInput;
return nullptr;
}
diff --git a/src/codec/SkGifCodec.h b/src/codec/SkGifCodec.h
index 7bb86f824b..6ecd419ea3 100644
--- a/src/codec/SkGifCodec.h
+++ b/src/codec/SkGifCodec.h
@@ -27,10 +27,9 @@ public:
/*
* Assumes IsGif was called and returned true
- * Creates a gif decoder
* Reads enough of the stream to determine the image format
*/
- static SkCodec* NewFromStream(SkStream*);
+ static SkCodec* NewFromStream(SkStream*, Result*);
// Callback for SkGifImageReader when a row is available.
bool haveDecodedRow(int frameIndex, const unsigned char* rowBegin,
diff --git a/src/codec/SkIcoCodec.cpp b/src/codec/SkIcoCodec.cpp
index d1cbed0377..2e61bfe45d 100644
--- a/src/codec/SkIcoCodec.cpp
+++ b/src/codec/SkIcoCodec.cpp
@@ -26,12 +26,7 @@ bool SkIcoCodec::IsIco(const void* buffer, size_t bytesRead) {
!memcmp(buffer, curSig, sizeof(curSig)));
}
-/*
- * Assumes IsIco was called and returned true
- * Creates an Ico decoder
- * Reads enough of the stream to determine the image format
- */
-SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
+SkCodec* SkIcoCodec::NewFromStream(SkStream* stream, Result* result) {
// Ensure that we do not leak the input stream
std::unique_ptr<SkStream> inputStream(stream);
@@ -44,6 +39,7 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
if (inputStream.get()->read(dirBuffer.get(), kIcoDirectoryBytes) !=
kIcoDirectoryBytes) {
SkCodecPrintf("Error: unable to read ico directory header.\n");
+ *result = kIncompleteInput;
return nullptr;
}
@@ -51,6 +47,7 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
const uint16_t numImages = get_short(dirBuffer.get(), 4);
if (0 == numImages) {
SkCodecPrintf("Error: No images embedded in ico.\n");
+ *result = kInvalidInput;
return nullptr;
}
@@ -66,6 +63,7 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
if (!dirEntryBuffer) {
SkCodecPrintf("Error: OOM allocating ICO directory for %i images.\n",
numImages);
+ *result = kInternalError;
return nullptr;
}
auto* directoryEntries = reinterpret_cast<Entry*>(dirEntryBuffer.get());
@@ -76,6 +74,7 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
if (inputStream->read(entryBuffer, kIcoDirEntryBytes) !=
kIcoDirEntryBytes) {
SkCodecPrintf("Error: Dir entries truncated in ico.\n");
+ *result = kIncompleteInput;
return nullptr;
}
@@ -98,6 +97,9 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
directoryEntries[i].size = size;
}
+ // Default Result, if no valid embedded codecs are found.
+ *result = kInvalidInput;
+
// It is "customary" that the embedded images will be stored in order of
// increasing offset. However, the specification does not indicate that
// they must be stored in this order, so we will not trust that this is the
@@ -141,6 +143,7 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
if (inputStream->read(buffer.get(), size) != size) {
SkCodecPrintf("Warning: could not create embedded stream.\n");
+ *result = kIncompleteInput;
break;
}
@@ -150,10 +153,11 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
// Check if the embedded codec is bmp or png and create the codec
SkCodec* codec = nullptr;
+ Result dummyResult;
if (SkPngCodec::IsPng((const char*) data->bytes(), data->size())) {
- codec = SkPngCodec::NewFromStream(embeddedStream.release());
+ codec = SkPngCodec::NewFromStream(embeddedStream.release(), &dummyResult);
} else {
- codec = SkBmpCodec::NewFromIco(embeddedStream.release());
+ codec = SkBmpCodec::NewFromIco(embeddedStream.release(), &dummyResult);
}
// Save a valid codec
@@ -185,8 +189,9 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
SkEncodedInfo info = codecs->operator[](maxIndex)->getEncodedInfo();
SkColorSpace* colorSpace = codecs->operator[](maxIndex)->getInfo().colorSpace();
- // Note that stream is owned by the embedded codec, the ico does not need
- // direct access to the stream.
+ *result = kSuccess;
+ // The original stream is no longer needed, because the embedded codecs own their
+ // own streams.
return new SkIcoCodec(width, height, info, codecs.release(), sk_ref_sp(colorSpace));
}
diff --git a/src/codec/SkIcoCodec.h b/src/codec/SkIcoCodec.h
index 9f2457aa0d..0c27934e0c 100644
--- a/src/codec/SkIcoCodec.h
+++ b/src/codec/SkIcoCodec.h
@@ -25,7 +25,7 @@ public:
* Creates an Ico decoder
* Reads enough of the stream to determine the image format
*/
- static SkCodec* NewFromStream(SkStream*);
+ static SkCodec* NewFromStream(SkStream*, Result*);
protected:
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index 728298917a..f3dbdf199c 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -185,15 +185,15 @@ static sk_sp<SkData> get_icc_profile(jpeg_decompress_struct* dinfo) {
return iccData;
}
-bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, JpegDecoderMgr** decoderMgrOut,
- sk_sp<SkColorSpace> defaultColorSpace) {
+SkCodec::Result SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
+ JpegDecoderMgr** decoderMgrOut, sk_sp<SkColorSpace> defaultColorSpace) {
// Create a JpegDecoderMgr to own all of the decompress information
std::unique_ptr<JpegDecoderMgr> decoderMgr(new JpegDecoderMgr(stream));
// libjpeg errors will be caught and reported here
if (setjmp(decoderMgr->getJmpBuf())) {
- return decoderMgr->returnFalse("ReadHeader");
+ return decoderMgr->returnFailure("ReadHeader", kInvalidInput);
}
// Initialize the decompress info and the source manager
@@ -208,15 +208,20 @@ bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, JpegDecoderMg
}
// Read the jpeg header
- if (JPEG_HEADER_OK != jpeg_read_header(decoderMgr->dinfo(), true)) {
- return decoderMgr->returnFalse("ReadHeader");
+ switch (jpeg_read_header(decoderMgr->dinfo(), true)) {
+ case JPEG_HEADER_OK:
+ break;
+ case JPEG_SUSPENDED:
+ return decoderMgr->returnFailure("ReadHeader", kIncompleteInput);
+ default:
+ return decoderMgr->returnFailure("ReadHeader", kInvalidInput);
}
if (codecOut) {
// Get the encoded color type
SkEncodedInfo::Color color;
if (!decoderMgr->getEncodedColor(&color)) {
- return false;
+ return kInvalidInput;
}
// Create image info object and the codec
@@ -254,17 +259,19 @@ bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, JpegDecoderMg
SkASSERT(nullptr != decoderMgrOut);
*decoderMgrOut = decoderMgr.release();
}
- return true;
+ return kSuccess;
}
-SkCodec* SkJpegCodec::NewFromStream(SkStream* stream) {
- return SkJpegCodec::NewFromStream(stream, SkColorSpace::MakeSRGB());
+SkCodec* SkJpegCodec::NewFromStream(SkStream* stream, Result* result) {
+ return SkJpegCodec::NewFromStream(stream, result, SkColorSpace::MakeSRGB());
}
-SkCodec* SkJpegCodec::NewFromStream(SkStream* stream, sk_sp<SkColorSpace> defaultColorSpace) {
+SkCodec* SkJpegCodec::NewFromStream(SkStream* stream, Result* result,
+ sk_sp<SkColorSpace> defaultColorSpace) {
std::unique_ptr<SkStream> streamDeleter(stream);
SkCodec* codec = nullptr;
- if (ReadHeader(stream, &codec, nullptr, std::move(defaultColorSpace))) {
+ *result = ReadHeader(stream, &codec, nullptr, std::move(defaultColorSpace));
+ if (kSuccess == *result) {
// Codec has taken ownership of the stream, we do not need to delete it
SkASSERT(codec);
streamDeleter.release();
@@ -347,7 +354,7 @@ SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const {
bool SkJpegCodec::onRewind() {
JpegDecoderMgr* decoderMgr = nullptr;
- if (!ReadHeader(this->stream(), nullptr, &decoderMgr, nullptr)) {
+ if (kSuccess != ReadHeader(this->stream(), nullptr, &decoderMgr, nullptr)) {
return fDecoderMgr->returnFalse("onRewind");
}
SkASSERT(nullptr != decoderMgr);
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
index 2d6822e8af..96228f305a 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -29,10 +29,9 @@ public:
/*
* Assumes IsJpeg was called and returned true
- * Creates a jpeg decoder
* Takes ownership of the stream
*/
- static SkCodec* NewFromStream(SkStream*);
+ static SkCodec* NewFromStream(SkStream*, Result*);
protected:
@@ -64,7 +63,7 @@ private:
/*
* Allows SkRawCodec to communicate the color space from the exif data.
*/
- static SkCodec* NewFromStream(SkStream*, sk_sp<SkColorSpace> defaultColorSpace);
+ static SkCodec* NewFromStream(SkStream*, Result*, sk_sp<SkColorSpace> defaultColorSpace);
/*
* Read enough of the stream to initialize the SkJpegCodec.
@@ -88,7 +87,7 @@ private:
* If the jpeg does not have an embedded color space, the image data should
* be tagged with this color space.
*/
- static bool ReadHeader(SkStream* stream, SkCodec** codecOut,
+ static Result ReadHeader(SkStream* stream, SkCodec** codecOut,
JpegDecoderMgr** decoderMgrOut, sk_sp<SkColorSpace> defaultColorSpace);
/*
diff --git a/src/codec/SkPngCodec.cpp b/src/codec/SkPngCodec.cpp
index ea832ff688..560b5bfe6b 100644
--- a/src/codec/SkPngCodec.cpp
+++ b/src/codec/SkPngCodec.cpp
@@ -761,31 +761,30 @@ private:
// png_structp on success.
// @param info_ptrp Optional output variable. If non-NULL, will be set to a new
// png_infop on success;
-// @return true on success, in which case the caller is responsible for calling
+// @return if kSuccess, the caller is responsible for calling
// png_destroy_read_struct(png_ptrp, info_ptrp).
-// If it returns false, the passed in fields (except stream) are unchanged.
-static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec** outCodec,
- png_structp* png_ptrp, png_infop* info_ptrp) {
+// Otherwise, the passed in fields (except stream) are unchanged.
+static SkCodec::Result read_header(SkStream* stream, SkPngChunkReader* chunkReader,
+ SkCodec** outCodec,
+ png_structp* png_ptrp, png_infop* info_ptrp) {
// The image is known to be a PNG. Decode enough to know the SkImageInfo.
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
sk_error_fn, sk_warning_fn);
if (!png_ptr) {
- return false;
+ return SkCodec::kInternalError;
}
AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec);
png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == nullptr) {
- return false;
+ return SkCodec::kInternalError;
}
autoClean.setInfoPtr(info_ptr);
- // FIXME: Could we use the return value of setjmp to specify the type of
- // error?
if (setjmp(PNG_JMPBUF(png_ptr))) {
- return false;
+ return SkCodec::kInvalidInput;
}
#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
@@ -801,7 +800,7 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec
const bool decodedBounds = autoClean.decodeBounds();
if (!decodedBounds) {
- return false;
+ return SkCodec::kIncompleteInput;
}
// On success, decodeBounds releases ownership of png_ptr and info_ptr.
@@ -816,7 +815,7 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec
if (outCodec) {
SkASSERT(*outCodec);
}
- return true;
+ return SkCodec::kSuccess;
}
void AutoCleanPng::infoCallback(size_t idatLength) {
@@ -1071,7 +1070,8 @@ bool SkPngCodec::onRewind() {
png_structp png_ptr;
png_infop info_ptr;
- if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, &info_ptr)) {
+ if (kSuccess != read_header(this->stream(), fPngChunkReader.get(), nullptr,
+ &png_ptr, &info_ptr)) {
return false;
}
@@ -1145,16 +1145,15 @@ uint64_t SkPngCodec::onGetFillValue(const SkImageInfo& dstInfo) const {
return INHERITED::onGetFillValue(dstInfo);
}
-SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) {
+SkCodec* SkPngCodec::NewFromStream(SkStream* stream, Result* result, SkPngChunkReader* chunkReader) {
std::unique_ptr<SkStream> streamDeleter(stream);
SkCodec* outCodec = nullptr;
- if (read_header(streamDeleter.get(), chunkReader, &outCodec, nullptr, nullptr)) {
+ *result = read_header(stream, chunkReader, &outCodec, nullptr, nullptr);
+ if (kSuccess == *result) {
// Codec has taken ownership of the stream.
SkASSERT(outCodec);
streamDeleter.release();
- return outCodec;
}
-
- return nullptr;
+ return outCodec;
}
diff --git a/src/codec/SkPngCodec.h b/src/codec/SkPngCodec.h
index 25a5f07877..b5f9347705 100644
--- a/src/codec/SkPngCodec.h
+++ b/src/codec/SkPngCodec.h
@@ -23,7 +23,8 @@ public:
static bool IsPng(const char*, size_t);
// Assume IsPng was called and returned true.
- static SkCodec* NewFromStream(SkStream*, SkPngChunkReader* = NULL);
+ static SkCodec* NewFromStream(SkStream*, Result*,
+ SkPngChunkReader* = nullptr);
// FIXME (scroggo): Temporarily needed by AutoCleanPng.
void setIdatLength(size_t len) { fIdatLength = len; }
diff --git a/src/codec/SkRawCodec.cpp b/src/codec/SkRawCodec.cpp
index 83b7947ab6..d23eec0de6 100644
--- a/src/codec/SkRawCodec.cpp
+++ b/src/codec/SkRawCodec.cpp
@@ -447,16 +447,10 @@ public:
*/
static SkDngImage* NewFromStream(SkRawStream* stream) {
std::unique_ptr<SkDngImage> dngImage(new SkDngImage(stream));
- if (!dngImage->isTiffHeaderValid()) {
+ if (!dngImage->initFromPiex() && !dngImage->readDng()) {
return nullptr;
}
- if (!dngImage->initFromPiex()) {
- if (!dngImage->readDng()) {
- return nullptr;
- }
- }
-
return dngImage.release();
}
@@ -535,12 +529,12 @@ public:
return fIsXtransImage;
}
-private:
// Quick check if the image contains a valid TIFF header as requested by DNG format.
- bool isTiffHeaderValid() const {
+ // Does not affect ownership of stream.
+ static bool IsTiffHeaderValid(SkRawStream* stream) {
const size_t kHeaderSize = 4;
- SkAutoSTMalloc<kHeaderSize, unsigned char> header(kHeaderSize);
- if (!fStream->read(header.get(), 0 /* offset */, kHeaderSize)) {
+ unsigned char header[kHeaderSize];
+ if (!stream->read(header, 0 /* offset */, kHeaderSize)) {
return false;
}
@@ -553,6 +547,7 @@ private:
return 0x2A == get_endian_short(header + 2, littleEndian);
}
+private:
bool init(int width, int height, const dng_point& cfaPatternSize) {
fWidth = width;
fHeight = height;
@@ -635,7 +630,7 @@ private:
* SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases,
* fallback to create SkRawCodec for DNG images.
*/
-SkCodec* SkRawCodec::NewFromStream(SkStream* stream) {
+SkCodec* SkRawCodec::NewFromStream(SkStream* stream, Result* result) {
std::unique_ptr<SkRawStream> rawStream;
if (is_asset_stream(*stream)) {
rawStream.reset(new SkRawAssetStream(stream));
@@ -649,6 +644,7 @@ SkCodec* SkRawCodec::NewFromStream(SkStream* stream) {
if (::piex::IsRaw(&piexStream)) {
::piex::Error error = ::piex::GetPreviewImageData(&piexStream, &imageData);
if (error == ::piex::Error::kFail) {
+ *result = kInvalidInput;
return nullptr;
}
@@ -672,17 +668,27 @@ SkCodec* SkRawCodec::NewFromStream(SkStream* stream) {
// FIXME: one may avoid the copy of memoryStream and use the buffered rawStream.
SkMemoryStream* memoryStream =
rawStream->transferBuffer(imageData.preview.offset, imageData.preview.length);
- return memoryStream ? SkJpegCodec::NewFromStream(memoryStream, std::move(colorSpace))
- : nullptr;
+ if (!memoryStream) {
+ *result = kInvalidInput;
+ return nullptr;
+ }
+ return SkJpegCodec::NewFromStream(memoryStream, result, std::move(colorSpace));
}
}
+ if (!SkDngImage::IsTiffHeaderValid(rawStream.get())) {
+ *result = kUnimplemented;
+ return nullptr;
+ }
+
// Takes the ownership of the rawStream.
std::unique_ptr<SkDngImage> dngImage(SkDngImage::NewFromStream(rawStream.release()));
if (!dngImage) {
+ *result = kInvalidInput;
return nullptr;
}
+ *result = kSuccess;
return new SkRawCodec(dngImage.release());
}
diff --git a/src/codec/SkRawCodec.h b/src/codec/SkRawCodec.h
index d2ef921f1d..5ed721190f 100644
--- a/src/codec/SkRawCodec.h
+++ b/src/codec/SkRawCodec.h
@@ -28,7 +28,7 @@ public:
* Creates a RAW decoder
* Takes ownership of the stream
*/
- static SkCodec* NewFromStream(SkStream*);
+ static SkCodec* NewFromStream(SkStream*, Result*);
~SkRawCodec() override;
diff --git a/src/codec/SkWbmpCodec.cpp b/src/codec/SkWbmpCodec.cpp
index aab0a41b14..9a0ed6ee72 100644
--- a/src/codec/SkWbmpCodec.cpp
+++ b/src/codec/SkWbmpCodec.cpp
@@ -148,12 +148,16 @@ bool SkWbmpCodec::IsWbmp(const void* buffer, size_t bytesRead) {
return read_header(&stream, nullptr);
}
-SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) {
+SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream, Result* result) {
std::unique_ptr<SkStream> streamDeleter(stream);
SkISize size;
if (!read_header(stream, &size)) {
+ // This already succeeded in IsWbmp, so this stream was corrupted in/
+ // after rewind.
+ *result = kCouldNotRewind;
return nullptr;
}
+ *result = kSuccess;
SkEncodedInfo info = SkEncodedInfo::Make(SkEncodedInfo::kGray_Color,
SkEncodedInfo::kOpaque_Alpha, 1);
return new SkWbmpCodec(size.width(), size.height(), info, streamDeleter.release());
diff --git a/src/codec/SkWbmpCodec.h b/src/codec/SkWbmpCodec.h
index e8a6e40aba..f81b428e4f 100644
--- a/src/codec/SkWbmpCodec.h
+++ b/src/codec/SkWbmpCodec.h
@@ -21,7 +21,7 @@ public:
* Creates a wbmp codec
* Takes ownership of the stream
*/
- static SkCodec* NewFromStream(SkStream*);
+ static SkCodec* NewFromStream(SkStream*, Result*);
protected:
SkEncodedImageFormat onGetEncodedFormat() const override;
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index 096d50529a..8d3d8cc80f 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -43,7 +43,7 @@ static SkAlphaType alpha_type(bool hasAlpha) {
// Parse headers of RIFF container, and check for valid Webp (VP8) content.
// Returns an SkWebpCodec on success
-SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
+SkCodec* SkWebpCodec::NewFromStream(SkStream* stream, Result* result) {
std::unique_ptr<SkStream> streamDeleter(stream);
// Webp demux needs a contiguous data buffer.
@@ -62,9 +62,19 @@ SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
// pointer in |webpData| to remain valid. This works because the pointer remains valid
// until the SkData is freed.
WebPData webpData = { data->bytes(), data->size() };
- SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> demux(WebPDemuxPartial(&webpData, nullptr));
- if (nullptr == demux) {
- return nullptr;
+ WebPDemuxState state;
+ SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> demux(WebPDemuxPartial(&webpData, &state));
+ switch (state) {
+ case WEBP_DEMUX_PARSE_ERROR:
+ *result = kInvalidInput;
+ return nullptr;
+ case WEBP_DEMUX_PARSING_HEADER:
+ *result = kIncompleteInput;
+ return nullptr;
+ case WEBP_DEMUX_PARSED_HEADER:
+ case WEBP_DEMUX_DONE:
+ SkASSERT(demux);
+ break;
}
const int width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
@@ -73,11 +83,9 @@ SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
// Sanity check for image size that's about to be decoded.
{
const int64_t size = sk_64_mul(width, height);
- if (!sk_64_isS32(size)) {
- return nullptr;
- }
// now check that if we are 4-bytes per pixel, we also don't overflow
- if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) {
+ if (!sk_64_isS32(size) || sk_64_asS32(size) > (0x7FFFFFFF >> 2)) {
+ *result = kInvalidInput;
return nullptr;
}
}
@@ -96,13 +104,21 @@ SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
WebPIterator frame;
SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
if (!WebPDemuxGetFrame(demux, 1, &frame)) {
+ *result = kIncompleteInput;
return nullptr;
}
WebPBitstreamFeatures features;
- VP8StatusCode status = WebPGetFeatures(frame.fragment.bytes, frame.fragment.size, &features);
- if (VP8_STATUS_OK != status) {
- return nullptr;
+ switch (WebPGetFeatures(frame.fragment.bytes, frame.fragment.size, &features)) {
+ case VP8_STATUS_OK:
+ break;
+ case VP8_STATUS_SUSPENDED:
+ case VP8_STATUS_NOT_ENOUGH_DATA:
+ *result = kIncompleteInput;
+ return nullptr;
+ default:
+ *result = kInvalidInput;
+ return nullptr;
}
const bool hasAlpha = SkToBool(frame.has_alpha)
@@ -139,14 +155,15 @@ SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
}
break;
default:
+ *result = kInvalidInput;
return nullptr;
}
+ *result = kSuccess;
SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
- SkWebpCodec* codecOut = new SkWebpCodec(width, height, info, std::move(colorSpace),
- streamDeleter.release(), demux.release(),
- std::move(data));
- return codecOut;
+ return new SkWebpCodec(width, height, info, std::move(colorSpace),
+ streamDeleter.release(), demux.release(),
+ std::move(data));
}
SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const {
diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h
index c911ebd585..e06d0972fe 100644
--- a/src/codec/SkWebpCodec.h
+++ b/src/codec/SkWebpCodec.h
@@ -28,7 +28,7 @@ static const size_t WEBP_VP8_HEADER_SIZE = 30;
class SkWebpCodec final : public SkCodec {
public:
// Assumes IsWebp was called and returned true.
- static SkCodec* NewFromStream(SkStream*);
+ static SkCodec* NewFromStream(SkStream*, Result*);
static bool IsWebp(const void*, size_t);
protected:
Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*) override;
diff --git a/tests/CodecPartialTest.cpp b/tests/CodecPartialTest.cpp
index ee012711a6..4a56f46e21 100644
--- a/tests/CodecPartialTest.cpp
+++ b/tests/CodecPartialTest.cpp
@@ -419,3 +419,38 @@ DEF_TEST(Codec_emptyIDAT, r) {
const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
}
+
+DEF_TEST(Codec_incomplete, r) {
+ for (const char* name : { "baby_tux.png",
+ "baby_tux.webp",
+ "CMYK.jpg",
+ "color_wheel.gif",
+ "google_chrome.ico",
+ "rle.bmp",
+ "mandrill.wbmp",
+ }) {
+ sk_sp<SkData> file = GetResourceAsData(name);
+ if (!name) {
+ continue;
+ }
+
+ for (size_t len = 14; len <= file->size(); len += 5) {
+ SkCodec::Result result;
+ auto* stream = new SkMemoryStream(file->data(), len);
+ std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream, &result));
+ if (codec) {
+ if (result != SkCodec::kSuccess) {
+ ERRORF(r, "Created an SkCodec for %s with %lu bytes, but "
+ "reported an error %i", name, len, result);
+ }
+ break;
+ }
+
+ if (SkCodec::kIncompleteInput != result) {
+ ERRORF(r, "Reported error %i for %s with %lu bytes",
+ result, name, len);
+ break;
+ }
+ }
+ }
+}
diff --git a/third_party/gif/SkGifImageReader.cpp b/third_party/gif/SkGifImageReader.cpp
index e76028dac9..e3b225dda7 100644
--- a/third_party/gif/SkGifImageReader.cpp
+++ b/third_party/gif/SkGifImageReader.cpp
@@ -420,17 +420,15 @@ bool SkGifImageReader::decode(int frameIndex, bool* frameComplete)
}
// Parse incoming GIF data stream into internal data structures.
-// Return true if parsing has progressed or there is not enough data.
-// Return false if a fatal error is encountered.
-bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
+SkCodec::Result SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
{
if (m_parseCompleted) {
- return true;
+ return SkCodec::kSuccess;
}
if (SkGIFLoopCountQuery == query && m_loopCount != cLoopCountNotSeen) {
// Loop count has already been parsed.
- return true;
+ return SkCodec::kSuccess;
}
// SkGIFSizeQuery and SkGIFFrameCountQuery are negative, so this is only meaningful when >= 0.
@@ -438,13 +436,13 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
if (lastFrameToParse >= 0 && (int) m_frames.size() > lastFrameToParse
&& m_frames[lastFrameToParse]->isComplete()) {
// We have already parsed this frame.
- return true;
+ return SkCodec::kSuccess;
}
while (true) {
if (!m_streamBuffer.buffer(m_bytesToConsume)) {
// The stream does not yet have enough data.
- return true;
+ return SkCodec::kIncompleteInput;
}
switch (m_state) {
@@ -475,7 +473,7 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
else {
// This prevents attempting to continue reading this invalid stream.
GETN(0, SkGIFDone);
- return false;
+ return SkCodec::kInvalidInput;
}
GETN(7, SkGIFGlobalHeader);
break;
@@ -687,7 +685,7 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
if (SkGIFLoopCountQuery == query) {
m_streamBuffer.flush();
- return true;
+ return SkCodec::kSuccess;
}
} else if (netscapeExtension == 2) {
// Wait for specified # of bytes to enter buffer.
@@ -700,7 +698,7 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
// 0,3-7 are yet to be defined netscape extension codes
// This prevents attempting to continue reading this invalid stream.
GETN(0, SkGIFDone);
- return false;
+ return SkCodec::kInvalidInput;
}
break;
}
@@ -748,7 +746,7 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
if (!height || !width) {
// This prevents attempting to continue reading this invalid stream.
GETN(0, SkGIFDone);
- return false;
+ return SkCodec::kInvalidInput;
}
}
@@ -778,7 +776,7 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
// The decoder needs to stop, so we return here, before
// flushing the buffer. Next time through, we'll be in the same
// state, requiring the same amount in the buffer.
- return true;
+ return SkCodec::kSuccess;
}
@@ -830,7 +828,7 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
GETN(1, SkGIFImageStart);
if (lastFrameToParse >= 0 && (int) m_frames.size() > lastFrameToParse) {
m_streamBuffer.flush();
- return true;
+ return SkCodec::kSuccess;
}
}
break;
@@ -838,20 +836,18 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
case SkGIFDone: {
m_parseCompleted = true;
- return true;
+ return SkCodec::kSuccess;
}
default:
// We shouldn't ever get here.
// This prevents attempting to continue reading this invalid stream.
GETN(0, SkGIFDone);
- return false;
+ return SkCodec::kInvalidInput;
break;
} // switch
m_streamBuffer.flush();
}
-
- return true;
}
bool SkGifImageReader::hasTransparency(int transparentPixel, bool isLocalColormapDefined,
diff --git a/third_party/gif/SkGifImageReader.h b/third_party/gif/SkGifImageReader.h
index 49655c23df..0b89b04818 100644
--- a/third_party/gif/SkGifImageReader.h
+++ b/third_party/gif/SkGifImageReader.h
@@ -312,9 +312,7 @@ public:
// Parse incoming GIF data stream into internal data structures.
// Non-negative values are used to indicate to parse through that frame.
- // Return true if parsing has progressed or there is not enough data.
- // Return false if a fatal error is encountered.
- bool parse(SkGIFParseQuery);
+ SkCodec::Result parse(SkGIFParseQuery);
// Decode the frame indicated by frameIndex.
// frameComplete will be set to true if the frame is completely decoded.