michael@0: /* michael@0: * Copyright 2013 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkData.h" michael@0: #include "SkDecodingImageGenerator.h" michael@0: #include "SkImageDecoder.h" michael@0: #include "SkImageInfo.h" michael@0: #include "SkImageGenerator.h" michael@0: #include "SkImagePriv.h" michael@0: #include "SkStream.h" michael@0: #include "SkUtils.h" michael@0: michael@0: static bool equal_modulo_alpha(const SkImageInfo& a, const SkImageInfo& b) { michael@0: return a.width() == b.width() && a.height() == b.height() && michael@0: a.colorType() == b.colorType(); michael@0: } michael@0: michael@0: namespace { michael@0: /** michael@0: * Special allocator used by getPixels(). Uses preallocated memory michael@0: * provided if possible, else fall-back on the default allocator michael@0: */ michael@0: class TargetAllocator : public SkBitmap::Allocator { michael@0: public: michael@0: TargetAllocator(const SkImageInfo& info, michael@0: void* target, michael@0: size_t rowBytes) michael@0: : fInfo(info) michael@0: , fTarget(target) michael@0: , fRowBytes(rowBytes) michael@0: {} michael@0: michael@0: bool isReady() { return (fTarget != NULL); } michael@0: michael@0: virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) { michael@0: if (NULL == fTarget || !equal_modulo_alpha(fInfo, bm->info())) { michael@0: // Call default allocator. michael@0: return bm->allocPixels(NULL, ct); michael@0: } michael@0: michael@0: // TODO(halcanary): verify that all callers of this function michael@0: // will respect new RowBytes. Will be moot once rowbytes belongs michael@0: // to PixelRef. michael@0: bm->installPixels(fInfo, fTarget, fRowBytes, NULL, NULL); michael@0: michael@0: fTarget = NULL; // never alloc same pixels twice! michael@0: return true; michael@0: } michael@0: michael@0: private: michael@0: const SkImageInfo fInfo; michael@0: void* fTarget; // Block of memory to be supplied as pixel memory michael@0: // in allocPixelRef. Must be large enough to hold michael@0: // a bitmap described by fInfo and fRowBytes michael@0: const size_t fRowBytes; // rowbytes for the destination bitmap michael@0: michael@0: typedef SkBitmap::Allocator INHERITED; michael@0: }; michael@0: michael@0: // TODO(halcanary): Give this macro a better name and move it into SkTypes.h michael@0: #ifdef SK_DEBUG michael@0: #define SkCheckResult(expr, value) SkASSERT((value) == (expr)) michael@0: #else michael@0: #define SkCheckResult(expr, value) (void)(expr) michael@0: #endif michael@0: michael@0: #ifdef SK_DEBUG michael@0: inline bool check_alpha(SkAlphaType reported, SkAlphaType actual) { michael@0: return ((reported == actual) michael@0: || ((reported == kPremul_SkAlphaType) michael@0: && (actual == kOpaque_SkAlphaType))); michael@0: } michael@0: #endif // SK_DEBUG michael@0: michael@0: } // namespace michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkDecodingImageGenerator::SkDecodingImageGenerator( michael@0: SkData* data, michael@0: SkStreamRewindable* stream, michael@0: const SkImageInfo& info, michael@0: int sampleSize, michael@0: bool ditherImage) michael@0: : fData(data) michael@0: , fStream(stream) michael@0: , fInfo(info) michael@0: , fSampleSize(sampleSize) michael@0: , fDitherImage(ditherImage) michael@0: { michael@0: SkASSERT(stream != NULL); michael@0: SkSafeRef(fData); // may be NULL. michael@0: } michael@0: michael@0: SkDecodingImageGenerator::~SkDecodingImageGenerator() { michael@0: SkSafeUnref(fData); michael@0: fStream->unref(); michael@0: } michael@0: michael@0: bool SkDecodingImageGenerator::getInfo(SkImageInfo* info) { michael@0: if (info != NULL) { michael@0: *info = fInfo; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: SkData* SkDecodingImageGenerator::refEncodedData() { michael@0: // This functionality is used in `gm --serialize` michael@0: // Does not encode options. michael@0: if (fData != NULL) { michael@0: return SkSafeRef(fData); michael@0: } michael@0: // TODO(halcanary): SkStreamRewindable needs a refData() function michael@0: // which returns a cheap copy of the underlying data. michael@0: if (!fStream->rewind()) { michael@0: return NULL; michael@0: } michael@0: size_t length = fStream->getLength(); michael@0: if (0 == length) { michael@0: return NULL; michael@0: } michael@0: void* buffer = sk_malloc_flags(length, 0); michael@0: SkCheckResult(fStream->read(buffer, length), length); michael@0: fData = SkData::NewFromMalloc(buffer, length); michael@0: return SkSafeRef(fData); michael@0: } michael@0: michael@0: bool SkDecodingImageGenerator::getPixels(const SkImageInfo& info, michael@0: void* pixels, michael@0: size_t rowBytes) { michael@0: if (NULL == pixels) { michael@0: return false; michael@0: } michael@0: if (fInfo != info) { michael@0: // The caller has specified a different info. This is an michael@0: // error for this kind of SkImageGenerator. Use the Options michael@0: // to change the settings. michael@0: return false; michael@0: } michael@0: if (info.minRowBytes() > rowBytes) { michael@0: // The caller has specified a bad rowBytes. michael@0: return false; michael@0: } michael@0: michael@0: SkAssertResult(fStream->rewind()); michael@0: SkAutoTDelete decoder(SkImageDecoder::Factory(fStream)); michael@0: if (NULL == decoder.get()) { michael@0: return false; michael@0: } michael@0: decoder->setDitherImage(fDitherImage); michael@0: decoder->setSampleSize(fSampleSize); michael@0: michael@0: SkBitmap bitmap; michael@0: TargetAllocator allocator(fInfo, pixels, rowBytes); michael@0: decoder->setAllocator(&allocator); michael@0: // TODO: need to be able to pass colortype directly to decoder michael@0: SkBitmap::Config legacyConfig = SkColorTypeToBitmapConfig(info.colorType()); michael@0: bool success = decoder->decode(fStream, &bitmap, legacyConfig, michael@0: SkImageDecoder::kDecodePixels_Mode); michael@0: decoder->setAllocator(NULL); michael@0: if (!success) { michael@0: return false; michael@0: } michael@0: if (allocator.isReady()) { // Did not use pixels! michael@0: SkBitmap bm; michael@0: SkASSERT(bitmap.canCopyTo(info.colorType())); michael@0: bool copySuccess = bitmap.copyTo(&bm, info.colorType(), &allocator); michael@0: if (!copySuccess || allocator.isReady()) { michael@0: SkDEBUGFAIL("bitmap.copyTo(requestedConfig) failed."); michael@0: // Earlier we checked canCopyto(); we expect consistency. michael@0: return false; michael@0: } michael@0: SkASSERT(check_alpha(info.alphaType(), bm.alphaType())); michael@0: } else { michael@0: SkASSERT(check_alpha(info.alphaType(), bitmap.alphaType())); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: SkImageGenerator* SkDecodingImageGenerator::Create( michael@0: SkData* data, michael@0: const SkDecodingImageGenerator::Options& opts) { michael@0: SkASSERT(data != NULL); michael@0: if (NULL == data) { michael@0: return NULL; michael@0: } michael@0: SkStreamRewindable* stream = SkNEW_ARGS(SkMemoryStream, (data)); michael@0: SkASSERT(stream != NULL); michael@0: SkASSERT(stream->unique()); michael@0: return SkDecodingImageGenerator::Create(data, stream, opts); michael@0: } michael@0: michael@0: SkImageGenerator* SkDecodingImageGenerator::Create( michael@0: SkStreamRewindable* stream, michael@0: const SkDecodingImageGenerator::Options& opts) { michael@0: SkASSERT(stream != NULL); michael@0: SkASSERT(stream->unique()); michael@0: if ((stream == NULL) || !stream->unique()) { michael@0: SkSafeUnref(stream); michael@0: return NULL; michael@0: } michael@0: return SkDecodingImageGenerator::Create(NULL, stream, opts); michael@0: } michael@0: michael@0: // A contructor-type function that returns NULL on failure. This michael@0: // prevents the returned SkImageGenerator from ever being in a bad michael@0: // state. Called by both Create() functions michael@0: SkImageGenerator* SkDecodingImageGenerator::Create( michael@0: SkData* data, michael@0: SkStreamRewindable* stream, michael@0: const SkDecodingImageGenerator::Options& opts) { michael@0: SkASSERT(stream); michael@0: SkAutoTUnref autoStream(stream); // always unref this. michael@0: if (opts.fUseRequestedColorType && michael@0: (kIndex_8_SkColorType == opts.fRequestedColorType)) { michael@0: // We do not support indexed color with SkImageGenerators, michael@0: return NULL; michael@0: } michael@0: SkAssertResult(autoStream->rewind()); michael@0: SkAutoTDelete decoder(SkImageDecoder::Factory(autoStream)); michael@0: if (NULL == decoder.get()) { michael@0: return NULL; michael@0: } michael@0: SkBitmap bitmap; michael@0: decoder->setSampleSize(opts.fSampleSize); michael@0: if (!decoder->decode(stream, &bitmap, michael@0: SkImageDecoder::kDecodeBounds_Mode)) { michael@0: return NULL; michael@0: } michael@0: if (bitmap.config() == SkBitmap::kNo_Config) { michael@0: return NULL; michael@0: } michael@0: michael@0: SkImageInfo info = bitmap.info(); michael@0: michael@0: if (!opts.fUseRequestedColorType) { michael@0: // Use default michael@0: if (kIndex_8_SkColorType == bitmap.colorType()) { michael@0: // We don't support kIndex8 because we don't support michael@0: // colortables in this workflow. michael@0: info.fColorType = kPMColor_SkColorType; michael@0: } michael@0: } else { michael@0: if (!bitmap.canCopyTo(opts.fRequestedColorType)) { michael@0: SkASSERT(bitmap.colorType() != opts.fRequestedColorType); michael@0: return NULL; // Can not translate to needed config. michael@0: } michael@0: info.fColorType = opts.fRequestedColorType; michael@0: } michael@0: return SkNEW_ARGS(SkDecodingImageGenerator, michael@0: (data, autoStream.detach(), info, michael@0: opts.fSampleSize, opts.fDitherImage)); michael@0: }