michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project 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: michael@0: #include "SkImageDecoder.h" michael@0: #include "SkBitmap.h" michael@0: #include "SkImagePriv.h" michael@0: #include "SkPixelRef.h" michael@0: #include "SkStream.h" michael@0: #include "SkTemplates.h" michael@0: #include "SkCanvas.h" michael@0: michael@0: static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config; michael@0: michael@0: SkBitmap::Config SkImageDecoder::GetDeviceConfig() michael@0: { michael@0: return gDeviceConfig; michael@0: } michael@0: michael@0: void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config) michael@0: { michael@0: gDeviceConfig = config; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkImageDecoder::SkImageDecoder() michael@0: : fPeeker(NULL) michael@0: , fChooser(NULL) michael@0: , fAllocator(NULL) michael@0: , fSampleSize(1) michael@0: , fDefaultPref(SkBitmap::kNo_Config) michael@0: , fDitherImage(true) michael@0: , fUsePrefTable(false) michael@0: , fSkipWritingZeroes(false) michael@0: , fPreferQualityOverSpeed(false) michael@0: , fRequireUnpremultipliedColors(false) { michael@0: } michael@0: michael@0: SkImageDecoder::~SkImageDecoder() { michael@0: SkSafeUnref(fPeeker); michael@0: SkSafeUnref(fChooser); michael@0: SkSafeUnref(fAllocator); michael@0: } michael@0: michael@0: void SkImageDecoder::copyFieldsToOther(SkImageDecoder* other) { michael@0: if (NULL == other) { michael@0: return; michael@0: } michael@0: other->setPeeker(fPeeker); michael@0: other->setChooser(fChooser); michael@0: other->setAllocator(fAllocator); michael@0: other->setSampleSize(fSampleSize); michael@0: if (fUsePrefTable) { michael@0: other->setPrefConfigTable(fPrefTable); michael@0: } else { michael@0: other->fDefaultPref = fDefaultPref; michael@0: } michael@0: other->setDitherImage(fDitherImage); michael@0: other->setSkipWritingZeroes(fSkipWritingZeroes); michael@0: other->setPreferQualityOverSpeed(fPreferQualityOverSpeed); michael@0: other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors); michael@0: } michael@0: michael@0: SkImageDecoder::Format SkImageDecoder::getFormat() const { michael@0: return kUnknown_Format; michael@0: } michael@0: michael@0: const char* SkImageDecoder::getFormatName() const { michael@0: return GetFormatName(this->getFormat()); michael@0: } michael@0: michael@0: const char* SkImageDecoder::GetFormatName(Format format) { michael@0: switch (format) { michael@0: case kUnknown_Format: michael@0: return "Unknown Format"; michael@0: case kBMP_Format: michael@0: return "BMP"; michael@0: case kGIF_Format: michael@0: return "GIF"; michael@0: case kICO_Format: michael@0: return "ICO"; michael@0: case kJPEG_Format: michael@0: return "JPEG"; michael@0: case kPNG_Format: michael@0: return "PNG"; michael@0: case kWBMP_Format: michael@0: return "WBMP"; michael@0: case kWEBP_Format: michael@0: return "WEBP"; michael@0: default: michael@0: SkDEBUGFAIL("Invalid format type!"); michael@0: } michael@0: return "Unknown Format"; michael@0: } michael@0: michael@0: SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) { michael@0: SkRefCnt_SafeAssign(fPeeker, peeker); michael@0: return peeker; michael@0: } michael@0: michael@0: SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) { michael@0: SkRefCnt_SafeAssign(fChooser, chooser); michael@0: return chooser; michael@0: } michael@0: michael@0: SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) { michael@0: SkRefCnt_SafeAssign(fAllocator, alloc); michael@0: return alloc; michael@0: } michael@0: michael@0: void SkImageDecoder::setSampleSize(int size) { michael@0: if (size < 1) { michael@0: size = 1; michael@0: } michael@0: fSampleSize = size; michael@0: } michael@0: michael@0: bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width, michael@0: int height) const { michael@0: Chooser* chooser = fChooser; michael@0: michael@0: if (NULL == chooser) { // no chooser, we just say YES to decoding :) michael@0: return true; michael@0: } michael@0: chooser->begin(1); michael@0: chooser->inspect(0, config, width, height); michael@0: return chooser->choose() == 0; michael@0: } michael@0: michael@0: bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap, michael@0: SkColorTable* ctable) const { michael@0: return bitmap->allocPixels(fAllocator, ctable); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) { michael@0: fUsePrefTable = true; michael@0: fPrefTable = prefTable; michael@0: } michael@0: michael@0: SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth, michael@0: bool srcHasAlpha) const { michael@0: SkBitmap::Config config = SkBitmap::kNo_Config; michael@0: michael@0: if (fUsePrefTable) { michael@0: switch (srcDepth) { michael@0: case kIndex_SrcDepth: michael@0: config = srcHasAlpha ? fPrefTable.fPrefFor_8Index_YesAlpha_src michael@0: : fPrefTable.fPrefFor_8Index_NoAlpha_src; michael@0: break; michael@0: case k8BitGray_SrcDepth: michael@0: config = fPrefTable.fPrefFor_8Gray_src; michael@0: break; michael@0: case k32Bit_SrcDepth: michael@0: config = srcHasAlpha ? fPrefTable.fPrefFor_8bpc_YesAlpha_src michael@0: : fPrefTable.fPrefFor_8bpc_NoAlpha_src; michael@0: break; michael@0: } michael@0: } else { michael@0: config = fDefaultPref; michael@0: } michael@0: michael@0: if (SkBitmap::kNo_Config == config) { michael@0: config = SkImageDecoder::GetDeviceConfig(); michael@0: } michael@0: return config; michael@0: } michael@0: michael@0: bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, michael@0: SkBitmap::Config pref, Mode mode) { michael@0: // we reset this to false before calling onDecode michael@0: fShouldCancelDecode = false; michael@0: // assign this, for use by getPrefConfig(), in case fUsePrefTable is false michael@0: fDefaultPref = pref; michael@0: michael@0: // pass a temporary bitmap, so that if we return false, we are assured of michael@0: // leaving the caller's bitmap untouched. michael@0: SkBitmap tmp; michael@0: if (!this->onDecode(stream, &tmp, mode)) { michael@0: return false; michael@0: } michael@0: bm->swap(tmp); michael@0: return true; michael@0: } michael@0: michael@0: bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect, michael@0: SkBitmap::Config pref) { michael@0: // we reset this to false before calling onDecodeSubset michael@0: fShouldCancelDecode = false; michael@0: // assign this, for use by getPrefConfig(), in case fUsePrefTable is false michael@0: fDefaultPref = pref; michael@0: michael@0: return this->onDecodeSubset(bm, rect); michael@0: } michael@0: michael@0: bool SkImageDecoder::buildTileIndex(SkStreamRewindable* stream, michael@0: int *width, int *height) { michael@0: // we reset this to false before calling onBuildTileIndex michael@0: fShouldCancelDecode = false; michael@0: michael@0: return this->onBuildTileIndex(stream, width, height); michael@0: } michael@0: michael@0: bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize, michael@0: int dstX, int dstY, int width, int height, michael@0: int srcX, int srcY) { michael@0: int w = width / sampleSize; michael@0: int h = height / sampleSize; michael@0: if (src->config() == SkBitmap::kIndex8_Config) { michael@0: // kIndex8 does not allow drawing via an SkCanvas, as is done below. michael@0: // Instead, use extractSubset. Note that this shares the SkPixelRef and michael@0: // SkColorTable. michael@0: // FIXME: Since src is discarded in practice, this holds on to more michael@0: // pixels than is strictly necessary. Switch to a copy if memory michael@0: // savings are more important than speed here. This also means michael@0: // that the pixels in dst can not be reused (though there is no michael@0: // allocation, which was already done on src). michael@0: int x = (dstX - srcX) / sampleSize; michael@0: int y = (dstY - srcY) / sampleSize; michael@0: SkIRect subset = SkIRect::MakeXYWH(x, y, w, h); michael@0: return src->extractSubset(dst, subset); michael@0: } michael@0: // if the destination has no pixels then we must allocate them. michael@0: if (dst->isNull()) { michael@0: dst->setConfig(src->config(), w, h, 0, src->alphaType()); michael@0: michael@0: if (!this->allocPixelRef(dst, NULL)) { michael@0: SkDEBUGF(("failed to allocate pixels needed to crop the bitmap")); michael@0: return false; michael@0: } michael@0: } michael@0: // check to see if the destination is large enough to decode the desired michael@0: // region. If this assert fails we will just draw as much of the source michael@0: // into the destination that we can. michael@0: if (dst->width() < w || dst->height() < h) { michael@0: SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n")); michael@0: } michael@0: michael@0: // Set the Src_Mode for the paint to prevent transparency issue in the michael@0: // dest in the event that the dest was being re-used. michael@0: SkPaint paint; michael@0: paint.setXfermodeMode(SkXfermode::kSrc_Mode); michael@0: michael@0: SkCanvas canvas(*dst); michael@0: canvas.drawSprite(*src, (srcX - dstX) / sampleSize, michael@0: (srcY - dstY) / sampleSize, michael@0: &paint); michael@0: return true; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, michael@0: SkBitmap::Config pref, Mode mode, Format* format) { michael@0: SkASSERT(file); michael@0: SkASSERT(bm); michael@0: michael@0: SkAutoTUnref stream(SkStream::NewFromFile(file)); michael@0: if (stream.get()) { michael@0: if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) { michael@0: bm->pixelRef()->setURI(file); michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm, michael@0: SkBitmap::Config pref, Mode mode, Format* format) { michael@0: if (0 == size) { michael@0: return false; michael@0: } michael@0: SkASSERT(buffer); michael@0: michael@0: SkMemoryStream stream(buffer, size); michael@0: return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format); michael@0: } michael@0: michael@0: bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, michael@0: SkBitmap::Config pref, Mode mode, michael@0: Format* format) { michael@0: SkASSERT(stream); michael@0: SkASSERT(bm); michael@0: michael@0: bool success = false; michael@0: SkImageDecoder* codec = SkImageDecoder::Factory(stream); michael@0: michael@0: if (NULL != codec) { michael@0: success = codec->decode(stream, bm, pref, mode); michael@0: if (success && format) { michael@0: *format = codec->getFormat(); michael@0: if (kUnknown_Format == *format) { michael@0: if (stream->rewind()) { michael@0: *format = GetStreamFormat(stream); michael@0: } michael@0: } michael@0: } michael@0: delete codec; michael@0: } michael@0: return success; michael@0: }