| Index: src/codec/SkCodec_libpng.cpp
|
| diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
|
| index ee8d49263ff3e1ee8031da3a46b4c748a6e849c2..e828e2499922cb2badc789a5e8914660a8951783 100644
|
| --- a/src/codec/SkCodec_libpng.cpp
|
| +++ b/src/codec/SkCodec_libpng.cpp
|
| @@ -466,7 +466,8 @@ bool SkPngCodec::onRewind() {
|
|
|
| SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
|
| size_t dstRowBytes, const Options& options,
|
| - SkPMColor ctable[], int* ctableCount) {
|
| + SkPMColor ctable[], int* ctableCount,
|
| + int* rowsDecoded) {
|
| if (!conversion_possible(requestedInfo, this->getInfo())) {
|
| return kInvalidConversion;
|
| }
|
| @@ -483,14 +484,32 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
|
| }
|
| // FIXME: Could we use the return value of setjmp to specify the type of
|
| // error?
|
| + int row = 0;
|
| + // This must be declared above the call to setjmp to avoid memory leaks on incomplete images.
|
| + SkAutoMalloc storage;
|
| if (setjmp(png_jmpbuf(fPng_ptr))) {
|
| - SkCodecPrintf("setjmp long jump!\n");
|
| - return kInvalidInput;
|
| + // Assume that any error that occurs while reading rows is caused by an incomplete input.
|
| + if (fNumberPasses > 1) {
|
| + // FIXME (msarett): Handle incomplete interlaced pngs.
|
| + return kInvalidInput;
|
| + }
|
| + // FIXME: We do a poor job on incomplete pngs compared to other decoders (ex: Chromium,
|
| + // Ubuntu Image Viewer). This is because we use the default buffer size in libpng (8192
|
| + // bytes), and if we can't fill the buffer, we immediately fail.
|
| + // For example, if we try to read 8192 bytes, and the image (incorrectly) only contains
|
| + // half that, which may have been enough to contain a non-zero number of lines, we fail
|
| + // when we could have decoded a few more lines and then failed.
|
| + // The read function that we provide for libpng has no way of indicating that we have
|
| + // made a partial read.
|
| + // Making our buffer size smaller improves our incomplete decodes, but what impact does
|
| + // it have on regular decode performance? Should we investigate using a different API
|
| + // instead of png_read_row(s)? Chromium uses png_process_data.
|
| + *rowsDecoded = row;
|
| + return kIncompleteInput;
|
| }
|
|
|
| bool hasAlpha = false;
|
| // FIXME: We could split these out based on subclass.
|
| - SkAutoMalloc storage;
|
| void* dstRow = dst;
|
| if (fNumberPasses > 1) {
|
| const int width = requestedInfo.width();
|
| @@ -520,7 +539,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
|
| } else {
|
| storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
|
| uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
|
| - for (int y = 0; y < requestedInfo.height(); y++) {
|
| + for (; row < requestedInfo.height(); row++) {
|
| png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
|
| // FIXME: Only call IsOpaque once, outside the loop. Same for onGetScanlines.
|
| hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
|
| @@ -549,6 +568,14 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
|
| return kSuccess;
|
| }
|
|
|
| +uint32_t SkPngCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const {
|
| + const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
|
| + if (colorPtr) {
|
| + return get_color_table_fill_value(colorType, colorPtr, 0);
|
| + }
|
| + return INHERITED::onGetFillValue(colorType, alphaType);
|
| +}
|
| +
|
| bool SkPngCodec::onReallyHasAlpha() const {
|
| switch (fAlphaState) {
|
| case kOpaque_AlphaState:
|
| @@ -603,15 +630,17 @@ public:
|
| return kSuccess;
|
| }
|
|
|
| - Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
|
| + int onGetScanlines(void* dst, int count, size_t rowBytes) override {
|
| + // Assume that an error in libpng indicates an incomplete input.
|
| + int row = 0;
|
| if (setjmp(png_jmpbuf(this->png_ptr()))) {
|
| SkCodecPrintf("setjmp long jump!\n");
|
| - return kInvalidInput;
|
| + return row;
|
| }
|
|
|
| void* dstRow = dst;
|
| bool hasAlpha = false;
|
| - for (int i = 0; i < count; i++) {
|
| + for (; row < count; row++) {
|
| png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
|
| hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, fSrcRow));
|
| dstRow = SkTAddOffset<void>(dstRow, rowBytes);
|
| @@ -626,23 +655,22 @@ public:
|
| // Otherwise, the AlphaState is unchanged.
|
| }
|
|
|
| - return kSuccess;
|
| + return row;
|
| }
|
|
|
| - Result onSkipScanlines(int count) override {
|
| - // FIXME: Could we use the return value of setjmp to specify the type of
|
| - // error?
|
| + bool onSkipScanlines(int count) override {
|
| + // Assume that an error in libpng indicates an incomplete input.
|
| if (setjmp(png_jmpbuf(this->png_ptr()))) {
|
| SkCodecPrintf("setjmp long jump!\n");
|
| - return kInvalidInput;
|
| + return false;
|
| }
|
| //there is a potential tradeoff of memory vs speed created by putting this in a loop.
|
| //calling png_read_rows in a loop is insignificantly slower than calling it once with count
|
| //as png_read_rows has it's own loop which calls png_read_row count times.
|
| - for (int i = 0; i < count; i++) {
|
| + for (int row = 0; row < count; row++) {
|
| png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
|
| }
|
| - return SkCodec::kSuccess;
|
| + return true;
|
| }
|
|
|
| AlphaState alphaInScanlineDecode() const override {
|
| @@ -694,7 +722,7 @@ public:
|
| return SkCodec::kSuccess;
|
| }
|
|
|
| - Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
|
| + int onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
|
| // rewind stream if have previously called onGetScanlines,
|
| // since we need entire progressive image to get scanlines
|
| if (fCanSkipRewind) {
|
| @@ -707,7 +735,7 @@ public:
|
| // decoding is the exception, since it needs to rewind between
|
| // calls to getScanlines. Keep track of fCurrScanline, to undo the
|
| // reset.
|
| - const int currScanline = this->onNextScanline();
|
| + const int currScanline = this->nextScanline();
|
| // This method would never be called if currScanline is -1
|
| SkASSERT(currScanline != -1);
|
|
|
| @@ -719,12 +747,15 @@ public:
|
|
|
| if (setjmp(png_jmpbuf(this->png_ptr()))) {
|
| SkCodecPrintf("setjmp long jump!\n");
|
| - return kInvalidInput;
|
| + // FIXME (msarett): Returning 0 is pessimistic. If we can complete a single pass,
|
| + // we may be able to report that all of the memory has been initialized. Even if we
|
| + // fail on the first pass, we can still report than some scanlines are initialized.
|
| + return 0;
|
| }
|
| SkAutoMalloc storage(count * fSrcRowBytes);
|
| uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
|
| uint8_t* srcRow;
|
| - const int startRow = this->onNextScanline();
|
| + const int startRow = this->nextScanline();
|
| for (int i = 0; i < this->numberPasses(); i++) {
|
| // read rows we planned to skip into garbage row
|
| for (int y = 0; y < startRow; y++){
|
| @@ -760,12 +791,12 @@ public:
|
| // Otherwise, the AlphaState is unchanged.
|
| }
|
|
|
| - return kSuccess;
|
| + return count;
|
| }
|
|
|
| - SkCodec::Result onSkipScanlines(int count) override {
|
| + bool onSkipScanlines(int count) override {
|
| // The non-virtual version will update fCurrScanline.
|
| - return SkCodec::kSuccess;
|
| + return true;
|
| }
|
|
|
| AlphaState alphaInScanlineDecode() const override {
|
|
|