aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar scroggo <scroggo@chromium.org>2015-11-03 07:55:11 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2015-11-03 07:55:12 -0800
commit501b7344f116ccc821d437d324aa7883d7ce97bf (patch)
tree6be104babfba2a62fc82d4645bc85bf9a7d948fb
parentf7de08a52b5287cb16b2e89a8e3691676a4dbe5f (diff)
Combine native sampling with sampling
In SkSampledCodec, allow the native codec to do its scaling first, then sample on top of that. Since the only codec which can do native scaling is JPEG, and we know what it can do, hard-code for JPEG. Check to see if the sampleSize is something JPEG supports, or a multiple of something it supports. If so, use JPEG directly or combine them. BUG=skia:4320 Review URL: https://codereview.chromium.org/1417583009
-rw-r--r--bench/nanobench.cpp2
-rw-r--r--dm/DM.cpp7
-rw-r--r--src/codec/SkAndroidCodec.cpp18
-rw-r--r--src/codec/SkSampledCodec.cpp100
-rw-r--r--src/codec/SkSampledCodec.h13
-rw-r--r--tests/CodexTest.cpp2
6 files changed, 109 insertions, 33 deletions
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index dad280df0b..2e566da8e1 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -978,7 +978,7 @@ public:
// All use cases we are aware of only scale by powers of two.
// PNG decodes use the indicated sampling strategy regardless of the sample size, so
// these tests are sufficient to provide good coverage of our scaling options.
- const uint32_t sampleSizes[] = { 1, 2, 4, 8, 16 };
+ const uint32_t sampleSizes[] = { 1, 2, 4, 8, 16, 32, 64 };
const uint32_t minOutputSize = 512;
while (fCurrentBRDImage < fImages.count()) {
while (fCurrentBRDStrategy < (int) SK_ARRAY_COUNT(strategies)) {
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 236b10017c..1aaa1768e1 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -472,7 +472,12 @@ static void push_brd_srcs(Path path) {
SkBitmapRegionDecoderInterface::kAndroidCodec_Strategy,
};
- const uint32_t sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ // Test on a variety of sampleSizes, making sure to include:
+ // - 2, 4, and 8, which are natively supported by jpeg
+ // - multiples of 2 which are not divisible by 4 (analogous for 4)
+ // - larger powers of two, since BRD clients generally use powers of 2
+ // We will only produce output for the larger sizes on large images.
+ const uint32_t sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 64 };
// We will only test to one backend (8888), but we will test all of the
// color types that we need to decode to on this backend.
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 = &subset;
}
// 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().
diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp
index bd3e2031c9..205bd49e2b 100644
--- a/tests/CodexTest.cpp
+++ b/tests/CodexTest.cpp
@@ -581,7 +581,7 @@ static void test_dimensions(skiatest::Reporter* r, const char path[]) {
}
// Check that the decode is successful for a variety of scales
- for (int sampleSize = 1; sampleSize < 10; sampleSize++) {
+ for (int sampleSize = 1; sampleSize < 32; sampleSize++) {
// Scale the output dimensions
SkISize scaledDims = codec->getSampledDimensions(sampleSize);
SkImageInfo scaledInfo = codec->getInfo()