diff options
Diffstat (limited to 'src/images')
-rw-r--r-- | src/images/SkDecodingImageGenerator.cpp | 307 | ||||
-rw-r--r-- | src/images/SkDecodingImageGenerator.h | 181 |
2 files changed, 294 insertions, 194 deletions
diff --git a/src/images/SkDecodingImageGenerator.cpp b/src/images/SkDecodingImageGenerator.cpp index a833c636ff..153d1e220b 100644 --- a/src/images/SkDecodingImageGenerator.cpp +++ b/src/images/SkDecodingImageGenerator.cpp @@ -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,50 +21,89 @@ 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; }; + +// 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) +SkDecodingImageGenerator::SkDecodingImageGenerator( + SkData* data, + SkStreamRewindable* stream, + const SkImageInfo& info, + int sampleSize, + bool ditherImage, + SkBitmap::Config requestedConfig) : 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()); + , fInfo(info) + , fSampleSize(sampleSize) + , fDitherImage(ditherImage) + , fRequestedConfig(requestedConfig) { + SkASSERT(stream != NULL); + SkSafeRef(fData); // may be NULL. } SkDecodingImageGenerator::~SkDecodingImageGenerator() { @@ -71,15 +111,16 @@ SkDecodingImageGenerator::~SkDecodingImageGenerator() { fStream->unref(); } -// 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 +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)) { - return false; - } - if (SkImageInfoToBitmapConfig(info) == SkBitmap::kNo_Config) { - return false; // Unsupported SkColorType. - } - 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. + // 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(SkImageInfoToBitmapConfig(info)); + int bpp = SkBitmap::ComputeBytesPerPixel(fRequestedConfig); if (static_cast<size_t>(bpp * info.fWidth) > rowBytes) { + // The caller has specified a bad rowBytes. return false; } - SkBitmap bitmap; - if (!bitmap.setConfig(info, rowBytes)) { + + SkAssertResult(fStream->rewind()); + SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); + if (NULL == decoder.get()) { 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; + } + 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); } - SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (stream))); - return SkInstallDiscardablePixelRef(gen, dst, factory); + return SkNEW_ARGS(SkDecodingImageGenerator, + (data, autoStream.detach(), info, + opts.fSampleSize, opts.fDitherImage, config)); } diff --git a/src/images/SkDecodingImageGenerator.h b/src/images/SkDecodingImageGenerator.h index dba234bcf1..12a49d59c4 100644 --- a/src/images/SkDecodingImageGenerator.h +++ b/src/images/SkDecodingImageGenerator.h @@ -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. - * - * 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 - * stream->unique(). - * - * For example: - * SkStreamRewindable* stream; - * ... - * SkImageGenerator* gen - * = SkNEW_ARGS(SkDecodingImageGenerator, - * (stream->duplicate())); - * ... - * SkDELETE(gen); - */ - explicit SkDecodingImageGenerator(SkStreamRewindable* stream); - 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; - /** - * 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. + * These options will be passed on to the image decoder. The + * defaults are sensible. * - * @param destination Upon success, this bitmap will be - * configured and have a pixelref installed. + * @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 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. + * @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. * - * @return true iff successful. + * @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. */ - static bool Install(SkData* data, SkBitmap* destination, - SkDiscardableMemory::Factory* factory = NULL); + 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; + }; + /** - * Install the stream into the destination bitmap, using a new - * SkDiscardablePixelRef and a new SkDecodingImageGenerator. + * 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. + * 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()). + * 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(). * - * @param destination Upon success, this bitmap will be - * configured and have a pixelref installed. + * For example: + * SkStreamRewindable* stream; + * ... + * SkImageGenerator* gen + * = SkDecodingImageGenerator::Create( + * stream->duplicate(), SkDecodingImageGenerator::Options()); + * ... + * SkDELETE(gen); * - * @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. + * @param Options (see above) * - * @return true iff successful. + * @return NULL on failure, a new SkImageGenerator on success. */ - static bool Install(SkStreamRewindable* stream, SkBitmap* destination, - SkDiscardableMemory::Factory* factory = NULL); + static SkImageGenerator* Create(SkStreamRewindable* stream, + const Options& opt); + + /** + * @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 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 |