/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkImageRefPool.h"
#include "SkImageRef.h"
#include "SkThread.h"
SkImageRefPool::SkImageRefPool() {
fRAMBudget = 0; // means no explicit limit
fRAMUsed = 0;
fCount = 0;
fHead = fTail = NULL;
}
SkImageRefPool::~SkImageRefPool() {
// SkASSERT(NULL == fHead);
}
void SkImageRefPool::setRAMBudget(size_t size) {
if (fRAMBudget != size) {
fRAMBudget = size;
this->purgeIfNeeded();
}
}
void SkImageRefPool::justAddedPixels(SkImageRef* ref) {
#ifdef DUMP_IMAGEREF_LIFECYCLE
SkDebugf("=== ImagePool: add pixels %s [%d %d %d] bytes=%d heap=%d\n",
ref->getURI(),
ref->fBitmap.width(), ref->fBitmap.height(),
ref->fBitmap.bytesPerPixel(),
ref->fBitmap.getSize(), (int)fRAMUsed);
#endif
fRAMUsed += ref->ramUsed();
this->purgeIfNeeded();
}
void SkImageRefPool::canLosePixels(SkImageRef* ref) {
// the refs near fHead have recently been released (used)
// if we purge, we purge from the tail
this->detach(ref);
this->addToHead(ref);
this->purgeIfNeeded();
}
void SkImageRefPool::purgeIfNeeded() {
// do nothing if we have a zero-budget (i.e. unlimited)
if (fRAMBudget != 0) {
this->setRAMUsed(fRAMBudget);
}
}
void SkImageRefPool::setRAMUsed(size_t limit) {
SkImageRef* ref = fTail;
while (NULL != ref && fRAMUsed > limit) {
// only purge it if its pixels are unlocked
if (!ref->isLocked() && ref->fBitmap.getPixels()) {
size_t size = ref->ramUsed();
SkASSERT(size <= fRAMUsed);
fRAMUsed -= size;
#ifdef DUMP_IMAGEREF_LIFECYCLE
SkDebugf("=== ImagePool: purge %s [%d %d %d] bytes=%d heap=%d\n",
ref->getURI(),
ref->fBitmap.width(), ref->fBitmap.height(),
ref->fBitmap.bytesPerPixel(),
(int)size, (int)fRAMUsed);
#endif
// remember the bitmap config (don't call reset),
// just clear the pixel memory
ref->fBitmap.setPixels(NULL);
SkASSERT(NULL == ref->fBitmap.getPixels());
}
ref = ref->fPrev;
}
}
///////////////////////////////////////////////////////////////////////////////
void SkImageRefPool::addToHead(SkImageRef* ref) {
ref->fNext = fHead;
ref->fPrev = NULL;
if (fHead) {
SkASSERT(NULL == fHead->fPrev);
fHead->fPrev = ref;
}
fHead = ref;
if (NULL == fTail) {
fTail = ref;
}
fCount += 1;
SkASSERT(computeCount() == fCount);
fRAMUsed += ref->ramUsed();
}
void SkImageRefPool::addToTail(SkImageRef* ref) {
ref->fNext = NULL;
ref->fPrev = fTail;
if (fTail) {
SkASSERT(NULL == fTail->fNext);
fTail->fNext = ref;
}
fTail = ref;
if (NULL == fHead) {
fHead = ref;
}
fCount += 1;
SkASSERT(computeCount() == fCount);
fRAMUsed += ref->ramUsed();
}
void SkImageRefPool::detach(SkImageRef* ref) {
SkASSERT(fCount > 0);
if (fHead == ref) {
fHead = ref->fNext;
}
if (fTail == ref) {
fTail = ref->fPrev;
}
if (ref->fPrev) {
ref->fPrev->fNext = ref->fNext;
}
if (ref->fNext) {
ref->fNext->fPrev = ref->fPrev;
}
ref->fNext = ref->fPrev = NULL;
fCount -= 1;
SkASSERT(computeCount() == fCount);
SkASSERT(fRAMUsed >= ref->ramUsed());
fRAMUsed -= ref->ramUsed();
}
int SkImageRefPool::computeCount() const {
SkImageRef* ref = fHead;
int count = 0;
while (ref != NULL) {
count += 1;
ref = ref->fNext;
}
#ifdef SK_DEBUG
ref = fTail;
int count2 = 0;
while (ref != NULL) {
count2 += 1;
ref = ref->fPrev;
}
SkASSERT(count2 == count);
#endif
return count;
}
///////////////////////////////////////////////////////////////////////////////
#include "SkStream.h"
void SkImageRefPool::dump() const {
#if defined(SK_DEBUG) || defined(DUMP_IMAGEREF_LIFECYCLE)
SkDebugf("ImagePool dump: bugdet: %d used: %d count: %d\n",
(int)fRAMBudget, (int)fRAMUsed, fCount);
SkImageRef* ref = fHead;
while (ref != NULL) {
SkDebugf(" [%3d %3d %d] ram=%d data=%d locked=%d %s\n", ref->fBitmap.width(),
ref->fBitmap.height(), ref->fBitmap.config(),
ref->ramUsed(), (int)ref->fStream->getLength(),
ref->isLocked(), ref->getURI());
ref = ref->fNext;
}
#endif
}