Index: src/codec/SkCodec_libpng.cpp |
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp |
index 9330ac5aee3974fd8044b6fd22333997be444462..18574f70be705ae15a2ae959006d6399b86b2cea 100644 |
--- a/src/codec/SkCodec_libpng.cpp |
+++ b/src/codec/SkCodec_libpng.cpp |
@@ -201,19 +201,25 @@ bool SkPngCodec::IsPng(SkStream* stream) { |
return true; |
} |
-SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { |
+// Reads the header, and initializes the passed in fields, if not NULL (except |
+// stream, which is passed to the read function). |
+// Returns true on success, in which case the caller is responsible for calling |
+// png_destroy_read_struct. If it returns false, the passed in fields (except |
+// stream) are unchanged. |
+static bool read_header(SkStream* stream, png_structp* png_ptrp, |
+ png_infop* info_ptrp, SkImageInfo* imageInfo) { |
// 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, NULL, |
sk_error_fn, sk_warning_fn); |
if (!png_ptr) { |
- return NULL; |
+ return false; |
} |
AutoCleanPng autoClean(png_ptr); |
png_infop info_ptr = png_create_info_struct(png_ptr); |
if (info_ptr == NULL) { |
- return NULL; |
+ return false; |
} |
autoClean.setInfoPtr(info_ptr); |
@@ -221,7 +227,7 @@ SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { |
// FIXME: Could we use the return value of setjmp to specify the type of |
// error? |
if (setjmp(png_jmpbuf(png_ptr))) { |
- return NULL; |
+ return false; |
} |
png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn); |
@@ -244,7 +250,7 @@ SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { |
int64_t size = sk_64_mul(origWidth, origHeight); |
// now check that if we are 4-bytes per pixel, we also don't overflow |
if (size < 0 || size > (0x7FFFFFFF >> 2)) { |
- return NULL; |
+ return false; |
} |
} |
@@ -325,11 +331,28 @@ SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { |
// FIXME: Also need to check for sRGB (skbug.com/3471). |
- SkImageInfo info = SkImageInfo::Make(origWidth, origHeight, skColorType, |
- skAlphaType); |
- SkCodec* codec = SkNEW_ARGS(SkPngCodec, (info, stream, png_ptr, info_ptr)); |
+ if (imageInfo) { |
+ *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType, |
+ skAlphaType); |
+ } |
autoClean.detach(); |
- return codec; |
+ if (png_ptrp) { |
+ *png_ptrp = png_ptr; |
+ } |
+ if (info_ptrp) { |
+ *info_ptrp = info_ptr; |
+ } |
+ return true; |
+} |
+ |
+SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { |
+ png_structp png_ptr; |
+ png_infop info_ptr; |
+ SkImageInfo imageInfo; |
+ if (read_header(stream, &png_ptr, &info_ptr, &imageInfo)) { |
+ return SkNEW_ARGS(SkPngCodec, (imageInfo, stream, png_ptr, info_ptr)); |
+ } |
+ return NULL; |
} |
#define INVALID_NUMBER_PASSES -1 |
@@ -344,7 +367,17 @@ SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, |
{} |
SkPngCodec::~SkPngCodec() { |
- png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL); |
+ this->destroyReadStruct(); |
+} |
+ |
+void SkPngCodec::destroyReadStruct() { |
+ if (fPng_ptr) { |
+ // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr |
+ SkASSERT(fInfo_ptr); |
+ png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL); |
+ fPng_ptr = NULL; |
+ fInfo_ptr = NULL; |
+ } |
} |
/////////////////////////////////////////////////////////////////////////////// |
@@ -424,8 +457,20 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* |
if (rewindState == kCouldNotRewind_RewindState) { |
return kCouldNotRewind; |
} else if (rewindState == kRewound_RewindState) { |
- // TODO(scroggo): handle rewinds |
- return kCouldNotRewind; |
+ // This sets fPng_ptr and fInfo_ptr to NULL. If read_header succeeds, |
+ // they will be repopulated, and if it fails, they will remain NULL. |
+ // Any future accesses to fPng_ptr and fInfo_ptr will come through this |
+ // function which will rewind and again attempt to reinitialize them. |
+ this->destroyReadStruct(); |
+ png_structp png_ptr; |
+ png_infop info_ptr; |
+ if (read_header(this->stream(), &png_ptr, &info_ptr, NULL)) { |
+ fPng_ptr = png_ptr; |
+ fInfo_ptr = info_ptr; |
+ } else { |
+ return kCouldNotRewind; |
+ } |
+ |
} |
if (requestedInfo.dimensions() != this->getInfo().dimensions()) { |
return kInvalidScale; |