aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/codec/SkCodec.cpp
diff options
context:
space:
mode:
authorGravatar msarett <msarett@google.com>2015-10-09 11:07:34 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-10-09 11:07:34 -0700
commite6dd004c1b8a81dc37a370570877b8b7d6dbe308 (patch)
tree16ecfdede3939dcd7f1b3db311371d974477e7c5 /src/codec/SkCodec.cpp
parent79da63fa0bec40b560597c79c2e1231fa51aef36 (diff)
Fill incomplete images in SkCodec parent class
Rather than implementing some sort of "fill" in every SkCodec subclass for incomplete images, let's make the parent class handle this situation. This includes an API change to SkCodec.h SkCodec::getScanlines() now returns the number of lines it read successfully, rather than an SkCodec::Result enum. getScanlines() most often fails on an incomplete input, in which case it is useful to know how many lines were successfully decoded - this provides more information than kIncomplete vs kSuccess. We do lose information when the API is used improperly, as we are no longer able to return kInvalidParameter or kScanlineNotStarted. Known Issues: Does not work for incomplete fFrameIsSubset gifs. Does not work for incomplete icos. BUG=skia: Review URL: https://codereview.chromium.org/1332053002
Diffstat (limited to 'src/codec/SkCodec.cpp')
-rw-r--r--src/codec/SkCodec.cpp106
1 files changed, 93 insertions, 13 deletions
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 1a901a97ac..0047d599ac 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;
+ }
+ }
+}