Add Options to SkDecodingImageGenerator, simplify API.

Motivation: We want to remove redundant classes from Skia.  To
that end we want to remove SkImageRef and its subclasses and
replace their uses with SkDiscardablePixelRef +
SkDecodingImageGenerator.  Since Android uses SkImageRef, we need
to make sure that SkDecodingImageGenerator allows all of the
settings that Android exposes in BitmapFactory.Options.

To that end, we have created an Options struct for the
SkDecodingImageGenerator which lets the client of the generator set
sample size, dithering, and bitmap config.

We have made the SkDecodingImageGenerator constructor private
and replaced the SkDecodingImageGenerator::Install functions
with a SkDecodingImageGenerator::Create functions (one for
SkData and one for SkStream) which now take a
SkDecodingImageGenerator::Options struct.

Also added a ImageDecoderOptions test which loops through a list
of sets of options and tries them on a set of 5 small encoded
images.

Also updated several users of SkDecodingImageGenerator::Install to
follow new call signature - gm/factory.cpp, LazyDecodeBitmap.cpp,
and PictureTest.cpp, CachedDecodingPixelRefTest.cpp.

We also added a new ImprovedBitmapFactory Test which simulates the
exact function that Android will need to modify to use this,
installPixelRef() in BitmapFactory.

R=reed@google.com, scroggo@google.com

Committed: https://code.google.com/p/skia/source/detail?r=12744

Review URL: https://codereview.chromium.org/93703004

git-svn-id: http://skia.googlecode.com/svn/trunk@12855 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
halcanary@google.com 2014-01-02 13:15:13 +00:00
parent f8affa2e5c
commit 3d50ea1b87
14 changed files with 675 additions and 251 deletions

View File

@ -12,6 +12,7 @@
#include "SkDiscardableMemoryPool.h"
#include "SkDiscardablePixelRef.h"
#include "SkImageDecoder.h"
#include "SkImageGenerator.h"
#include "SkOSFile.h"
#include "SkStream.h"
@ -35,8 +36,10 @@ protected:
// bitmap is unlocked.
SkAutoTUnref<SkDiscardableMemoryPool> pool(
SkNEW_ARGS(SkDiscardableMemoryPool, (1)));
SkAssertResult(SkDecodingImageGenerator::Install(data,
&fBitmap, pool));
SkAssertResult(SkInstallDiscardablePixelRef(
SkDecodingImageGenerator::Create(
data, SkDecodingImageGenerator::Options()),
&fBitmap, pool));
}
}
@ -68,4 +71,4 @@ private:
static GM* MyFactory(void*) { return new FactoryGM; }
static GMRegistry reg(MyFactory);
}
} // namespace skiagm

View File

@ -21,6 +21,8 @@ class SkImageGenerator;
* the generator. If it succeeds, it will modify destination
* bitmap.
*
* If generator is NULL, will safely return false.
*
* If this fails or when the SkDiscardablePixelRef that is
* installed into destination is destroyed, it will call
* SkDELETE() on the generator. Therefore, generator should be

View File

@ -40,7 +40,8 @@ static SkBitmap load_bitmap() {
SkString path = SkOSPath::SkPathJoin(directory.c_str(), "mandrill_512.png");
SkAutoDataUnref data(SkData::NewFromFileName(path.c_str()));
if (data.get() != NULL) {
SkDecodingImageGenerator::Install(data.get(), &bm);
SkInstallDiscardablePixelRef(SkDecodingImageGenerator::Create(
data, SkDecodingImageGenerator::Options()), &bm, NULL);
}
return bm;
}

View File

@ -9,8 +9,8 @@
#include "SkCanvas.h"
#include "SkPicture.h"
SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo& info) {
switch (info.fColorType) {
SkBitmap::Config SkColorTypeToBitmapConfig(SkColorType colorType) {
switch (colorType) {
case kAlpha_8_SkColorType:
return SkBitmap::kA8_Config;
@ -33,6 +33,39 @@ SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo& info) {
return SkBitmap::kNo_Config;
}
SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo& info) {
return SkColorTypeToBitmapConfig(info.fColorType);
}
bool SkBitmapConfigToColorType(SkBitmap::Config config, SkColorType* ctOut) {
SkColorType ct;
switch (config) {
case SkBitmap::kA8_Config:
ct = kAlpha_8_SkColorType;
break;
case SkBitmap::kIndex8_Config:
ct = kIndex_8_SkColorType;
break;
case SkBitmap::kRGB_565_Config:
ct = kRGB_565_SkColorType;
break;
case SkBitmap::kARGB_4444_Config:
ct = kARGB_4444_SkColorType;
break;
case SkBitmap::kARGB_8888_Config:
ct = kPMColor_SkColorType;
break;
case SkBitmap::kNo_Config:
default:
return false;
}
if (ctOut) {
*ctOut = ct;
}
return true;
}
SkImage* SkNewImageFromBitmap(const SkBitmap& bm, bool canSharePixelRef) {
SkImageInfo info;
if (!bm.asImageInfo(&info)) {

View File

@ -14,6 +14,8 @@
class SkPicture;
extern SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo&);
extern SkBitmap::Config SkColorTypeToBitmapConfig(SkColorType);
extern bool SkBitmapConfigToColorType(SkBitmap::Config, SkColorType* ctOut);
// Call this if you explicitly want to use/share this pixelRef in the image
extern SkImage* SkNewImageFromPixelRef(const SkImageInfo&, SkPixelRef*,

View File

@ -5,13 +5,14 @@
* found in the LICENSE file.
*/
#include "SkDecodingImageGenerator.h"
#include "SkData.h"
#include "SkDecodingImageGenerator.h"
#include "SkImageDecoder.h"
#include "SkImageInfo.h"
#include "SkImageGenerator.h"
#include "SkImagePriv.h"
#include "SkStream.h"
#include "SkUtils.h"
namespace {
/**
@ -20,56 +21,55 @@ namespace {
*/
class TargetAllocator : public SkBitmap::Allocator {
public:
TargetAllocator(void* target, size_t rowBytes, const SkImageInfo& info)
TargetAllocator(void* target,
size_t rowBytes,
int width,
int height,
SkBitmap::Config config)
: fTarget(target)
, fRowBytes(rowBytes)
, fInfo(info) { }
, fWidth(width)
, fHeight(height)
, fConfig(config) { }
virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
if ((SkImageInfoToBitmapConfig(fInfo) != bm->config())
|| (bm->width() != fInfo.fWidth)
|| (bm->height() != fInfo.fHeight)) {
return false;
bool isReady() { return (fTarget != NULL); }
virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
if ((NULL == fTarget)
|| (fConfig != bm->config())
|| (fWidth != bm->width())
|| (fHeight != bm->height())
|| (ct != NULL)) {
// Call default allocator.
return bm->allocPixels(NULL, ct);
}
bm->setConfig(bm->config(), bm->width(), bm->height(),
fRowBytes, bm->alphaType());
bm->setPixels(fTarget, ct);
// make sure fRowBytes is correct.
bm->setConfig(fConfig, fWidth, fHeight, fRowBytes, bm->alphaType());
// TODO(halcanary): verify that all callers of this function
// will respect new RowBytes. Will be moot once rowbytes belongs
// to PixelRef.
bm->setPixels(fTarget, NULL);
fTarget = NULL; // never alloc same pixels twice!
return true;
}
private:
void* fTarget;
size_t fRowBytes;
SkImageInfo fInfo;
void* fTarget; // Block of memory to be supplied as pixel memory
// in allocPixelRef. Must be large enough to hold
// a bitmap described by fWidth, fHeight, and
// fRowBytes.
size_t fRowBytes; // rowbytes for the destination bitmap
int fWidth; // Along with fHeight and fConfig, the information
int fHeight; // about the bitmap whose pixels this allocator is
// expected to allocate. If they do not match the
// bitmap passed to allocPixelRef, it is assumed
// that the bitmap will be copied to a bitmap with
// the correct info using this allocator, so the
// default allocator will be used instead of
// fTarget.
SkBitmap::Config fConfig;
typedef SkBitmap::Allocator INHERITED;
};
} // namespace
////////////////////////////////////////////////////////////////////////////////
SkDecodingImageGenerator::SkDecodingImageGenerator(SkData* data)
: fData(data)
, fHasInfo(false)
, fDoCopyTo(false) {
SkASSERT(fData != NULL);
fStream = SkNEW_ARGS(SkMemoryStream, (fData));
SkASSERT(fStream != NULL);
SkASSERT(fStream->unique());
fData->ref();
}
SkDecodingImageGenerator::SkDecodingImageGenerator(SkStreamRewindable* stream)
: fData(NULL)
, fStream(stream)
, fHasInfo(false)
, fDoCopyTo(false) {
SkASSERT(fStream != NULL);
SkASSERT(fStream->unique());
}
SkDecodingImageGenerator::~SkDecodingImageGenerator() {
SkSafeUnref(fData);
fStream->unref();
}
// TODO(halcanary): Give this macro a better name and move it into SkTypes.h
#ifdef SK_DEBUG
@ -78,8 +78,49 @@ SkDecodingImageGenerator::~SkDecodingImageGenerator() {
#define SkCheckResult(expr, value) (void)(expr)
#endif
#ifdef SK_DEBUG
inline bool check_alpha(SkAlphaType reported, SkAlphaType actual) {
return ((reported == actual)
|| ((reported == kPremul_SkAlphaType)
&& (actual == kOpaque_SkAlphaType)));
}
#endif // SK_DEBUG
} // namespace
////////////////////////////////////////////////////////////////////////////////
SkDecodingImageGenerator::SkDecodingImageGenerator(
SkData* data,
SkStreamRewindable* stream,
const SkImageInfo& info,
int sampleSize,
bool ditherImage,
SkBitmap::Config requestedConfig)
: fData(data)
, fStream(stream)
, fInfo(info)
, fSampleSize(sampleSize)
, fDitherImage(ditherImage)
, fRequestedConfig(requestedConfig) {
SkASSERT(stream != NULL);
SkSafeRef(fData); // may be NULL.
}
SkDecodingImageGenerator::~SkDecodingImageGenerator() {
SkSafeUnref(fData);
fStream->unref();
}
bool SkDecodingImageGenerator::getInfo(SkImageInfo* info) {
if (info != NULL) {
*info = fInfo;
}
return true;
}
SkData* SkDecodingImageGenerator::refEncodedData() {
// This functionality is used in `gm --serialize`
// Does not encode options.
if (fData != NULL) {
return SkSafeRef(fData);
}
@ -98,111 +139,149 @@ SkData* SkDecodingImageGenerator::refEncodedData() {
return SkSafeRef(fData);
}
bool SkDecodingImageGenerator::getInfo(SkImageInfo* info) {
// info can be NULL. If so, will update fInfo, fDoCopyTo, and fHasInfo.
if (fHasInfo) {
if (info != NULL) {
*info = fInfo;
}
return true;
}
SkAssertResult(fStream->rewind());
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
if (NULL == decoder.get()) {
return false;
}
SkBitmap bitmap;
if (!decoder->decode(fStream, &bitmap,
SkImageDecoder::kDecodeBounds_Mode)) {
return false;
}
if (bitmap.config() == SkBitmap::kNo_Config) {
return false;
}
if (!bitmap.asImageInfo(&fInfo)) {
// We can't use bitmap.config() as is.
if (!bitmap.canCopyTo(SkBitmap::kARGB_8888_Config)) {
SkDEBUGFAIL("!bitmap->canCopyTo(SkBitmap::kARGB_8888_Config)");
return false;
}
fDoCopyTo = true;
fInfo.fWidth = bitmap.width();
fInfo.fHeight = bitmap.height();
fInfo.fColorType = kPMColor_SkColorType;
fInfo.fAlphaType = bitmap.alphaType();
}
if (info != NULL) {
*info = fInfo;
}
fHasInfo = true;
return true;
}
bool SkDecodingImageGenerator::getPixels(const SkImageInfo& info,
void* pixels,
size_t rowBytes) {
if (NULL == pixels) {
return false;
}
if (!this->getInfo(NULL)) {
if (fInfo != info) {
// The caller has specified a different info. This is an
// error for this kind of SkImageGenerator. Use the Options
// to change the settings.
return false;
}
if (SkImageInfoToBitmapConfig(info) == SkBitmap::kNo_Config) {
return false; // Unsupported SkColorType.
int bpp = SkBitmap::ComputeBytesPerPixel(fRequestedConfig);
if (static_cast<size_t>(bpp * info.fWidth) > rowBytes) {
// The caller has specified a bad rowBytes.
return false;
}
SkAssertResult(fStream->rewind());
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
if (NULL == decoder.get()) {
return false;
}
if (fInfo != info) {
// The caller has specified a different info. For now, this
// is an error. In the future, we will check to see if we can
// convert.
return false;
}
int bpp = SkBitmap::ComputeBytesPerPixel(SkImageInfoToBitmapConfig(info));
if (static_cast<size_t>(bpp * info.fWidth) > rowBytes) {
return false;
}
SkBitmap bitmap;
if (!bitmap.setConfig(info, rowBytes)) {
return false;
}
decoder->setDitherImage(fDitherImage);
decoder->setSampleSize(fSampleSize);
TargetAllocator allocator(pixels, rowBytes, info);
if (!fDoCopyTo) {
decoder->setAllocator(&allocator);
}
bool success = decoder->decode(fStream, &bitmap,
SkBitmap bitmap;
TargetAllocator allocator(pixels, rowBytes, info.fWidth,
info.fHeight, fRequestedConfig);
decoder->setAllocator(&allocator);
bool success = decoder->decode(fStream, &bitmap, fRequestedConfig,
SkImageDecoder::kDecodePixels_Mode);
decoder->setAllocator(NULL);
if (!success) {
return false;
}
if (fDoCopyTo) {
SkBitmap bm8888;
bitmap.copyTo(&bm8888, SkBitmap::kARGB_8888_Config, &allocator);
if (allocator.isReady()) { // Did not use pixels!
SkBitmap bm;
SkASSERT(bitmap.canCopyTo(fRequestedConfig));
if (!bitmap.copyTo(&bm, fRequestedConfig, &allocator)
|| allocator.isReady()) {
SkDEBUGFAIL("bitmap.copyTo(requestedConfig) failed.");
// Earlier we checked canCopyto(); we expect consistency.
return false;
}
SkASSERT(check_alpha(fInfo.fAlphaType, bm.alphaType()));
} else {
SkASSERT(check_alpha(fInfo.fAlphaType, bitmap.alphaType()));
}
return true;
}
bool SkDecodingImageGenerator::Install(SkData* data, SkBitmap* dst,
SkDiscardableMemory::Factory* factory) {
SkImageGenerator* SkDecodingImageGenerator::Create(
SkData* data,
const SkDecodingImageGenerator::Options& opts) {
SkASSERT(data != NULL);
SkASSERT(dst != NULL);
SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (data)));
return SkInstallDiscardablePixelRef(gen, dst, factory);
if (NULL == data) {
return NULL;
}
SkStreamRewindable* stream = SkNEW_ARGS(SkMemoryStream, (data));
SkASSERT(stream != NULL);
SkASSERT(stream->unique());
return SkDecodingImageGenerator::Create(data, stream, opts);
}
bool SkDecodingImageGenerator::Install(SkStreamRewindable* stream,
SkBitmap* dst,
SkDiscardableMemory::Factory* factory) {
SkImageGenerator* SkDecodingImageGenerator::Create(
SkStreamRewindable* stream,
const SkDecodingImageGenerator::Options& opts) {
SkASSERT(stream != NULL);
SkASSERT(dst != NULL);
SkASSERT(stream->unique());
if ((stream == NULL) || !stream->unique()) {
SkSafeUnref(stream);
return false;
return NULL;
}
SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (stream)));
return SkInstallDiscardablePixelRef(gen, dst, factory);
return SkDecodingImageGenerator::Create(NULL, stream, opts);
}
// A contructor-type function that returns NULL on failure. This
// prevents the returned SkImageGenerator from ever being in a bad
// state. Called by both Create() functions
SkImageGenerator* SkDecodingImageGenerator::Create(
SkData* data,
SkStreamRewindable* stream,
const SkDecodingImageGenerator::Options& opts) {
SkASSERT(stream);
SkAutoTUnref<SkStreamRewindable> autoStream(stream); // always unref this.
if (opts.fUseRequestedColorType &&
(kIndex_8_SkColorType == opts.fRequestedColorType)) {
// We do not support indexed color with SkImageGenerators,
return NULL;
}
SkAssertResult(autoStream->rewind());
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(autoStream));
if (NULL == decoder.get()) {
return NULL;
}
SkBitmap bitmap;
decoder->setSampleSize(opts.fSampleSize);
if (!decoder->decode(stream, &bitmap,
SkImageDecoder::kDecodeBounds_Mode)) {
return NULL;
}
if (bitmap.config() == SkBitmap::kNo_Config) {
return NULL;
}
SkImageInfo info;
SkBitmap::Config config;
if (!opts.fUseRequestedColorType) {
// Use default config.
if (SkBitmap::kIndex8_Config == bitmap.config()) {
// We don't support kIndex8 because we don't support
// colortables in this workflow.
config = SkBitmap::kARGB_8888_Config;
info.fWidth = bitmap.width();
info.fHeight = bitmap.height();
info.fColorType = kPMColor_SkColorType;
info.fAlphaType = bitmap.alphaType();
} else {
config = bitmap.config(); // Save for later!
if (!bitmap.asImageInfo(&info)) {
SkDEBUGFAIL("Getting SkImageInfo from bitmap failed.");
return NULL;
}
}
} else {
config = SkColorTypeToBitmapConfig(opts.fRequestedColorType);
if (!bitmap.canCopyTo(config)) {
SkASSERT(bitmap.config() != config);
return NULL; // Can not translate to needed config.
}
info.fWidth = bitmap.width();
info.fHeight = bitmap.height();
info.fColorType = opts.fRequestedColorType;
info.fAlphaType = bitmap.alphaType();
// Sanity check.
SkDEBUGCODE(SkColorType tmp;)
SkASSERT(SkBitmapConfigToColorType(config, &tmp));
SkASSERT(tmp == opts.fRequestedColorType);
}
return SkNEW_ARGS(SkDecodingImageGenerator,
(data, autoStream.detach(), info,
opts.fSampleSize, opts.fDitherImage, config));
}

View File

@ -8,113 +8,134 @@
#ifndef SkDecodingImageGenerator_DEFINED
#define SkDecodingImageGenerator_DEFINED
#include "SkDiscardableMemory.h"
#include "SkBitmap.h"
#include "SkImageGenerator.h"
#include "SkImageInfo.h"
class SkBitmap;
class SkData;
class SkStreamRewindable;
/**
* Calls into SkImageDecoder::DecodeMemoryToTarget to implement a
* SkImageGenerator
* An implementation of SkImageGenerator that calls into
* SkImageDecoder.
*/
class SkDecodingImageGenerator : public SkImageGenerator {
public:
/*
* The constructor will take a reference to the SkData. The
* destructor will unref() it.
*/
explicit SkDecodingImageGenerator(SkData* data);
/*
* The SkData version of this constructor is preferred. If the
* stream has an underlying SkData (such as a SkMemoryStream)
* pass that in.
virtual ~SkDecodingImageGenerator();
virtual SkData* refEncodedData() SK_OVERRIDE;
// This implementaion of getInfo() always returns true.
virtual bool getInfo(SkImageInfo* info) SK_OVERRIDE;
virtual bool getPixels(const SkImageInfo& info,
void* pixels,
size_t rowBytes) SK_OVERRIDE;
/**
* These options will be passed on to the image decoder. The
* defaults are sensible.
*
* This object will unref the stream when done. Since streams
* have internal state (position), the caller should not pass a
* shared stream in. Pass either a new duplicated stream in or
* transfer ownership of the stream. In the latter case, be sure
* that there are no other consumers of the stream who will
* modify the stream's position. This constructor asserts
* @param fSampleSize If set to > 1, tells the decoder to return a
* smaller than original bitmap, sampling 1 pixel for
* every size pixels. e.g. if sample size is set to 3,
* then the returned bitmap will be 1/3 as wide and high,
* and will contain 1/9 as many pixels as the original.
* Note: this is a hint, and the codec may choose to
* ignore this, or only approximate the sample size.
*
* @param fDitherImage Set to true if the the decoder should try to
* dither the resulting image when decoding to a smaller
* color-space. The default is true.
*
* @param fRequestedColorType If not given, then use whichever
* config the decoder wants. Else try to use this color
* type. If the decoder won't support this color type,
* SkDecodingImageGenerator::Create will return
* NULL. kIndex_8_SkColorType is not supported.
*/
struct Options {
Options()
: fSampleSize(1)
, fDitherImage(true)
, fUseRequestedColorType(false)
, fRequestedColorType() { }
Options(int sampleSize, bool dither)
: fSampleSize(sampleSize)
, fDitherImage(dither)
, fUseRequestedColorType(false)
, fRequestedColorType() { }
Options(int sampleSize, bool dither, SkColorType colorType)
: fSampleSize(sampleSize)
, fDitherImage(dither)
, fUseRequestedColorType(true)
, fRequestedColorType(colorType) { }
const int fSampleSize;
const bool fDitherImage;
const bool fUseRequestedColorType;
const SkColorType fRequestedColorType;
};
/**
* These two functions return a SkImageGenerator that calls into
* SkImageDecoder. They return NULL on failure.
*
* The SkData version of this function is preferred. If the stream
* has an underlying SkData (such as a SkMemoryStream) pass that in.
*
* This object will unref the stream when done or on failure. Since
* streams have internal state (position), the caller should not pass
* a shared stream in. Pass either a new duplicated stream in or
* transfer ownership of the stream. This factory asserts
* stream->unique().
*
* For example:
* SkStreamRewindable* stream;
* ...
* SkImageGenerator* gen
* = SkNEW_ARGS(SkDecodingImageGenerator,
* (stream->duplicate()));
* = SkDecodingImageGenerator::Create(
* stream->duplicate(), SkDecodingImageGenerator::Options());
* ...
* SkDELETE(gen);
*
* @param Options (see above)
*
* @return NULL on failure, a new SkImageGenerator on success.
*/
explicit SkDecodingImageGenerator(SkStreamRewindable* stream);
virtual ~SkDecodingImageGenerator();
virtual SkData* refEncodedData() SK_OVERRIDE;
virtual bool getInfo(SkImageInfo* info) SK_OVERRIDE;
virtual bool getPixels(const SkImageInfo& info,
void* pixels,
size_t rowBytes) SK_OVERRIDE;
static SkImageGenerator* Create(SkStreamRewindable* stream,
const Options& opt);
/**
* Install the SkData into the destination bitmap, using a new
* SkDiscardablePixelRef and a new SkDecodingImageGenerator.
*
* @param data Contains the encoded image data that will be used
* by the SkDecodingImageGenerator. Will be ref()ed.
*
* @param destination Upon success, this bitmap will be
* configured and have a pixelref installed.
*
* @param factory If not NULL, this object will be used as a
* source of discardable memory when decoding. If NULL, then
* SkDiscardableMemory::Create() will be called.
*
* @return true iff successful.
* @param data Contains the encoded image data that will be used by
* the SkDecodingImageGenerator. Will be ref()ed by the
* SkImageGenerator constructor and and unref()ed on deletion.
*/
static bool Install(SkData* data, SkBitmap* destination,
SkDiscardableMemory::Factory* factory = NULL);
/**
* Install the stream into the destination bitmap, using a new
* SkDiscardablePixelRef and a new SkDecodingImageGenerator.
*
* The SkData version of this function is preferred. If the
* stream has an underlying SkData (such as a SkMemoryStream)
* pass that in.
*
* @param stream The source of encoded data that will be passed
* to the decoder. The installed SkDecodingImageGenerator will
* unref the stream when done. If false is returned, this
* function will perform the unref. Since streams have internal
* state (position), the caller should not pass a shared stream
* in. Pass either a new duplicated stream in or transfer
* ownership of the stream. In the latter case, be sure that
* there are no other consumers of the stream who will modify the
* stream's position. This function will fail if
* (!stream->unique()).
*
* @param destination Upon success, this bitmap will be
* configured and have a pixelref installed.
*
* @param factory If not NULL, this object will be used as a
* source of discardable memory when decoding. If NULL, then
* SkDiscardableMemory::Create() will be called.
*
* @return true iff successful.
*/
static bool Install(SkStreamRewindable* stream, SkBitmap* destination,
SkDiscardableMemory::Factory* factory = NULL);
static SkImageGenerator* Create(SkData* data, const Options& opt);
private:
SkData* fData;
SkStreamRewindable* fStream;
SkImageInfo fInfo;
bool fHasInfo;
bool fDoCopyTo;
SkData* fData;
SkStreamRewindable* fStream;
const SkImageInfo fInfo;
const int fSampleSize;
const bool fDitherImage;
const SkBitmap::Config fRequestedConfig;
SkDecodingImageGenerator(SkData* data,
SkStreamRewindable* stream,
const SkImageInfo& info,
int sampleSize,
bool ditherImage,
SkBitmap::Config requestedConfig);
static SkImageGenerator* Create(SkData*, SkStreamRewindable*,
const Options&);
typedef SkImageGenerator INHERITED;
};
// // Example of most basic use case:
//
// bool install_data(SkData* data, SkBitmap* dst) {
// return SkInstallDiscardablePixelRef(
// SkDecodingImageGenerator::Create(
// data, SkDecodingImageGenerator::Options()), dst, NULL);
// }
// bool install_stream(SkStreamRewindable* stream, SkBitmap* dst) {
// return SkInstallDiscardablePixelRef(
// SkDecodingImageGenerator::Create(
// stream, SkDecodingImageGenerator::Options()), dst, NULL);
// }
#endif // SkDecodingImageGenerator_DEFINED

View File

@ -12,7 +12,6 @@
bool SkCachingPixelRef::Install(SkImageGenerator* generator,
SkBitmap* dst) {
SkImageInfo info;
SkASSERT(generator != NULL);
SkASSERT(dst != NULL);
if ((NULL == generator)
|| !(generator->getInfo(&info))

View File

@ -72,20 +72,19 @@ bool SkInstallDiscardablePixelRef(SkImageGenerator* generator,
SkBitmap* dst,
SkDiscardableMemory::Factory* factory) {
SkImageInfo info;
SkASSERT(generator != NULL);
if ((NULL == generator)
|| (!generator->getInfo(&info))
SkAutoTDelete<SkImageGenerator> autoGenerator(generator);
if ((NULL == autoGenerator.get())
|| (!autoGenerator->getInfo(&info))
|| (!dst->setConfig(info, 0))) {
SkDELETE(generator);
return false;
}
SkASSERT(dst->config() != SkBitmap::kNo_Config);
if (dst->empty()) { // Use a normal pixelref.
SkDELETE(generator); // Do not need this anymore.
if (dst->empty()) { // Use a normal pixelref.
return dst->allocPixels(NULL, NULL);
}
SkAutoTUnref<SkDiscardablePixelRef> ref(SkNEW_ARGS(SkDiscardablePixelRef,
(info, generator, dst->rowBytes(), factory)));
SkAutoTUnref<SkDiscardablePixelRef> ref(
SkNEW_ARGS(SkDiscardablePixelRef,
(info, autoGenerator.detach(), dst->rowBytes(), factory)));
dst->setPixelRef(ref);
return true;
}

View File

@ -13,6 +13,15 @@
#include "SkImageInfo.h"
#include "SkPixelRef.h"
/**
* A PixelRef backed by SkDiscardableMemory, with the ability to
* re-generate the pixels (via a SkImageGenerator) if the DM is
* purged.
*
* Since SkColorTable is reference-counted, we do not support indexed
* color with this class; there would be no way for the discardable
* memory system to unref the color table.
*/
class SkDiscardablePixelRef : public SkPixelRef {
public:
SK_DECLARE_UNFLATTENABLE_OBJECT()

View File

@ -146,11 +146,14 @@ static void test_three_encodings(skiatest::Reporter* reporter,
////////////////////////////////////////////////////////////////////////////////
static bool install_skCachingPixelRef(SkData* encoded, SkBitmap* dst) {
return SkCachingPixelRef::Install(
SkNEW_ARGS(SkDecodingImageGenerator, (encoded)), dst);
SkDecodingImageGenerator::Create(
encoded, SkDecodingImageGenerator::Options()), dst);
}
static bool install_skDiscardablePixelRef(SkData* encoded, SkBitmap* dst) {
// Use system-default discardable memory.
return SkDecodingImageGenerator::Install(encoded, dst, NULL);
return SkInstallDiscardablePixelRef(
SkDecodingImageGenerator::Create(
encoded, SkDecodingImageGenerator::Options()), dst, NULL);
}
////////////////////////////////////////////////////////////////////////////////
@ -213,10 +216,12 @@ public:
}
return true;
}
private:
const TestType fType;
skiatest::Reporter* const fReporter;
};
void CheckTestImageGeneratorBitmap(skiatest::Reporter* reporter,
const SkBitmap& bm) {
REPORTER_ASSERT(reporter, TestImageGenerator::Width() == bm.width());

View File

@ -13,10 +13,13 @@
#include "SkColorPriv.h"
#include "SkData.h"
#include "SkDecodingImageGenerator.h"
#include "SkDiscardableMemoryPool.h"
#include "SkForceLinking.h"
#include "SkGradientShader.h"
#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkImageGenerator.h"
#include "SkImagePriv.h"
#include "SkOSFile.h"
#include "SkPoint.h"
#include "SkShader.h"
@ -153,7 +156,7 @@ static void test_unpremul(skiatest::Reporter* reporter) {
if (iter.next(&basename)) {
do {
SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
//SkDebugf("about to decode \"%s\"\n", filename.c_str());
// SkDebugf("about to decode \"%s\"\n", filename.c_str());
compare_unpremul(reporter, filename);
} while (iter.next(&basename));
} else {
@ -201,7 +204,7 @@ static void test_stream_life() {
SkImageEncoder::kWEBP_Type,
};
for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) {
//SkDebugf("encoding to %i\n", i);
// SkDebugf("encoding to %i\n", i);
SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i]));
if (NULL == stream.get()) {
SkDebugf("no stream\n");
@ -228,7 +231,7 @@ static void test_stream_life() {
// Test inside SkScaledBitmapSampler.cpp
extern void test_row_proc_choice();
#endif // SK_DEBUG
#endif // SK_DEBUG
DEF_TEST(ImageDecoding, reporter) {
test_unpremul(reporter);
@ -239,6 +242,31 @@ DEF_TEST(ImageDecoding, reporter) {
}
////////////////////////////////////////////////////////////////////////////////
namespace {
// expected output for 8x8 bitmap
const int kExpectedWidth = 8;
const int kExpectedHeight = 8;
const SkColor kExpectedPixels[] = {
0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
};
SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
== SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
} // namespace
DEF_TEST(WebP, reporter) {
const unsigned char encodedWebP[] = {
@ -269,38 +297,26 @@ DEF_TEST(WebP, reporter) {
0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
};
const SkColor thePixels[] = {
0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
};
SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
sizeof(encodedWebP)));
SkBitmap bm;
bool success = SkDecodingImageGenerator::Install(encoded, &bm, NULL);
bool success = SkInstallDiscardablePixelRef(
SkDecodingImageGenerator::Create(encoded,
SkDecodingImageGenerator::Options()), &bm, NULL);
REPORTER_ASSERT(reporter, success);
if (!success) {
return;
}
SkAutoLockPixels alp(bm);
bool rightSize = SK_ARRAY_COUNT(thePixels) == bm.width() * bm.height();
bool rightSize = ((kExpectedWidth == bm.width())
&& (kExpectedHeight == bm.height()));
REPORTER_ASSERT(reporter, rightSize);
if (rightSize) {
bool error = false;
const SkColor* correctPixel = thePixels;
const SkColor* correctPixel = kExpectedPixels;
for (int y = 0; y < bm.height(); ++y) {
for (int x = 0; x < bm.width(); ++x) {
error |= (*correctPixel != bm.getColor(x, y));
@ -310,3 +326,256 @@ DEF_TEST(WebP, reporter) {
REPORTER_ASSERT(reporter, !error);
}
}
////////////////////////////////////////////////////////////////////////////////
// example of how Android will do this inside their BitmapFactory
static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
SkStreamRewindable* stream,
int sampleSize, bool ditherImage) {
SkASSERT(bitmap != NULL);
SkASSERT(stream != NULL);
SkASSERT(stream->rewind());
SkASSERT(stream->unique());
SkColorType colorType;
if (!SkBitmapConfigToColorType(bitmap->config(), &colorType)) {
return NULL;
}
SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
SkAutoTDelete<SkImageGenerator> gen(
SkDecodingImageGenerator::Create(stream, opts));
SkImageInfo info;
if ((NULL == gen.get()) || !gen->getInfo(&info)) {
return NULL;
}
SkDiscardableMemory::Factory* factory = NULL;
if (info.getSafeSize(info.minRowBytes()) < (32 * 1024)) {
// only use ashmem for large images, since mmaps come at a price
factory = SkGetGlobalDiscardableMemoryPool();
}
if (SkInstallDiscardablePixelRef(gen.detach(), bitmap, factory)) {
return bitmap->pixelRef();
}
return NULL;
}
/**
* A test for the SkDecodingImageGenerator::Create and
* SkInstallDiscardablePixelRef functions.
*/
DEF_TEST(ImprovedBitmapFactory, reporter) {
SkString resourcePath = skiatest::Test::GetResourcePath();
SkString directory = SkOSPath::SkPathJoin(resourcePath.c_str(), "encoding");
SkString path = SkOSPath::SkPathJoin(directory.c_str(), "randPixels.png");
SkAutoTUnref<SkStreamRewindable> stream(
SkStream::NewFromFile(path.c_str()));
if (sk_exists(path.c_str())) {
SkBitmap bm;
SkAssertResult(bm.setConfig(SkBitmap::kARGB_8888_Config, 1, 1));
REPORTER_ASSERT(reporter,
NULL != install_pixel_ref(&bm, stream.detach(), 1, true));
SkAutoLockPixels alp(bm);
REPORTER_ASSERT(reporter, NULL != bm.getPixels());
}
}
////////////////////////////////////////////////////////////////////////////////
#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
static inline bool check_rounding(int value, int dividend, int divisor) {
// returns true if (dividend/divisor) rounds up OR down to value
return (((divisor * value) > (dividend - divisor))
&& ((divisor * value) < (dividend + divisor)));
}
#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
#define kBackwards_SkColorType kRGBA_8888_SkColorType
#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
#define kBackwards_SkColorType kBGRA_8888_SkColorType
#else
#error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
#endif
static inline const char* SkColorType_to_string(SkColorType colorType) {
switch(colorType) {
case kAlpha_8_SkColorType: return "Alpha_8";
case kRGB_565_SkColorType: return "RGB_565";
case kARGB_4444_SkColorType: return "ARGB_4444";
case kPMColor_SkColorType: return "PMColor";
case kBackwards_SkColorType: return "Backwards";
case kIndex_8_SkColorType: return "Index_8";
default: return "ERROR";
}
}
/**
* Given either a SkStream or a SkData, try to decode the encoded
* image using the specified options and report errors.
*/
static void test_options(skiatest::Reporter* reporter,
const SkDecodingImageGenerator::Options& opts,
SkStreamRewindable* encodedStream,
SkData* encodedData,
bool useData,
const SkString& path) {
SkBitmap bm;
bool success = false;
if (useData) {
if (NULL == encodedData) {
return;
}
success = SkInstallDiscardablePixelRef(
SkDecodingImageGenerator::Create(encodedData, opts), &bm, NULL);
} else {
if (NULL == encodedStream) {
return;
}
success = SkInstallDiscardablePixelRef(
SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts),
&bm, NULL);
}
if (!success) {
if (opts.fUseRequestedColorType
&& (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
return; // Ignore known conversion inabilities.
}
// If we get here, it's a failure and we will need more
// information about why it failed.
reporter->reportFailed(SkStringPrintf(
"Bounds decode failed "
"[sampleSize=%d dither=%s colorType=%s %s] %s:%d",
opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"),
(opts.fUseRequestedColorType
? SkColorType_to_string(opts.fRequestedColorType) : "(none)"),
path.c_str(), __FILE__, __LINE__));
return;
}
#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
// Android is the only system that use Skia's image decoders in
// production. For now, we'll only verify that samplesize works
// on systems where it already is known to work.
REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
opts.fSampleSize));
REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
opts.fSampleSize));
#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
SkAutoLockPixels alp(bm);
if (bm.getPixels() == NULL) {
reporter->reportFailed(SkStringPrintf(
"Pixel decode failed "
"[sampleSize=%d dither=%s colorType=%s %s] %s:%d",
opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"),
(opts.fUseRequestedColorType
? SkColorType_to_string(opts.fRequestedColorType) : "(none)"),
path.c_str(), __FILE__, __LINE__));
return;
}
SkBitmap::Config requestedConfig
= SkColorTypeToBitmapConfig(opts.fRequestedColorType);
REPORTER_ASSERT(reporter,
(!opts.fUseRequestedColorType)
|| (bm.config() == requestedConfig));
// Condition under which we should check the decoding results:
if ((SkBitmap::kARGB_8888_Config == bm.config())
&& (!path.endsWith(".jpg")) // lossy
&& (opts.fSampleSize == 1)) { // scaled
const SkColor* correctPixels = kExpectedPixels;
SkASSERT(bm.height() == kExpectedHeight);
SkASSERT(bm.width() == kExpectedWidth);
int pixelErrors = 0;
for (int y = 0; y < bm.height(); ++y) {
for (int x = 0; x < bm.width(); ++x) {
if (*correctPixels != bm.getColor(x, y)) {
++pixelErrors;
}
++correctPixels;
}
}
if (pixelErrors != 0) {
reporter->reportFailed(SkStringPrintf(
"Pixel-level mismatch (%d of %d) [sampleSize=%d "
"dither=%s colorType=%s %s] %s:%d",
pixelErrors, kExpectedHeight * kExpectedWidth,
opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"),
(opts.fUseRequestedColorType
? SkColorType_to_string(opts.fRequestedColorType)
: "(none)"), path.c_str(), __FILE__, __LINE__));
}
}
}
/**
* SkDecodingImageGenerator has an Options struct which lets the
* client of the generator set sample size, dithering, and bitmap
* config. This test loops through many possible options and tries
* them on a set of 5 small encoded images (each in a different
* format). We test both SkData and SkStreamRewindable decoding.
*/
DEF_TEST(ImageDecoderOptions, reporter) {
const char* files[] = {
"randPixels.bmp",
"randPixels.jpg",
"randPixels.png",
"randPixels.webp",
#if !defined(SK_BUILD_FOR_WIN)
// TODO(halcanary): Find out why this fails sometimes.
"randPixels.gif",
#endif
};
SkString resourceDir = skiatest::Test::GetResourcePath();
SkString directory = SkOSPath::SkPathJoin(resourceDir.c_str(), "encoding");
if (!sk_exists(directory.c_str())) {
return;
}
int scaleList[] = {1, 2, 3, 4};
bool ditherList[] = {true, false};
SkColorType colorList[] = {
kAlpha_8_SkColorType,
kRGB_565_SkColorType,
kARGB_4444_SkColorType, // Most decoders will fail on 4444.
kPMColor_SkColorType
// Note that indexed color is left out of the list. Lazy
// decoding doesn't do indexed color.
};
const bool useDataList[] = {true, false};
for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
SkString path = SkOSPath::SkPathJoin(directory.c_str(), files[fidx]);
if (!sk_exists(path.c_str())) {
continue;
}
SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
REPORTER_ASSERT(reporter, encodedData.get() != NULL);
SkAutoTUnref<SkStreamRewindable> encodedStream(
SkStream::NewFromFile(path.c_str()));
REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
SkDecodingImageGenerator::Options opts(scaleList[i],
ditherList[j],
colorList[k]);
test_options(reporter, opts, encodedStream, encodedData,
useDataList[m], path);
}
SkDecodingImageGenerator::Options options(scaleList[i],
ditherList[j]);
test_options(reporter, options, encodedStream, encodedData,
useDataList[m], path);
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -13,6 +13,8 @@
#include "SkData.h"
#include "SkDecodingImageGenerator.h"
#include "SkError.h"
#include "SkImageEncoder.h"
#include "SkImageGenerator.h"
#include "SkPaint.h"
#include "SkPicture.h"
#include "SkPictureUtils.h"
@ -338,8 +340,6 @@ static void test_bad_bitmap() {
}
#endif
#include "SkImageEncoder.h"
static SkData* encode_bitmap_to_data(size_t* offset, const SkBitmap& bm) {
*offset = 0;
return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
@ -381,7 +381,8 @@ static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
SkAutoDataUnref data(wStream.copyToData());
SkBitmap bm;
bool installSuccess = SkDecodingImageGenerator::Install(data, &bm);
bool installSuccess = SkInstallDiscardablePixelRef(
SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL);
REPORTER_ASSERT(reporter, installSuccess);
// Write both bitmaps to pictures, and ensure that the resulting data streams are the same.

View File

@ -31,10 +31,11 @@ bool sk_tools::LazyDecodeBitmap(const void* src,
return false;
}
SkAutoTDelete<SkImageGenerator> gen(SkNEW_ARGS(SkDecodingImageGenerator,
(data)));
SkAutoTDelete<SkImageGenerator> gen(
SkDecodingImageGenerator::Create(
data, SkDecodingImageGenerator::Options()));
SkImageInfo info;
if (!gen->getInfo(&info)) {
if ((NULL == gen.get()) || !gen->getInfo(&info)) {
return false;
}
SkDiscardableMemory::Factory* pool = NULL;