Implementation of SkBitmapRegionDecoder using SkAndroidCodec
Includes testing in DM and nanobench BUG=skia: Review URL: https://codereview.chromium.org/1402863002
This commit is contained in:
parent
ed0935a28a
commit
26ad17b8f8
@ -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) {
|
||||
|
@ -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
|
||||
|
25
dm/DM.cpp
25
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) {
|
||||
|
@ -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 {
|
||||
|
@ -52,6 +52,7 @@
|
||||
'type': 'static_library',
|
||||
'sources': [
|
||||
'../tools/SkBitmapRegionCanvas.cpp',
|
||||
'../tools/SkBitmapRegionCodec.cpp',
|
||||
'../tools/SkBitmapRegionDecoderInterface.cpp',
|
||||
'../tools/SkBitmapRegionSampler.cpp',
|
||||
],
|
||||
|
@ -15,30 +15,6 @@ SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkCodec* decoder)
|
||||
, fDecoder(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.
|
||||
@ -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);
|
||||
|
143
tools/SkBitmapRegionCodec.cpp
Normal file
143
tools/SkBitmapRegionCodec.cpp
Normal file
@ -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());
|
||||
}
|
41
tools/SkBitmapRegionCodec.h
Normal file
41
tools/SkBitmapRegionCodec.h
Normal file
@ -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;
|
||||
|
||||
};
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user