blob: 870056091f13c1d045ebed092045298187aabdf6 [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Sk64.h"
#include "SkLazyPixelRef.h"
#include "SkColorTable.h"
#include "SkData.h"
#include "SkImageCache.h"
#include "SkImagePriv.h"
#if LAZY_CACHE_STATS
#include "SkThread.h"
int32_t SkLazyPixelRef::gCacheHits;
int32_t SkLazyPixelRef::gCacheMisses;
#endif
SkLazyPixelRef::SkLazyPixelRef(SkData* data, SkBitmapFactory::DecodeProc proc, SkImageCache* cache)
// Pass NULL for the Mutex so that the default (ring buffer) will be used.
: INHERITED(NULL)
, fDecodeProc(proc)
, fImageCache(cache)
, fCacheId(SkImageCache::UNINITIALIZED_ID)
, fRowBytes(0) {
SkASSERT(fDecodeProc != NULL);
if (NULL == data) {
fData = SkData::NewEmpty();
fErrorInDecoding = true;
} else {
fData = data;
fData->ref();
fErrorInDecoding = data->size() == 0;
}
SkASSERT(cache != NULL);
cache->ref();
// Since this pixel ref bases its data on encoded data, it should never change.
this->setImmutable();
}
SkLazyPixelRef::~SkLazyPixelRef() {
SkASSERT(fData != NULL);
fData->unref();
SkASSERT(fImageCache);
if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
fImageCache->throwAwayCache(fCacheId);
}
fImageCache->unref();
}
static size_t ComputeMinRowBytesAndSize(const SkImage::Info& info, size_t* rowBytes) {
*rowBytes = SkImageMinRowBytes(info);
Sk64 safeSize;
safeSize.setZero();
if (info.fHeight > 0) {
safeSize.setMul(info.fHeight, SkToS32(*rowBytes));
}
SkASSERT(!safeSize.isNeg());
return safeSize.is32() ? safeSize.get32() : 0;
}
void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
if (fErrorInDecoding) {
return NULL;
}
SkBitmapFactory::Target target;
// Check to see if the pixels still exist in the cache.
if (SkImageCache::UNINITIALIZED_ID == fCacheId) {
target.fAddr = NULL;
} else {
SkImageCache::DataStatus status;
target.fAddr = fImageCache->pinCache(fCacheId, &status);
if (target.fAddr == NULL) {
fCacheId = SkImageCache::UNINITIALIZED_ID;
} else {
if (SkImageCache::kRetained_DataStatus == status) {
#if LAZY_CACHE_STATS
sk_atomic_inc(&gCacheHits);
#endif
return target.fAddr;
}
SkASSERT(SkImageCache::kUninitialized_DataStatus == status);
}
// Cache miss. Either pinCache returned NULL or it returned a memory address without the old
// data
#if LAZY_CACHE_STATS
sk_atomic_inc(&gCacheMisses);
#endif
}
SkImage::Info info;
SkASSERT(fData != NULL && fData->size() > 0);
if (NULL == target.fAddr) {
// Determine the size of the image in order to determine how much memory to allocate.
// FIXME: As an optimization, only do this part once.
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
if (fErrorInDecoding) {
// We can only reach here if fCacheId was already set to UNINITIALIZED_ID, or if
// pinCache returned NULL, in which case it was reset to UNINITIALIZED_ID.
SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
return NULL;
}
size_t bytes = ComputeMinRowBytesAndSize(info, &target.fRowBytes);
target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId);
if (NULL == target.fAddr) {
// Space could not be allocated.
// Just like the last assert, fCacheId must be UNINITIALIZED_ID.
SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
return NULL;
}
} else {
// pinCache returned purged memory to which target.fAddr already points. Set
// target.fRowBytes properly.
target.fRowBytes = fRowBytes;
// Assume that the size is correct, since it was determined by this same function
// previously.
}
SkASSERT(target.fAddr != NULL);
SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
if (fErrorInDecoding) {
fImageCache->throwAwayCache(fCacheId);
fCacheId = SkImageCache::UNINITIALIZED_ID;
return NULL;
}
// Upon success, store fRowBytes so it can be used in case pinCache later returns purged memory.
fRowBytes = target.fRowBytes;
return target.fAddr;
}
void SkLazyPixelRef::onUnlockPixels() {
if (fErrorInDecoding) {
return;
}
if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
fImageCache->releaseCache(fCacheId);
}
}
SkData* SkLazyPixelRef::onRefEncodedData() {
fData->ref();
return fData;
}
#include "SkImagePriv.h"
static bool init_from_info(SkBitmap* bm, const SkImage::Info& info,
size_t rowBytes) {
bool isOpaque;
SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
if (SkBitmap::kNo_Config == config) {
return false;
}
bm->setConfig(config, info.fWidth, info.fHeight, rowBytes);
bm->setIsOpaque(isOpaque);
return bm->allocPixels();
}
bool SkLazyPixelRef::onImplementsDecodeInto() {
return true;
}
bool SkLazyPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) {
SkASSERT(fData != NULL && fData->size() > 0);
if (fErrorInDecoding) {
return false;
}
SkImage::Info info;
// Determine the size of the image in order to determine how much memory to allocate.
// FIXME: As an optimization, only do this part once.
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
if (fErrorInDecoding) {
return false;
}
SkBitmapFactory::Target target;
(void)ComputeMinRowBytesAndSize(info, &target.fRowBytes);
SkBitmap tmp;
if (!init_from_info(&tmp, info, target.fRowBytes)) {
return false;
}
target.fAddr = tmp.getPixels();
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
if (fErrorInDecoding) {
return false;
}
*bitmap = tmp;
return true;
}