Index: src/codec/SkScaledCodec.cpp |
diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f1b9f73f321db6405ce009109256ae671290997d |
--- /dev/null |
+++ b/src/codec/SkScaledCodec.cpp |
@@ -0,0 +1,166 @@ |
+/* |
+ * Copyright 2015 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "SkCodecPriv.h" |
+#include "SkScaledCodec.h" |
+#include "SkStream.h" |
+#include "SkWebpCodec.h" |
+ |
+ |
+SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) { |
+ bool isWebp = SkWebpCodec::IsWebp(stream); |
scroggo
2015/07/27 19:29:57
I'd prefer to not include SkWebpCodec here, and re
emmaleer
2015/07/28 14:19:16
Okay, I like that method better too.
Just checking
scroggo
2015/07/28 15:58:10
Yeah.
For a long time, C++ did not have a standar
emmaleer
2015/07/29 21:55:08
Acknowledged.
|
+ if (!stream->rewind()) { |
+ return NULL; |
+ } |
+ if (isWebp) { |
+ // Webp supports scaling natively |
+ return SkWebpCodec::NewFromStream(stream); |
+ } |
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream)); |
+ if (NULL == codec) { |
+ return NULL; |
+ } |
+ return SkNEW_ARGS(SkScaledCodec, (codec.detach())); |
+} |
+ |
+SkCodec* SkScaledCodec::NewFromData(SkData* data) { |
+ if (!data) { |
+ return NULL; |
+ } |
+ return NewFromStream(SkNEW_ARGS(SkMemoryStream, (data))); |
+} |
+ |
+SkScaledCodec::SkScaledCodec(SkCodec* codec) |
+ : INHERITED(codec->getInfo(), NULL) |
+ , fCodec(codec) |
+{} |
+ |
+SkScaledCodec::~SkScaledCodec() {} |
+ |
+/* |
+ * Return a valid set of output dimensions for this decoder, given an input scale |
+ */ |
+SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const { |
+ // support scaling down by integer numbers. Ex: 1/2, 1/3, 1/4 ... |
+ |
+ if (desiredScale > 0.5f) { |
+ // sampleSize = 1 |
+ return fCodec->getInfo().dimensions(); |
+ } |
+ // sampleSize determines the step size between samples |
+ // Ex: sampleSize = 2, sample every second pixel in x and y directions |
+ int sampleSize = 1 / desiredScale; |
+ |
+ // if sampleSize > width or height, will only sample 1 pixel in that direction |
+ int sampleX = SkMin32(sampleSize, this->getInfo().width()); |
+ int sampleY = SkMin32(sampleSize, this->getInfo().height()); |
+ |
+ int scaledWidth = this->getInfo().width() / sampleX; |
+ int scaledHeight = this->getInfo().height() / sampleY; |
msarett
2015/07/27 20:42:18
You're probably already aware of this, but we need
emmaleer
2015/07/28 14:19:16
This is inconsistent. I think it would be better i
msarett
2015/07/28 20:53:10
AFAICT it's an arbitrary decision.
|
+ |
+ // Return the calculated output dimensions for the given scale |
+ return SkISize::Make(scaledWidth, scaledHeight); |
+} |
+ |
+// check if scaling to dstInfo size from srcInfo size is possible |
+static bool scaling_supported(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo){ |
+ const int dstWidth = dstInfo.width(); |
+ const int dstHeight = dstInfo.height(); |
+ const int srcWidth = srcInfo.width(); |
+ const int srcHeight = srcInfo.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 |
+ int sampleSizeX = srcWidth / dstWidth; |
+ if (srcWidth / sampleSizeX != dstWidth) { |
+ return false; |
+ } |
+ // check that src height is scaled down by an integer value |
+ int sampleSizeY = srcHeight / dstHeight; |
+ if (srcHeight / sampleSizeY != 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 (sampleSizeX != sampleSizeY){ |
+ if (1 != dstWidth && 1 != dstHeight) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, |
+ size_t rowBytes, const Options& options, |
+ SkPMColor ctable[], int* ctableCount) { |
+ |
+ Result result = fCodec->getPixels(requestedInfo, dst, rowBytes, &options, ctable, ctableCount); |
msarett
2015/07/27 20:42:18
I was going to comment that this seems strange, an
emmaleer
2015/07/28 14:19:16
Yes, this was intended. Okay, I've added a comment
msarett
2015/07/28 20:53:10
Thanks!
|
+ if (kInvalidScale != result) { |
+ // no scaling requested |
+ return result; |
+ } |
+ // scaling requested |
+ |
+ if (!scaling_supported(requestedInfo, fCodec->getInfo())) { |
+ return kInvalidScale; |
+ } |
+ |
+ bool isInterlaced = fCodec->isInterlaced(); |
msarett
2015/07/27 20:42:18
For now, isInterlaced() is a decent name for this.
scroggo
2015/07/27 20:47:24
My first thought was the name isSampleable(), but
emmaleer
2015/07/28 14:19:16
What about PostYSample()? Since we have to sample
scroggo
2015/07/28 15:58:10
PostYSample() sounds to me like it actually does s
msarett
2015/07/28 20:53:10
What about isHardToSample()?
emmaleer
2015/07/29 21:55:08
I like isHardToSample, since it's more broad, and
scroggo
2015/07/30 17:53:01
It is broad, but it's not obvious to me what hard
emmaleer
2015/07/30 22:27:55
Should we use requiresPostSampling() ?
or what abo
|
+ Options scaledOptions = options; |
+ scaledOptions.fSampled = true; |
+ |
+ int dstHeight = requestedInfo.height(); |
+ int srcHeight = fCodec->getInfo().height(); |
+ |
+ SkImageInfo info = requestedInfo; |
+ if (isInterlaced){ |
+ // must decode entire height for interlaced, then scale after |
+ info = info.makeWH(requestedInfo.width(), srcHeight); |
+ } |
+ |
+ SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(fCodec->getScanlineDecoder(info, |
+ &scaledOptions, ctable, ctableCount)); |
+ if (NULL == scanlineDecoder) { |
+ SkCodecPrintf("failed to create scanline decoder.\n"); |
+ return kInvalidInput; |
+ } |
+ |
+ // set sample values in x and y directions |
+ int sampleY = srcHeight / dstHeight; |
+ int Y0 = sampleY >> 1; |
+ int sampleX = fCodec->getInfo().width() / requestedInfo.width(); |
+ if (!scanlineDecoder->setSampleX(sampleX)) { |
+ return kInvalidScale; |
+ } |
+ |
+ if (isInterlaced) { |
+ SkAutoMalloc storage(srcHeight * rowBytes); |
+ void* storagePtr = storage.get(); |
+ result = scanlineDecoder->getScanlines(storagePtr, srcHeight, rowBytes); |
+ |
+ storagePtr += Y0 * rowBytes; |
+ for (int y = 0; y < dstHeight; y++) { |
+ memcpy(dst, storagePtr, rowBytes); |
+ storagePtr += sampleY * rowBytes; |
+ dst += rowBytes; |
+ } |
+ } else { |
+ // not interlaced |
+ scanlineDecoder->skipScanlines(Y0); |
+ for (int y = 0; y < dstHeight; y++) { |
+ scanlineDecoder->getScanlines(dst, 1, rowBytes); |
+ if (y < dstHeight - 1) { |
+ scanlineDecoder->skipScanlines(sampleY - 1); |
+ } |
+ dst += rowBytes; |
+ } |
+ } |
+ return kSuccess; |
+} |