michael@0: michael@0: /* michael@0: * Copyright 2011 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: #include "SkImageRef.h" michael@0: #include "SkBitmap.h" michael@0: #include "SkReadBuffer.h" michael@0: #include "SkWriteBuffer.h" michael@0: #include "SkImageDecoder.h" michael@0: #include "SkStream.h" michael@0: #include "SkTemplates.h" michael@0: #include "SkThread.h" michael@0: michael@0: //#define DUMP_IMAGEREF_LIFECYCLE michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkImageRef::SkImageRef(const SkImageInfo& info, SkStreamRewindable* stream, michael@0: int sampleSize, SkBaseMutex* mutex) michael@0: : INHERITED(info, mutex), fErrorInDecoding(false) michael@0: { michael@0: SkASSERT(stream); michael@0: stream->ref(); michael@0: fStream = stream; michael@0: fSampleSize = sampleSize; michael@0: fDoDither = true; michael@0: fPrev = fNext = NULL; michael@0: fFactory = NULL; michael@0: michael@0: // This sets the colortype/alphatype to exactly match our info, so that this michael@0: // can get communicated down to the codec. michael@0: fBitmap.setConfig(info); michael@0: michael@0: #ifdef DUMP_IMAGEREF_LIFECYCLE michael@0: SkDebugf("add ImageRef %p [%d] data=%d\n", michael@0: this, this->info().fColorType, (int)stream->getLength()); michael@0: #endif michael@0: } michael@0: michael@0: SkImageRef::~SkImageRef() { michael@0: michael@0: #ifdef DUMP_IMAGEREF_LIFECYCLE michael@0: SkDebugf("delete ImageRef %p [%d] data=%d\n", michael@0: this, this->info().fColorType, (int)fStream->getLength()); michael@0: #endif michael@0: michael@0: fStream->unref(); michael@0: SkSafeUnref(fFactory); michael@0: } michael@0: michael@0: bool SkImageRef::getInfo(SkBitmap* bitmap) { michael@0: SkAutoMutexAcquire ac(this->mutex()); michael@0: michael@0: if (!this->prepareBitmap(SkImageDecoder::kDecodeBounds_Mode)) { michael@0: return false; michael@0: } michael@0: michael@0: SkASSERT(SkBitmap::kNo_Config != fBitmap.config()); michael@0: if (bitmap) { michael@0: bitmap->setConfig(fBitmap.config(), fBitmap.width(), fBitmap.height()); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool SkImageRef::isOpaque(SkBitmap* bitmap) { michael@0: if (bitmap && bitmap->pixelRef() == this) { michael@0: bitmap->lockPixels(); michael@0: // what about colortables?????? michael@0: bitmap->setAlphaType(fBitmap.alphaType()); michael@0: bitmap->unlockPixels(); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: SkImageDecoderFactory* SkImageRef::setDecoderFactory( michael@0: SkImageDecoderFactory* fact) { michael@0: SkRefCnt_SafeAssign(fFactory, fact); michael@0: return fact; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool SkImageRef::onDecode(SkImageDecoder* codec, SkStreamRewindable* stream, michael@0: SkBitmap* bitmap, SkBitmap::Config config, michael@0: SkImageDecoder::Mode mode) { michael@0: return codec->decode(stream, bitmap, config, mode); michael@0: } michael@0: michael@0: bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { michael@0: michael@0: if (fErrorInDecoding) { michael@0: return false; michael@0: } michael@0: michael@0: if (NULL != fBitmap.getPixels() || michael@0: (SkBitmap::kNo_Config != fBitmap.config() && michael@0: SkImageDecoder::kDecodeBounds_Mode == mode)) { michael@0: return true; michael@0: } michael@0: michael@0: SkASSERT(fBitmap.getPixels() == NULL); michael@0: michael@0: if (!fStream->rewind()) { michael@0: SkDEBUGF(("Failed to rewind SkImageRef stream!")); michael@0: return false; michael@0: } michael@0: michael@0: SkImageDecoder* codec; michael@0: if (fFactory) { michael@0: codec = fFactory->newDecoder(fStream); michael@0: } else { michael@0: codec = SkImageDecoder::Factory(fStream); michael@0: } michael@0: michael@0: if (codec) { michael@0: SkAutoTDelete ad(codec); michael@0: michael@0: codec->setSampleSize(fSampleSize); michael@0: codec->setDitherImage(fDoDither); michael@0: codec->setRequireUnpremultipliedColors(this->info().fAlphaType == kUnpremul_SkAlphaType); michael@0: if (this->onDecode(codec, fStream, &fBitmap, fBitmap.config(), mode)) { michael@0: if (kOpaque_SkAlphaType == fBitmap.alphaType()) { michael@0: this->changeAlphaType(kOpaque_SkAlphaType); michael@0: } michael@0: SkASSERT(this->info() == fBitmap.info()); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: #ifdef DUMP_IMAGEREF_LIFECYCLE michael@0: if (NULL == codec) { michael@0: SkDebugf("--- ImageRef: <%s> failed to find codec\n", this->getURI()); michael@0: } else { michael@0: SkDebugf("--- ImageRef: <%s> failed in codec for %d mode\n", michael@0: this->getURI(), mode); michael@0: } michael@0: #endif michael@0: fErrorInDecoding = true; michael@0: fBitmap.reset(); michael@0: return false; michael@0: } michael@0: michael@0: bool SkImageRef::onNewLockPixels(LockRec* rec) { michael@0: if (NULL == fBitmap.getPixels()) { michael@0: (void)this->prepareBitmap(SkImageDecoder::kDecodePixels_Mode); michael@0: } michael@0: michael@0: if (NULL == fBitmap.getPixels()) { michael@0: return false; michael@0: } michael@0: rec->fPixels = fBitmap.getPixels(); michael@0: rec->fColorTable = NULL; michael@0: rec->fRowBytes = fBitmap.rowBytes(); michael@0: return true; michael@0: } michael@0: michael@0: size_t SkImageRef::ramUsed() const { michael@0: size_t size = 0; michael@0: michael@0: if (fBitmap.getPixels()) { michael@0: size = fBitmap.getSize(); michael@0: if (fBitmap.getColorTable()) { michael@0: size += fBitmap.getColorTable()->count() * sizeof(SkPMColor); michael@0: } michael@0: } michael@0: return size; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkImageRef::SkImageRef(SkReadBuffer& buffer, SkBaseMutex* mutex) michael@0: : INHERITED(buffer, mutex), fErrorInDecoding(false) { michael@0: fSampleSize = buffer.readInt(); michael@0: fDoDither = buffer.readBool(); michael@0: michael@0: size_t length = buffer.getArrayCount(); michael@0: if (buffer.validateAvailable(length)) { michael@0: fStream = SkNEW_ARGS(SkMemoryStream, (length)); michael@0: buffer.readByteArray((void*)fStream->getMemoryBase(), length); michael@0: } else { michael@0: fStream = NULL; michael@0: } michael@0: michael@0: fPrev = fNext = NULL; michael@0: fFactory = NULL; michael@0: michael@0: // This sets the colortype/alphatype to exactly match our info, so that this michael@0: // can get communicated down to the codec. michael@0: fBitmap.setConfig(this->info()); michael@0: } michael@0: michael@0: void SkImageRef::flatten(SkWriteBuffer& buffer) const { michael@0: this->INHERITED::flatten(buffer); michael@0: michael@0: buffer.writeInt(fSampleSize); michael@0: buffer.writeBool(fDoDither); michael@0: // FIXME: Consider moving this logic should go into writeStream itself. michael@0: // writeStream currently has no other callers, so this may be fine for michael@0: // now. michael@0: if (!fStream->rewind()) { michael@0: SkDEBUGF(("Failed to rewind SkImageRef stream!")); michael@0: buffer.write32(0); michael@0: } else { michael@0: // FIXME: Handle getLength properly here. Perhaps this class should michael@0: // take an SkStreamAsset. michael@0: buffer.writeStream(fStream, fStream->getLength()); michael@0: } michael@0: }