Index: src/codec/SkSampledCodec.cpp |
diff --git a/src/codec/SkSampledCodec.cpp b/src/codec/SkSampledCodec.cpp |
index 47f99f796633203ec242ace5d7b833f8438634b6..403b42f7107a8572dfa3ce1d9bc66af1ded3e1e5 100644 |
--- a/src/codec/SkSampledCodec.cpp |
+++ b/src/codec/SkSampledCodec.cpp |
@@ -101,37 +101,65 @@ SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void |
int scaledSubsetWidth = info.width(); |
int scaledSubsetHeight = info.height(); |
+ const SkImageInfo scaledInfo = info.makeWH(scaledSize.width(), scaledSize.height()); |
+ |
+ { |
+ // Although startScanlineDecode expects the bottom and top to match the |
+ // SkImageInfo, startIncrementalDecode uses them to determine which rows to |
+ // decode. |
+ SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubsetY, |
+ scaledSubsetWidth, scaledSubsetHeight); |
+ codecOptions.fSubset = &incrementalSubset; |
+ const SkCodec::Result startResult = this->codec()->startIncrementalDecode( |
+ scaledInfo, pixels, rowBytes, &codecOptions, |
+ options.fColorPtr, options.fColorCount); |
+ if (SkCodec::kSuccess == startResult) { |
+ int rowsDecoded; |
+ const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded); |
+ if (incResult == SkCodec::kSuccess) { |
+ return SkCodec::kSuccess; |
+ } |
+ SkASSERT(SkCodec::kIncompleteInput == incResult); |
+ |
+ // FIXME: Can zero initialized be read from SkCodec::fOptions? |
+ this->codec()->fillIncompleteImage(scaledInfo, pixels, rowBytes, |
+ options.fZeroInitialized, scaledSubsetHeight, rowsDecoded); |
+ return SkCodec::kIncompleteInput; |
+ } else if (startResult != SkCodec::kUnimplemented) { |
+ return startResult; |
+ } |
+ // Otherwise fall down to use the old scanline decoder. |
+ // codecOptions.fSubset will be reset below, so it will not continue to |
+ // point to the object that is no longer on the stack. |
+ } |
+ |
// Start the scanline decode. |
SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth, |
scaledSize.height()); |
codecOptions.fSubset = &scanlineSubset; |
- SkCodec::Result result = this->codec()->startScanlineDecode(info.makeWH(scaledSize.width(), |
- scaledSize.height()), &codecOptions, options.fColorPtr, options.fColorCount); |
+ |
+ SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo, |
+ &codecOptions, options.fColorPtr, options.fColorCount); |
if (SkCodec::kSuccess != result) { |
return result; |
} |
// At this point, we are only concerned with subsetting. Either no scale was |
// requested, or the this->codec() is handling the scale. |
- switch (this->codec()->getScanlineOrder()) { |
- case SkCodec::kTopDown_SkScanlineOrder: |
- case SkCodec::kNone_SkScanlineOrder: { |
- if (!this->codec()->skipScanlines(scaledSubsetY)) { |
- this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, |
- scaledSubsetHeight, 0); |
- return SkCodec::kIncompleteInput; |
- } |
+ // Note that subsetting is only supported for kTopDown, so this code will not be |
+ // reached for other orders. |
+ SkASSERT(this->codec()->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder); |
+ if (!this->codec()->skipScanlines(scaledSubsetY)) { |
+ this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, |
+ scaledSubsetHeight, 0); |
+ return SkCodec::kIncompleteInput; |
+ } |
- int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes); |
- if (decodedLines != scaledSubsetHeight) { |
- return SkCodec::kIncompleteInput; |
- } |
- return SkCodec::kSuccess; |
- } |
- default: |
- SkASSERT(false); |
- return SkCodec::kUnimplemented; |
+ int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes); |
+ if (decodedLines != scaledSubsetHeight) { |
+ return SkCodec::kIncompleteInput; |
} |
+ return SkCodec::kSuccess; |
} |
@@ -174,10 +202,74 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix |
sampledOptions.fSubset = ⊂ |
} |
+ // Since we guarantee that output dimensions are always at least one (even if the sampleSize |
+ // is greater than a given dimension), the input sampleSize is not always the sampleSize that |
+ // we use in practice. |
+ const int sampleX = subsetWidth / info.width(); |
+ const int sampleY = subsetHeight / info.height(); |
+ |
+ const int samplingOffsetY = get_start_coord(sampleY); |
+ const int startY = samplingOffsetY + subsetY; |
+ int dstHeight = info.height(); |
+ |
+ const SkImageInfo nativeInfo = info.makeWH(nativeSize.width(), nativeSize.height()); |
+ |
+ { |
+ // Although startScanlineDecode expects the bottom and top to match the |
+ // SkImageInfo, startIncrementalDecode uses them to determine which rows to |
+ // decode. |
+ // Note: We *could* use "subsetY" and "subsetHeight" (calculated above) for |
+ // incrementalSubset, but this code gives us a tighter bounds on the subset, |
+ // meaning that we can start with the first row actually needed by the output, |
+ // and stop when we've decoded the last row needed by the output. |
+ SkIRect incrementalSubset; |
+ incrementalSubset.fTop = startY; |
+ incrementalSubset.fBottom = startY + (dstHeight - 1) * sampleY + 1; |
+ if (sampledOptions.fSubset) { |
+ incrementalSubset.fLeft = sampledOptions.fSubset->fLeft; |
+ incrementalSubset.fRight = sampledOptions.fSubset->fRight; |
+ } else { |
+ incrementalSubset.fLeft = 0; |
+ incrementalSubset.fRight = nativeSize.width(); |
+ } |
+ SkCodec::Options incrementalOptions = sampledOptions; |
+ incrementalOptions.fSubset = &incrementalSubset; |
+ const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo, |
+ pixels, rowBytes, &incrementalOptions, options.fColorPtr, options.fColorCount); |
+ if (SkCodec::kSuccess == startResult) { |
+ SkSampler* sampler = this->codec()->getSampler(true); |
+ if (!sampler) { |
+ return SkCodec::kUnimplemented; |
+ } |
+ |
+ if (sampler->setSampleX(sampleX) != info.width()) { |
+ return SkCodec::kInvalidScale; |
+ } |
+ if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { |
+ return SkCodec::kInvalidScale; |
+ } |
+ |
+ sampler->setSampleY(sampleY); |
+ |
+ int rowsDecoded; |
+ const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded); |
+ if (incResult == SkCodec::kSuccess) { |
+ return SkCodec::kSuccess; |
+ } |
+ SkASSERT(incResult == SkCodec::kIncompleteInput); |
+ const int lastRowInOutput = (rowsDecoded - startY) / sampleY; |
+ // FIXME: Should this be info or nativeInfo? Does it make a difference? |
+ this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, |
+ info.height(), lastRowInOutput); |
+ return SkCodec::kIncompleteInput; |
+ } else if (startResult != SkCodec::kUnimplemented) { |
+ return startResult; |
+ } // kUnimplemented means use the old method. |
+ } |
+ |
// Start the scanline decode. |
- SkCodec::Result result = this->codec()->startScanlineDecode( |
- info.makeWH(nativeSize.width(), nativeSize.height()), &sampledOptions, |
- options.fColorPtr, options.fColorCount); |
+ SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo, |
+ &sampledOptions, options.fColorPtr, options.fColorCount); |
if (SkCodec::kSuccess != result) { |
return result; |
} |
@@ -187,11 +279,6 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix |
return SkCodec::kUnimplemented; |
} |
- // Since we guarantee that output dimensions are always at least one (even if the sampleSize |
- // is greater than a given dimension), the input sampleSize is not always the sampleSize that |
- // we use in practice. |
- const int sampleX = subsetWidth / info.width(); |
- const int sampleY = subsetHeight / info.height(); |
if (sampler->setSampleX(sampleX) != info.width()) { |
return SkCodec::kInvalidScale; |
} |
@@ -199,9 +286,6 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix |
return SkCodec::kInvalidScale; |
} |
- const int samplingOffsetY = get_start_coord(sampleY); |
- const int startY = samplingOffsetY + subsetY; |
- int dstHeight = info.height(); |
switch(this->codec()->getScanlineOrder()) { |
case SkCodec::kTopDown_SkScanlineOrder: { |
if (!this->codec()->skipScanlines(startY)) { |
@@ -266,30 +350,6 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix |
} |
return SkCodec::kIncompleteInput; |
} |
- case SkCodec::kNone_SkScanlineOrder: { |
- const int linesNeeded = subsetHeight - samplingOffsetY; |
- SkAutoTMalloc<uint8_t> storage(linesNeeded * rowBytes); |
- uint8_t* storagePtr = storage.get(); |
- |
- if (!this->codec()->skipScanlines(startY)) { |
- this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, |
- dstHeight, 0); |
- return SkCodec::kIncompleteInput; |
- } |
- int scanlines = this->codec()->getScanlines(storagePtr, linesNeeded, rowBytes); |
- |
- for (int y = 0; y < dstHeight; y++) { |
- memcpy(pixels, storagePtr, info.minRowBytes()); |
- storagePtr += sampleY * rowBytes; |
- pixels = SkTAddOffset<void>(pixels, rowBytes); |
- } |
- |
- if (scanlines < dstHeight) { |
- // this->codec() has already handled filling uninitialized memory. |
- return SkCodec::kIncompleteInput; |
- } |
- return SkCodec::kSuccess; |
- } |
default: |
SkASSERT(false); |
return SkCodec::kUnimplemented; |