michael@0: michael@0: /* michael@0: * Copyright 2010 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "SkBitmapCache.h" michael@0: michael@0: struct SkBitmapCache::Entry { michael@0: Entry* fPrev; michael@0: Entry* fNext; michael@0: michael@0: void* fBuffer; michael@0: size_t fSize; michael@0: SkBitmap fBitmap; michael@0: michael@0: Entry(const void* buffer, size_t size, const SkBitmap& bm) michael@0: : fPrev(NULL), michael@0: fNext(NULL), michael@0: fBitmap(bm) { michael@0: fBuffer = sk_malloc_throw(size); michael@0: fSize = size; michael@0: memcpy(fBuffer, buffer, size); michael@0: } michael@0: michael@0: ~Entry() { sk_free(fBuffer); } michael@0: michael@0: bool equals(const void* buffer, size_t size) const { michael@0: return (fSize == size) && !memcmp(fBuffer, buffer, size); michael@0: } michael@0: }; michael@0: michael@0: SkBitmapCache::SkBitmapCache(int max) : fMaxEntries(max) { michael@0: fEntryCount = 0; michael@0: fHead = fTail = NULL; michael@0: michael@0: this->validate(); michael@0: } michael@0: michael@0: SkBitmapCache::~SkBitmapCache() { michael@0: this->validate(); michael@0: michael@0: Entry* entry = fHead; michael@0: while (entry) { michael@0: Entry* next = entry->fNext; michael@0: delete entry; michael@0: entry = next; michael@0: } michael@0: } michael@0: michael@0: SkBitmapCache::Entry* SkBitmapCache::detach(Entry* entry) const { michael@0: if (entry->fPrev) { michael@0: SkASSERT(fHead != entry); michael@0: entry->fPrev->fNext = entry->fNext; michael@0: } else { michael@0: SkASSERT(fHead == entry); michael@0: fHead = entry->fNext; michael@0: } michael@0: if (entry->fNext) { michael@0: SkASSERT(fTail != entry); michael@0: entry->fNext->fPrev = entry->fPrev; michael@0: } else { michael@0: SkASSERT(fTail == entry); michael@0: fTail = entry->fPrev; michael@0: } michael@0: return entry; michael@0: } michael@0: michael@0: void SkBitmapCache::attachToHead(Entry* entry) const { michael@0: entry->fPrev = NULL; michael@0: entry->fNext = fHead; michael@0: if (fHead) { michael@0: fHead->fPrev = entry; michael@0: } else { michael@0: fTail = entry; michael@0: } michael@0: fHead = entry; michael@0: } michael@0: michael@0: bool SkBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const { michael@0: AutoValidate av(this); michael@0: michael@0: Entry* entry = fHead; michael@0: while (entry) { michael@0: if (entry->equals(buffer, size)) { michael@0: if (bm) { michael@0: *bm = entry->fBitmap; michael@0: } michael@0: // move to the head of our list, so we purge it last michael@0: this->detach(entry); michael@0: this->attachToHead(entry); michael@0: return true; michael@0: } michael@0: entry = entry->fNext; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void SkBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) { michael@0: AutoValidate av(this); michael@0: michael@0: if (fEntryCount == fMaxEntries) { michael@0: SkASSERT(fTail); michael@0: delete this->detach(fTail); michael@0: fEntryCount -= 1; michael@0: } michael@0: michael@0: Entry* entry = SkNEW_ARGS(Entry, (buffer, len, bm)); michael@0: this->attachToHead(entry); michael@0: fEntryCount += 1; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #ifdef SK_DEBUG michael@0: michael@0: void SkBitmapCache::validate() const { michael@0: SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries); michael@0: michael@0: if (fEntryCount > 0) { michael@0: SkASSERT(NULL == fHead->fPrev); michael@0: SkASSERT(NULL == fTail->fNext); michael@0: michael@0: if (fEntryCount == 1) { michael@0: SkASSERT(fHead == fTail); michael@0: } else { michael@0: SkASSERT(fHead != fTail); michael@0: } michael@0: michael@0: Entry* entry = fHead; michael@0: int count = 0; michael@0: while (entry) { michael@0: count += 1; michael@0: entry = entry->fNext; michael@0: } michael@0: SkASSERT(count == fEntryCount); michael@0: michael@0: entry = fTail; michael@0: while (entry) { michael@0: count -= 1; michael@0: entry = entry->fPrev; michael@0: } michael@0: SkASSERT(0 == count); michael@0: } else { michael@0: SkASSERT(NULL == fHead); michael@0: SkASSERT(NULL == fTail); michael@0: } michael@0: } michael@0: michael@0: #endif