diff -r 000000000000 -r 6474c204b198 gfx/skia/trunk/src/images/SkDecodingImageGenerator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/skia/trunk/src/images/SkDecodingImageGenerator.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,256 @@ +/* + * 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" + +static bool equal_modulo_alpha(const SkImageInfo& a, const SkImageInfo& b) { + return a.width() == b.width() && a.height() == b.height() && + a.colorType() == b.colorType(); +} + +namespace { +/** + * Special allocator used by getPixels(). Uses preallocated memory + * provided if possible, else fall-back on the default allocator + */ +class TargetAllocator : public SkBitmap::Allocator { +public: + TargetAllocator(const SkImageInfo& info, + void* target, + size_t rowBytes) + : fInfo(info) + , fTarget(target) + , fRowBytes(rowBytes) + {} + + bool isReady() { return (fTarget != NULL); } + + virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) { + if (NULL == fTarget || !equal_modulo_alpha(fInfo, bm->info())) { + // Call default allocator. + return bm->allocPixels(NULL, ct); + } + + // TODO(halcanary): verify that all callers of this function + // will respect new RowBytes. Will be moot once rowbytes belongs + // to PixelRef. + bm->installPixels(fInfo, fTarget, fRowBytes, NULL, NULL); + + fTarget = NULL; // never alloc same pixels twice! + return true; + } + +private: + const 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 fInfo and fRowBytes + const size_t fRowBytes; // rowbytes for the destination bitmap + + 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) + : fData(data) + , fStream(stream) + , fInfo(info) + , fSampleSize(sampleSize) + , fDitherImage(ditherImage) +{ + 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; + } + if (info.minRowBytes() > rowBytes) { + // The caller has specified a bad rowBytes. + return false; + } + + SkAssertResult(fStream->rewind()); + SkAutoTDelete decoder(SkImageDecoder::Factory(fStream)); + if (NULL == decoder.get()) { + return false; + } + decoder->setDitherImage(fDitherImage); + decoder->setSampleSize(fSampleSize); + + SkBitmap bitmap; + TargetAllocator allocator(fInfo, pixels, rowBytes); + decoder->setAllocator(&allocator); + // TODO: need to be able to pass colortype directly to decoder + SkBitmap::Config legacyConfig = SkColorTypeToBitmapConfig(info.colorType()); + bool success = decoder->decode(fStream, &bitmap, legacyConfig, + SkImageDecoder::kDecodePixels_Mode); + decoder->setAllocator(NULL); + if (!success) { + return false; + } + if (allocator.isReady()) { // Did not use pixels! + SkBitmap bm; + SkASSERT(bitmap.canCopyTo(info.colorType())); + bool copySuccess = bitmap.copyTo(&bm, info.colorType(), &allocator); + if (!copySuccess || allocator.isReady()) { + SkDEBUGFAIL("bitmap.copyTo(requestedConfig) failed."); + // Earlier we checked canCopyto(); we expect consistency. + return false; + } + SkASSERT(check_alpha(info.alphaType(), bm.alphaType())); + } else { + SkASSERT(check_alpha(info.alphaType(), 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 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 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 = bitmap.info(); + + if (!opts.fUseRequestedColorType) { + // Use default + if (kIndex_8_SkColorType == bitmap.colorType()) { + // We don't support kIndex8 because we don't support + // colortables in this workflow. + info.fColorType = kPMColor_SkColorType; + } + } else { + if (!bitmap.canCopyTo(opts.fRequestedColorType)) { + SkASSERT(bitmap.colorType() != opts.fRequestedColorType); + return NULL; // Can not translate to needed config. + } + info.fColorType = opts.fRequestedColorType; + } + return SkNEW_ARGS(SkDecodingImageGenerator, + (data, autoStream.detach(), info, + opts.fSampleSize, opts.fDitherImage)); +}