| 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;
|
|
|