blob: 153d1e220bc6be643dea492e8c4d74192872268c [file] [log] [blame]
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#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 {
/**
* Special allocator used by getPixels(). Uses preallocated memory
* provided.
*/
class TargetAllocator : public SkBitmap::Allocator {
public:
TargetAllocator(void* target,
size_t rowBytes,
int width,
int height,
SkBitmap::Config config)
: fTarget(target)
, fRowBytes(rowBytes)
, fWidth(width)
, fHeight(height)
, fConfig(config) { }
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);
}
// 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; // 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;
};
// TODO(halcanary): Give this macro a better name and move it into SkTypes.h
#ifdef SK_DEBUG
#define SkCheckResult(expr, value) SkASSERT((value) == (expr))
#else
#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);
}
// TODO(halcanary): SkStreamRewindable needs a refData() function
// which returns a cheap copy of the underlying data.
if (!fStream->rewind()) {
return NULL;
}
size_t length = fStream->getLength();
if (0 == length) {
return NULL;
}
void* buffer = sk_malloc_flags(length, 0);
SkCheckResult(fStream->read(buffer, length), length);
fData = SkData::NewFromMalloc(buffer, length);
return SkSafeRef(fData);
}
bool SkDecodingImageGenerator::getPixels(const SkImageInfo& info,
void* pixels,
size_t rowBytes) {
if (NULL == pixels) {
return false;
}
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;
}
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;
}
decoder->setDitherImage(fDitherImage);
decoder->setSampleSize(fSampleSize);
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 (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;
}
SkImageGenerator* SkDecodingImageGenerator::Create(
SkData* data,
const SkDecodingImageGenerator::Options& opts) {
SkASSERT(data != NULL);
if (NULL == data) {
return NULL;
}
SkStreamRewindable* stream = SkNEW_ARGS(SkMemoryStream, (data));
SkASSERT(stream != NULL);
SkASSERT(stream->unique());
return SkDecodingImageGenerator::Create(data, stream, opts);
}
SkImageGenerator* SkDecodingImageGenerator::Create(
SkStreamRewindable* stream,
const SkDecodingImageGenerator::Options& opts) {
SkASSERT(stream != NULL);
SkASSERT(stream->unique());
if ((stream == NULL) || !stream->unique()) {
SkSafeUnref(stream);
return NULL;
}
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));
}