Index: src/core/SkScaledImageCache.cpp |
diff --git a/src/core/SkScaledImageCache.cpp b/src/core/SkScaledImageCache.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1570103e4318c4de56c7d8a25bd2e45a80e509cb |
--- /dev/null |
+++ b/src/core/SkScaledImageCache.cpp |
@@ -0,0 +1,347 @@ |
+#include "SkScaledImageCache.h" |
+#include "SkPixelRef.h" |
+#include "SkRect.h" |
+ |
+static const size_t DEF_BYTE_LIMIT = 2 * 1024 * 1024; |
ernstm
2013/07/23 06:36:50
Two megabyte is quite low. Should we set this to a
reed1
2013/07/23 14:50:29
There is now a define that chrome can set in skia.
|
+ |
+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; |
+ 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 = &fGenID; |
+ const uint32_t* b = &other.fGenID; |
+ for (int i = 0; i < 7; ++i) { |
+ if (a[i] != b[i]) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ uint32_t fGenID; |
+ SkIRect fBounds; |
+ float fScaleX; |
+ float fScaleY; |
+}; |
+ |
+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; |
+ |
+ int32_t fLockCount; |
+ Key fKey; |
+ SkBitmap fBitmap; |
+}; |
+ |
+SkScaledImageCache::SkScaledImageCache() { |
+ fHead = NULL; |
+ fTail = NULL; |
+ fBytesUsed = 0; |
+ fByteLimit = DEF_BYTE_LIMIT; |
+ fCount = 0; |
+} |
+ |
+SkScaledImageCache::~SkScaledImageCache() { |
+ Rec* rec = fHead; |
+ while (rec) { |
+ Rec* next = rec->fNext; |
+ SkDELETE(rec); |
+ rec = next; |
+ } |
+} |
+ |
+SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig, |
humper
2013/07/23 01:25:44
Possibly worth a comment here saying that any nece
reed1
2013/07/23 14:50:29
Done.
|
+ 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, |
humper
2013/07/23 01:25:44
same as above re: comment
reed1
2013/07/23 14:50:29
Done.
|
+ 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) { |
humper
2013/07/23 01:25:44
ditto above re: comment
reed1
2013/07/23 14:50:29
Done.
|
+ 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; |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+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(); |
+ |
+ if (3 == fCount) |
+ SkDebugf(""); |
humper
2013/07/23 01:25:44
Obviously the debug statements are intended to be
reed1
2013/07/23 14:50:29
Done.
|
+ 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; |
humper
2013/07/23 01:25:44
I know the standard says that this will be initial
reed1
2013/07/23 14:50:29
I don't know if anything is written in the doc, bu
|
+ if (!gCache) { |
+ gCache = SkNEW(SkScaledImageCache); |
+ } |
+ 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::GetByteLimit() { |
+ SkAutoMutexAcquire am(gMutex); |
+ return get_cache()->fByteLimit; |
+} |
+ |
+size_t SkScaledImageCache::SetByteLimit(size_t newLimit) { |
+ SkAutoMutexAcquire am(gMutex); |
+ SkScaledImageCache* cache = get_cache(); |
+ size_t prevLimit = cache->fByteLimit; |
+ cache->fByteLimit = newLimit; |
+ cache->purgeAsNeeded(); |
+ return prevLimit; |
+} |
+ |
+size_t SkScaledImageCache::GetBytesUsed() { |
+ SkAutoMutexAcquire am(gMutex); |
+ return get_cache()->fBytesUsed; |
+} |
+ |