aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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.