diff options
author | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-07-23 19:13:54 +0000 |
---|---|---|
committer | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-07-23 19:13:54 +0000 |
commit | 602a1d70257eb3fcb746d758577f042d8c94f6d9 (patch) | |
tree | 268b7f22d8e8255e125c3b55dba6fe29fb18cc0d | |
parent | 7699eeb84762b8fea78333a00826c1e9b0da9541 (diff) |
add scaledimagecache
BUG=
Review URL: https://codereview.chromium.org/20005003
git-svn-id: http://skia.googlecode.com/svn/trunk@10286 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | bench/ImageCacheBench.cpp | 60 | ||||
-rw-r--r-- | gyp/bench.gypi | 1 | ||||
-rw-r--r-- | gyp/core.gypi | 1 | ||||
-rw-r--r-- | gyp/tests.gyp | 1 | ||||
-rw-r--r-- | include/config/SkUserConfig.h | 7 | ||||
-rw-r--r-- | include/core/SkGraphics.h | 4 | ||||
-rw-r--r-- | src/core/SkBitmapProcState.cpp | 37 | ||||
-rw-r--r-- | src/core/SkBitmapProcState.h | 6 | ||||
-rw-r--r-- | src/core/SkScaledImageCache.cpp | 421 | ||||
-rw-r--r-- | src/core/SkScaledImageCache.h | 107 | ||||
-rw-r--r-- | tests/ImageCacheTest.cpp | 65 |
11 files changed, 701 insertions, 9 deletions
diff --git a/bench/ImageCacheBench.cpp b/bench/ImageCacheBench.cpp new file mode 100644 index 0000000000..3a22a47826 --- /dev/null +++ b/bench/ImageCacheBench.cpp @@ -0,0 +1,60 @@ +/* + * 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 "SkBenchmark.h" +#include "SkScaledImageCache.h" + +class ImageCacheBench : public SkBenchmark { + SkScaledImageCache fCache; + SkBitmap fBM; + + enum { + N = SkBENCHLOOP(1000), + DIM = 1, + CACHE_COUNT = 500 + }; +public: + ImageCacheBench(void* param) : INHERITED(param) , fCache(CACHE_COUNT * 100) { + fBM.setConfig(SkBitmap::kARGB_8888_Config, DIM, DIM); + fBM.allocPixels(); + } + + void populateCache() { + SkScalar scale = 1; + for (int i = 0; i < CACHE_COUNT; ++i) { + SkBitmap tmp; + tmp.setConfig(SkBitmap::kARGB_8888_Config, 1, 1); + tmp.allocPixels(); + fCache.unlock(fCache.addAndLock(fBM, scale, scale, tmp)); + scale += 1; + } + } + +protected: + virtual const char* onGetName() SK_OVERRIDE { + return "imagecache"; + } + + virtual void onDraw(SkCanvas*) SK_OVERRIDE { + if (fCache.getBytesUsed() == 0) { + this->populateCache(); + } + + SkBitmap tmp; + // search for a miss (-1 scale) + for (int i = 0; i < N; ++i) { + (void)fCache.findAndLock(fBM, -1, -1, &tmp); + } + } + +private: + typedef SkBenchmark INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +DEF_BENCH( return new ImageCacheBench(p); ) diff --git a/gyp/bench.gypi b/gyp/bench.gypi index 4d9dcbe908..a2d5369f7a 100644 --- a/gyp/bench.gypi +++ b/gyp/bench.gypi @@ -29,6 +29,7 @@ '../bench/GameBench.cpp', '../bench/GradientBench.cpp', '../bench/GrMemoryPoolBench.cpp', + '../bench/ImageCacheBench.cpp', '../bench/ImageDecodeBench.cpp', '../bench/InterpBench.cpp', '../bench/HairlinePathBench.cpp', diff --git a/gyp/core.gypi b/gyp/core.gypi index 51cf640429..5c3fdf575d 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -150,6 +150,7 @@ '<(skia_src_path)/core/SkRRect.cpp', '<(skia_src_path)/core/SkRTree.h', '<(skia_src_path)/core/SkRTree.cpp', + '<(skia_src_path)/core/SkScaledImageCache.cpp', '<(skia_src_path)/core/SkScalar.cpp', '<(skia_src_path)/core/SkScalerContext.cpp', '<(skia_src_path)/core/SkScalerContext.h', diff --git a/gyp/tests.gyp b/gyp/tests.gyp index e93a12f493..3f4cbe733e 100644 --- a/gyp/tests.gyp +++ b/gyp/tests.gyp @@ -67,6 +67,7 @@ '../tests/GrMemoryPoolTest.cpp', '../tests/GrSurfaceTest.cpp', '../tests/HashCacheTest.cpp', + '../tests/ImageCacheTest.cpp', '../tests/ImageDecodingTest.cpp', '../tests/InfRectTest.cpp', '../tests/LListTest.cpp', diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h index 63fc90db72..64263e2d83 100644 --- a/include/config/SkUserConfig.h +++ b/include/config/SkUserConfig.h @@ -122,6 +122,13 @@ */ //#define SK_DEFAULT_FONT_CACHE_LIMIT (1024 * 1024) +/* + * To specify the default size of the image cache, undefine this and set it to + * the desired value (in bytes). SkGraphics.h as a runtime API to set this + * value as well. If this is undefined, a built-in value will be used. + */ +//#define SK_DEFAULT_IMAGE_CACHE_LIMIT (1024 * 1024) + /* If defined, use CoreText instead of ATSUI on OS X. */ //#define SK_USE_MAC_CORE_TEXT diff --git a/include/core/SkGraphics.h b/include/core/SkGraphics.h index 87d66ad1f0..c876042590 100644 --- a/include/core/SkGraphics.h +++ b/include/core/SkGraphics.h @@ -60,6 +60,10 @@ public: */ static void PurgeFontCache(); + static size_t GetImageCacheBytesUsed(); + static size_t GetImageCacheByteLimit(); + static size_t SetImageCacheByteLimit(size_t newLimit); + /** * Applications with command line options may pass optional state, such * as cache sizes, here, for instance: diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp index 991521a95d..f880a86196 100644 --- a/src/core/SkBitmapProcState.cpp +++ b/src/core/SkBitmapProcState.cpp @@ -12,6 +12,7 @@ #include "SkShader.h" // for tilemodes #include "SkUtilsArm.h" #include "SkBitmapScaler.h" +#include "SkScaledImageCache.h" #if !SK_ARM_NEON_IS_NONE // These are defined in src/opts/SkBitmapProcState_arm_neon.cpp @@ -142,14 +143,29 @@ void SkBitmapProcState::possiblyScaleImage() { fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) && fOrigBitmap.config() == SkBitmap::kARGB_8888_Config) { - int dest_width = SkScalarCeilToInt(fOrigBitmap.width() / fInvMatrix.getScaleX()); - int dest_height = SkScalarCeilToInt(fOrigBitmap.height() / fInvMatrix.getScaleY()); - - // All the criteria are met; let's make a new bitmap. - - fScaledBitmap = SkBitmapScaler::Resize( fOrigBitmap, SkBitmapScaler::RESIZE_BEST, - dest_width, dest_height, fConvolutionProcs ); - + SkScalar invScaleX = fInvMatrix.getScaleX(); + SkScalar invScaleY = fInvMatrix.getScaleY(); + + SkASSERT(NULL == fScaledCacheID); + fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, + invScaleX, invScaleY, + &fScaledBitmap); + if (NULL == fScaledCacheID) { + int dest_width = SkScalarCeilToInt(fOrigBitmap.width() / invScaleX); + int dest_height = SkScalarCeilToInt(fOrigBitmap.height() / invScaleY); + + // All the criteria are met; let's make a new bitmap. + + fScaledBitmap = SkBitmapScaler::Resize(fOrigBitmap, + SkBitmapScaler::RESIZE_BEST, + dest_width, + dest_height, + fConvolutionProcs); + fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, + invScaleX, + invScaleY, + fScaledBitmap); + } fScaledBitmap.lockPixels(); fBitmap = &fScaledBitmap; @@ -234,6 +250,11 @@ void SkBitmapProcState::endContext() { SkDELETE(fBitmapFilter); fBitmapFilter = NULL; fScaledBitmap.reset(); + + if (fScaledCacheID) { + SkScaledImageCache::Unlock(fScaledCacheID); + fScaledCacheID = NULL; + } } bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { diff --git a/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h index 349194f3b4..70d7c0cb04 100644 --- a/src/core/SkBitmapProcState.h +++ b/src/core/SkBitmapProcState.h @@ -13,6 +13,7 @@ #include "SkBitmap.h" #include "SkBitmapFilter.h" #include "SkMatrix.h" +#include "SkScaledImageCache.h" #define FractionalInt_IS_64BIT @@ -35,8 +36,9 @@ struct SkConvolutionProcs; struct SkBitmapProcState { - SkBitmapProcState(): fBitmapFilter(NULL) {} + SkBitmapProcState(): fScaledCacheID(NULL), fBitmapFilter(NULL) {} ~SkBitmapProcState() { + SkASSERT(NULL == fScaledCacheID); SkDELETE(fBitmapFilter); } @@ -157,6 +159,8 @@ private: SkBitmap fOrigBitmap; // CONSTRUCTOR SkBitmap fScaledBitmap; // chooseProcs + SkScaledImageCache::ID* fScaledCacheID; + MatrixProc chooseMatrixProc(bool trivial_matrix); bool chooseProcs(const SkMatrix& inv, const SkPaint&); ShaderProc32 chooseShaderProc32(); diff --git a/src/core/SkScaledImageCache.cpp b/src/core/SkScaledImageCache.cpp new file mode 100644 index 0000000000..993b81bbdf --- /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); +} + diff --git a/src/core/SkScaledImageCache.h b/src/core/SkScaledImageCache.h new file mode 100644 index 0000000000..6802043150 --- /dev/null +++ b/src/core/SkScaledImageCache.h @@ -0,0 +1,107 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkScaledImageCache_DEFINED +#define SkScaledImageCache_DEFINED + +#include "SkBitmap.h" + +/** + * Cache object for bitmaps (with possible scale in X Y as part of the key). + * + * Multiple caches can be instantiated, but each instance is not implicitly + * thread-safe, so if a given instance is to be shared across threads, the + * caller must manage the access itself (e.g. via a mutex). + * + * As a convenience, a global instance is also defined, which can be safely + * access across threads via the static methods (e.g. FindAndLock, etc.). + */ +class SkScaledImageCache { +public: + struct ID; + + /* + * The following static methods are thread-safe wrappers around a global + * instance of this cache. + */ + + static ID* FindAndLock(const SkBitmap& original, SkScalar scaleX, + SkScalar scaleY, SkBitmap* scaled); + + static ID* AddAndLock(const SkBitmap& original, SkScalar scaleX, + SkScalar scaleY, const SkBitmap& scaled); + + static void Unlock(ID*); + + static size_t GetBytesUsed(); + static size_t GetByteLimit(); + static size_t SetByteLimit(size_t newLimit); + + /////////////////////////////////////////////////////////////////////////// + + SkScaledImageCache(size_t byteLimit); + ~SkScaledImageCache(); + + /** + * Search the cache for a scaled version of original. If found, return it + * in scaled, and return its ID pointer. Use the returned ptr to unlock + * the cache when you are done using scaled. + * + * If a match is not found, scaled will be unmodifed, and NULL will be + * returned. + */ + ID* findAndLock(const SkBitmap& original, SkScalar scaleX, + SkScalar scaleY, SkBitmap* scaled); + + /** + * To add a new (scaled) bitmap to the cache, call AddAndLock. Use the + * returned ptr to unlock the cache when you are done using scaled. + */ + ID* addAndLock(const SkBitmap& original, SkScalar scaleX, + SkScalar scaleY, const SkBitmap& scaled); + + /** + * Given a non-null ID ptr returned by either findAndLock or addAndLock, + * this releases the associated resources to be available to be purged + * if needed. After this, the cached bitmap should no longer be + * referenced by the caller. + */ + void unlock(ID*); + + size_t getBytesUsed() const { return fBytesUsed; } + size_t getByteLimit() const { return fByteLimit; } + + /** + * Set the maximum number of bytes available to this cache. If the current + * cache exceeds this new value, it will be purged to try to fit within + * this new limit. + */ + size_t setByteLimit(size_t newLimit); + +private: + struct Rec; + Rec* fHead; + Rec* fTail; + + size_t fBytesUsed; + size_t fByteLimit; + int fCount; + + void purgeAsNeeded(); + + // linklist management + void moveToHead(Rec*); + void addToHead(Rec*); + void detach(Rec*); +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif +}; + +#endif diff --git a/tests/ImageCacheTest.cpp b/tests/ImageCacheTest.cpp new file mode 100644 index 0000000000..63b18e8a07 --- /dev/null +++ b/tests/ImageCacheTest.cpp @@ -0,0 +1,65 @@ +/* + * 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 "Test.h" +#include "SkScaledImageCache.h" + +static void make_bm(SkBitmap* bm, int w, int h) { + bm->setConfig(SkBitmap::kARGB_8888_Config, w, h); + bm->allocPixels(); +} + +static void TestImageCache(skiatest::Reporter* reporter) { + static const int COUNT = 10; + static const int DIM = 256; + static const size_t defLimit = DIM * DIM * 4 * COUNT + 1024; // 1K slop + SkScaledImageCache cache(defLimit); + SkScaledImageCache::ID* id; + + SkBitmap bm[COUNT]; + + SkScalar scale = 2; + for (size_t i = 0; i < COUNT; ++i) { + SkBitmap tmp; + + make_bm(&bm[i], DIM, DIM); + id = cache.findAndLock(bm[i], scale, scale, &tmp); + REPORTER_ASSERT(reporter, NULL == id); + + make_bm(&tmp, DIM, DIM); + id = cache.addAndLock(bm[i], scale, scale, tmp); + REPORTER_ASSERT(reporter, NULL != id); + + SkBitmap tmp2; + SkScaledImageCache::ID* id2 = cache.findAndLock(bm[i], scale, scale, + &tmp2); + REPORTER_ASSERT(reporter, id == id2); + REPORTER_ASSERT(reporter, tmp.pixelRef() == tmp2.pixelRef()); + REPORTER_ASSERT(reporter, tmp.width() == tmp2.width()); + REPORTER_ASSERT(reporter, tmp.height() == tmp2.height()); + cache.unlock(id2); + + cache.unlock(id); + } + + // stress test, should trigger purges + for (size_t i = 0; i < COUNT * 100; ++i) { + SkBitmap tmp; + + make_bm(&tmp, DIM, DIM); + id = cache.addAndLock(bm[0], scale, scale, tmp); + REPORTER_ASSERT(reporter, NULL != id); + cache.unlock(id); + + scale += 1; + } + + cache.setByteLimit(0); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("ImageCache", TestImageCacheClass, TestImageCache) |