diff options
-rw-r--r-- | include/codec/SkCodec.h | 110 | ||||
-rw-r--r-- | src/codec/SkAndroidCodec.cpp | 2 | ||||
-rw-r--r-- | src/codec/SkBmpCodec.cpp | 83 | ||||
-rw-r--r-- | src/codec/SkBmpCodec.h | 13 | ||||
-rw-r--r-- | src/codec/SkCodec.cpp | 25 | ||||
-rw-r--r-- | src/codec/SkGifCodec.cpp | 12 | ||||
-rw-r--r-- | src/codec/SkGifCodec.h | 3 | ||||
-rw-r--r-- | src/codec/SkIcoCodec.cpp | 25 | ||||
-rw-r--r-- | src/codec/SkIcoCodec.h | 2 | ||||
-rw-r--r-- | src/codec/SkJpegCodec.cpp | 31 | ||||
-rw-r--r-- | src/codec/SkJpegCodec.h | 7 | ||||
-rw-r--r-- | src/codec/SkPngCodec.cpp | 33 | ||||
-rw-r--r-- | src/codec/SkPngCodec.h | 3 | ||||
-rw-r--r-- | src/codec/SkRawCodec.cpp | 34 | ||||
-rw-r--r-- | src/codec/SkRawCodec.h | 2 | ||||
-rw-r--r-- | src/codec/SkWbmpCodec.cpp | 6 | ||||
-rw-r--r-- | src/codec/SkWbmpCodec.h | 2 | ||||
-rw-r--r-- | src/codec/SkWebpCodec.cpp | 47 | ||||
-rw-r--r-- | src/codec/SkWebpCodec.h | 2 | ||||
-rw-r--r-- | tests/CodecPartialTest.cpp | 35 | ||||
-rw-r--r-- | third_party/gif/SkGifImageReader.cpp | 30 | ||||
-rw-r--r-- | third_party/gif/SkGifImageReader.h | 4 |
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. |