Chromium Code Reviews| Index: src/codec/SkScaledCodec.cpp |
| diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp |
| index 27fbd59a89c18e6729bcc91327d4de85e9bf10c0..7fad945847cdcc583ee9f5f16c2e0ddde2ad6080 100644 |
| --- a/src/codec/SkScaledCodec.cpp |
| +++ b/src/codec/SkScaledCodec.cpp |
| @@ -18,7 +18,7 @@ SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) { |
| } |
| if (isWebp) { |
| // Webp codec supports scaling and subsetting natively |
| - return SkWebpCodec::NewFromStream(stream); |
| + return SkWebpCodec::NewFromStream(stream); |
| } |
| SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream)); |
| @@ -44,16 +44,28 @@ SkScaledCodec::SkScaledCodec(SkCodec* codec) |
| SkScaledCodec::~SkScaledCodec() {} |
| -static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& nativeDims, |
| - const SkISize& scaledCodecDims, float desiredScale) { |
| - if (nativeDims == scaledCodecDims) { |
| - // does not matter which to return if equal. Return here to skip below calculations |
| - return nativeDims; |
| +static bool is_in_subset(int coord, int offset, int length) { |
|
scroggo
2015/10/02 18:27:03
I thought we had something like this function, but
|
| + if (coord < offset || coord >= offset + length) { |
|
scroggo
2015/10/02 18:27:03
Why not:
return coord >= offset && coord < offset
|
| + return false; |
| } |
| + return true; |
| +} |
| + |
| +static int get_sample_size(float scale) { |
|
scroggo
2015/10/02 18:27:03
Don't you define this somewhere else?
|
| + return SkScalarRoundToInt(1.0f / scale); |
| +} |
| + |
| +static bool use_native_scaling(const SkISize& origDims, const SkISize& nativeDims, |
| + const SkISize& sampledDims, float desiredScale) { |
| + if (nativeDims == sampledDims) { |
| + // If both options are the same, choose native scaling. |
| + return true; |
| + } |
| + |
| float idealWidth = origDims.width() * desiredScale; |
| float idealHeight = origDims.height() * desiredScale; |
| - // calculate difference between native dimensions and ideal dimensions |
| + // Calculate difference between native scaling and ideal scaling. |
| float nativeWDiff = SkTAbs(idealWidth - nativeDims.width()); |
| float nativeHDiff = SkTAbs(idealHeight - nativeDims.height()); |
| float nativeDiff = nativeWDiff + nativeHDiff; |
| @@ -61,52 +73,77 @@ static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& na |
| // Native scaling is preferred to sampling. If we can scale natively to |
| // within one of the ideal value, we should choose to scale natively. |
| if (nativeWDiff < 1.0f && nativeHDiff < 1.0f) { |
| - return nativeDims; |
| + return true; |
| } |
| - // calculate difference between scaledCodec dimensions and ideal dimensions |
| - float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width()); |
| - float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height()); |
| - float scaledCodecDiff = scaledCodecWDiff + scaledCodecHDiff; |
| + // Calculate difference between native scaling and sampled scaling. |
| + float sampledWDiff = SkTAbs(idealWidth - sampledDims.width()); |
| + float sampledHDiff = SkTAbs(idealHeight - sampledDims.height()); |
| + float sampledDiff = sampledWDiff + sampledHDiff; |
| - // return dimensions closest to ideal dimensions. |
| - // If the differences are equal, return nativeDims, as native scaling is more efficient. |
| - return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims; |
| + // Use native scaling if it is closer to the ideal scale. |
| + return nativeDiff <= sampledDiff; |
| } |
| /* |
| * Return a valid set of output dimensions for this decoder, given an input scale |
| */ |
| SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const { |
| - SkISize nativeDimensions = fCodec->getScaledDimensions(desiredScale); |
| - // support scaling down by integer numbers. Ex: 1/2, 1/3, 1/4 ... |
| - SkISize scaledCodecDimensions; |
| - if (desiredScale > 0.5f) { |
| - // sampleSize = 1 |
| - scaledCodecDimensions = fCodec->getInfo().dimensions(); |
| + SkISize nativeDims = fCodec->getScaledDimensions(desiredScale); |
| + int sampleSize = get_sample_size(desiredScale); |
| + SkISize sampledDims = SkISize::Make( |
| + get_scaled_dimension(this->getInfo().width(), sampleSize), |
| + get_scaled_dimension(this->getInfo().height(), sampleSize)); |
| + |
| + if (use_native_scaling(this->getInfo().dimensions(), nativeDims, sampledDims, desiredScale)) { |
| + return nativeDims; |
| } |
| - // sampleSize determines the step size between samples |
| - // Ex: sampleSize = 2, sample every second pixel in x and y directions |
| - int sampleSize = int ((1.0f / desiredScale) + 0.5f); |
| - int scaledWidth = get_scaled_dimension(this->getInfo().width(), sampleSize); |
| - int scaledHeight = get_scaled_dimension(this->getInfo().height(), sampleSize); |
| + return sampledDims; |
| +} |
| - // Return the calculated output dimensions for the given scale |
| - scaledCodecDimensions = SkISize::Make(scaledWidth, scaledHeight); |
| +bool SkScaledCodec::onGetScaledSubsetDimensions(float desiredScale, Options* options) const { |
| + |
| + SkISize nativeDims = fCodec->getScaledDimensions(desiredScale); |
| + int sampleSize = get_sample_size(desiredScale); |
| + SkISize sampledDims = SkISize::Make( |
| + get_scaled_dimension(this->getInfo().width(), sampleSize), |
| + get_scaled_dimension(this->getInfo().height(), sampleSize)); |
| + |
| + if (use_native_scaling(this->getInfo().dimensions(), nativeDims, sampledDims, desiredScale)) { |
| + // Set the scaled dimensions and calculate subset size using native scaling. |
| + options->fScaledDimensions = nativeDims; |
| + float widthScale = ((float) nativeDims.width()) / ((float) this->getInfo().width()); |
| + float heightScale = ((float) nativeDims.height()) / ((float) this->getInfo().height()); |
| + // Notice that we may round the size of the subset up to 1. This means that we must |
| + // floor the left and top offsets to ensure that we do not suggest a subset that is |
| + // off the edge of the image. |
| + options->fScaledSubset = SkIRect::MakeXYWH( |
| + int (((float) options->fSubset->left()) * widthScale), |
| + int (((float) options->fSubset->top()) * heightScale), |
| + SkTMax(1, SkScalarRoundToInt(((float) options->fSubset->width()) * widthScale)), |
| + SkTMax(1, SkScalarRoundToInt(((float) options->fSubset->height()) * heightScale))); |
| + return true; |
| + } |
| - return best_scaled_dimensions(this->getInfo().dimensions(), nativeDimensions, |
| - scaledCodecDimensions, desiredScale); |
| + // Set the scaled dimensions and calculate subset size using sampling. |
| + options->fScaledDimensions = sampledDims; |
| + options->fScaledSubset = SkIRect::MakeXYWH( |
| + options->fSubset->left() / sampleSize, |
| + options->fSubset->top() / sampleSize, |
| + get_scaled_dimension(options->fSubset->width(), sampleSize), |
| + get_scaled_dimension(options->fSubset->height(), sampleSize)); |
| + return true; |
| } |
| // check if scaling to dstInfo size from srcInfo size using sampleSize is possible |
| -static bool scaling_supported(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo, |
| +static bool scaling_supported(const SkISize& dstSize, const SkISize& srcSize, |
| int* sampleX, int* sampleY) { |
| - SkScaledCodec::ComputeSampleSize(dstInfo, srcInfo, sampleX, sampleY); |
| - const int dstWidth = dstInfo.width(); |
| - const int dstHeight = dstInfo.height(); |
| - const int srcWidth = srcInfo.width(); |
| - const int srcHeight = srcInfo.height(); |
| + SkScaledCodec::ComputeSampleSize(dstSize, srcSize, sampleX, sampleY); |
| + const int dstWidth = dstSize.width(); |
| + const int dstHeight = dstSize.height(); |
| + const int srcWidth = srcSize.width(); |
| + const int srcHeight = srcSize.height(); |
| // only support down sampling, not up sampling |
| if (dstWidth > srcWidth || dstHeight > srcHeight) { |
| return false; |
| @@ -131,12 +168,12 @@ static bool scaling_supported(const SkImageInfo& dstInfo, const SkImageInfo& src |
| } |
| // calculates sampleSize in x and y direction |
| -void SkScaledCodec::ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo, |
| +void SkScaledCodec::ComputeSampleSize(const SkISize& dstSize, const SkISize& srcSize, |
| int* sampleXPtr, int* sampleYPtr) { |
| - int srcWidth = srcInfo.width(); |
| - int dstWidth = dstInfo.width(); |
| - int srcHeight = srcInfo.height(); |
| - int dstHeight = dstInfo.height(); |
| + int srcWidth = srcSize.width(); |
| + int dstWidth = dstSize.width(); |
| + int srcHeight = srcSize.height(); |
| + int dstHeight = dstSize.height(); |
| int sampleX = srcWidth / dstWidth; |
| int sampleY = srcHeight / dstHeight; |
| @@ -163,6 +200,7 @@ void SkScaledCodec::ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageI |
| } else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) { |
| sampleY = sampleX; |
| } |
| + // FIXME (msarett): Should this never be reached? |
| } |
| } |
| @@ -174,83 +212,173 @@ void SkScaledCodec::ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageI |
| } |
| } |
| -// TODO: Implement subsetting in onGetPixels which works when and when not sampling |
| - |
| -SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, |
| - size_t rowBytes, const Options& options, |
| - SkPMColor ctable[], int* ctableCount, |
| - int* rowsDecoded) { |
| - |
| - if (options.fSubset) { |
| - // Subsets are not supported. |
| - return kUnimplemented; |
| - } |
| +SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& scaledSubsetInfo, void* dst, |
| + size_t rowBytes, const Options& options, SkPMColor ctable[], int* ctableCount, |
| + int* rowsDecoded) { |
| + |
| + // There are various values of Rect, Size, and Info used in this function. I think it is |
| + // useful to go ahead and define what they mean. |
| + // orig_: Refers to the original image size. |
| + // scaledSubset_: Refers to the size of the final output. This can match the original |
| + // dimensions, be a subset of the original dimensions, be a scaled version |
| + // of the original dimensions, or be a scaled subset of the original dimensions. |
| + // subset_: Refers to the size of the unscaled subset in terms of the original image |
| + // dimensions. If this is not a subset decode, this will match the original |
| + // image dimensions. |
| + // scaled_: Refers to the scaled size of the original image, ignoring any subsetting. |
| + // If we are not scaling, this will match the original dimensions. |
| + SkISize origSize = this->getInfo().dimensions(); |
| + SkIRect subsetRect; |
| + SkISize scaledSize; |
| + SkIRect scaledSubsetRect; |
| + if (nullptr == options.fSubset) { |
| + // This is not a subset decode. |
| + SkASSERT(options.fScaledDimensions.isZero()); |
| + SkASSERT(options.fScaledSubset.isEmpty()); |
| + |
| + // Set the "subset" to the full image dimensions. |
| + subsetRect = SkIRect::MakeSize(origSize); |
| + |
| + // This may be scaled or unscaled, depending on if scaledSize matches origSize. |
| + scaledSize = scaledSubsetInfo.dimensions(); |
| + scaledSubsetRect = SkIRect::MakeSize(scaledSize); |
| + } else { |
| + // This is a subset decode. |
| + if (!is_valid_subset(options.fSubset, origSize)) { |
| + return kInvalidParameters; |
| + } |
| - // FIXME: If no scaling/subsets are requested, we can call fCodec->getPixels. |
| - Result result = fCodec->startScanlineDecode(requestedInfo, &options, ctable, ctableCount); |
| - if (kSuccess == result) { |
| - // native decode supported |
| - switch (fCodec->getScanlineOrder()) { |
| - case SkCodec::kTopDown_SkScanlineOrder: |
| - case SkCodec::kBottomUp_SkScanlineOrder: |
| - case SkCodec::kNone_SkScanlineOrder: |
| - if (fCodec->getScanlines(dst, requestedInfo.height(), rowBytes) != |
| - requestedInfo.height()) { |
| - // fCodec has already handled filling uninitialized memory. |
| - *rowsDecoded = requestedInfo.height(); |
| - return kIncompleteInput; |
| - } |
| - return kSuccess; |
| - case SkCodec::kOutOfOrder_SkScanlineOrder: { |
| - for (int y = 0; y < requestedInfo.height(); y++) { |
| - int dstY = fCodec->nextScanline(); |
| - void* dstPtr = SkTAddOffset<void>(dst, rowBytes * dstY); |
| - if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) { |
| - *rowsDecoded = y + 1; |
| - return kIncompleteInput; |
| - } |
| - } |
| - return kSuccess; |
| + subsetRect = *(options.fSubset); |
| + if (options.fScaledDimensions.isZero()) { |
| + // This is an unscaled subset decode. |
| + SkASSERT(options.fScaledSubset.isEmpty()); |
| + SkASSERT(scaledSubsetInfo.dimensions() == subsetRect.size()); |
| + |
| + scaledSize = origSize; |
| + scaledSubsetRect = subsetRect; |
| + } else { |
| + // This is a scaled subset decode. |
| + SkASSERT(!options.fScaledSubset.isEmpty()); |
| + SkASSERT(scaledSubsetInfo.dimensions() == options.fScaledSubset.size()); |
| + if (!is_valid_subset(&options.fScaledSubset, options.fScaledDimensions)) { |
| + return kInvalidParameters; |
| } |
| + |
| + scaledSize = options.fScaledDimensions; |
| + scaledSubsetRect = options.fScaledSubset; |
| } |
| } |
| - if (kInvalidScale != result) { |
| - // no scaling requested |
| - return result; |
| + // Reset the options for use by fCodec. We will handle scaling and subsetting |
| + // from this level, the native codec does not need to know about it. |
| + Options newOptions = options; |
| + newOptions.fSubset = nullptr; |
| + newOptions.fScaledDimensions = SkISize::Make(0, 0); |
| + newOptions.fScaledSubset = SkIRect::MakeEmpty(); |
| + |
| + // The native decoder needs the scaled size of the entire image to check if it can decode to |
| + // the requested scale. |
| + SkImageInfo scaledInfo = scaledSubsetInfo.makeWH(scaledSize.width(), scaledSize.height()); |
| + Result result = fCodec->startScanlineDecode(scaledInfo, &newOptions, ctable, ctableCount, |
| + scaledSubsetRect.left(), scaledSubsetRect.width()); |
| + switch (result) { |
| + case kSuccess: |
| + return this->nativeDecode(scaledInfo, dst, rowBytes, scaledSubsetRect, |
| + options.fZeroInitialized, rowsDecoded); |
| + case kInvalidScale: |
| + // We will attempt to scale by sampling below. |
| + break; |
| + default: |
| + return result; |
| } |
| - // scaling requested |
| + // Try to provide the scale using sampling. |
| int sampleX; |
| int sampleY; |
| - if (!scaling_supported(requestedInfo, fCodec->getInfo(), &sampleX, &sampleY)) { |
| + if (!scaling_supported(scaledSubsetInfo.dimensions(), subsetRect.size(), &sampleX, &sampleY)) { |
| return kInvalidScale; |
| } |
| - // set first sample pixel in y direction |
| - int Y0 = get_start_coord(sampleY); |
| - |
| - int dstHeight = requestedInfo.height(); |
| - int srcHeight = fCodec->getInfo().height(); |
| - |
| - SkImageInfo info = requestedInfo; |
| - // use original height as codec does not support y sampling natively |
| - info = info.makeWH(requestedInfo.width(), srcHeight); |
| - |
| - // update codec with new info |
| - // FIXME: The previous call to start returned kInvalidScale. This call may |
| - // require a rewind. (skbug.com/4284) |
| - result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount); |
| + |
| + // Create the image info that will be passed to fCodec. We support scaling in the |
| + // x-dimension, but we will perform scaling in the y-dimension here, so we pass the scaled |
| + // width and the original height. |
| + // FIXME: This is a bit confusing. I think there is a plan to move scaling completely out |
| + // of the native codec. |
| + SkImageInfo decodeInfo = scaledSubsetInfo.makeWH(scaledSize.width(), this->getInfo().height()); |
| + |
| + // When starting the fCodec, we pass the left offset based on the original image |
| + // dimensions, but need the scaled version of the subset width. |
| + // FIXME: This is a bit confusing. I think there is a plan to move scaling completely out |
| + // of the native codec. |
| + result = fCodec->startScanlineDecode(decodeInfo, &newOptions, ctable, ctableCount, |
| + subsetRect.left(), scaledSubsetInfo.width()); |
| if (kSuccess != result) { |
| + SkASSERT(kInvalidScale != result); |
| return result; |
| } |
| + return this->sampledDecode(scaledSubsetInfo, dst, rowBytes, subsetRect, scaledSubsetRect, |
| + sampleX, sampleY, options.fZeroInitialized, rowsDecoded); |
| +} |
| + |
| +SkCodec::Result SkScaledCodec::nativeDecode(const SkImageInfo& scaledInfo, void* dst, |
| + size_t rowBytes, const SkIRect& scaledSubsetRect, ZeroInitialized zeroInit, |
| + int* rowsDecoded) { |
| + |
| + int scaledSubsetTop = scaledSubsetRect.top(); |
| + int scaledSubsetHeight = scaledSubsetRect.height(); |
| + switch (fCodec->getScanlineOrder()) { |
| + case SkCodec::kTopDown_SkScanlineOrder: |
| + case SkCodec::kBottomUp_SkScanlineOrder: |
| + case SkCodec::kNone_SkScanlineOrder: { |
| + if (!fCodec->skipScanlines(scaledSubsetTop)) { |
| + *rowsDecoded = 0; |
| + return kIncompleteInput; |
| + } |
| + |
| + uint32_t decodedLines = fCodec->getScanlines(dst, scaledSubsetHeight, |
| + rowBytes); |
| + if (decodedLines != scaledSubsetHeight) { |
| + *rowsDecoded = decodedLines; |
| + return kIncompleteInput; |
| + } |
| + return kSuccess; |
| + } |
| + case SkCodec::kOutOfOrder_SkScanlineOrder: { |
| + for (int y = 0; y < scaledInfo.height(); y++) { |
| + int dstY = fCodec->nextScanline(); |
| + if (is_in_subset(dstY, scaledSubsetTop, scaledSubsetHeight)) { |
| + void* dstPtr = SkTAddOffset<void>(dst, rowBytes * (dstY - scaledSubsetTop)); |
| + if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) { |
| + *rowsDecoded = y + 1; |
| + return kIncompleteInput; |
| + } |
| + } else { |
| + if (!fCodec->skipScanlines(1)) { |
| + *rowsDecoded = y + 1; |
| + return kIncompleteInput; |
| + } |
| + } |
| + } |
| + return kSuccess; |
| + } |
| + } |
| +} |
| + |
| +SkCodec::Result SkScaledCodec::sampledDecode(const SkImageInfo& scaledSubsetInfo, void* dst, |
| + size_t rowBytes, const SkIRect& subsetRect, const SkIRect& scaledSubsetRect, int sampleX, |
| + int sampleY, ZeroInitialized zeroInit, int* rowsDecoded) { |
| + |
| + // Set first sample pixel in y direction. |
| + int y0 = get_start_coord(sampleY); |
| + int scaledSubsetHeight = scaledSubsetRect.height(); |
| switch(fCodec->getScanlineOrder()) { |
| - case SkCodec::kTopDown_SkScanlineOrder: { |
| - if (!fCodec->skipScanlines(Y0)) { |
| + case SkCodec::kTopDown_SkScanlineOrder: |
| + if (!fCodec->skipScanlines(y0 + subsetRect.top())) { |
| *rowsDecoded = 0; |
| return kIncompleteInput; |
| } |
| - for (int y = 0; y < dstHeight; y++) { |
| + for (int y = 0; y < scaledSubsetHeight; y++) { |
| if (1 != fCodec->getScanlines(dst, 1, rowBytes)) { |
| // The failed call to getScanlines() will take care of |
| // filling the failed row, so we indicate that we have |
| @@ -258,7 +386,7 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi |
| *rowsDecoded = y + 1; |
| return kIncompleteInput; |
| } |
| - if (y < dstHeight - 1) { |
| + if (y < scaledSubsetHeight - 1) { |
| if (!fCodec->skipScanlines(sampleY - 1)) { |
| *rowsDecoded = y + 1; |
| return kIncompleteInput; |
| @@ -267,14 +395,13 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi |
| dst = SkTAddOffset<void>(dst, rowBytes); |
| } |
| return kSuccess; |
| - } |
| case SkCodec::kBottomUp_SkScanlineOrder: |
| case SkCodec::kOutOfOrder_SkScanlineOrder: { |
| Result result = kSuccess; |
| int y; |
| - for (y = 0; y < srcHeight; y++) { |
| + for (y = 0; y < this->getInfo().height(); y++) { |
| int srcY = fCodec->nextScanline(); |
| - if (is_coord_necessary(srcY, sampleY, dstHeight)) { |
| + if (is_coord_necessary(srcY, sampleY, scaledSubsetHeight, subsetRect.top())) { |
| void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_coord(srcY, sampleY)); |
| if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) { |
| result = kIncompleteInput; |
| @@ -291,39 +418,42 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi |
| // We handle filling uninitialized memory here instead of in the parent class. |
| // The parent class does not know that we are sampling. |
| if (kIncompleteInput == result) { |
| - const SkImageInfo fillInfo = requestedInfo.makeWH(requestedInfo.width(), 1); |
| + const SkImageInfo fillInfo = scaledSubsetInfo.makeWH(scaledSubsetRect.width(), 1); |
| const uint32_t fillValue = fCodec->getFillValue(fillInfo.colorType(), |
| fillInfo.alphaType()); |
| - for (; y < srcHeight; y++) { |
| + for (; y < this->getInfo().height(); y++) { |
| int srcY = fCodec->outputScanline(y); |
| - if (is_coord_necessary(srcY, sampleY, dstHeight)) { |
| + if (is_coord_necessary(srcY, sampleY, scaledSubsetHeight, subsetRect.top())) { |
| void* dstRow = SkTAddOffset<void>(dst, |
| rowBytes * get_dst_coord(srcY, sampleY)); |
| - SkSwizzler::Fill(dstRow, fillInfo, rowBytes, fillValue, |
| - options.fZeroInitialized); |
| + SkSwizzler::Fill(dstRow, fillInfo, rowBytes, fillValue, zeroInit); |
| } |
| } |
| - *rowsDecoded = dstHeight; |
| + *rowsDecoded = scaledSubsetInfo.height(); |
| } |
| return result; |
| } |
| case SkCodec::kNone_SkScanlineOrder: { |
| - SkAutoMalloc storage(srcHeight * rowBytes); |
| + SkAutoMalloc storage(subsetRect.height() * rowBytes); |
| uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); |
| - int scanlines = fCodec->getScanlines(storagePtr, srcHeight, rowBytes); |
| - storagePtr += Y0 * rowBytes; |
| - scanlines -= Y0; |
| + if (!fCodec->skipScanlines(subsetRect.top())) { |
| + *rowsDecoded = 0; |
| + return kIncompleteInput; |
| + } |
| + int scanlines = fCodec->getScanlines(storagePtr, subsetRect.height(), rowBytes); |
| + scanlines -= y0; |
| + storagePtr += y0 * rowBytes; |
| int y = 0; |
| - while (y < dstHeight && scanlines > 0) { |
| - memcpy(dst, storagePtr, rowBytes); |
| + while (y < scaledSubsetHeight && scanlines > 0) { |
| + memcpy(dst, storagePtr, scaledSubsetInfo.minRowBytes()); |
| storagePtr += sampleY * rowBytes; |
| dst = SkTAddOffset<void>(dst, rowBytes); |
| scanlines -= sampleY; |
| y++; |
| } |
| - if (y < dstHeight) { |
| + if (y < scaledSubsetHeight) { |
| // fCodec has already handled filling uninitialized memory. |
| - *rowsDecoded = dstHeight; |
| + *rowsDecoded = scaledSubsetInfo.height(); |
| return kIncompleteInput; |
| } |
| return kSuccess; |