/* * 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(bpp * info.fWidth) > 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(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 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; 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)); }