Chromium Code Reviews| Index: src/codec/SkScaledCodec.cpp |
| diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp |
| index 6b50a09f268c3889e99831098980486aef235af7..84488235fb610b0336649cb1cd77d615851b9917 100644 |
| --- a/src/codec/SkScaledCodec.cpp |
| +++ b/src/codec/SkScaledCodec.cpp |
| @@ -5,344 +5,289 @@ |
| * found in the LICENSE file. |
| */ |
| +#include "SkCodec.h" |
| #include "SkCodecPriv.h" |
| #include "SkScaledCodec.h" |
| -#include "SkStream.h" |
| -#include "SkWebpCodec.h" |
| - |
| -SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) { |
| - SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream)); |
| - if (nullptr == codec) { |
| - return nullptr; |
| - } |
| - |
| - switch (codec->getEncodedFormat()) { |
| - case kWEBP_SkEncodedFormat: |
| - // Webp codec supports scaling and subsetting natively |
| - return codec.detach(); |
| - case kPNG_SkEncodedFormat: |
| - case kJPEG_SkEncodedFormat: |
| - // wrap in new SkScaledCodec |
| - return new SkScaledCodec(codec.detach()); |
| - default: |
| - // FIXME: SkScaledCodec is temporarily disabled for other formats |
| - // while focusing on the formats that are supported by |
| - // BitmapRegionDecoder. |
| - return nullptr; |
| - } |
| -} |
| - |
| -SkCodec* SkScaledCodec::NewFromData(SkData* data) { |
| - if (!data) { |
| - return nullptr; |
| - } |
| - return NewFromStream(new SkMemoryStream(data)); |
| -} |
| - |
| -SkScaledCodec::SkScaledCodec(SkCodec* codec) |
| - : INHERITED(codec->getInfo(), nullptr) |
| +SkSampledCodec::SkSampledCodec(SkCodec* codec) |
| + : INHERITED(codec->getInfo()) |
| , fCodec(codec) |
| {} |
| -SkScaledCodec::~SkScaledCodec() {} |
| - |
| -bool SkScaledCodec::onRewind() { |
| - return fCodec->onRewind(); |
| +static bool is_in_range(int coord, int offset, int length) { |
|
scroggo
2015/10/16 21:13:55
I still need to look over this file in more detail
|
| + return coord >= offset && coord < offset + length; |
| } |
| -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; |
| +SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const { |
| + // Fast path for when we are not scaling. |
| + if (1 == sampleSize) { |
| + return fCodec->getInfo().dimensions(); |
| } |
| - float idealWidth = origDims.width() * desiredScale; |
| - float idealHeight = origDims.height() * desiredScale; |
| - |
| - // calculate difference between native dimensions and ideal dimensions |
| - float nativeWDiff = SkTAbs(idealWidth - nativeDims.width()); |
| - float nativeHDiff = SkTAbs(idealHeight - nativeDims.height()); |
| - float nativeDiff = nativeWDiff + nativeHDiff; |
| - // 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; |
| + const int width = fCodec->getInfo().width(); |
| + const int height = fCodec->getInfo().height(); |
| + |
| + // Check if the codec can provide the scaling natively. |
| + float scale = get_scale_from_sample_size(sampleSize); |
| + SkSize idealSize = SkSize::Make(scale * (float) width, scale * (float) height); |
| + SkISize nativeSize = fCodec->getScaledDimensions(scale); |
| + float widthDiff = SkTAbs(((float) nativeSize.width()) - idealSize.width()); |
| + float heightDiff = SkTAbs(((float) nativeSize.height()) - idealSize.height()); |
| + if (widthDiff < 1.0f && heightDiff < 1.0f) { |
| + return nativeSize; |
| } |
| - // calculate difference between scaledCodec dimensions and ideal dimensions |
| - float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width()); |
| - float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height()); |
| - float scaledCodecDiff = scaledCodecWDiff + scaledCodecHDiff; |
| - |
| - // return dimensions closest to ideal dimensions. |
| - // If the differences are equal, return nativeDims, as native scaling is more efficient. |
| - return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims; |
| + // Provide the scaling by sampling. |
| + return SkISize::Make(get_scaled_dimension(width, sampleSize), |
| + get_scaled_dimension(height, sampleSize)); |
| } |
| -/* |
| - * 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 SkSampledCodec::onGetSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const { |
|
scroggo
2015/10/19 20:03:20
Interesting - this function is no longer virtual (
msarett
2015/10/19 21:39:21
Yeah I agree that it makes sense but is a little w
|
| + // This call is less expensive than onGetSampledDimensions() because, regardless |
| + // of the scaling method, we are free to choose how many of the pixels that we |
| + // want to clip. |
| + // Alternatively, we could call onGetSampledDimensions(), calculate the scale |
| + // as a float and do our best to apply an identical scale here. But I'm not |
| + // sure that there is any added benefit to that approach. |
| + |
| + // We should, however, be sure that if the subset specifies the entire image, |
| + // we will perform a full image decode. |
| + if (fCodec->getInfo().dimensions() == subset.size()) { |
| + return onGetSampledDimensions(sampleSize); |
| } |
| - // 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 SkISize::Make(get_scaled_dimension(subset.width(), sampleSize), |
| + get_scaled_dimension(subset.height(), sampleSize)); |
| +} |
| - // Return the calculated output dimensions for the given scale |
| - scaledCodecDimensions = SkISize::Make(scaledWidth, scaledHeight); |
| +SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels, |
| + size_t rowBytes, AndroidOptions& options) { |
| + // Create an Options struct for the codec. |
| + SkCodec::Options codecOptions; |
| + codecOptions.fZeroInitialized = options.fZeroInitialized; |
| - return best_scaled_dimensions(this->getInfo().dimensions(), nativeDimensions, |
| - scaledCodecDimensions, desiredScale); |
| -} |
| + SkIRect* subset = options.fSubset; |
| + if (!subset || subset->size() == fCodec->getInfo().dimensions()) { |
| + // Try to use the native codec to perform the decode. |
| + SkCodec::Result result = fCodec->getPixels(info, pixels, rowBytes, &codecOptions, |
| + options.fColorPtr, options.fColorCount); |
| -// check if scaling to dstInfo size from srcInfo size using sampleSize is possible |
| -static bool scaling_supported(const SkISize& dstDim, const SkISize& srcDim, |
| - int* sampleX, int* sampleY) { |
| - SkScaledCodec::ComputeSampleSize(dstDim, srcDim, sampleX, sampleY); |
| - const int dstWidth = dstDim.width(); |
| - const int dstHeight = dstDim.height(); |
| - const int srcWidth = srcDim.width(); |
| - const int srcHeight = srcDim.height(); |
| - // only support down sampling, not up sampling |
| - if (dstWidth > srcWidth || dstHeight > srcHeight) { |
| - return false; |
| - } |
| - // check that srcWidth is scaled down by an integer value |
| - if (get_scaled_dimension(srcWidth, *sampleX) != dstWidth) { |
| - return false; |
| - } |
| - // check that src height is scaled down by an integer value |
| - if (get_scaled_dimension(srcHeight, *sampleY) != dstHeight) { |
| - return false; |
| - } |
| - // sampleX and sampleY should be equal unless the original sampleSize requested was larger |
| - // than srcWidth or srcHeight. If so, the result of this is dstWidth or dstHeight = 1. |
| - // This functionality allows for tall thin images to still be scaled down by scaling factors. |
| - if (*sampleX != *sampleY){ |
| - if (1 != dstWidth && 1 != dstHeight) { |
| - return false; |
| + if (SkCodec::kInvalidScale != result) { |
| + return result; |
| } |
| - } |
| - return true; |
| -} |
| -bool SkScaledCodec::onDimensionsSupported(const SkISize& dim) { |
| - // Check with fCodec first. No need to call the non-virtual version, which |
| - // just checks if it matches the original, since a match means this method |
| - // will not be called. |
| - if (fCodec->onDimensionsSupported(dim)) { |
| - return true; |
| + // If the native codec does not support the requested scale, scale by sampling. |
| + return sampledDecode(info, pixels, rowBytes, options); |
| } |
| - // FIXME: These variables are unused, but are needed by scaling_supported. |
| - // This class could also cache these values, and avoid calling this in |
| - // onGetPixels (since getPixels already calls it). |
| - int sampleX; |
| - int sampleY; |
| - return scaling_supported(dim, this->getInfo().dimensions(), &sampleX, &sampleY); |
| -} |
| - |
| -// calculates sampleSize in x and y direction |
| -void SkScaledCodec::ComputeSampleSize(const SkISize& dstDim, const SkISize& srcDim, |
| - int* sampleXPtr, int* sampleYPtr) { |
| - int srcWidth = srcDim.width(); |
| - int dstWidth = dstDim.width(); |
| - int srcHeight = srcDim.height(); |
| - int dstHeight = dstDim.height(); |
| - |
| - int sampleX = srcWidth / dstWidth; |
| - int sampleY = srcHeight / dstHeight; |
| - |
| - // only support down sampling, not up sampling |
| - SkASSERT(dstWidth <= srcWidth); |
| - SkASSERT(dstHeight <= srcHeight); |
| - |
| - // sampleX and sampleY should be equal unless the original sampleSize requested was |
| - // larger than srcWidth or srcHeight. |
| - // If so, the result of this is dstWidth or dstHeight = 1. This functionality |
| - // allows for tall thin images to still be scaled down by scaling factors. |
| - |
| - if (sampleX != sampleY){ |
| - if (1 != dstWidth && 1 != dstHeight) { |
| + // We are performing a subset decode. |
| + // First attempt to use the native codec. The native codec needs to know the scaled size |
| + // of the image and the subset. |
| + int sampleSize = options.fSampleSize; |
| + // FIXME: We already make this same function call in getAndroidPixels(). |
| + SkISize scaledSize = this->onGetSampledDimensions(sampleSize); |
| + int scaledSubsetX = subset->x() / sampleSize; |
| + int scaledSubsetY = subset->y() / sampleSize; |
| + int scaledSubsetWidth = info.width(); |
| + int scaledSubsetHeight = info.height(); |
| + |
| + // The scanline decoder only needs to be aware of scaledSubsetX and scaledSubestWidth. |
| + // We will handling subsetting in the y-dimension by skipping scanlines. |
| + SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth, |
| + scaledSize.height()); |
| + codecOptions.fSubset = &scanlineSubset; |
| + SkCodec::Result result = fCodec->startScanlineDecode(info.makeWH(scaledSize.width(), |
| + scaledSize.height()), &codecOptions, options.fColorPtr, options.fColorCount); |
| + switch (result) { |
| + case SkCodec::kSuccess: |
| + break; |
| + case SkCodec::kInvalidScale: |
| + // If the native codec does not support the requested scale, scale by sampling. |
| + return sampledDecode(info, pixels, rowBytes, options); |
| + default: |
| + return result; |
| + } |
| - // rounding during onGetScaledDimensions can cause different sampleSizes |
| - // Ex: srcWidth = 79, srcHeight = 20, sampleSize = 10 |
| - // dstWidth = 7, dstHeight = 2, sampleX = 79/7 = 11, sampleY = 20/2 = 10 |
| - // correct for this rounding by comparing width to sampleY and height to sampleX |
| + switch (fCodec->getScanlineOrder()) { |
| + case SkCodec::kTopDown_SkScanlineOrder: |
| + case SkCodec::kNone_SkScanlineOrder: { |
| + if (!fCodec->skipScanlines(scaledSubsetY)) { |
| + fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, |
| + scaledSubsetHeight, 0); |
| + return SkCodec::kIncompleteInput; |
| + } |
| - if (get_scaled_dimension(srcWidth, sampleY) == dstWidth) { |
| - sampleX = sampleY; |
| - } else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) { |
| - sampleY = sampleX; |
| + int decodedLines = fCodec->getScanlines(pixels, scaledSubsetHeight, rowBytes); |
| + if (decodedLines != scaledSubsetHeight) { |
| + return SkCodec::kIncompleteInput; |
| } |
| + return SkCodec::kSuccess; |
| } |
| - } |
| - |
| - if (sampleXPtr) { |
| - *sampleXPtr = sampleX; |
| - } |
| - if (sampleYPtr) { |
| - *sampleYPtr = sampleY; |
| + case SkCodec::kBottomUp_SkScanlineOrder: |
| + case SkCodec::kOutOfOrder_SkScanlineOrder: { |
| + for (int y = 0; y < scaledSize.height(); y++) { |
| + int dstY = fCodec->nextScanline(); |
| + if (is_in_range(dstY, scaledSubsetY, scaledSubsetHeight)) { |
| + void* pixelsPtr = SkTAddOffset<void>(pixels, rowBytes * (dstY - scaledSubsetY)); |
| + if (1 != fCodec->getScanlines(pixelsPtr, 1, rowBytes)) { |
| + // FIXME: Fill incomplete images skbug.com/4428 |
| + return SkCodec::kIncompleteInput; |
| + } |
| + } else { |
| + if (!fCodec->skipScanlines(1)) { |
| + // FIXME: Fill incomplete images skbug.com/4428 |
| + return SkCodec::kIncompleteInput; |
| + } |
| + } |
| + } |
| + return SkCodec::kSuccess; |
| + } |
| + default: |
| + SkASSERT(false); |
| + return SkCodec::kUnimplemented; |
| } |
| } |
| -// 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) { |
| +SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels, |
| + size_t rowBytes, AndroidOptions& options) { |
| + // Create options struct for the codec. |
| + SkCodec::Options sampledOptions; |
| + sampledOptions.fZeroInitialized = options.fZeroInitialized; |
| + // Check if there is a subset. |
| + SkIRect subset; |
| + int subsetY = 0; |
| + int subsetWidth = fCodec->getInfo().width(); |
| + int subsetHeight = fCodec->getInfo().height(); |
| if (options.fSubset) { |
| - // Subsets are not supported. |
| - return kUnimplemented; |
| + // We will need to know about subsetting in the y-dimension in order to use the |
| + // scanline decoder. |
| + SkIRect* subsetPtr = options.fSubset; |
| + subsetY = subsetPtr->y(); |
| + subsetWidth = subsetPtr->width(); |
| + subsetHeight = subsetPtr->height(); |
| + |
| + // The scanline decoder only needs to be aware of subsetting in the x-dimension. |
| + subset.setXYWH(subsetPtr->x(), 0, subsetWidth, fCodec->getInfo().height()); |
| + sampledOptions.fSubset = ⊂ |
| } |
| - if (fCodec->dimensionsSupported(requestedInfo.dimensions())) { |
| - // Make sure that the parent class does not fill on an incomplete decode, since |
| - // fCodec will take care of filling the uninitialized lines. |
| - *rowsDecoded = requestedInfo.height(); |
| - return fCodec->getPixels(requestedInfo, dst, rowBytes, &options, ctable, ctableCount); |
| - } |
| - |
| - // scaling requested |
| - int sampleX; |
| - int sampleY; |
| - if (!scaling_supported(requestedInfo.dimensions(), fCodec->getInfo().dimensions(), |
| - &sampleX, &sampleY)) { |
| - // onDimensionsSupported would have returned false, meaning we should never reach here. |
| - SkASSERT(false); |
| - return kInvalidScale; |
| - } |
| - |
| - // set first sample pixel in y direction |
| - const int Y0 = get_start_coord(sampleY); |
| - |
| - const int dstHeight = requestedInfo.height(); |
| - const int srcWidth = fCodec->getInfo().width(); |
| - const int srcHeight = fCodec->getInfo().height(); |
| - |
| - const SkImageInfo info = requestedInfo.makeWH(srcWidth, srcHeight); |
| - |
| - Result result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount); |
| - |
| - if (kSuccess != result) { |
| + // Start the scanline decode. |
| + SkCodec::Result result = fCodec->startScanlineDecode( |
| + info.makeWH(fCodec->getInfo().width(), fCodec->getInfo().height()), &sampledOptions, |
| + options.fColorPtr, options.fColorCount); |
| + if (SkCodec::kSuccess != result) { |
| + // If scaling is not supported, we should have caught the error |
| + // in getAndroidPixels(). |
| + SkASSERT(SkCodec::kInvalidScale != result); |
| return result; |
| } |
| SkSampler* sampler = fCodec->getSampler(true); |
| if (!sampler) { |
| - return kUnimplemented; |
| + return SkCodec::kUnimplemented; |
| } |
| - if (sampler->setSampleX(sampleX) != requestedInfo.width()) { |
| - return kInvalidScale; |
| + // 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. |
| + int sampleX = subsetWidth / info.width(); |
| + int sampleY = subsetHeight / info.height(); |
| + if (sampler->setSampleX(sampleX) != info.width()) { |
| + SkASSERT(false); |
| + return SkCodec::kInvalidScale; |
| } |
| + int y0 = get_start_coord(sampleY); |
| + int dstHeight = info.height(); |
| switch(fCodec->getScanlineOrder()) { |
| case SkCodec::kTopDown_SkScanlineOrder: { |
| - if (!fCodec->skipScanlines(Y0)) { |
| - *rowsDecoded = 0; |
| - return kIncompleteInput; |
| + if (!fCodec->skipScanlines(y0 + subsetY)) { |
| + fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, |
| + dstHeight, 0); |
| + return SkCodec::kIncompleteInput; |
| } |
| + void* pixelPtr = pixels; |
| for (int y = 0; y < dstHeight; 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 |
| - // decoded (y + 1) rows. |
| - *rowsDecoded = y + 1; |
| - return kIncompleteInput; |
| + if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) { |
| + fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, |
| + dstHeight, y + 1); |
| + return SkCodec::kIncompleteInput; |
| } |
| - if (y < dstHeight - 1) { |
| - if (!fCodec->skipScanlines(sampleY - 1)) { |
| - *rowsDecoded = y + 1; |
| - return kIncompleteInput; |
| - } |
| + int linesToSkip = SkTMin(sampleY - 1, dstHeight - y - 1); |
| + if (!fCodec->skipScanlines(linesToSkip)) { |
| + fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, |
| + dstHeight, y + 1); |
| + return SkCodec::kIncompleteInput; |
| } |
| - dst = SkTAddOffset<void>(dst, rowBytes); |
| + pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes); |
| } |
| - return kSuccess; |
| + return SkCodec::kSuccess; |
| } |
| case SkCodec::kBottomUp_SkScanlineOrder: |
| case SkCodec::kOutOfOrder_SkScanlineOrder: { |
| - Result result = kSuccess; |
| + SkCodec::Result result = SkCodec::kSuccess; |
| int y; |
| - for (y = 0; y < srcHeight; y++) { |
| + for (y = 0; y < fCodec->getInfo().height(); y++) { |
| int srcY = fCodec->nextScanline(); |
| - if (is_coord_necessary(srcY, sampleY, dstHeight)) { |
| - void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_coord(srcY, sampleY)); |
| - if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) { |
| - result = kIncompleteInput; |
| + if (is_coord_necessary(srcY, sampleY, dstHeight, subsetY)) { |
| + void* pixelPtr = SkTAddOffset<void>(pixels, |
| + rowBytes * get_dst_coord(srcY, sampleY)); |
| + if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) { |
| + result = SkCodec::kIncompleteInput; |
| break; |
| } |
| } else { |
| if (!fCodec->skipScanlines(1)) { |
| - result = kIncompleteInput; |
| + result = SkCodec::kIncompleteInput; |
| break; |
| } |
| } |
| } |
| - // 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 uint32_t fillValue = fCodec->getFillValue(requestedInfo.colorType(), |
| - requestedInfo.alphaType()); |
| - for (; y < srcHeight; y++) { |
| + // We handle filling uninitialized memory here instead of using fCodec. |
| + // fCodec does not know that we are sampling. |
| + if (SkCodec::kIncompleteInput == result) { |
| + const uint32_t fillValue = fCodec->getFillValue(info.colorType(), |
| + info.alphaType()); |
| + for (; y < fCodec->getInfo().height(); y++) { |
| int srcY = fCodec->outputScanline(y); |
| - if (is_coord_necessary(srcY, sampleY, dstHeight)) { |
| - void* dstRow = SkTAddOffset<void>(dst, |
| + if (is_coord_necessary(srcY, sampleY, dstHeight, subsetY)) { |
| + void* pixelPtr = SkTAddOffset<void>(pixels, |
| rowBytes * get_dst_coord(srcY, sampleY)); |
| - SkSampler::Fill(requestedInfo.makeWH(requestedInfo.width(), 1), dstRow, |
| - rowBytes, fillValue, options.fZeroInitialized); |
| + SkSampler::Fill(info.makeWH(info.width(), 1), pixelPtr, rowBytes, fillValue, |
| + options.fZeroInitialized); |
| } |
| } |
| - *rowsDecoded = dstHeight; |
| } |
| return result; |
| } |
| case SkCodec::kNone_SkScanlineOrder: { |
| - SkAutoMalloc storage(srcHeight * rowBytes); |
| + SkAutoMalloc storage((subsetHeight - y0) * 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(subsetY + y0)) { |
| + fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, |
| + dstHeight, 0); |
| + return SkCodec::kIncompleteInput; |
| + } |
| + int scanlines = fCodec->getScanlines(storagePtr, subsetHeight - y0, rowBytes); |
| int y = 0; |
| - while (y < dstHeight && scanlines > 0) { |
| - memcpy(dst, storagePtr, rowBytes); |
| + void* pixelPtr = pixels; |
| + while (y < dstHeight) { |
| + memcpy(pixelPtr, storagePtr, info.minRowBytes()); |
| storagePtr += sampleY * rowBytes; |
| - dst = SkTAddOffset<void>(dst, rowBytes); |
| - scanlines -= sampleY; |
| + pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes); |
| y++; |
| } |
| - if (y < dstHeight) { |
| + if (scanlines < dstHeight) { |
| // fCodec has already handled filling uninitialized memory. |
| - *rowsDecoded = dstHeight; |
| - return kIncompleteInput; |
| + return SkCodec::kIncompleteInput; |
| } |
| - return kSuccess; |
| + return SkCodec::kSuccess; |
| } |
| default: |
| SkASSERT(false); |
| - return kUnimplemented; |
| + return SkCodec::kUnimplemented; |
| } |
| } |
| - |
| -uint32_t SkScaledCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const { |
| - return fCodec->onGetFillValue(colorType, alphaType); |
| -} |
| - |
| -SkCodec::SkScanlineOrder SkScaledCodec::onGetScanlineOrder() const { |
| - return fCodec->onGetScanlineOrder(); |
| -} |