diff options
author | Leon Scroggins III <scroggo@google.com> | 2018-01-16 15:01:17 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-01-16 21:23:08 +0000 |
commit | 07a722cdcf064fca22213dc06a433dd82f080c23 (patch) | |
tree | 8d8e7de91637af359b9ff8649c8bed317019cbf7 /src/codec | |
parent | 539c6f5c92c2a4f04816d562c3d23556a35a2e98 (diff) |
Add SkAndroidCodec::computeSampledSize
Bug: b/63909536
Android's ImageDecoder API takes as input an arbitrary width and height
to scale the image to. Internally, this uses SkAndroidCodec to sample,
and then (if not a perfect match) scales to the desired size with
drawing.
computeSampledSize is a modified version of what ImageDecoder currently
does to convert from arbitrary dimensions to a sampleSize. Moving it
here allows it to be shared by SkAnimatedImage. The modified version
also corrects two bugs:
- a client using the dimensions returned by getSampledDimensions
previously may have resulted in ImageDecoder decoding to a larger
size and then scaling it. (example found in tests: dog.jpg is
180 x 180. getSampledDimensions(8) returns 23 x 23, but the old
method resulted in using sampleSize of 7 and downscaling the resulting
25 x 25 image.)
- recompute the sampleSize based on the size returned by
getSampledDimensions.
Change-Id: I022040e8bac31c20988903a0452257f7ae902bc7
Reviewed-on: https://skia-review.googlesource.com/94620
Reviewed-by: Derek Sollenberger <djsollen@google.com>
Commit-Queue: Leon Scroggins <scroggo@google.com>
Diffstat (limited to 'src/codec')
-rw-r--r-- | src/codec/SkAndroidCodec.cpp | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp index a7ffa4e93b..486257f9a3 100644 --- a/src/codec/SkAndroidCodec.cpp +++ b/src/codec/SkAndroidCodec.cpp @@ -186,6 +186,100 @@ sk_sp<SkColorSpace> SkAndroidCodec::computeOutputColorSpace(SkColorType outputCo } } +static bool supports_any_down_scale(const SkCodec* codec) { + return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP; +} + +// There are a variety of ways two SkISizes could be compared. This method +// returns true if either dimensions of a is < that of b. +// computeSampleSize also uses the opposite, which means that both +// dimensions of a >= b. +static inline bool smaller_than(const SkISize& a, const SkISize& b) { + return a.width() < b.width() || a.height() < b.height(); +} + +// Both dimensions of a > that of b. +static inline bool strictly_bigger_than(const SkISize& a, const SkISize& b) { + return a.width() > b.width() && a.height() > b.height(); +} + +int SkAndroidCodec::computeSampleSize(SkISize* desiredSize) const { + SkASSERT(desiredSize); + + if (!desiredSize || *desiredSize == fInfo.dimensions()) { + return 1; + } + + if (smaller_than(fInfo.dimensions(), *desiredSize)) { + *desiredSize = fInfo.dimensions(); + return 1; + } + + // Handle bad input: + if (desiredSize->width() < 1 || desiredSize->height() < 1) { + *desiredSize = SkISize::Make(std::max(1, desiredSize->width()), + std::max(1, desiredSize->height())); + } + + if (supports_any_down_scale(fCodec.get())) { + return 1; + } + + int sampleX = fInfo.width() / desiredSize->width(); + int sampleY = fInfo.height() / desiredSize->height(); + int sampleSize = std::min(sampleX, sampleY); + auto computedSize = this->getSampledDimensions(sampleSize); + if (computedSize == *desiredSize) { + return sampleSize; + } + + if (computedSize == fInfo.dimensions() || sampleSize == 1) { + // Cannot downscale + *desiredSize = computedSize; + return 1; + } + + if (strictly_bigger_than(computedSize, *desiredSize)) { + // See if there is a tighter fit. + while (true) { + auto smaller = this->getSampledDimensions(sampleSize + 1); + if (smaller == *desiredSize) { + return sampleSize + 1; + } + if (smaller == computedSize || smaller_than(smaller, *desiredSize)) { + // Cannot get any smaller without being smaller than desired. + *desiredSize = computedSize; + return sampleSize; + } + + sampleSize++; + computedSize = smaller; + } + + SkASSERT(false); + } + + if (!smaller_than(computedSize, *desiredSize)) { + // This means one of the computed dimensions is equal to desired, and + // the other is bigger. This is as close as we can get. + *desiredSize = computedSize; + return sampleSize; + } + + // computedSize is too small. Make it larger. + while (sampleSize > 2) { + auto bigger = this->getSampledDimensions(sampleSize - 1); + if (bigger == *desiredSize || !smaller_than(bigger, *desiredSize)) { + *desiredSize = bigger; + return sampleSize - 1; + } + sampleSize--; + } + + *desiredSize = fInfo.dimensions(); + return 1; +} + SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const { if (!is_valid_sample_size(sampleSize)) { return {0, 0}; |