Index: src/codec/SkCodec.cpp |
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp |
index 1a901a97ac4314b210dd6deeeebf14d4957760e9..0047d599ac5cb629a78fa5ac9a6c8ab19b7fe29a 100644 |
--- a/src/codec/SkCodec.cpp |
+++ b/src/codec/SkCodec.cpp |
@@ -167,11 +167,26 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t |
return kInvalidScale; |
} |
- const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount); |
+ // On an incomplete decode, the subclass will specify the number of scanlines that it decoded |
+ // successfully. |
+ int rowsDecoded = 0; |
+ const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount, |
+ &rowsDecoded); |
if ((kIncompleteInput == result || kSuccess == result) && ctableCount) { |
SkASSERT(*ctableCount >= 0 && *ctableCount <= 256); |
} |
+ |
+ // A return value of kIncompleteInput indicates a truncated image stream. |
+ // In this case, we will fill any uninitialized memory with a default value. |
+ // Some subclasses will take care of filling any uninitialized memory on |
+ // their own. They indicate that all of the memory has been filled by |
+ // setting rowsDecoded equal to the height. |
+ if (kIncompleteInput == result && rowsDecoded != info.height()) { |
+ this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(), |
+ rowsDecoded); |
+ } |
+ |
return result; |
} |
@@ -233,36 +248,101 @@ SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo) { |
return this->startScanlineDecode(dstInfo, nullptr, nullptr, nullptr); |
} |
-SkCodec::Result SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) { |
+int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) { |
if (fCurrScanline < 0) { |
- return kScanlineDecodingNotStarted; |
+ return 0; |
} |
SkASSERT(!fDstInfo.isEmpty()); |
- |
if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) { |
- return kInvalidParameters; |
+ return 0; |
} |
- const Result result = this->onGetScanlines(dst, countLines, rowBytes); |
+ const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes); |
+ if (linesDecoded < countLines) { |
+ this->fillIncompleteImage(this->dstInfo(), dst, rowBytes, this->options().fZeroInitialized, |
+ countLines, linesDecoded); |
+ } |
fCurrScanline += countLines; |
- return result; |
+ return linesDecoded; |
} |
-SkCodec::Result SkCodec::skipScanlines(int countLines) { |
+bool SkCodec::skipScanlines(int countLines) { |
if (fCurrScanline < 0) { |
- return kScanlineDecodingNotStarted; |
+ return false; |
} |
SkASSERT(!fDstInfo.isEmpty()); |
- if (fCurrScanline + countLines > fDstInfo.height()) { |
+ if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) { |
// Arguably, we could just skip the scanlines which are remaining, |
- // and return kSuccess. We choose to return invalid so the client |
+ // and return true. We choose to return false so the client |
// can catch their bug. |
- return SkCodec::kInvalidParameters; |
+ return false; |
} |
- const Result result = this->onSkipScanlines(countLines); |
+ bool result = this->onSkipScanlines(countLines); |
fCurrScanline += countLines; |
return result; |
} |
+ |
+int SkCodec::outputScanline(int inputScanline) const { |
+ SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height()); |
+ return this->onOutputScanline(inputScanline); |
+} |
+ |
+int SkCodec::onOutputScanline(int inputScanline) const { |
+ switch (this->getScanlineOrder()) { |
+ case kTopDown_SkScanlineOrder: |
+ case kNone_SkScanlineOrder: |
+ return inputScanline; |
+ case kBottomUp_SkScanlineOrder: |
+ return this->getInfo().height() - inputScanline - 1; |
+ default: |
+ // This case indicates an interlaced gif and is implemented by SkGifCodec. |
+ SkASSERT(false); |
+ return 0; |
+ } |
+} |
+ |
+static void fill_proc(const SkImageInfo& info, void* dst, size_t rowBytes, |
+ uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit, SkSampler* sampler) { |
+ if (sampler) { |
+ sampler->fill(info, dst, rowBytes, colorOrIndex, zeroInit); |
+ } else { |
+ SkSampler::Fill(info, dst, rowBytes, colorOrIndex, zeroInit); |
+ } |
+} |
+ |
+void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes, |
+ ZeroInitialized zeroInit, int linesRequested, int linesDecoded) { |
+ |
+ void* fillDst; |
+ const uint32_t fillValue = this->getFillValue(info.colorType(), info.alphaType()); |
+ const int linesRemaining = linesRequested - linesDecoded; |
+ SkSampler* sampler = this->getSampler(false); |
+ |
+ switch (this->getScanlineOrder()) { |
+ case kTopDown_SkScanlineOrder: |
+ case kNone_SkScanlineOrder: { |
+ const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining); |
+ fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes); |
+ fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler); |
+ break; |
+ } |
+ case kBottomUp_SkScanlineOrder: { |
+ fillDst = dst; |
+ const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining); |
+ fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler); |
+ break; |
+ } |
+ case kOutOfOrder_SkScanlineOrder: { |
+ SkASSERT(1 == linesRequested || this->getInfo().height() == linesRequested); |
+ const SkImageInfo fillInfo = info.makeWH(info.width(), 1); |
+ for (int srcY = linesDecoded; srcY < linesRequested; srcY++) { |
+ fillDst = SkTAddOffset<void>(dst, this->outputScanline(srcY) * rowBytes); |
+ fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler); |
+ } |
+ break; |
+ } |
+ } |
+} |