diff options
author | msarett <msarett@google.com> | 2015-10-22 07:29:19 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-22 07:29:19 -0700 |
commit | 26ad17b8f872fc8bc18df7f49067edbd8b9799e8 (patch) | |
tree | ff8699a8e9614cc328011d172fdb8cd6b2fbdae2 | |
parent | ed0935a28a29af7d3b16ac8d9365f291a335c6bd (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.cpp | 6 | ||||
-rw-r--r-- | bench/nanobench.cpp | 8 | ||||
-rw-r--r-- | dm/DM.cpp | 25 | ||||
-rw-r--r-- | dm/DMSrcSink.cpp | 3 | ||||
-rw-r--r-- | gyp/tools.gyp | 1 | ||||
-rw-r--r-- | tools/SkBitmapRegionCanvas.cpp | 95 | ||||
-rw-r--r-- | tools/SkBitmapRegionCodec.cpp | 143 | ||||
-rw-r--r-- | tools/SkBitmapRegionCodec.h | 41 | ||||
-rw-r--r-- | tools/SkBitmapRegionDecoderInterface.cpp | 18 | ||||
-rw-r--r-- | tools/SkBitmapRegionDecoderInterface.h | 13 | ||||
-rw-r--r-- | tools/SkCodecTools.h | 48 |
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 @@ -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 = ⊂ + 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 |