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