Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Unified Diff: src/codec/SkScaledCodec.cpp

Issue 1260673002: SkScaledCodec class (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: use SkWebpCodec native scaling, update comments and spacing Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
+}

Powered by Google App Engine
This is Rietveld 408576698