aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--dm/DM.cpp78
-rw-r--r--dm/DMSrcSink.cpp238
-rw-r--r--dm/DMSrcSink.h23
-rw-r--r--gyp/codec.gyp2
-rw-r--r--include/codec/SkAndroidCodec.h224
-rw-r--r--include/codec/SkCodec.h2
-rw-r--r--include/codec/SkScaledCodec.h59
-rw-r--r--src/codec/SkAndroidCodec.cpp115
-rw-r--r--src/codec/SkBmpCodec.cpp1
-rw-r--r--src/codec/SkBmpRLECodec.cpp1
-rw-r--r--src/codec/SkCodecPriv.h10
-rw-r--r--src/codec/SkCodec_libpng.cpp1
-rw-r--r--src/codec/SkJpegCodec.cpp1
-rw-r--r--src/codec/SkMaskSwizzler.cpp1
-rw-r--r--src/codec/SkSampledCodec.h51
-rw-r--r--src/codec/SkScaledCodec.cpp430
-rw-r--r--src/codec/SkSwizzler.cpp1
-rw-r--r--src/codec/SkWebpAdapterCodec.cpp46
-rw-r--r--src/codec/SkWebpAdapterCodec.h41
-rw-r--r--tests/CodexTest.cpp98
20 files changed, 994 insertions, 429 deletions
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 5d98fe7ca5..c51a6fb8aa 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -226,9 +226,6 @@ static void push_codec_src(Path path, CodecSrc::Mode mode, CodecSrc::DstColorTyp
case CodecSrc::kCodec_Mode:
folder.append("codec");
break;
- case CodecSrc::kScaledCodec_Mode:
- folder.append("scaled_codec");
- break;
case CodecSrc::kScanline_Mode:
folder.append("scanline");
break;
@@ -262,6 +259,37 @@ static void push_codec_src(Path path, CodecSrc::Mode mode, CodecSrc::DstColorTyp
push_src("image", folder, src);
}
+static void push_android_codec_src(Path path, AndroidCodecSrc::Mode mode,
+ CodecSrc::DstColorType dstColorType, int sampleSize) {
+ SkString folder;
+ switch (mode) {
+ case AndroidCodecSrc::kFullImage_Mode:
+ folder.append("scaled_codec");
+ break;
+ case AndroidCodecSrc::kDivisor_Mode:
+ folder.append("scaled_codec_divisor");
+ break;
+ }
+
+ switch (dstColorType) {
+ case CodecSrc::kGrayscale_Always_DstColorType:
+ folder.append("_kGray8");
+ break;
+ case CodecSrc::kIndex8_Always_DstColorType:
+ folder.append("_kIndex8");
+ break;
+ default:
+ break;
+ }
+
+ if (1 != sampleSize) {
+ folder.appendf("_%.3f", get_scale_from_sample_size(sampleSize));
+ }
+
+ AndroidCodecSrc* src = new AndroidCodecSrc(path, mode, dstColorType, sampleSize);
+ push_src("image", folder, src);
+}
+
static void push_codec_srcs(Path path) {
SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
if (!encoded) {
@@ -288,7 +316,9 @@ static void push_codec_srcs(Path path) {
switch (codec->getInfo().colorType()) {
case kGray_8_SkColorType:
// FIXME: Is this a long term solution for testing wbmps decodes to kIndex8?
- // Further discussion on this topic is at skbug.com/3683
+ // Further discussion on this topic is at skbug.com/3683.
+ // This causes us to try to convert grayscale jpegs to kIndex8. We currently
+ // fail non-fatally in this case.
colorTypes[0] = CodecSrc::kGetFromCanvas_DstColorType;
colorTypes[1] = CodecSrc::kGrayscale_Always_DstColorType;
colorTypes[2] = CodecSrc::kIndex8_Always_DstColorType;
@@ -313,24 +343,34 @@ static void push_codec_srcs(Path path) {
}
}
- if (path.endsWith(".ico") || path.endsWith(".ICO")) {
- // FIXME: skbug.com/4404: ICO does not have the ability to decode scanlines, so we cannot
- // use SkScaledCodec with it.
+ // skbug.com/4428
+ static const char* const exts[] = {
+ "jpg", "jpeg", "png", "webp",
+ "JPG", "JPEG", "PNG", "WEBP",
+ };
+ bool supported = false;
+ for (const char* ext : exts) {
+ if (path.endsWith(ext)) {
+ supported = true;
+ break;
+ }
+ }
+ if (!supported) {
return;
}
- // SkScaledCodec Scales
- // The native scales are included to make sure that SkScaledCodec defaults to the native
- // scaling strategy when possible.
- // 0.1, 0.16, 0.2 etc allow us to test SkScaledCodec with sampleSize 10, 6, 5, etc.
- // 0.4, 0.7 etc allow to test what happens when the client requests a scale that
- // does not exactly match a sampleSize or native scaling capability.
- const float samplingScales[] = { 0.1f, 0.125f, 0.167f, 0.2f, 0.25f, 0.333f, 0.375f, 0.4f, 0.5f,
- 0.6f, 0.625f, 0.750f, 0.8f, 0.875f, 1.0f };
-
- for (float scale : samplingScales) {
- for (uint32_t i = 0; i < numColorTypes; i++) {
- push_codec_src(path, CodecSrc::kScaledCodec_Mode, colorTypes[i], scale);
+ const int sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ const AndroidCodecSrc::Mode androidModes[] = {
+ AndroidCodecSrc::kFullImage_Mode,
+ AndroidCodecSrc::kDivisor_Mode,
+ };
+
+ for (int sampleSize : sampleSizes) {
+ for (AndroidCodecSrc::Mode mode : androidModes) {
+ for (uint32_t i = 0; i < numColorTypes; i++) {
+ push_android_codec_src(path, mode, colorTypes[i], sampleSize);
+ }
}
}
}
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 394b84fa7b..24ae2d6087 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -7,6 +7,7 @@
#include "DMSrcSink.h"
#include "SamplePipeControllers.h"
+#include "SkAndroidCodec.h"
#include "SkCodec.h"
#include "SkCodecTools.h"
#include "SkCommonFlags.h"
@@ -25,11 +26,9 @@
#include "SkRecorder.h"
#include "SkRemote.h"
#include "SkSVGCanvas.h"
-#include "SkScaledCodec.h"
#include "SkStream.h"
#include "SkTLogic.h"
#include "SkXMLWriter.h"
-#include "SkScaledCodec.h"
#include "SkSwizzler.h"
DEFINE_bool(multiPage, false, "For document-type backends, render the source"
@@ -244,46 +243,47 @@ bool CodecSrc::veto(SinkFlags flags) const {
|| flags.approach != SinkFlags::kDirect;
}
+bool get_decode_info(SkImageInfo* decodeInfo, const SkImageInfo& defaultInfo,
+ SkColorType canvasColorType, CodecSrc::DstColorType dstColorType) {
+ switch (dstColorType) {
+ case CodecSrc::kIndex8_Always_DstColorType:
+ if (kRGB_565_SkColorType == canvasColorType) {
+ return false;
+ }
+ *decodeInfo = defaultInfo.makeColorType(kIndex_8_SkColorType);
+ break;
+ case CodecSrc::kGrayscale_Always_DstColorType:
+ if (kRGB_565_SkColorType == canvasColorType) {
+ return false;
+ }
+ *decodeInfo = defaultInfo.makeColorType(kGray_8_SkColorType);
+ break;
+ default:
+ *decodeInfo = defaultInfo.makeColorType(canvasColorType);
+ break;
+ }
+
+ // FIXME: Currently we cannot draw unpremultiplied sources.
+ if (decodeInfo->alphaType() == kUnpremul_SkAlphaType) {
+ decodeInfo->makeAlphaType(kPremul_SkAlphaType);
+ }
+ return true;
+}
+
Error CodecSrc::draw(SkCanvas* canvas) const {
SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
if (!encoded) {
return SkStringPrintf("Couldn't read %s.", fPath.c_str());
}
- SkAutoTDelete<SkCodec> codec(NULL);
- if (kScaledCodec_Mode == fMode) {
- codec.reset(SkScaledCodec::NewFromData(encoded));
- // TODO (msarett): This should fall through to a fatal error once we support scaled
- // codecs for all image types.
- if (nullptr == codec.get()) {
- return Error::Nonfatal(SkStringPrintf("Couldn't create scaled codec for %s.",
- fPath.c_str()));
- }
- } else {
- codec.reset(SkCodec::NewFromData(encoded));
- }
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
if (nullptr == codec.get()) {
return SkStringPrintf("Couldn't create codec for %s.", fPath.c_str());
}
- // Choose the color type to decode to
- SkImageInfo decodeInfo = codec->getInfo();
- SkColorType canvasColorType = canvas->imageInfo().colorType();
- switch (fDstColorType) {
- case kIndex8_Always_DstColorType:
- decodeInfo = codec->getInfo().makeColorType(kIndex_8_SkColorType);
- if (kRGB_565_SkColorType == canvasColorType) {
- return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
- }
- break;
- case kGrayscale_Always_DstColorType:
- decodeInfo = codec->getInfo().makeColorType(kGray_8_SkColorType);
- if (kRGB_565_SkColorType == canvasColorType) {
- return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
- }
- break;
- default:
- decodeInfo = decodeInfo.makeColorType(canvasColorType);
- break;
+ SkImageInfo decodeInfo;
+ if (!get_decode_info(&decodeInfo, codec->getInfo(), canvas->imageInfo().colorType(),
+ fDstColorType)) {
+ return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
}
// Try to scale the image if it is desired
@@ -311,11 +311,6 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
colorCountPtr = &maxColors;
}
- // FIXME: Currently we cannot draw unpremultiplied sources.
- if (decodeInfo.alphaType() == kUnpremul_SkAlphaType) {
- decodeInfo = decodeInfo.makeAlphaType(kPremul_SkAlphaType);
- }
-
SkBitmap bitmap;
if (!bitmap.tryAllocPixels(decodeInfo, nullptr, colorTable.get())) {
return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(),
@@ -323,7 +318,6 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
switch (fMode) {
- case kScaledCodec_Mode:
case kCodec_Mode: {
switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), nullptr,
colorPtr, colorCountPtr)) {
@@ -585,14 +579,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
SkISize CodecSrc::size() const {
SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
- SkAutoTDelete<SkCodec> codec(nullptr);
-
- if (kScaledCodec_Mode == fMode) {
- codec.reset(SkScaledCodec::NewFromData(encoded));
- } else {
- codec.reset(SkCodec::NewFromData(encoded));
- }
-
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
if (nullptr == codec) {
return SkISize::Make(0, 0);
}
@@ -608,6 +595,163 @@ Name CodecSrc::name() const {
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+AndroidCodecSrc::AndroidCodecSrc(Path path, Mode mode, CodecSrc::DstColorType dstColorType,
+ int sampleSize)
+ : fPath(path)
+ , fMode(mode)
+ , fDstColorType(dstColorType)
+ , fSampleSize(sampleSize)
+{}
+
+bool AndroidCodecSrc::veto(SinkFlags flags) const {
+ // No need to test decoding to non-raster or indirect backend.
+ // TODO: Once we implement GPU paths (e.g. JPEG YUV), we should use a deferred decode to
+ // let the GPU handle it.
+ return flags.type != SinkFlags::kRaster
+ || flags.approach != SinkFlags::kDirect;
+}
+
+Error AndroidCodecSrc::draw(SkCanvas* canvas) const {
+ SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
+ if (!encoded) {
+ return SkStringPrintf("Couldn't read %s.", fPath.c_str());
+ }
+ SkAutoTDelete<SkAndroidCodec> codec(SkAndroidCodec::NewFromData(encoded));
+ if (nullptr == codec.get()) {
+ return SkStringPrintf("Couldn't create android codec for %s.", fPath.c_str());
+ }
+
+ SkImageInfo decodeInfo;
+ if (!get_decode_info(&decodeInfo, codec->getInfo(), canvas->imageInfo().colorType(),
+ fDstColorType)) {
+ return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
+ }
+
+ // Scale the image if it is desired.
+ SkISize size = codec->getSampledDimensions(fSampleSize);
+
+ // Visually inspecting very small output images is not necessary. We will
+ // cover these cases in unit testing.
+ if ((size.width() <= 10 || size.height() <= 10) && 1 != fSampleSize) {
+ return Error::Nonfatal("Scaling very small images is uninteresting.");
+ }
+ decodeInfo = decodeInfo.makeWH(size.width(), size.height());
+
+ // Construct a color table for the decode if necessary
+ SkAutoTUnref<SkColorTable> colorTable(nullptr);
+ SkPMColor* colorPtr = nullptr;
+ int* colorCountPtr = nullptr;
+ int maxColors = 256;
+ if (kIndex_8_SkColorType == decodeInfo.colorType()) {
+ SkPMColor colors[256];
+ colorTable.reset(new SkColorTable(colors, maxColors));
+ colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
+ colorCountPtr = &maxColors;
+ }
+
+ SkBitmap bitmap;
+ if (!bitmap.tryAllocPixels(decodeInfo, nullptr, colorTable.get())) {
+ return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(),
+ decodeInfo.width(), decodeInfo.height());
+ }
+
+ // Create options for the codec.
+ SkAndroidCodec::AndroidOptions options;
+ options.fColorPtr = colorPtr;
+ options.fColorCount = colorCountPtr;
+ options.fSampleSize = fSampleSize;
+
+ switch (fMode) {
+ case kFullImage_Mode: {
+ switch (codec->getAndroidPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(),
+ &options)) {
+ case SkCodec::kSuccess:
+ case SkCodec::kIncompleteInput:
+ break;
+ case SkCodec::kInvalidConversion:
+ return Error::Nonfatal("Cannot convert to requested color type.\n");
+ default:
+ return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
+ }
+ canvas->drawBitmap(bitmap, 0, 0);
+ return "";
+ }
+ case kDivisor_Mode: {
+ const int width = codec->getInfo().width();
+ const int height = codec->getInfo().height();
+ const int divisor = 2;
+ if (width < divisor || height < divisor) {
+ return Error::Nonfatal("Divisor is larger than image dimension.\n");
+ }
+
+ // Rounding the size of the subsets may leave some pixels uninitialized on the bottom
+ // and right edges of the bitmap.
+ bitmap.eraseColor(0);
+ for (int x = 0; x < divisor; x++) {
+ for (int y = 0; y < divisor; y++) {
+ // Calculate the subset dimensions
+ int subsetWidth = width / divisor;
+ int subsetHeight = height / divisor;
+ const int left = x * subsetWidth;
+ const int top = y * subsetHeight;
+
+ // Increase the size of the last subset in each row or column, when the
+ // divisor does not divide evenly into the image dimensions
+ subsetWidth += (x + 1 == divisor) ? (width % divisor) : 0;
+ subsetHeight += (y + 1 == divisor) ? (height % divisor) : 0;
+ SkIRect subset = SkIRect::MakeXYWH(left, top, subsetWidth, subsetHeight);
+ if (!codec->getSupportedSubset(&subset)) {
+ return "Could not get supported subset to decode.\n";
+ }
+ options.fSubset = &subset;
+ void* pixels = bitmap.getAddr(subset.left() / fSampleSize,
+ subset.top() / fSampleSize);
+ SkISize scaledSubsetSize = codec->getSampledSubsetDimensions(fSampleSize,
+ subset);
+ SkImageInfo subsetDecodeInfo = decodeInfo.makeWH(scaledSubsetSize.width(),
+ scaledSubsetSize.height());
+
+ switch (codec->getAndroidPixels(subsetDecodeInfo, pixels, bitmap.rowBytes(),
+ &options)) {
+ case SkCodec::kSuccess:
+ case SkCodec::kIncompleteInput:
+ break;
+ case SkCodec::kInvalidConversion:
+ return Error::Nonfatal("Cannot convert to requested color type.\n");
+ default:
+ return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
+ }
+ }
+ }
+ canvas->drawBitmap(bitmap, 0, 0);
+ return "";
+ }
+ default:
+ SkASSERT(false);
+ return "Error: Should not be reached.\n";
+ }
+}
+
+SkISize AndroidCodecSrc::size() const {
+ SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
+ SkAutoTDelete<SkAndroidCodec> codec(SkAndroidCodec::NewFromData(encoded));
+ if (nullptr == codec) {
+ return SkISize::Make(0, 0);
+ }
+ return codec->getSampledDimensions(fSampleSize);
+}
+
+Name AndroidCodecSrc::name() const {
+ // We will replicate the names used by CodecSrc so that images can
+ // be compared in Gold.
+ if (1 == fSampleSize) {
+ return SkOSPath::Basename(fPath.c_str());
+ }
+ return get_scaled_name(fPath, get_scale_from_sample_size(fSampleSize));
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
ImageSrc::ImageSrc(Path path, int divisor) : fPath(path), fDivisor(divisor) {}
bool ImageSrc::veto(SinkFlags flags) const {
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index 1beaec9a69..a7cca8396a 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -104,7 +104,6 @@ private:
class CodecSrc : public Src {
public:
enum Mode {
- kScaledCodec_Mode,
kCodec_Mode,
kScanline_Mode,
kScanline_Subset_Mode,
@@ -129,6 +128,28 @@ private:
float fScale;
};
+class AndroidCodecSrc : public Src {
+public:
+ enum Mode {
+ kFullImage_Mode,
+ // Splits the image into multiple subsets using a divisor and decodes the subsets
+ // separately.
+ kDivisor_Mode,
+ };
+
+ AndroidCodecSrc(Path, Mode, CodecSrc::DstColorType, int sampleSize);
+
+ Error draw(SkCanvas*) const override;
+ SkISize size() const override;
+ Name name() const override;
+ bool veto(SinkFlags) const override;
+private:
+ Path fPath;
+ Mode fMode;
+ CodecSrc::DstColorType fDstColorType;
+ int fSampleSize;
+};
+
// Allows for testing of various implementations of Android's BitmapRegionDecoder
class BRDSrc : public Src {
public:
diff --git a/gyp/codec.gyp b/gyp/codec.gyp
index 1f3287ab52..350410a665 100644
--- a/gyp/codec.gyp
+++ b/gyp/codec.gyp
@@ -32,6 +32,7 @@
'../src/core',
],
'sources': [
+ '../src/codec/SkAndroidCodec.cpp',
'../src/codec/SkBmpCodec.cpp',
'../src/codec/SkBmpMaskCodec.cpp',
'../src/codec/SkBmpRLECodec.cpp',
@@ -49,6 +50,7 @@
'../src/codec/SkSampler.cpp',
'../src/codec/SkScaledCodec.cpp',
'../src/codec/SkSwizzler.cpp',
+ '../src/codec/SkWebpAdapterCodec.cpp',
'../src/codec/SkWebpCodec.cpp',
],
'direct_dependent_settings': {
diff --git a/include/codec/SkAndroidCodec.h b/include/codec/SkAndroidCodec.h
new file mode 100644
index 0000000000..42e7fa8b9f
--- /dev/null
+++ b/include/codec/SkAndroidCodec.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkAndroidCodec_DEFINED
+#define SkAndroidCodec_DEFINED
+
+#include "SkCodec.h"
+#include "SkEncodedFormat.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+
+/**
+ * Abstract interface defining image codec functionality that is necessary for
+ * Android.
+ */
+class SkAndroidCodec : SkNoncopyable {
+public:
+ /**
+ * If this stream represents an encoded image that we know how to decode,
+ * return an SkAndroidCodec that can decode it. Otherwise return NULL.
+ *
+ * If NULL is returned, the stream is deleted immediately. Otherwise, the
+ * SkCodec takes ownership of it, and will delete it when done with it.
+ */
+ static SkAndroidCodec* NewFromStream(SkStream*);
+
+ /**
+ * If this data represents an encoded image that we know how to decode,
+ * return an SkAndroidCodec that can decode it. Otherwise return NULL.
+ *
+ * Will take a ref if it returns a codec, else will not affect the data.
+ */
+ static SkAndroidCodec* NewFromData(SkData*);
+
+ virtual ~SkAndroidCodec() {}
+
+
+ const SkImageInfo& getInfo() const { return fInfo; }
+
+ /**
+ * Format of the encoded data.
+ */
+ SkEncodedFormat getEncodedFormat() const { return this->onGetEncodedFormat(); }
+
+ /**
+ * Returns the dimensions of the scaled output image, for an input
+ * sampleSize.
+ *
+ * When the sample size divides evenly into the original dimensions, the
+ * scaled output dimensions will simply be equal to the original
+ * dimensions divided by the sample size.
+ *
+ * When the sample size does not divide even into the original
+ * dimensions, the codec may round up or down, depending on what is most
+ * efficient to decode.
+ *
+ * Finally, the codec will always recommend a non-zero output, so the output
+ * dimension will always be one if the sampleSize is greater than the
+ * original dimension.
+ */
+ SkISize getSampledDimensions(int sampleSize) const;
+
+ /**
+ * Return (via desiredSubset) a subset which can decoded from this codec,
+ * or false if the input subset is invalid.
+ *
+ * @param desiredSubset in/out parameter
+ * As input, a desired subset of the original bounds
+ * (as specified by getInfo).
+ * As output, if true is returned, desiredSubset may
+ * have been modified to a subset which is
+ * supported. Although a particular change may have
+ * been made to desiredSubset to create something
+ * supported, it is possible other changes could
+ * result in a valid subset. If false is returned,
+ * desiredSubset's value is undefined.
+ * @return true If the input desiredSubset is valid.
+ * desiredSubset may be modified to a subset
+ * supported by the codec.
+ * false If desiredSubset is invalid (NULL or not fully
+ * contained within the image).
+ */
+ bool getSupportedSubset(SkIRect* desiredSubset) const;
+ // TODO: Rename SkCodec::getValidSubset() to getSupportedSubset()
+
+ /**
+ * Returns the dimensions of the scaled, partial output image, for an
+ * input sampleSize and subset.
+ *
+ * @param sampleSize Factor to scale down by.
+ * @param subset Must be a valid subset of the original image
+ * dimensions and a subset supported by SkAndroidCodec.
+ * getSubset() can be used to obtain a subset supported
+ * by SkAndroidCodec.
+ * @return Size of the scaled partial image. Or zero size
+ * if either of the inputs is invalid.
+ */
+ SkISize getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const;
+
+ /**
+ * Additional options to pass to getAndroidPixels().
+ */
+ // FIXME: It's a bit redundant to name these AndroidOptions when this class is already
+ // called SkAndroidCodec. On the other hand, it's may be a bit confusing to call
+ // these Options when SkCodec has a slightly different set of Options. Maybe these
+ // should be DecodeOptions or SamplingOptions?
+ struct AndroidOptions {
+ AndroidOptions()
+ : fZeroInitialized(SkCodec::kNo_ZeroInitialized)
+ , fSubset(nullptr)
+ , fColorPtr(nullptr)
+ , fColorCount(nullptr)
+ , fSampleSize(1)
+ {}
+
+ /**
+ * Indicates is destination pixel memory is zero initialized.
+ */
+ SkCodec::ZeroInitialized fZeroInitialized;
+
+ /**
+ * If not NULL, represents a subset of the original image to decode.
+ *
+ * Must be within the bounds returned by getInfo().
+ *
+ * If the EncodedFormat is kWEBP_SkEncodedFormat, the top and left
+ * values must be even.
+ */
+ SkIRect* fSubset;
+
+ /**
+ * If the client has requested a decode to kIndex8_SkColorType
+ * (specified in the SkImageInfo), then the caller must provide
+ * storage for up to 256 SkPMColor values in fColorPtr. On success,
+ * the codec must copy N colors into that storage, (where N is the
+ * logical number of table entries) and set fColorCount to N.
+ *
+ * If the client does not request kIndex8_SkColorType, then the last
+ * two parameters may be NULL. If fColorCount is not null, it will be
+ * set to 0.
+ */
+ SkPMColor* fColorPtr;
+ int* fColorCount;
+
+ /**
+ * The client may provide an integer downscale factor for the decode.
+ * The codec may implement this downscaling by sampling or another
+ * method if it is more efficient.
+ */
+ int fSampleSize;
+ };
+
+ /**
+ * Decode into the given pixels, a block of memory of size at
+ * least (info.fHeight - 1) * rowBytes + (info.fWidth *
+ * bytesPerPixel)
+ *
+ * Repeated calls to this function should give the same results,
+ * allowing the PixelRef to be immutable.
+ *
+ * @param info A description of the format (config, size)
+ * expected by the caller. This can simply be identical
+ * to the info returned by getInfo().
+ *
+ * This contract also allows the caller to specify
+ * different output-configs, which the implementation can
+ * decide to support or not.
+ *
+ * A size that does not match getInfo() implies a request
+ * to scale or subset. If the codec cannot perform this
+ * scaling or subsetting, it will return an error code.
+ *
+ * If info is kIndex8_SkColorType, then the caller must provide storage for up to 256
+ * SkPMColor values in options->fColorPtr. On success the codec must copy N colors into
+ * that storage, (where N is the logical number of table entries) and set
+ * options->fColorCount to N.
+ *
+ * If info is not kIndex8_SkColorType, options->fColorPtr and options->fColorCount may
+ * be nullptr.
+ *
+ * The AndroidOptions object is also used to specify any requested scaling or subsetting
+ * using options->fSampleSize and options->fSubset.
+ *
+ * @return Result kSuccess, or another value explaining the type of failure.
+ */
+ // FIXME: It's a bit redundant to name this getAndroidPixels() when this class is already
+ // called SkAndroidCodec. On the other hand, it's may be a bit confusing to call
+ // this getPixels() when it is a slightly different API than SkCodec's getPixels().
+ // Maybe this should be decode() or decodeSubset()?
+ SkCodec::Result getAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
+ AndroidOptions* options);
+
+ /**
+ * Simplified version of getAndroidPixels() where we supply the default AndroidOptions.
+ *
+ * This will return an error if the info is kIndex_8_SkColorType and also will not perform
+ * any scaling or subsetting.
+ */
+ SkCodec::Result getAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes);
+
+protected:
+
+ SkAndroidCodec(const SkImageInfo&);
+
+ virtual SkEncodedFormat onGetEncodedFormat() const = 0;
+
+ virtual SkISize onGetSampledDimensions(int sampleSize) const = 0;
+
+ virtual bool onGetSupportedSubset(SkIRect* desiredSubset) const = 0;
+
+ virtual SkCodec::Result onGetAndroidPixels(const SkImageInfo& info, void* pixels,
+ size_t rowBytes, AndroidOptions& options) = 0;
+
+private:
+
+ // This will always be a reference to the info that is contained by the
+ // embedded SkCodec.
+ const SkImageInfo& fInfo;
+};
+#endif // SkAndroidCodec_DEFINED
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index 8b6e210167..5127a7cc41 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -584,6 +584,6 @@ private:
*/
virtual SkSampler* getSampler(bool createIfNecessary) { return nullptr; }
- friend class SkScaledCodec;
+ friend class SkSampledCodec;
};
#endif // SkCodec_DEFINED
diff --git a/include/codec/SkScaledCodec.h b/include/codec/SkScaledCodec.h
deleted file mode 100644
index 028706bc94..0000000000
--- a/include/codec/SkScaledCodec.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkScaledCodec_DEFINED
-#define SkScaledCodec_DEFINED
-
-#include "SkCodec.h"
-
-class SkStream;
-
-/**
- * This class implements scaling, by sampling scanlines in the y direction.
- * x-wise sampling is implemented in the swizzler, when getScanlines() is called.
- */
-class SkScaledCodec : public SkCodec {
-public:
- static SkCodec* NewFromStream(SkStream*);
- static SkCodec* NewFromData(SkData*);
-
- virtual ~SkScaledCodec();
-
- static void ComputeSampleSize(const SkISize& dstDim, const SkISize& srcDim,
- int* sampleSizeX, int* sampleSizeY);
-
-protected:
- bool onRewind() override;
-
- /**
- * Recommend a set of destination dimensions given a requested scale
- */
- SkISize onGetScaledDimensions(float desiredScale) const override;
- bool onDimensionsSupported(const SkISize&) override;
-
- Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*, int*)
- override;
- SkEncodedFormat onGetEncodedFormat() const override {
- return fCodec->getEncodedFormat();
- }
-
- bool onReallyHasAlpha() const override {
- return fCodec->reallyHasAlpha();
- }
-
- uint32_t onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const override;
-
- SkScanlineOrder onGetScanlineOrder() const override;
-
-private:
-
- SkAutoTDelete<SkCodec> fCodec;
-
- explicit SkScaledCodec(SkCodec*);
-
- typedef SkCodec INHERITED;
-};
-#endif // SkScaledCodec_DEFINED
diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp
new file mode 100644
index 0000000000..43ed90f6c0
--- /dev/null
+++ b/src/codec/SkAndroidCodec.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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 "SkAndroidCodec.h"
+#include "SkCodec.h"
+#include "SkCodecPriv.h"
+#include "SkSampledCodec.h"
+#include "SkWebpAdapterCodec.h"
+
+static bool is_valid_sample_size(int sampleSize) {
+ // FIXME: As Leon has mentioned elsewhere, surely there is also a maximum sampleSize?
+ return sampleSize > 0;
+}
+
+SkAndroidCodec::SkAndroidCodec(const SkImageInfo& info)
+ : fInfo(info)
+{}
+
+SkAndroidCodec* SkAndroidCodec::NewFromStream(SkStream* stream) {
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream));
+ if (nullptr == codec) {
+ return nullptr;
+ }
+
+ switch (codec->getEncodedFormat()) {
+ case kWEBP_SkEncodedFormat:
+ return new SkWebpAdapterCodec((SkWebpCodec*) codec.detach());
+ case kPNG_SkEncodedFormat:
+ case kJPEG_SkEncodedFormat:
+ return new SkSampledCodec(codec.detach());
+ default:
+ // FIXME: SkSampledCodec is temporarily disabled for other formats
+ // while focusing on the formats that are supported by
+ // BitmapRegionDecoder.
+ return nullptr;
+ }
+}
+
+SkAndroidCodec* SkAndroidCodec::NewFromData(SkData* data) {
+ if (!data) {
+ return nullptr;
+ }
+
+ return NewFromStream(new SkMemoryStream(data));
+}
+
+SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
+ if (!is_valid_sample_size(sampleSize)) {
+ return SkISize::Make(0, 0);
+ }
+
+ return this->onGetSampledDimensions(sampleSize);
+}
+
+bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const {
+ if (!desiredSubset || !is_valid_subset(*desiredSubset, fInfo.dimensions())) {
+ return false;
+ }
+
+ return this->onGetSupportedSubset(desiredSubset);
+}
+
+SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const {
+ if (!is_valid_sample_size(sampleSize)) {
+ return SkISize::Make(0, 0);
+ }
+
+ // We require that the input subset is a subset that is supported by SkAndroidCodec.
+ // We test this by calling getSupportedSubset() and verifying that no modifications
+ // are made to the subset.
+ SkIRect copySubset = subset;
+ if (!this->getSupportedSubset(&copySubset) || copySubset != subset) {
+ return SkISize::Make(0, 0);
+ }
+
+ // If the subset is the entire image, for consistency, use onGetSampledDimensions().
+ if (fInfo.dimensions() == subset.size()) {
+ return onGetSampledDimensions(sampleSize);
+ }
+
+ // This should perhaps call a virtual function, but currently both of our subclasses
+ // want the same implementation.
+ return SkISize::Make(get_scaled_dimension(subset.width(), sampleSize),
+ get_scaled_dimension(subset.height(), sampleSize));
+}
+
+SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
+ size_t rowBytes, AndroidOptions* options) {
+ if (!pixels) {
+ return SkCodec::kInvalidParameters;
+ }
+ if (rowBytes < info.minRowBytes()) {
+ return SkCodec::kInvalidParameters;
+ }
+
+ AndroidOptions defaultOptions;
+ if (!options) {
+ options = &defaultOptions;
+ } else if (options->fSubset) {
+ if (!is_valid_subset(*options->fSubset, fInfo.dimensions())) {
+ return SkCodec::kInvalidParameters;
+ }
+ }
+
+ return this->onGetAndroidPixels(info, pixels, rowBytes, *options);
+}
+
+SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
+ size_t rowBytes) {
+ return this->getAndroidPixels(info, pixels, rowBytes, nullptr);
+}
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index 0222c8ce9f..191c2ad800 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -11,7 +11,6 @@
#include "SkBmpStandardCodec.h"
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
-#include "SkScaledCodec.h"
#include "SkStream.h"
/*
diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp
index 67e262a704..32580c3fa5 100644
--- a/src/codec/SkBmpRLECodec.cpp
+++ b/src/codec/SkBmpRLECodec.cpp
@@ -8,7 +8,6 @@
#include "SkBmpRLECodec.h"
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
-#include "SkScaledCodec.h"
#include "SkStream.h"
/*
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
index 0442625dd2..1b6723fd4a 100644
--- a/src/codec/SkCodecPriv.h
+++ b/src/codec/SkCodecPriv.h
@@ -31,9 +31,19 @@
#define COMPUTE_RESULT_ALPHA \
SkSwizzler::GetResult(zeroAlpha, maxAlpha);
+// FIXME: Consider sharing with dm, nanbench, and tools.
+inline float get_scale_from_sample_size(int sampleSize) {
+ return 1.0f / ((float) sampleSize);
+}
+
+inline bool is_valid_subset(const SkIRect& subset, const SkISize& imageDims) {
+ return SkIRect::MakeSize(imageDims).contains(subset);
+}
+
/*
* returns a scaled dimension based on the original dimension and the sampleSize
* NOTE: we round down here for scaled dimension to match the behavior of SkImageDecoder
+ * FIXME: I think we should call this get_sampled_dimension().
*/
inline int get_scaled_dimension(int srcDimension, int sampleSize) {
if (sampleSize > srcDimension) {
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index f4d75561d8..54b57870a3 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -11,7 +11,6 @@
#include "SkColorTable.h"
#include "SkBitmap.h"
#include "SkMath.h"
-#include "SkScaledCodec.h"
#include "SkSize.h"
#include "SkStream.h"
#include "SkSwizzler.h"
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index cca71c1cad..9975748cf4 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -11,7 +11,6 @@
#include "SkJpegUtility_codec.h"
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
-#include "SkScaledCodec.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkTypes.h"
diff --git a/src/codec/SkMaskSwizzler.cpp b/src/codec/SkMaskSwizzler.cpp
index 958df61cc7..e5facc1f0c 100644
--- a/src/codec/SkMaskSwizzler.cpp
+++ b/src/codec/SkMaskSwizzler.cpp
@@ -8,7 +8,6 @@
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
#include "SkMaskSwizzler.h"
-#include "SkScaledCodec.h"
static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_opaque(
void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
diff --git a/src/codec/SkSampledCodec.h b/src/codec/SkSampledCodec.h
new file mode 100644
index 0000000000..277a50f0ee
--- /dev/null
+++ b/src/codec/SkSampledCodec.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkSampledCodec_DEFINED
+#define SkSampledCodec_DEFINED
+
+#include "SkAndroidCodec.h"
+#include "SkCodec.h"
+
+/**
+ * This class implements the functionality of SkAndroidCodec. Scaling will
+ * be provided by sampling if it cannot be provided by fCodec.
+ */
+class SkSampledCodec : public SkAndroidCodec {
+public:
+
+ explicit SkSampledCodec(SkCodec*);
+
+ virtual ~SkSampledCodec() {}
+
+protected:
+
+ SkEncodedFormat onGetEncodedFormat() const override { return fCodec->getEncodedFormat(); };
+
+ SkISize onGetSampledDimensions(int sampleSize) const override;
+
+ bool onGetSupportedSubset(SkIRect* desiredSubset) const override { return true; }
+
+ SkCodec::Result onGetAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
+ AndroidOptions& options) override;
+
+private:
+
+ /**
+ * This fulfills the same contract as onGetAndroidPixels().
+ *
+ * We call this function from onGetAndroidPixels() if we have determined
+ * that fCodec does not support the requested scale, and we need to
+ * provide the scale by sampling.
+ */
+ SkCodec::Result sampledDecode(const SkImageInfo& info, void* pixels, size_t rowBytes,
+ AndroidOptions& options);
+
+ SkAutoTDelete<SkCodec> fCodec;
+
+ typedef SkAndroidCodec INHERITED;
+};
+#endif // SkSampledCodec_DEFINED
diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp
index 6b50a09f26..c2be2d6ede 100644
--- a/src/codec/SkScaledCodec.cpp
+++ b/src/codec/SkScaledCodec.cpp
@@ -5,344 +5,210 @@
* found in the LICENSE file.
*/
+#include "SkCodec.h"
#include "SkCodecPriv.h"
-#include "SkScaledCodec.h"
-#include "SkStream.h"
-#include "SkWebpCodec.h"
+#include "SkSampledCodec.h"
+// FIXME: Rename this file to SkSampledCodec.cpp
-SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) {
- SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream));
- if (nullptr == codec) {
- return nullptr;
- }
-
- switch (codec->getEncodedFormat()) {
- case kWEBP_SkEncodedFormat:
- // Webp codec supports scaling and subsetting natively
- return codec.detach();
- case kPNG_SkEncodedFormat:
- case kJPEG_SkEncodedFormat:
- // wrap in new SkScaledCodec
- return new SkScaledCodec(codec.detach());
- default:
- // FIXME: SkScaledCodec is temporarily disabled for other formats
- // while focusing on the formats that are supported by
- // BitmapRegionDecoder.
- return nullptr;
- }
-}
-
-SkCodec* SkScaledCodec::NewFromData(SkData* data) {
- if (!data) {
- return nullptr;
- }
- return NewFromStream(new SkMemoryStream(data));
-}
-
-SkScaledCodec::SkScaledCodec(SkCodec* codec)
- : INHERITED(codec->getInfo(), nullptr)
+SkSampledCodec::SkSampledCodec(SkCodec* codec)
+ : INHERITED(codec->getInfo())
, fCodec(codec)
{}
-SkScaledCodec::~SkScaledCodec() {}
-
-bool SkScaledCodec::onRewind() {
- return fCodec->onRewind();
-}
-
-static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& nativeDims,
- const SkISize& scaledCodecDims, float desiredScale) {
- if (nativeDims == scaledCodecDims) {
- // does not matter which to return if equal. Return here to skip below calculations
- return nativeDims;
+SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const {
+ // Fast path for when we are not scaling.
+ if (1 == sampleSize) {
+ return fCodec->getInfo().dimensions();
}
- float idealWidth = origDims.width() * desiredScale;
- float idealHeight = origDims.height() * desiredScale;
- // calculate difference between native dimensions and ideal dimensions
- float nativeWDiff = SkTAbs(idealWidth - nativeDims.width());
- float nativeHDiff = SkTAbs(idealHeight - nativeDims.height());
- float nativeDiff = nativeWDiff + nativeHDiff;
+ 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 (nativeWDiff < 1.0f && nativeHDiff < 1.0f) {
- return nativeDims;
+ if (widthDiff < 1.0f && heightDiff < 1.0f) {
+ return nativeSize;
}
- // calculate difference between scaledCodec dimensions and ideal dimensions
- float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width());
- float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height());
- float scaledCodecDiff = scaledCodecWDiff + scaledCodecHDiff;
-
- // return dimensions closest to ideal dimensions.
- // If the differences are equal, return nativeDims, as native scaling is more efficient.
- return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims;
+ // Provide the scaling by sampling.
+ return SkISize::Make(get_scaled_dimension(width, sampleSize),
+ get_scaled_dimension(height, sampleSize));
}
-/*
- * Return a valid set of output dimensions for this decoder, given an input scale
- */
-SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const {
- SkISize nativeDimensions = fCodec->getScaledDimensions(desiredScale);
- // support scaling down by integer numbers. Ex: 1/2, 1/3, 1/4 ...
- SkISize scaledCodecDimensions;
- if (desiredScale > 0.5f) {
- // sampleSize = 1
- scaledCodecDimensions = fCodec->getInfo().dimensions();
- }
- // sampleSize determines the step size between samples
- // Ex: sampleSize = 2, sample every second pixel in x and y directions
- int sampleSize = int ((1.0f / desiredScale) + 0.5f);
-
- int scaledWidth = get_scaled_dimension(this->getInfo().width(), sampleSize);
- int scaledHeight = get_scaled_dimension(this->getInfo().height(), sampleSize);
-
- // Return the calculated output dimensions for the given scale
- scaledCodecDimensions = SkISize::Make(scaledWidth, scaledHeight);
-
- return best_scaled_dimensions(this->getInfo().dimensions(), nativeDimensions,
- scaledCodecDimensions, desiredScale);
-}
-
-// check if scaling to dstInfo size from srcInfo size using sampleSize is possible
-static bool scaling_supported(const SkISize& dstDim, const SkISize& srcDim,
- int* sampleX, int* sampleY) {
- SkScaledCodec::ComputeSampleSize(dstDim, srcDim, sampleX, sampleY);
- const int dstWidth = dstDim.width();
- const int dstHeight = dstDim.height();
- const int srcWidth = srcDim.width();
- const int srcHeight = srcDim.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
- if (get_scaled_dimension(srcWidth, *sampleX) != dstWidth) {
- return false;
- }
- // check that src height is scaled down by an integer value
- if (get_scaled_dimension(srcHeight, *sampleY) != 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 (*sampleX != *sampleY){
- if (1 != dstWidth && 1 != dstHeight) {
- return false;
+SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels,
+ size_t rowBytes, AndroidOptions& options) {
+ // Create an Options struct for the codec.
+ SkCodec::Options codecOptions;
+ codecOptions.fZeroInitialized = options.fZeroInitialized;
+
+ SkIRect* subset = options.fSubset;
+ if (!subset || subset->size() == fCodec->getInfo().dimensions()) {
+ if (fCodec->dimensionsSupported(info.dimensions())) {
+ return fCodec->getPixels(info, pixels, rowBytes, &codecOptions, options.fColorPtr,
+ options.fColorCount);
}
- }
- return true;
-}
-bool SkScaledCodec::onDimensionsSupported(const SkISize& dim) {
- // Check with fCodec first. No need to call the non-virtual version, which
- // just checks if it matches the original, since a match means this method
- // will not be called.
- if (fCodec->onDimensionsSupported(dim)) {
- return true;
+ // If the native codec does not support the requested scale, scale by sampling.
+ return this->sampledDecode(info, pixels, rowBytes, options);
}
- // FIXME: These variables are unused, but are needed by scaling_supported.
- // This class could also cache these values, and avoid calling this in
- // onGetPixels (since getPixels already calls it).
- int sampleX;
- int sampleY;
- return scaling_supported(dim, this->getInfo().dimensions(), &sampleX, &sampleY);
-}
-
-// calculates sampleSize in x and y direction
-void SkScaledCodec::ComputeSampleSize(const SkISize& dstDim, const SkISize& srcDim,
- int* sampleXPtr, int* sampleYPtr) {
- int srcWidth = srcDim.width();
- int dstWidth = dstDim.width();
- int srcHeight = srcDim.height();
- int dstHeight = dstDim.height();
-
- int sampleX = srcWidth / dstWidth;
- int sampleY = srcHeight / dstHeight;
-
- // only support down sampling, not up sampling
- SkASSERT(dstWidth <= srcWidth);
- SkASSERT(dstHeight <= srcHeight);
+ // We are performing a subset decode.
+ int sampleSize = options.fSampleSize;
+ SkISize scaledSize = this->onGetSampledDimensions(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);
+ }
- // 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.
+ // Calculate the scaled subset bounds.
+ int scaledSubsetX = subset->x() / sampleSize;
+ int scaledSubsetY = subset->y() / sampleSize;
+ int scaledSubsetWidth = info.width();
+ int scaledSubsetHeight = info.height();
- if (sampleX != sampleY){
- if (1 != dstWidth && 1 != dstHeight) {
+ // Start the scanline decode.
+ SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
+ scaledSize.height());
+ codecOptions.fSubset = &scanlineSubset;
+ SkCodec::Result result = fCodec->startScanlineDecode(info.makeWH(scaledSize.width(),
+ scaledSize.height()), &codecOptions, options.fColorPtr, options.fColorCount);
+ if (SkCodec::kSuccess != result) {
+ return result;
+ }
- // rounding during onGetScaledDimensions can cause different sampleSizes
- // Ex: srcWidth = 79, srcHeight = 20, sampleSize = 10
- // dstWidth = 7, dstHeight = 2, sampleX = 79/7 = 11, sampleY = 20/2 = 10
- // correct for this rounding by comparing width to sampleY and height to sampleX
+ // At this point, we are only concerned with subsetting. Either no scale was
+ // requested, or the fCodec is handling the scale.
+ switch (fCodec->getScanlineOrder()) {
+ case SkCodec::kTopDown_SkScanlineOrder:
+ case SkCodec::kNone_SkScanlineOrder: {
+ if (!fCodec->skipScanlines(scaledSubsetY)) {
+ fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
+ scaledSubsetHeight, 0);
+ return SkCodec::kIncompleteInput;
+ }
- if (get_scaled_dimension(srcWidth, sampleY) == dstWidth) {
- sampleX = sampleY;
- } else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) {
- sampleY = sampleX;
+ int decodedLines = fCodec->getScanlines(pixels, scaledSubsetHeight, rowBytes);
+ if (decodedLines != scaledSubsetHeight) {
+ return SkCodec::kIncompleteInput;
}
+ return SkCodec::kSuccess;
}
- }
-
- if (sampleXPtr) {
- *sampleXPtr = sampleX;
- }
- if (sampleYPtr) {
- *sampleYPtr = sampleY;
+ default:
+ SkASSERT(false);
+ return SkCodec::kUnimplemented;
}
}
-// TODO: Implement subsetting in onGetPixels which works when and when not sampling
-SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
- size_t rowBytes, const Options& options,
- SkPMColor ctable[], int* ctableCount,
- int* rowsDecoded) {
+SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels,
+ size_t rowBytes, AndroidOptions& options) {
+ // Create options struct for the codec.
+ SkCodec::Options sampledOptions;
+ sampledOptions.fZeroInitialized = options.fZeroInitialized;
+ // Check if there is a subset.
+ SkIRect subset;
+ int subsetY = 0;
+ int subsetWidth = fCodec->getInfo().width();
+ int subsetHeight = fCodec->getInfo().height();
if (options.fSubset) {
- // Subsets are not supported.
- return kUnimplemented;
- }
-
- if (fCodec->dimensionsSupported(requestedInfo.dimensions())) {
- // Make sure that the parent class does not fill on an incomplete decode, since
- // fCodec will take care of filling the uninitialized lines.
- *rowsDecoded = requestedInfo.height();
- return fCodec->getPixels(requestedInfo, dst, rowBytes, &options, ctable, ctableCount);
- }
-
- // scaling requested
- int sampleX;
- int sampleY;
- if (!scaling_supported(requestedInfo.dimensions(), fCodec->getInfo().dimensions(),
- &sampleX, &sampleY)) {
- // onDimensionsSupported would have returned false, meaning we should never reach here.
- SkASSERT(false);
- return kInvalidScale;
- }
-
- // set first sample pixel in y direction
- const int Y0 = get_start_coord(sampleY);
-
- const int dstHeight = requestedInfo.height();
- const int srcWidth = fCodec->getInfo().width();
- const int srcHeight = fCodec->getInfo().height();
-
- const SkImageInfo info = requestedInfo.makeWH(srcWidth, srcHeight);
-
- Result result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount);
-
- if (kSuccess != result) {
+ // We will need to know about subsetting in the y-dimension in order to use the
+ // scanline decoder.
+ SkIRect* subsetPtr = options.fSubset;
+ subsetY = subsetPtr->y();
+ subsetWidth = subsetPtr->width();
+ subsetHeight = subsetPtr->height();
+
+ // The scanline decoder only needs to be aware of subsetting in the x-dimension.
+ subset.setXYWH(subsetPtr->x(), 0, subsetWidth, fCodec->getInfo().height());
+ sampledOptions.fSubset = &subset;
+ }
+
+ // Start the scanline decode.
+ SkCodec::Result result = fCodec->startScanlineDecode(
+ info.makeWH(fCodec->getInfo().width(), fCodec->getInfo().height()), &sampledOptions,
+ options.fColorPtr, options.fColorCount);
+ if (SkCodec::kSuccess != result) {
return result;
}
SkSampler* sampler = fCodec->getSampler(true);
if (!sampler) {
- return kUnimplemented;
+ return SkCodec::kUnimplemented;
}
- if (sampler->setSampleX(sampleX) != requestedInfo.width()) {
- return kInvalidScale;
+ // Since we guarantee that output dimensions are always at least one (even if the sampleSize
+ // is greater than a given dimension), the input sampleSize is not always the sampleSize that
+ // we use in practice.
+ const int sampleX = subsetWidth / info.width();
+ const int sampleY = subsetHeight / info.height();
+ if (sampler->setSampleX(sampleX) != info.width()) {
+ return SkCodec::kInvalidScale;
+ }
+ if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
+ return SkCodec::kInvalidScale;
}
+ const int samplingOffsetY = get_start_coord(sampleY);
+ const int startY = samplingOffsetY + subsetY;
+ int dstHeight = info.height();
switch(fCodec->getScanlineOrder()) {
case SkCodec::kTopDown_SkScanlineOrder: {
- if (!fCodec->skipScanlines(Y0)) {
- *rowsDecoded = 0;
- return kIncompleteInput;
+ if (!fCodec->skipScanlines(startY)) {
+ fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
+ dstHeight, 0);
+ return SkCodec::kIncompleteInput;
}
+ void* pixelPtr = pixels;
for (int y = 0; y < dstHeight; y++) {
- if (1 != fCodec->getScanlines(dst, 1, rowBytes)) {
- // The failed call to getScanlines() will take care of
- // filling the failed row, so we indicate that we have
- // decoded (y + 1) rows.
- *rowsDecoded = y + 1;
- return kIncompleteInput;
- }
- if (y < dstHeight - 1) {
- if (!fCodec->skipScanlines(sampleY - 1)) {
- *rowsDecoded = y + 1;
- return kIncompleteInput;
- }
- }
- dst = SkTAddOffset<void>(dst, rowBytes);
- }
- return kSuccess;
- }
- case SkCodec::kBottomUp_SkScanlineOrder:
- case SkCodec::kOutOfOrder_SkScanlineOrder: {
- Result result = kSuccess;
- int y;
- for (y = 0; y < srcHeight; y++) {
- int srcY = fCodec->nextScanline();
- if (is_coord_necessary(srcY, sampleY, dstHeight)) {
- void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_coord(srcY, sampleY));
- if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) {
- result = kIncompleteInput;
- break;
- }
- } else {
- if (!fCodec->skipScanlines(1)) {
- result = kIncompleteInput;
- break;
- }
+ if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) {
+ fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
+ dstHeight, y + 1);
+ return SkCodec::kIncompleteInput;
}
- }
-
- // We handle filling uninitialized memory here instead of in the parent class.
- // The parent class does not know that we are sampling.
- if (kIncompleteInput == result) {
- const uint32_t fillValue = fCodec->getFillValue(requestedInfo.colorType(),
- requestedInfo.alphaType());
- for (; y < srcHeight; y++) {
- int srcY = fCodec->outputScanline(y);
- if (is_coord_necessary(srcY, sampleY, dstHeight)) {
- void* dstRow = SkTAddOffset<void>(dst,
- rowBytes * get_dst_coord(srcY, sampleY));
- SkSampler::Fill(requestedInfo.makeWH(requestedInfo.width(), 1), dstRow,
- rowBytes, fillValue, options.fZeroInitialized);
- }
+ int linesToSkip = SkTMin(sampleY - 1, dstHeight - y - 1);
+ if (!fCodec->skipScanlines(linesToSkip)) {
+ fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
+ dstHeight, y + 1);
+ return SkCodec::kIncompleteInput;
}
- *rowsDecoded = dstHeight;
+ pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
}
- return result;
+ return SkCodec::kSuccess;
}
case SkCodec::kNone_SkScanlineOrder: {
- SkAutoMalloc storage(srcHeight * rowBytes);
+ const int linesNeeded = subsetHeight - samplingOffsetY;
+ SkAutoMalloc storage(linesNeeded * rowBytes);
uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
- int scanlines = fCodec->getScanlines(storagePtr, srcHeight, rowBytes);
- storagePtr += Y0 * rowBytes;
- scanlines -= Y0;
- int y = 0;
- while (y < dstHeight && scanlines > 0) {
- memcpy(dst, storagePtr, rowBytes);
+
+ if (!fCodec->skipScanlines(startY)) {
+ fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
+ dstHeight, 0);
+ return SkCodec::kIncompleteInput;
+ }
+ int scanlines = fCodec->getScanlines(storagePtr, linesNeeded, rowBytes);
+
+ for (int y = 0; y < dstHeight; y++) {
+ memcpy(pixels, storagePtr, info.minRowBytes());
storagePtr += sampleY * rowBytes;
- dst = SkTAddOffset<void>(dst, rowBytes);
- scanlines -= sampleY;
- y++;
+ pixels = SkTAddOffset<void>(pixels, rowBytes);
}
- if (y < dstHeight) {
+
+ if (scanlines < dstHeight) {
// fCodec has already handled filling uninitialized memory.
- *rowsDecoded = dstHeight;
- return kIncompleteInput;
+ return SkCodec::kIncompleteInput;
}
- return kSuccess;
+ return SkCodec::kSuccess;
}
default:
SkASSERT(false);
- return kUnimplemented;
+ return SkCodec::kUnimplemented;
}
}
-
-uint32_t SkScaledCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const {
- return fCodec->onGetFillValue(colorType, alphaType);
-}
-
-SkCodec::SkScanlineOrder SkScaledCodec::onGetScanlineOrder() const {
- return fCodec->onGetScanlineOrder();
-}
diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp
index de69124340..4eea8799c5 100644
--- a/src/codec/SkSwizzler.cpp
+++ b/src/codec/SkSwizzler.cpp
@@ -7,7 +7,6 @@
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
-#include "SkScaledCodec.h"
#include "SkSwizzler.h"
#include "SkTemplates.h"
diff --git a/src/codec/SkWebpAdapterCodec.cpp b/src/codec/SkWebpAdapterCodec.cpp
new file mode 100644
index 0000000000..0804e748a2
--- /dev/null
+++ b/src/codec/SkWebpAdapterCodec.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "SkCodec.h"
+#include "SkCodecPriv.h"
+#include "SkWebpAdapterCodec.h"
+
+SkWebpAdapterCodec::SkWebpAdapterCodec(SkWebpCodec* codec)
+ : INHERITED(codec->getInfo())
+ , fCodec(codec)
+{}
+
+SkISize SkWebpAdapterCodec::onGetSampledDimensions(int sampleSize) const {
+ float scale = get_scale_from_sample_size(sampleSize);
+ return fCodec->getScaledDimensions(scale);
+}
+
+bool SkWebpAdapterCodec::onGetSupportedSubset(SkIRect* desiredSubset) const {
+ return fCodec->getValidSubset(desiredSubset);
+}
+
+SkCodec::Result SkWebpAdapterCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels,
+ size_t rowBytes, AndroidOptions& options) {
+ // SkWebpCodec will support pretty much any dimensions that we provide, but we want
+ // to be stricter about the type of scaling that we allow, so we will add an extra
+ // check here.
+ SkISize supportedSize;
+ if (!options.fSubset) {
+ supportedSize = this->onGetSampledDimensions(options.fSampleSize);
+ } else {
+ supportedSize = this->getSampledSubsetDimensions(options.fSampleSize, *options.fSubset);
+ }
+ if (supportedSize != info.dimensions()) {
+ return SkCodec::kInvalidParameters;
+ }
+
+ SkCodec::Options codecOptions;
+ codecOptions.fZeroInitialized = options.fZeroInitialized;
+ codecOptions.fSubset = options.fSubset;
+ return fCodec->getPixels(info, pixels, rowBytes, &codecOptions, options.fColorPtr,
+ options.fColorCount);
+}
diff --git a/src/codec/SkWebpAdapterCodec.h b/src/codec/SkWebpAdapterCodec.h
new file mode 100644
index 0000000000..b48f39bb60
--- /dev/null
+++ b/src/codec/SkWebpAdapterCodec.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkWebpAdapterCodec_DEFINED
+#define SkWebpAdapterCodec_DEFINED
+
+#include "SkAndroidCodec.h"
+#include "SkWebpCodec.h"
+
+/**
+ * This class implements the functionality of SkAndroidCodec. It uses an
+ * SkWebpCodec.
+ */
+class SkWebpAdapterCodec : public SkAndroidCodec {
+public:
+
+ explicit SkWebpAdapterCodec(SkWebpCodec*);
+
+ virtual ~SkWebpAdapterCodec() {}
+
+protected:
+
+ SkEncodedFormat onGetEncodedFormat() const override { return kWEBP_SkEncodedFormat; };
+
+ SkISize onGetSampledDimensions(int sampleSize) const override;
+
+ bool onGetSupportedSubset(SkIRect* desiredSubset) const override;
+
+ SkCodec::Result onGetAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
+ AndroidOptions& options) override;
+
+private:
+
+ SkAutoTDelete<SkWebpCodec> fCodec;
+
+ typedef SkAndroidCodec INHERITED;
+};
+#endif // SkWebpAdapterCodec_DEFINED
diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp
index 5ef1f8a86e..b2743bfa1f 100644
--- a/tests/CodexTest.cpp
+++ b/tests/CodexTest.cpp
@@ -6,12 +6,12 @@
*/
#include "Resources.h"
+#include "SkAndroidCodec.h"
#include "SkBitmap.h"
#include "SkCodec.h"
#include "SkData.h"
#include "SkMD5.h"
#include "SkRandom.h"
-#include "SkScaledCodec.h"
#include "Test.h"
static SkStreamAsset* resource(const char path[]) {
@@ -63,6 +63,20 @@ static void test_info(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo&
}
}
+static void test_android_info(skiatest::Reporter* r, SkAndroidCodec* codec, const SkImageInfo& info,
+ SkCodec::Result expectedResult, const SkMD5::Digest* goodDigest) {
+ SkBitmap bm;
+ bm.allocPixels(info);
+ SkAutoLockPixels autoLockPixels(bm);
+
+ SkCodec::Result result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes());
+ REPORTER_ASSERT(r, result == expectedResult);
+
+ if (goodDigest) {
+ compare_to_good_digest(r, *goodDigest, bm);
+ }
+}
+
SkIRect generate_random_subset(SkRandom* rand, int w, int h) {
SkIRect rect;
do {
@@ -128,6 +142,59 @@ static void test_codec(skiatest::Reporter* r, SkCodec* codec, SkBitmap& bm, cons
}
}
+static void test_android_codec(skiatest::Reporter* r, SkAndroidCodec* codec, SkBitmap& bm,
+ const SkImageInfo& info, const SkISize& size, bool supports565,
+ SkCodec::Result expectedResult, SkMD5::Digest* digest, const SkMD5::Digest* goodDigest) {
+
+ REPORTER_ASSERT(r, info.dimensions() == size);
+ bm.allocPixels(info);
+ SkAutoLockPixels autoLockPixels(bm);
+
+ SkCodec::Result result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes());
+ REPORTER_ASSERT(r, result == expectedResult);
+
+ md5(bm, digest);
+ if (goodDigest) {
+ REPORTER_ASSERT(r, *digest == *goodDigest);
+ }
+
+ {
+ // Test decoding to 565
+ SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType);
+ SkCodec::Result expected565 = (supports565 && info.alphaType() == kOpaque_SkAlphaType) ?
+ expectedResult : SkCodec::kInvalidConversion;
+ test_android_info(r, codec, info565, expected565, nullptr);
+ }
+
+ // Verify that re-decoding gives the same result. It is interesting to check this after
+ // a decode to 565, since choosing to decode to 565 may result in some of the decode
+ // options being modified. These options should return to their defaults on another
+ // decode to kN32, so the new digest should match the old digest.
+ test_android_info(r, codec, info, expectedResult, digest);
+
+ {
+ // Check alpha type conversions
+ if (info.alphaType() == kOpaque_SkAlphaType) {
+ test_android_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType),
+ SkCodec::kInvalidConversion, nullptr);
+ test_android_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType),
+ SkCodec::kInvalidConversion, nullptr);
+ } else {
+ // Decoding to opaque should fail
+ test_android_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType),
+ SkCodec::kInvalidConversion, nullptr);
+ SkAlphaType otherAt = info.alphaType();
+ if (kPremul_SkAlphaType == otherAt) {
+ otherAt = kUnpremul_SkAlphaType;
+ } else {
+ otherAt = kPremul_SkAlphaType;
+ }
+ // The other non-opaque alpha type should always succeed, but not match.
+ test_android_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nullptr);
+ }
+ }
+}
+
// FIXME: SkScaledCodec is currently only supported for types used by BRD
// skbug.com/4428
static bool supports_scaled_codec(const char path[]) {
@@ -288,13 +355,13 @@ static void check(skiatest::Reporter* r,
return;
}
- SkAutoTDelete<SkCodec> codec(nullptr);
+ SkAutoTDelete<SkAndroidCodec> codec(nullptr);
if (isIncomplete) {
size_t size = stream->getLength();
SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3)));
- codec.reset(SkScaledCodec::NewFromData(data));
+ codec.reset(SkAndroidCodec::NewFromData(data));
} else {
- codec.reset(SkScaledCodec::NewFromStream(stream.detach()));
+ codec.reset(SkAndroidCodec::NewFromStream(stream.detach()));
}
if (!codec) {
ERRORF(r, "Unable to decode '%s'", path);
@@ -303,8 +370,8 @@ static void check(skiatest::Reporter* r,
SkBitmap bm;
SkMD5::Digest scaledCodecDigest;
- test_codec(r, codec, bm, info, size, supports565, expectedResult, &scaledCodecDigest,
- &codecDigest);
+ test_android_codec(r, codec, bm, info, size, supports565, expectedResult,
+ &scaledCodecDigest, &codecDigest);
}
// If we've just tested incomplete decodes, let's run the same test again on full decodes.
@@ -463,8 +530,9 @@ static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_
SkCodec* codec = SkCodec::NewFromStream(new SkMemoryStream(stream, len, false));
REPORTER_ASSERT(r, !codec);
- codec = SkScaledCodec::NewFromStream(new SkMemoryStream(stream, len, false));
- REPORTER_ASSERT(r, !codec);
+ SkAndroidCodec* androidCodec =
+ SkAndroidCodec::NewFromStream(new SkMemoryStream(stream, len, false));
+ REPORTER_ASSERT(r, !androidCodec);
}
// Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream,
@@ -496,8 +564,8 @@ DEF_TEST(Codec_null, r) {
SkCodec* codec = SkCodec::NewFromStream(nullptr);
REPORTER_ASSERT(r, !codec);
- codec = SkScaledCodec::NewFromStream(nullptr);
- REPORTER_ASSERT(r, !codec);
+ SkAndroidCodec* androidCodec = SkAndroidCodec::NewFromStream(nullptr);
+ REPORTER_ASSERT(r, !androidCodec);
}
static void test_dimensions(skiatest::Reporter* r, const char path[]) {
@@ -507,16 +575,16 @@ static void test_dimensions(skiatest::Reporter* r, const char path[]) {
SkDebugf("Missing resource '%s'\n", path);
return;
}
- SkAutoTDelete<SkCodec> codec(SkScaledCodec::NewFromStream(stream.detach()));
+ SkAutoTDelete<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.detach()));
if (!codec) {
ERRORF(r, "Unable to create codec '%s'", path);
return;
}
// Check that the decode is successful for a variety of scales
- for (float scale = 0.05f; scale < 2.0f; scale += 0.05f) {
+ for (int sampleSize = 1; sampleSize < 10; sampleSize++) {
// Scale the output dimensions
- SkISize scaledDims = codec->getScaledDimensions(scale);
+ SkISize scaledDims = codec->getSampledDimensions(sampleSize);
SkImageInfo scaledInfo = codec->getInfo()
.makeWH(scaledDims.width(), scaledDims.height())
.makeColorType(kN32_SkColorType);
@@ -526,8 +594,10 @@ static void test_dimensions(skiatest::Reporter* r, const char path[]) {
size_t totalBytes = scaledInfo.getSafeSize(rowBytes);
SkAutoTMalloc<SkPMColor> pixels(totalBytes);
+ SkAndroidCodec::AndroidOptions options;
+ options.fSampleSize = sampleSize;
SkCodec::Result result =
- codec->getPixels(scaledInfo, pixels.get(), rowBytes, nullptr, nullptr, nullptr);
+ codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &options);
REPORTER_ASSERT(r, SkCodec::kSuccess == result);
}
}