Chromium Code Reviews| Index: src/codec/SkSampledCodec.cpp |
| diff --git a/src/codec/SkSampledCodec.cpp b/src/codec/SkSampledCodec.cpp |
| index 49c939c1f887c500f8795173a031dc2c996b36eb..8c39f277ae2c34e07acdfee257a919c8ea3b9a78 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 when to start |
| + // and end calling the callback. |
| + 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,70 @@ 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 when to start |
| + // and end calling the callback. |
| + SkIRect incrementalSubset; |
| + incrementalSubset.fTop = startY; |
| + incrementalSubset.fBottom = startY + (dstHeight - 1) * sampleY + 1; |
|
msarett
2016/09/14 13:21:06
Why are we doing this again?
scroggo_chromium
2016/09/14 14:54:13
Modifying the top and bottom? Because unlike the s
msarett
2016/09/14 15:39:54
Sorry let me rephrase. Why do we need to do this
scroggo
2016/09/14 17:43:00
dstHeight is the height of the output (this was pa
msarett
2016/09/14 18:16:05
My latest interpretation:
We *could* get away with
scroggo_chromium
2016/09/14 22:01:41
Done.
|
| + 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 +275,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 +282,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 +346,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; |