| Index: src/core/SkScaledImageCache.cpp
|
| diff --git a/src/core/SkScaledImageCache.cpp b/src/core/SkScaledImageCache.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..993b81bbdf3ff0058d386c79129492d84533036e
|
| --- /dev/null
|
| +++ b/src/core/SkScaledImageCache.cpp
|
| @@ -0,0 +1,421 @@
|
| +/*
|
| + * Copyright 2013 Google Inc.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +#include "SkScaledImageCache.h"
|
| +#include "SkPixelRef.h"
|
| +#include "SkRect.h"
|
| +
|
| +#ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
|
| + #define SK_DEFAULT_IMAGE_CACHE_LIMIT (2 * 1024 * 1024)
|
| +#endif
|
| +
|
| +#if 1
|
| + // Implemented from en.wikipedia.org/wiki/MurmurHash.
|
| +static uint32_t compute_hash(const uint32_t data[], int count) {
|
| + uint32_t hash = 0;
|
| +
|
| + for (int i = 0; i < count; ++i) {
|
| + uint32_t k = data[i];
|
| + k *= 0xcc9e2d51;
|
| + k = (k << 15) | (k >> 17);
|
| + k *= 0x1b873593;
|
| +
|
| + hash ^= k;
|
| + hash = (hash << 13) | (hash >> 19);
|
| + hash *= 5;
|
| + hash += 0xe6546b64;
|
| + }
|
| +
|
| + // hash ^= size;
|
| + hash ^= hash >> 16;
|
| + hash *= 0x85ebca6b;
|
| + hash ^= hash >> 13;
|
| + hash *= 0xc2b2ae35;
|
| + hash ^= hash >> 16;
|
| +
|
| + return hash;
|
| +}
|
| +#else
|
| +static uint32_t mix(uint32_t a, uint32_t b) {
|
| + return ((a >> 1) | (a << 31)) ^ b;
|
| +}
|
| +
|
| +static uint32_t compute_hash(const uint32_t data[], int count) {
|
| + uint32_t hash = 0;
|
| +
|
| + for (int i = 0; i < count; ++i) {
|
| + hash = mix(hash, data[i]);
|
| + }
|
| + return hash;
|
| +}
|
| +#endif
|
| +
|
| +struct Key {
|
| + bool init(const SkBitmap& bm, SkScalar scaleX, SkScalar scaleY) {
|
| + SkPixelRef* pr = bm.pixelRef();
|
| + if (!pr) {
|
| + return false;
|
| + }
|
| +
|
| + size_t offset = bm.pixelRefOffset();
|
| + size_t rowBytes = bm.rowBytes();
|
| + int x = (offset % rowBytes) >> 2;
|
| + int y = offset / rowBytes;
|
| +
|
| + fGenID = pr->getGenerationID();
|
| + fBounds.set(x, y, x + bm.width(), y + bm.height());
|
| + fScaleX = scaleX;
|
| + fScaleY = scaleY;
|
| +
|
| + fHash = compute_hash(&fGenID, 7);
|
| + return true;
|
| + }
|
| +
|
| + bool operator<(const Key& other) {
|
| + const uint32_t* a = &fGenID;
|
| + const uint32_t* b = &other.fGenID;
|
| + for (int i = 0; i < 7; ++i) {
|
| + if (a[i] < b[i]) {
|
| + return true;
|
| + }
|
| + if (a[i] > b[i]) {
|
| + return false;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + bool operator==(const Key& other) {
|
| + const uint32_t* a = &fHash;
|
| + const uint32_t* b = &other.fHash;
|
| + for (int i = 0; i < 8; ++i) {
|
| + if (a[i] != b[i]) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + uint32_t fHash;
|
| + uint32_t fGenID;
|
| + float fScaleX;
|
| + float fScaleY;
|
| + SkIRect fBounds;
|
| +};
|
| +
|
| +struct SkScaledImageCache::Rec {
|
| + Rec(const Key& key, const SkBitmap& bm) : fKey(key), fBitmap(bm) {
|
| + fLockCount = 1;
|
| + }
|
| +
|
| + size_t bytesUsed() const {
|
| + return fBitmap.getSize();
|
| + }
|
| +
|
| + Rec* fNext;
|
| + Rec* fPrev;
|
| +
|
| + // this guy wants to be 64bit aligned
|
| + Key fKey;
|
| +
|
| + int32_t fLockCount;
|
| + SkBitmap fBitmap;
|
| +};
|
| +
|
| +SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
|
| + fHead = NULL;
|
| + fTail = NULL;
|
| + fBytesUsed = 0;
|
| + fByteLimit = byteLimit;
|
| + fCount = 0;
|
| +}
|
| +
|
| +SkScaledImageCache::~SkScaledImageCache() {
|
| + Rec* rec = fHead;
|
| + while (rec) {
|
| + Rec* next = rec->fNext;
|
| + SkDELETE(rec);
|
| + rec = next;
|
| + }
|
| +}
|
| +
|
| +SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig,
|
| + SkScalar scaleX,
|
| + SkScalar scaleY,
|
| + SkBitmap* scaled) {
|
| + Key key;
|
| + if (!key.init(orig, scaleX, scaleY)) {
|
| + return NULL;
|
| + }
|
| +
|
| + Rec* rec = fHead;
|
| + while (rec != NULL) {
|
| + if (rec->fKey == key) {
|
| + this->moveToHead(rec); // for our LRU
|
| + rec->fLockCount += 1;
|
| + *scaled = rec->fBitmap;
|
| +// SkDebugf("Found: [%d %d] %d\n", rec->fBitmap.width(), rec->fBitmap.height(), rec->fLockCount);
|
| + return (ID*)rec;
|
| + }
|
| + rec = rec->fNext;
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig,
|
| + SkScalar scaleX,
|
| + SkScalar scaleY,
|
| + const SkBitmap& scaled) {
|
| + Key key;
|
| + if (!key.init(orig, scaleX, scaleY)) {
|
| + return NULL;
|
| + }
|
| +
|
| + Rec* rec = SkNEW_ARGS(Rec, (key, scaled));
|
| + this->addToHead(rec);
|
| + SkASSERT(1 == rec->fLockCount);
|
| +
|
| +// SkDebugf("Added: [%d %d]\n", rec->fBitmap.width(), rec->fBitmap.height());
|
| +
|
| + // We may (now) be overbudget, so see if we need to purge something.
|
| + this->purgeAsNeeded();
|
| + return (ID*)rec;
|
| +}
|
| +
|
| +void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) {
|
| + SkASSERT(id);
|
| +
|
| +#ifdef SK_DEBUG
|
| + {
|
| + bool found = false;
|
| + Rec* rec = fHead;
|
| + while (rec != NULL) {
|
| + if ((ID*)rec == id) {
|
| + found = true;
|
| + break;
|
| + }
|
| + rec = rec->fNext;
|
| + }
|
| + SkASSERT(found);
|
| + }
|
| +#endif
|
| + Rec* rec = (Rec*)id;
|
| + SkASSERT(rec->fLockCount > 0);
|
| + rec->fLockCount -= 1;
|
| +
|
| +// SkDebugf("Unlock: [%d %d] %d\n", rec->fBitmap.width(), rec->fBitmap.height(), rec->fLockCount);
|
| +
|
| + // we may have been over-budget, but now have released something, so check
|
| + // if we should purge.
|
| + if (0 == rec->fLockCount) {
|
| + this->purgeAsNeeded();
|
| + }
|
| +}
|
| +
|
| +void SkScaledImageCache::purgeAsNeeded() {
|
| + size_t byteLimit = fByteLimit;
|
| + size_t bytesUsed = fBytesUsed;
|
| +
|
| + Rec* rec = fTail;
|
| + while (rec) {
|
| + if (bytesUsed < byteLimit) {
|
| + break;
|
| + }
|
| + Rec* prev = rec->fPrev;
|
| + if (0 == rec->fLockCount) {
|
| +// SkDebugf("Purge: [%d %d] %d\n", rec->fBitmap.width(), rec->fBitmap.height(), fCount);
|
| + size_t used = rec->bytesUsed();
|
| + SkASSERT(used <= bytesUsed);
|
| + bytesUsed -= used;
|
| + this->detach(rec);
|
| + SkDELETE(rec);
|
| + fCount -= 1;
|
| + }
|
| + rec = prev;
|
| + }
|
| + fBytesUsed = bytesUsed;
|
| +}
|
| +
|
| +size_t SkScaledImageCache::setByteLimit(size_t newLimit) {
|
| + size_t prevLimit = fByteLimit;
|
| + fByteLimit = newLimit;
|
| + if (newLimit < prevLimit) {
|
| + this->purgeAsNeeded();
|
| + }
|
| + return prevLimit;
|
| +}
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +
|
| +void SkScaledImageCache::detach(Rec* rec) {
|
| + Rec* prev = rec->fPrev;
|
| + Rec* next = rec->fNext;
|
| +
|
| + if (!prev) {
|
| + SkASSERT(fHead == rec);
|
| + fHead = next;
|
| + } else {
|
| + prev->fNext = next;
|
| + }
|
| +
|
| + if (!next) {
|
| + fTail = prev;
|
| + } else {
|
| + next->fPrev = prev;
|
| + }
|
| +
|
| + rec->fNext = rec->fPrev = NULL;
|
| +}
|
| +
|
| +void SkScaledImageCache::moveToHead(Rec* rec) {
|
| + if (fHead == rec) {
|
| + return;
|
| + }
|
| +
|
| + SkASSERT(fHead);
|
| + SkASSERT(fTail);
|
| +
|
| + this->validate();
|
| +
|
| + this->detach(rec);
|
| +
|
| + fHead->fPrev = rec;
|
| + rec->fNext = fHead;
|
| + fHead = rec;
|
| +
|
| + this->validate();
|
| +}
|
| +
|
| +void SkScaledImageCache::addToHead(Rec* rec) {
|
| + this->validate();
|
| +
|
| + rec->fPrev = NULL;
|
| + rec->fNext = fHead;
|
| + if (fHead) {
|
| + fHead->fPrev = rec;
|
| + }
|
| + fHead = rec;
|
| + if (!fTail) {
|
| + fTail = rec;
|
| + }
|
| + fBytesUsed += rec->bytesUsed();
|
| + fCount += 1;
|
| +
|
| + this->validate();
|
| +}
|
| +
|
| +#ifdef SK_DEBUG
|
| +void SkScaledImageCache::validate() const {
|
| + if (NULL == fHead) {
|
| + SkASSERT(NULL == fTail);
|
| + SkASSERT(0 == fBytesUsed);
|
| + return;
|
| + }
|
| +
|
| + if (fHead == fTail) {
|
| + SkASSERT(NULL == fHead->fPrev);
|
| + SkASSERT(NULL == fHead->fNext);
|
| + SkASSERT(fHead->bytesUsed() == fBytesUsed);
|
| + return;
|
| + }
|
| +
|
| + SkASSERT(NULL == fHead->fPrev);
|
| + SkASSERT(NULL != fHead->fNext);
|
| + SkASSERT(NULL == fTail->fNext);
|
| + SkASSERT(NULL != fTail->fPrev);
|
| +
|
| + size_t used = 0;
|
| + int count = 0;
|
| + const Rec* rec = fHead;
|
| + while (rec) {
|
| + count += 1;
|
| + used += rec->bytesUsed();
|
| + SkASSERT(used <= fBytesUsed);
|
| + rec = rec->fNext;
|
| + }
|
| + SkASSERT(fCount == count);
|
| +
|
| + rec = fTail;
|
| + while (rec) {
|
| + SkASSERT(count > 0);
|
| + count -= 1;
|
| + SkASSERT(used >= rec->bytesUsed());
|
| + used -= rec->bytesUsed();
|
| + rec = rec->fPrev;
|
| + }
|
| +
|
| + SkASSERT(0 == count);
|
| + SkASSERT(0 == used);
|
| +}
|
| +#endif
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +
|
| +#include "SkThread.h"
|
| +
|
| +static SkMutex gMutex;
|
| +
|
| +static SkScaledImageCache* get_cache() {
|
| + static SkScaledImageCache* gCache;
|
| + if (!gCache) {
|
| + gCache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
|
| + }
|
| + return gCache;
|
| +}
|
| +
|
| +SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(const SkBitmap& orig,
|
| + SkScalar scaleX,
|
| + SkScalar scaleY,
|
| + SkBitmap* scaled) {
|
| + SkAutoMutexAcquire am(gMutex);
|
| + return get_cache()->findAndLock(orig, scaleX, scaleY, scaled);
|
| +}
|
| +
|
| +SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(const SkBitmap& orig,
|
| + SkScalar scaleX,
|
| + SkScalar scaleY,
|
| + const SkBitmap& scaled) {
|
| + SkAutoMutexAcquire am(gMutex);
|
| + return get_cache()->addAndLock(orig, scaleX, scaleY, scaled);
|
| +}
|
| +
|
| +void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) {
|
| + SkAutoMutexAcquire am(gMutex);
|
| + return get_cache()->unlock(id);
|
| +}
|
| +
|
| +size_t SkScaledImageCache::GetBytesUsed() {
|
| + SkAutoMutexAcquire am(gMutex);
|
| + return get_cache()->getBytesUsed();
|
| +}
|
| +
|
| +size_t SkScaledImageCache::GetByteLimit() {
|
| + SkAutoMutexAcquire am(gMutex);
|
| + return get_cache()->getByteLimit();
|
| +}
|
| +
|
| +size_t SkScaledImageCache::SetByteLimit(size_t newLimit) {
|
| + SkAutoMutexAcquire am(gMutex);
|
| + return get_cache()->setByteLimit(newLimit);
|
| +}
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +
|
| +#include "SkGraphics.h"
|
| +
|
| +size_t SkGraphics::GetImageCacheBytesUsed() {
|
| + return SkScaledImageCache::GetBytesUsed();
|
| +}
|
| +
|
| +size_t SkGraphics::GetImageCacheByteLimit() {
|
| + return SkScaledImageCache::GetByteLimit();
|
| +}
|
| +
|
| +size_t SkGraphics::SetImageCacheByteLimit(size_t newLimit) {
|
| + return SkScaledImageCache::SetByteLimit(newLimit);
|
| +}
|
| +
|
|
|