diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/codec/SkAndroidCodec.cpp | 18 | ||||
-rw-r--r-- | src/codec/SkSampledCodec.cpp | 100 | ||||
-rw-r--r-- | src/codec/SkSampledCodec.h | 13 |
3 files changed, 101 insertions, 30 deletions
diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp index 43ed90f6c0..086caf2d81 100644 --- a/src/codec/SkAndroidCodec.cpp +++ b/src/codec/SkAndroidCodec.cpp @@ -53,6 +53,11 @@ SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const { return SkISize::Make(0, 0); } + // Fast path for when we are not scaling. + if (1 == sampleSize) { + return fInfo.dimensions(); + } + return this->onGetSampledDimensions(sampleSize); } @@ -77,9 +82,9 @@ SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect return SkISize::Make(0, 0); } - // If the subset is the entire image, for consistency, use onGetSampledDimensions(). + // If the subset is the entire image, for consistency, use getSampledDimensions(). if (fInfo.dimensions() == subset.size()) { - return onGetSampledDimensions(sampleSize); + return this->getSampledDimensions(sampleSize); } // This should perhaps call a virtual function, but currently both of our subclasses @@ -104,6 +109,15 @@ SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* if (!is_valid_subset(*options->fSubset, fInfo.dimensions())) { return SkCodec::kInvalidParameters; } + + if (SkIRect::MakeSize(fInfo.dimensions()) == *options->fSubset) { + // The caller wants the whole thing, rather than a subset. Modify + // the AndroidOptions passed to onGetAndroidPixels to not specify + // a subset. + defaultOptions = *options; + defaultOptions.fSubset = nullptr; + options = &defaultOptions; + } } return this->onGetAndroidPixels(info, pixels, rowBytes, *options); diff --git a/src/codec/SkSampledCodec.cpp b/src/codec/SkSampledCodec.cpp index 229555b9bf..ed4eb7f02d 100644 --- a/src/codec/SkSampledCodec.cpp +++ b/src/codec/SkSampledCodec.cpp @@ -7,6 +7,7 @@ #include "SkCodec.h" #include "SkCodecPriv.h" +#include "SkMath.h" #include "SkSampledCodec.h" SkSampledCodec::SkSampledCodec(SkCodec* codec) @@ -14,30 +15,58 @@ SkSampledCodec::SkSampledCodec(SkCodec* codec) , fCodec(codec) {} -SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const { - // Fast path for when we are not scaling. - if (1 == sampleSize) { - return fCodec->getInfo().dimensions(); +SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const { + SkISize preSampledSize = fCodec->getInfo().dimensions(); + int sampleSize = *sampleSizePtr; + SkASSERT(sampleSize > 1); + + if (nativeSampleSize) { + *nativeSampleSize = 1; } - 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()); - // 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 (widthDiff < 1.0f && heightDiff < 1.0f) { - return nativeSize; + // Only JPEG supports native downsampling. + if (fCodec->getEncodedFormat() == kJPEG_SkEncodedFormat) { + // See if libjpeg supports this scale directly + switch (sampleSize) { + case 2: + case 4: + case 8: + // This class does not need to do any sampling. + *sampleSizePtr = 1; + return fCodec->getScaledDimensions(get_scale_from_sample_size(sampleSize)); + default: + break; + } + + // Check if sampleSize is a multiple of something libjpeg can support. + int remainder; + const int sampleSizes[] = { 8, 4, 2 }; + for (int supportedSampleSize : sampleSizes) { + int actualSampleSize; + SkTDivMod(sampleSize, supportedSampleSize, &actualSampleSize, &remainder); + if (0 == remainder) { + float scale = get_scale_from_sample_size(supportedSampleSize); + + // fCodec will scale to this size. + preSampledSize = fCodec->getScaledDimensions(scale); + + // And then this class will sample it. + *sampleSizePtr = actualSampleSize; + if (nativeSampleSize) { + *nativeSampleSize = supportedSampleSize; + } + break; + } + } } - // Provide the scaling by sampling. - return SkISize::Make(get_scaled_dimension(width, sampleSize), - get_scaled_dimension(height, sampleSize)); + return preSampledSize; +} + +SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const { + const SkISize size = this->accountForNativeScaling(&sampleSize); + return SkISize::Make(get_scaled_dimension(size.width(), sampleSize), + get_scaled_dimension(size.height(), sampleSize)); } SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels, @@ -59,7 +88,7 @@ SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void // We are performing a subset decode. int sampleSize = options.fSampleSize; - SkISize scaledSize = this->onGetSampledDimensions(sampleSize); + SkISize scaledSize = this->getSampledDimensions(sampleSize); if (!fCodec->dimensionsSupported(scaledSize)) { // If the native codec does not support the requested scale, scale by sampling. return this->sampledDecode(info, pixels, rowBytes, options); @@ -107,31 +136,46 @@ SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels, size_t rowBytes, AndroidOptions& options) { + // We should only call this function when sampling. + SkASSERT(options.fSampleSize > 1); + // Create options struct for the codec. SkCodec::Options sampledOptions; sampledOptions.fZeroInitialized = options.fZeroInitialized; + // FIXME: This was already called by onGetAndroidPixels. Can we reduce that? + int sampleSize = options.fSampleSize; + int nativeSampleSize; + SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize); + // Check if there is a subset. SkIRect subset; int subsetY = 0; - int subsetWidth = fCodec->getInfo().width(); - int subsetHeight = fCodec->getInfo().height(); + int subsetWidth = nativeSize.width(); + int subsetHeight = nativeSize.height(); if (options.fSubset) { // We will need to know about subsetting in the y-dimension in order to use the // scanline decoder. + // Update the subset to account for scaling done by fCodec. SkIRect* subsetPtr = options.fSubset; - subsetY = subsetPtr->y(); - subsetWidth = subsetPtr->width(); - subsetHeight = subsetPtr->height(); + + // Do the divide ourselves, instead of calling get_scaled_dimension. If + // X and Y are 0, they should remain 0, rather than being upgraded to 1 + // due to being smaller than the sampleSize. + const int subsetX = subsetPtr->x() / nativeSampleSize; + subsetY = subsetPtr->y() / nativeSampleSize; + + subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize); + subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize); // The scanline decoder only needs to be aware of subsetting in the x-dimension. - subset.setXYWH(subsetPtr->x(), 0, subsetWidth, fCodec->getInfo().height()); + subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height()); sampledOptions.fSubset = ⊂ } // Start the scanline decode. SkCodec::Result result = fCodec->startScanlineDecode( - info.makeWH(fCodec->getInfo().width(), fCodec->getInfo().height()), &sampledOptions, + info.makeWH(nativeSize.width(), nativeSize.height()), &sampledOptions, options.fColorPtr, options.fColorCount); if (SkCodec::kSuccess != result) { return result; diff --git a/src/codec/SkSampledCodec.h b/src/codec/SkSampledCodec.h index 277a50f0ee..bbd7d3599c 100644 --- a/src/codec/SkSampledCodec.h +++ b/src/codec/SkSampledCodec.h @@ -33,6 +33,19 @@ protected: AndroidOptions& options) override; private: + /** + * Find the best way to account for native scaling. + * + * Return a size that fCodec can scale to, and adjust sampleSize to finish scaling. + * + * @param sampleSize As an input, the requested sample size. + * As an output, sampling needed after letting fCodec + * scale to the returned dimensions. + * @param nativeSampleSize Optional output parameter. Will be set to the + * effective sample size done by fCodec. + * @return SkISize The size that fCodec should scale to. + */ + SkISize accountForNativeScaling(int* sampleSize, int* nativeSampleSize = nullptr) const; /** * This fulfills the same contract as onGetAndroidPixels(). |