Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(165)

Unified Diff: src/core/SkScaledImageCache.cpp

Issue 20005003: add scaledimagecache (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/core/SkScaledImageCache.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
+}
+
« no previous file with comments | « src/core/SkScaledImageCache.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698