#include "SkImageRef.h"
#include "SkBitmap.h"
#include "SkFlattenable.h"
#include "SkImageDecoder.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkThread.h"
// can't be static, as SkImageRef_Pool needs to see it
SkMutex gImageRefMutex;
///////////////////////////////////////////////////////////////////////////////
SkImageRef::SkImageRef(SkStream* stream, SkBitmap::Config config,
int sampleSize)
: SkPixelRef(&gImageRefMutex), fErrorInDecoding(false) {
SkASSERT(stream);
stream->ref();
fStream = stream;
fConfig = config;
fSampleSize = sampleSize;
fPrev = fNext = NULL;
fFactory = NULL;
#ifdef DUMP_IMAGEREF_LIFECYCLE
SkDebugf("add ImageRef %p [%d] data=%d\n",
this, config, (int)stream->getLength());
#endif
}
SkImageRef::~SkImageRef() {
SkASSERT(&gImageRefMutex == this->mutex());
#ifdef DUMP_IMAGEREF_LIFECYCLE
SkDebugf("delete ImageRef %p [%d] data=%d\n",
this, fConfig, (int)fStream->getLength());
#endif
fStream->unref();
fFactory->safeUnref();
}
bool SkImageRef::getInfo(SkBitmap* bitmap) {
SkAutoMutexAcquire ac(gImageRefMutex);
if (!this->prepareBitmap(SkImageDecoder::kDecodeBounds_Mode)) {
return false;
}
SkASSERT(SkBitmap::kNo_Config != fBitmap.config());
if (bitmap) {
bitmap->setConfig(fBitmap.config(), fBitmap.width(), fBitmap.height());
}
return true;
}
SkImageDecoderFactory* SkImageRef::setDecoderFactory(
SkImageDecoderFactory* fact) {
SkRefCnt_SafeAssign(fFactory, fact);
return fact;
}
///////////////////////////////////////////////////////////////////////////////
bool SkImageRef::onDecode(SkImageDecoder* codec, SkStream* stream,
SkBitmap* bitmap, SkBitmap::Config config,
SkImageDecoder::Mode mode) {
return codec->decode(stream, bitmap, config, mode);
}
bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) {
SkASSERT(&gImageRefMutex == this->mutex());
if (fErrorInDecoding) {
return false;
}
/* As soon as we really know our config, we record it, so that on
subsequent calls to the codec, we are sure we will always get the same
result.
*/
if (SkBitmap::kNo_Config != fBitmap.config()) {
fConfig = fBitmap.config();
}
if (NULL != fBitmap.getPixels() ||
(SkBitmap::kNo_Config != fBitmap.config() &&
SkImageDecoder::kDecodeBounds_Mode == mode)) {
return true;
}
SkASSERT(fBitmap.getPixels() == NULL);
fStream->rewind();
SkImageDecoder* codec;
if (fFactory) {
codec = fFactory->newDecoder(fStream);
} else {
codec = SkImageDecoder::Factory(fStream);
}
if (codec) {
SkAutoTDelete<SkImageDecoder> ad(codec);
codec->setSampleSize(fSampleSize);
if (this->onDecode(codec, fStream, &fBitmap, fConfig, mode)) {
return true;
}
}
#ifdef DUMP_IMAGEREF_LIFECYCLE
if (NULL == codec) {
SkDebugf("--- ImageRef: <%s> failed to find codec\n", this->getURI());
} else {
SkDebugf("--- ImageRef: <%s> failed in codec for %d mode\n",
this->getURI(), mode);
}
#endif
fErrorInDecoding = true;
fBitmap.reset();
return false;
}
void* SkImageRef::onLockPixels(SkColorTable** ct) {
SkASSERT(&gImageRefMutex == this->mutex());
if (NULL == fBitmap.getPixels()) {
(void)this->prepareBitmap(SkImageDecoder::kDecodePixels_Mode);
}
if (ct) {
*ct = fBitmap.getColorTable();
}
return fBitmap.getPixels();
}
void SkImageRef::onUnlockPixels() {
// we're already have the mutex locked
SkASSERT(&gImageRefMutex == this->mutex());
}
size_t SkImageRef::ramUsed() const {
size_t size = 0;
if (fBitmap.getPixels()) {
size = fBitmap.getSize();
if (fBitmap.getColorTable()) {
size += fBitmap.getColorTable()->count() * sizeof(SkPMColor);
}
}
return size;
}
///////////////////////////////////////////////////////////////////////////////
SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer)
: INHERITED(buffer, &gImageRefMutex), fErrorInDecoding(false) {
fConfig = (SkBitmap::Config)buffer.readU8();
fSampleSize = buffer.readU8();
size_t length = buffer.readU32();
fStream = SkNEW_ARGS(SkMemoryStream, (length));
buffer.read((void*)fStream->getMemoryBase(), length);
fPrev = fNext = NULL;
fFactory = NULL;
}
void SkImageRef::flatten(SkFlattenableWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.write8(fConfig);
buffer.write8(fSampleSize);
size_t length = fStream->getLength();
buffer.write32(length);
fStream->rewind();
buffer.readFromStream(fStream, length);
}