Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(27)

Unified Diff: src/codec/SkSampledCodec.cpp

Issue 1997703003: Make SkPngCodec decode progressively. (Closed) Base URL: https://skia.googlesource.com/skia.git@foil
Patch Set: Rebase Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/codec/SkPngCodec.cpp ('k') | src/codec/SkSampler.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
« no previous file with comments | « src/codec/SkPngCodec.cpp ('k') | src/codec/SkSampler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698