aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar msarett <msarett@google.com>2015-10-22 07:29:19 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-10-22 07:29:19 -0700
commit26ad17b8f872fc8bc18df7f49067edbd8b9799e8 (patch)
treeff8699a8e9614cc328011d172fdb8cd6b2fbdae2
parented0935a28a29af7d3b16ac8d9365f291a335c6bd (diff)
Implementation of SkBitmapRegionDecoder using SkAndroidCodec
Includes testing in DM and nanobench BUG=skia: Review URL: https://codereview.chromium.org/1402863002
-rw-r--r--bench/BitmapRegionDecoderBench.cpp6
-rw-r--r--bench/nanobench.cpp8
-rw-r--r--dm/DM.cpp25
-rw-r--r--dm/DMSrcSink.cpp3
-rw-r--r--gyp/tools.gyp1
-rw-r--r--tools/SkBitmapRegionCanvas.cpp95
-rw-r--r--tools/SkBitmapRegionCodec.cpp143
-rw-r--r--tools/SkBitmapRegionCodec.h41
-rw-r--r--tools/SkBitmapRegionDecoderInterface.cpp18
-rw-r--r--tools/SkBitmapRegionDecoderInterface.h13
-rw-r--r--tools/SkCodecTools.h48
11 files changed, 305 insertions, 96 deletions
diff --git a/bench/BitmapRegionDecoderBench.cpp b/bench/BitmapRegionDecoderBench.cpp
index 0398b58420..092693619b 100644
--- a/bench/BitmapRegionDecoderBench.cpp
+++ b/bench/BitmapRegionDecoderBench.cpp
@@ -30,6 +30,9 @@ BitmapRegionDecoderBench::BitmapRegionDecoderBench(const char* baseName, SkData*
case SkBitmapRegionDecoderInterface::kCanvas_Strategy:
strategyName = "Canvas";
break;
+ case SkBitmapRegionDecoderInterface::kAndroidCodec_Strategy:
+ strategyName = "AndroidCodec";
+ break;
default:
SkASSERT(false);
strategyName = "";
@@ -54,8 +57,7 @@ bool BitmapRegionDecoderBench::isSuitableFor(Backend backend) {
}
void BitmapRegionDecoderBench::onDelayedSetup() {
- SkStreamRewindable* stream = new SkMemoryStream(fData);
- fBRD.reset(SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(stream, fStrategy));
+ fBRD.reset(SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(fData, fStrategy));
}
void BitmapRegionDecoderBench::onDraw(int n, SkCanvas* canvas) {
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index 16523545fa..054fbb27b1 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -596,9 +596,8 @@ static bool valid_subset_bench(const SkString& path, SkColorType colorType, bool
static bool valid_brd_bench(SkData* encoded, SkBitmapRegionDecoderInterface::Strategy strategy,
SkColorType colorType, uint32_t sampleSize, uint32_t minOutputSize, int* width,
int* height) {
- SkStreamRewindable* stream = new SkMemoryStream(encoded);
SkAutoTDelete<SkBitmapRegionDecoderInterface> brd(
- SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(stream, strategy));
+ SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(encoded, strategy));
if (nullptr == brd.get()) {
// This is indicates that subset decoding is not supported for a particular image format.
return false;
@@ -962,8 +961,9 @@ public:
SkBitmapRegionDecoderInterface::Strategy fStrategy;
const char* fName;
} strategies[] = {
- { SkBitmapRegionDecoderInterface::kOriginal_Strategy, "BRD" },
- { SkBitmapRegionDecoderInterface::kCanvas_Strategy, "BRD_canvas" },
+ { SkBitmapRegionDecoderInterface::kOriginal_Strategy, "BRD" },
+ { SkBitmapRegionDecoderInterface::kCanvas_Strategy, "BRD_canvas" },
+ { SkBitmapRegionDecoderInterface::kAndroidCodec_Strategy, "BRD_android_codec" },
};
// We intend to create benchmarks that model the use cases in
diff --git a/dm/DM.cpp b/dm/DM.cpp
index c51a6fb8aa..f1f54d3f4c 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -392,6 +392,15 @@ static bool brd_color_type_supported(SkBitmapRegionDecoderInterface::Strategy st
default:
return false;
}
+ case SkBitmapRegionDecoderInterface::kAndroidCodec_Strategy:
+ switch (dstColorType) {
+ case CodecSrc::kGetFromCanvas_DstColorType:
+ case CodecSrc::kIndex8_Always_DstColorType:
+ case CodecSrc::kGrayscale_Always_DstColorType:
+ return true;
+ default:
+ return false;
+ }
default:
SkASSERT(false);
return false;
@@ -408,6 +417,9 @@ static void push_brd_src(Path path, SkBitmapRegionDecoderInterface::Strategy str
case SkBitmapRegionDecoderInterface::kOriginal_Strategy:
folder.append("brd_sample");
break;
+ case SkBitmapRegionDecoderInterface::kAndroidCodec_Strategy:
+ folder.append("brd_android_codec");
+ break;
default:
SkASSERT(false);
return;
@@ -450,7 +462,8 @@ static void push_brd_srcs(Path path) {
const SkBitmapRegionDecoderInterface::Strategy strategies[] = {
SkBitmapRegionDecoderInterface::kCanvas_Strategy,
- SkBitmapRegionDecoderInterface::kOriginal_Strategy
+ SkBitmapRegionDecoderInterface::kOriginal_Strategy,
+ SkBitmapRegionDecoderInterface::kAndroidCodec_Strategy,
};
const uint32_t sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
@@ -458,14 +471,14 @@ static void push_brd_srcs(Path path) {
// 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.
const CodecSrc::DstColorType dstColorTypes[] = {
- CodecSrc::kGetFromCanvas_DstColorType,
- CodecSrc::kIndex8_Always_DstColorType,
- CodecSrc::kGrayscale_Always_DstColorType,
+ CodecSrc::kGetFromCanvas_DstColorType,
+ CodecSrc::kIndex8_Always_DstColorType,
+ CodecSrc::kGrayscale_Always_DstColorType,
};
const BRDSrc::Mode modes[] = {
- BRDSrc::kFullImage_Mode,
- BRDSrc::kDivisor_Mode
+ BRDSrc::kFullImage_Mode,
+ BRDSrc::kDivisor_Mode,
};
for (SkBitmapRegionDecoderInterface::Strategy strategy : strategies) {
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 587949dd1a..9a61b020e8 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -88,8 +88,7 @@ static SkBitmapRegionDecoderInterface* create_brd(Path path,
if (!encoded) {
return NULL;
}
- return SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(new SkMemoryStream(encoded),
- strategy);
+ return SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(encoded, strategy);
}
Error BRDSrc::draw(SkCanvas* canvas) const {
diff --git a/gyp/tools.gyp b/gyp/tools.gyp
index e30cb7b7a1..c25543ef2d 100644
--- a/gyp/tools.gyp
+++ b/gyp/tools.gyp
@@ -52,6 +52,7 @@
'type': 'static_library',
'sources': [
'../tools/SkBitmapRegionCanvas.cpp',
+ '../tools/SkBitmapRegionCodec.cpp',
'../tools/SkBitmapRegionDecoderInterface.cpp',
'../tools/SkBitmapRegionSampler.cpp',
],
diff --git a/tools/SkBitmapRegionCanvas.cpp b/tools/SkBitmapRegionCanvas.cpp
index 0892aa55e1..f9c9573c44 100644
--- a/tools/SkBitmapRegionCanvas.cpp
+++ b/tools/SkBitmapRegionCanvas.cpp
@@ -16,30 +16,6 @@ SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkCodec* decoder)
{}
/*
- * Chooses the correct image subset offsets and dimensions for the partial decode.
- *
- * @return true if the subset is completely contained within the image
- * false otherwise
- */
-static bool set_subset_region(int inputOffset, int inputDimension,
- int imageOriginalDimension, int* imageSubsetOffset, int* outOffset,
- int* imageSubsetDimension) {
-
- // This must be at least zero, we can't start decoding the image at a negative coordinate.
- *imageSubsetOffset = SkTMax(0, inputOffset);
-
- // If inputOffset is less than zero, we decode to an offset location in the output bitmap.
- *outOffset = *imageSubsetOffset - inputOffset;
-
- // Use imageSusetOffset to make sure we don't decode pixels past the edge of the image.
- // Use outOffset to make sure we don't decode pixels past the edge of the region.
- *imageSubsetDimension = SkTMin(imageOriginalDimension - *imageSubsetOffset,
- inputDimension - *outOffset);
-
- return (*outOffset == 0) && (*imageSubsetDimension == inputDimension);
-}
-
-/*
* Three differences from the Android version:
* Returns a Skia bitmap instead of an Android bitmap.
* Android version attempts to reuse a recycled bitmap.
@@ -56,48 +32,25 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
return nullptr;
}
- // The client may not necessarily request a region that is fully within
- // the image. We may need to do some calculation to determine what part
- // of the image to decode.
-
- // The left offset of the portion of the image we want, where zero
- // indicates the left edge of the image.
- int imageSubsetX;
+ // Fix the input sampleSize if necessary.
+ if (sampleSize < 1) {
+ sampleSize = 1;
+ }
// The size of the output bitmap is determined by the size of the
- // requested region, not by the size of the intersection of the region
- // and the image dimensions. If inputX is negative, we will need to
- // place decoded pixels into the output bitmap starting at a left offset.
- // If this is non-zero, imageSubsetX must be zero.
+ // requested subset, not by the size of the intersection of the subset
+ // and the image dimensions.
+ // If inputX is negative, we will need to place decoded pixels into the
+ // output bitmap starting at a left offset. Call this outX.
+ // If outX is non-zero, subsetX must be zero.
+ // If inputY is negative, we will need to place decoded pixels into the
+ // output bitmap starting at a top offset. Call this outY.
+ // If outY is non-zero, subsetY must be zero.
int outX;
-
- // The width of the portion of the image that we will write to the output
- // bitmap. If the region is not fully contained within the image, this
- // will not be the same as inputWidth.
- int imageSubsetWidth;
- bool imageContainsEntireSubset = set_subset_region(inputX, inputWidth, this->width(),
- &imageSubsetX, &outX, &imageSubsetWidth);
-
- // The top offset of the portion of the image we want, where zero
- // indicates the top edge of the image.
- int imageSubsetY;
-
- // The size of the output bitmap is determined by the size of the
- // requested region, not by the size of the intersection of the region
- // and the image dimensions. If inputY is negative, we will need to
- // place decoded pixels into the output bitmap starting at a top offset.
- // If this is non-zero, imageSubsetY must be zero.
int outY;
-
- // The height of the portion of the image that we will write to the output
- // bitmap. If the region is not fully contained within the image, this
- // will not be the same as inputHeight.
- int imageSubsetHeight;
- imageContainsEntireSubset &= set_subset_region(inputY, inputHeight, this->height(),
- &imageSubsetY, &outY, &imageSubsetHeight);
-
- if (imageSubsetWidth <= 0 || imageSubsetHeight <= 0) {
- SkCodecPrintf("Error: Region must intersect part of the image.\n");
+ SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
+ SubsetType type = adjust_subset_rect(fDecoder->getInfo().dimensions(), &subset, &outX, &outY);
+ if (SubsetType::kOutside_SubsetType == type) {
return nullptr;
}
@@ -108,7 +61,7 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
}
SkImageInfo decodeInfo = SkImageInfo::Make(this->width(), this->height(),
dstColorType, dstAlphaType);
-
+
// Start the scanline decoder
SkCodec::Result r = fDecoder->startScanlineDecode(decodeInfo);
if (SkCodec::kSuccess != r) {
@@ -118,20 +71,20 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
// Allocate a bitmap for the unscaled decode
SkBitmap tmp;
- SkImageInfo tmpInfo = decodeInfo.makeWH(this->width(), imageSubsetHeight);
+ SkImageInfo tmpInfo = decodeInfo.makeWH(this->width(), subset.height());
if (!tmp.tryAllocPixels(tmpInfo)) {
SkCodecPrintf("Error: Could not allocate pixels.\n");
return nullptr;
}
// Skip the unneeded rows
- if (!fDecoder->skipScanlines(imageSubsetY)) {
+ if (!fDecoder->skipScanlines(subset.y())) {
SkCodecPrintf("Error: Failed to skip scanlines.\n");
return nullptr;
}
// Decode the necessary rows
- fDecoder->getScanlines(tmp.getAddr(0, 0), imageSubsetHeight, tmp.rowBytes());
+ fDecoder->getScanlines(tmp.getAddr(0, 0), subset.height(), tmp.rowBytes());
// Calculate the size of the output
const int outWidth = get_scaled_dimension(inputWidth, sampleSize);
@@ -152,18 +105,18 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
// TODO (msarett): This could be skipped if memory is zero initialized.
// This would matter if this code is moved to Android and
// uses Android bitmaps.
- if (!imageContainsEntireSubset) {
+ if (SubsetType::kPartiallyInside_SubsetType == type) {
bitmap->eraseColor(0);
}
// Use a canvas to crop and scale to the destination bitmap
SkCanvas canvas(*bitmap);
// TODO (msarett): Maybe we can take advantage of the fact that SkRect uses floats?
- SkRect src = SkRect::MakeXYWH((SkScalar) imageSubsetX, (SkScalar) 0,
- (SkScalar) imageSubsetWidth, (SkScalar) imageSubsetHeight);
+ SkRect src = SkRect::MakeXYWH((SkScalar) subset.x(), (SkScalar) 0,
+ (SkScalar) subset.width(), (SkScalar) subset.height());
SkRect dst = SkRect::MakeXYWH((SkScalar) (outX / sampleSize), (SkScalar) (outY / sampleSize),
- (SkScalar) get_scaled_dimension(imageSubsetWidth, sampleSize),
- (SkScalar) get_scaled_dimension(imageSubsetHeight, sampleSize));
+ (SkScalar) get_scaled_dimension(subset.width(), sampleSize),
+ (SkScalar) get_scaled_dimension(subset.height(), sampleSize));
SkPaint paint;
// Overwrite the dst with the src pixels
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
diff --git a/tools/SkBitmapRegionCodec.cpp b/tools/SkBitmapRegionCodec.cpp
new file mode 100644
index 0000000000..3f2cd24b78
--- /dev/null
+++ b/tools/SkBitmapRegionCodec.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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 "SkBitmapRegionCodec.h"
+#include "SkAndroidCodec.h"
+#include "SkCodecPriv.h"
+#include "SkCodecTools.h"
+
+SkBitmapRegionCodec::SkBitmapRegionCodec(SkAndroidCodec* codec)
+ : INHERITED(codec->getInfo().width(), codec->getInfo().height())
+ , fCodec(codec)
+{}
+
+/*
+ * Three differences from the Android version:
+ * Returns a skia bitmap instead of an Android bitmap.
+ * Android version attempts to reuse a recycled bitmap.
+ * Removed the options object and used parameters for color type and sample size.
+ */
+// FIXME: Should this function should take in SkIRect?
+SkBitmap* SkBitmapRegionCodec::decodeRegion(int inputX, int inputY, int inputWidth, int inputHeight,
+ int sampleSize, SkColorType dstColorType) {
+
+ // Fix the input sampleSize if necessary.
+ if (sampleSize < 1) {
+ sampleSize = 1;
+ }
+
+ // The size of the output bitmap is determined by the size of the
+ // requested subset, not by the size of the intersection of the subset
+ // and the image dimensions.
+ // If inputX is negative, we will need to place decoded pixels into the
+ // output bitmap starting at a left offset. Call this outX.
+ // If outX is non-zero, subsetX must be zero.
+ // If inputY is negative, we will need to place decoded pixels into the
+ // output bitmap starting at a top offset. Call this outY.
+ // If outY is non-zero, subsetY must be zero.
+ int outX;
+ int outY;
+ SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
+ SubsetType type = adjust_subset_rect(fCodec->getInfo().dimensions(), &subset, &outX, &outY);
+ if (SubsetType::kOutside_SubsetType == type) {
+ return nullptr;
+ }
+
+ // Ask the codec for a scaled subset
+ if (!fCodec->getSupportedSubset(&subset)) {
+ SkCodecPrintf("Error: Could not get subset.\n");
+ return nullptr;
+ }
+ SkISize scaledSize = fCodec->getSampledSubsetDimensions(sampleSize, subset);
+
+ // Create the image info for the decode
+ SkAlphaType dstAlphaType = fCodec->getInfo().alphaType();
+ if (kUnpremul_SkAlphaType == dstAlphaType) {
+ dstAlphaType = kPremul_SkAlphaType;
+ }
+ SkImageInfo decodeInfo = SkImageInfo::Make(scaledSize.width(), scaledSize.height(),
+ dstColorType, dstAlphaType);
+
+ // Construct a color table for the decode if necessary
+ SkAutoTUnref<SkColorTable> colorTable(nullptr);
+ SkPMColor* colorPtr = nullptr;
+ int* colorCountPtr = nullptr;
+ int maxColors = 256;
+ SkPMColor colors[256];
+ if (kIndex_8_SkColorType == dstColorType) {
+ // TODO (msarett): This performs a copy that is unnecessary since
+ // we have not yet initialized the color table.
+ // And then we need to use a const cast to get
+ // a pointer to the color table that we can
+ // modify during the decode. We could alternatively
+ // perform the decode before creating the bitmap and
+ // the color table. We still would need to copy the
+ // colors into the color table after the decode.
+ colorTable.reset(new SkColorTable(colors, maxColors));
+ colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
+ colorCountPtr = &maxColors;
+ }
+
+ // Initialize the destination bitmap
+ SkAutoTDelete<SkBitmap> bitmap(new SkBitmap());
+ int scaledOutX = 0;
+ int scaledOutY = 0;
+ int scaledOutWidth = scaledSize.width();
+ int scaledOutHeight = scaledSize.height();
+ if (SubsetType::kPartiallyInside_SubsetType == type) {
+ scaledOutX = outX / sampleSize;
+ scaledOutY = outY / sampleSize;
+ // We need to be safe here because getSupportedSubset() may have modified the subset.
+ const int extraX = SkTMax(0, inputWidth - outX - subset.width());
+ const int extraY = SkTMax(0, inputHeight - outY - subset.height());
+ const int scaledExtraX = extraX / sampleSize;
+ const int scaledExtraY = extraY / sampleSize;
+ scaledOutWidth += scaledOutX + scaledExtraX;
+ scaledOutHeight += scaledOutY + scaledExtraY;
+ }
+ SkImageInfo outInfo = decodeInfo.makeWH(scaledOutWidth, scaledOutHeight);
+ if (!bitmap->tryAllocPixels(outInfo, nullptr, colorTable.get())) {
+ SkCodecPrintf("Error: Could not allocate pixels.\n");
+ return nullptr;
+ }
+
+ // Zero the bitmap if the region is not completely within the image.
+ // TODO (msarett): Can we make this faster by implementing it to only
+ // zero parts of the image that we won't overwrite with
+ // pixels?
+ // TODO (msarett): This could be skipped if memory is zero initialized.
+ // This would matter if this code is moved to Android and
+ // uses Android bitmaps.
+ if (SubsetType::kPartiallyInside_SubsetType == type) {
+ void* pixels = bitmap->getPixels();
+ size_t bytes = outInfo.getSafeSize(bitmap->rowBytes());
+ memset(pixels, 0, bytes);
+ }
+
+ // Decode into the destination bitmap
+ SkAndroidCodec::AndroidOptions options;
+ options.fSampleSize = sampleSize;
+ options.fSubset = &subset;
+ options.fColorPtr = colorPtr;
+ options.fColorCount = colorCountPtr;
+ void* dst = bitmap->getAddr(scaledOutX, scaledOutY);
+ size_t rowBytes = bitmap->rowBytes();
+ SkCodec::Result result = fCodec->getAndroidPixels(decodeInfo, dst, rowBytes, &options);
+ if (SkCodec::kSuccess != result && SkCodec::kIncompleteInput != result) {
+ SkCodecPrintf("Error: Could not get pixels.\n");
+ return nullptr;
+ }
+
+ return bitmap.detach();
+}
+
+bool SkBitmapRegionCodec::conversionSupported(SkColorType colorType) {
+ // FIXME: Call virtual function when it lands.
+ SkImageInfo info = SkImageInfo::Make(0, 0, colorType, fCodec->getInfo().alphaType(),
+ fCodec->getInfo().profileType());
+ return conversion_possible(info, fCodec->getInfo());
+}
diff --git a/tools/SkBitmapRegionCodec.h b/tools/SkBitmapRegionCodec.h
new file mode 100644
index 0000000000..14d024ea45
--- /dev/null
+++ b/tools/SkBitmapRegionCodec.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.
+ */
+
+#include "SkBitmap.h"
+#include "SkBitmapRegionDecoderInterface.h"
+#include "SkAndroidCodec.h"
+
+/*
+ * This class implements SkBitmapRegionDecoder using an SkAndroidCodec.
+ */
+class SkBitmapRegionCodec : public SkBitmapRegionDecoderInterface {
+public:
+
+ /*
+ * Takes ownership of pointer to codec
+ */
+ SkBitmapRegionCodec(SkAndroidCodec* codec);
+
+ /*
+ * Three differences from the Android version:
+ * Returns a Skia bitmap instead of an Android bitmap.
+ * Android version attempts to reuse a recycled bitmap.
+ * Removed the options object and used parameters for color type and
+ * sample size.
+ */
+ SkBitmap* decodeRegion(int start_x, int start_y, int width, int height,
+ int sampleSize, SkColorType prefColorType) override;
+
+ bool conversionSupported(SkColorType colorType) override;
+
+private:
+
+ SkAutoTDelete<SkAndroidCodec> fCodec;
+
+ typedef SkBitmapRegionDecoderInterface INHERITED;
+
+};
diff --git a/tools/SkBitmapRegionDecoderInterface.cpp b/tools/SkBitmapRegionDecoderInterface.cpp
index 835ed9aa56..ec6327e565 100644
--- a/tools/SkBitmapRegionDecoderInterface.cpp
+++ b/tools/SkBitmapRegionDecoderInterface.cpp
@@ -6,24 +6,26 @@
*/
#include "SkBitmapRegionCanvas.h"
+#include "SkBitmapRegionCodec.h"
#include "SkBitmapRegionDecoderInterface.h"
#include "SkBitmapRegionSampler.h"
+#include "SkAndroidCodec.h"
#include "SkCodec.h"
#include "SkCodecPriv.h"
#include "SkImageDecoder.h"
SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(
- SkStreamRewindable* stream, Strategy strategy) {
- SkAutoTDelete<SkStreamRewindable> streamDeleter(stream);
+ SkData* data, Strategy strategy) {
switch (strategy) {
case kOriginal_Strategy: {
+ SkAutoTDelete<SkStreamRewindable> stream(new SkMemoryStream(data));
SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
int width, height;
if (nullptr == decoder) {
SkCodecPrintf("Error: Could not create image decoder.\n");
return nullptr;
}
- if (!decoder->buildTileIndex(streamDeleter.detach(), &width, &height)) {
+ if (!decoder->buildTileIndex(stream.detach(), &width, &height)) {
SkCodecPrintf("Error: Could not build tile index.\n");
delete decoder;
return nullptr;
@@ -31,7 +33,7 @@ SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegi
return new SkBitmapRegionSampler(decoder, width, height);
}
case kCanvas_Strategy: {
- SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(streamDeleter.detach()));
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(data));
if (nullptr == codec) {
SkCodecPrintf("Error: Failed to create decoder.\n");
return nullptr;
@@ -46,6 +48,14 @@ SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegi
}
return new SkBitmapRegionCanvas(codec.detach());
}
+ case kAndroidCodec_Strategy: {
+ SkAutoTDelete<SkAndroidCodec> codec = SkAndroidCodec::NewFromData(data);
+ if (NULL == codec) {
+ SkCodecPrintf("Error: Failed to create codec.\n");
+ return NULL;
+ }
+ return new SkBitmapRegionCodec(codec.detach());
+ }
default:
SkASSERT(false);
return nullptr;
diff --git a/tools/SkBitmapRegionDecoderInterface.h b/tools/SkBitmapRegionDecoderInterface.h
index 047f023c80..e1f79bccb6 100644
--- a/tools/SkBitmapRegionDecoderInterface.h
+++ b/tools/SkBitmapRegionDecoderInterface.h
@@ -19,19 +19,18 @@ class SkBitmapRegionDecoderInterface {
public:
enum Strategy {
- kCanvas_Strategy, // Draw to the canvas, uses SkCodec
- kOriginal_Strategy, // Sampling, uses SkImageDecoder
- // TODO (msarett): Add strategy for SkScaledCodec
+ kCanvas_Strategy, // Draw to the canvas, uses SkCodec
+ kOriginal_Strategy, // Sampling, uses SkImageDecoder
+ kAndroidCodec_Strategy, // Uses SkAndroidCodec for scaling and subsetting
};
/*
- * @param stream Encoded image stream, takes ownership
+ * @param data Refs the data while this object exists, unrefs on destruction
* @param strategy Strategy used for scaling and subsetting
- * @return Tries to create an SkBitmapRegionDecoder, returns NULL
- * on failure
+ * @return Tries to create an SkBitmapRegionDecoder, returns NULL on failure
*/
static SkBitmapRegionDecoderInterface* CreateBitmapRegionDecoder(
- SkStreamRewindable* stream, Strategy strategy);
+ SkData* data, Strategy strategy);
/*
* Decode a scaled region of the encoded image stream
diff --git a/tools/SkCodecTools.h b/tools/SkCodecTools.h
index 097915b5d7..285d3a21de 100644
--- a/tools/SkCodecTools.h
+++ b/tools/SkCodecTools.h
@@ -12,4 +12,52 @@ inline float get_scale_from_sample_size(uint32_t sampleSize) {
return 1.0f / (float) sampleSize;
}
+enum SubsetType {
+ kFullyInside_SubsetType,
+ kPartiallyInside_SubsetType,
+ kOutside_SubsetType,
+};
+
+/*
+ * Corrects image subset offsets and dimensions in order to perform a valid decode.
+ * Also indicates if the image subset should be placed at an offset within the
+ * output bitmap.
+ *
+ * Values of output variables are undefined if the SubsetType is kInvalid.
+ *
+ * @param imageDims Original image dimensions.
+ * @param subset As input, the subset that the client requested.
+ * As output, the image subset that we will decode.
+ * @param outX The left offset of the image subset within the output bitmap.
+ * @param outY The top offset of the image subset within the output bitmap.
+ *
+ * @return An indication of how the subset is contained in the image.
+ * If the return value is kInvalid, values of output variables are undefined.
+ */
+inline SubsetType adjust_subset_rect(const SkISize& imageDims, SkIRect* subset, int* outX,
+ int* outY) {
+ // These must be at least zero, we can't start decoding the image at a negative coordinate.
+ int left = SkTMax(0, subset->fLeft);
+ int top = SkTMax(0, subset->fTop);
+
+ // If input offsets are less than zero, we decode to an offset location in the output bitmap.
+ *outX = left - subset->fLeft;
+ *outY = top - subset->fTop;
+
+ // Make sure we don't decode pixels past the edge of the image or past the edge of the subset.
+ int width = SkTMin(imageDims.width() - left, subset->width() - *outX);
+ int height = SkTMin(imageDims.height() - top, subset->height() - *outY);
+ if (width <= 0 || height <= 0) {
+ return SubsetType::kOutside_SubsetType;
+ }
+
+ subset->setXYWH(left, top, width, height);
+ if ((*outX != 0) || (*outY != 0) || (width != subset->width()) ||
+ (height != subset->height())) {
+ return SubsetType::kPartiallyInside_SubsetType;
+ }
+
+ return SubsetType::kFullyInside_SubsetType;
+}
+
#endif // SkCodecTools_DEFINED