blob: 25e29c2fb74aa3738e8b6f7a92ab0f07523dcc78 [file] [log] [blame]
reed@google.com602a1d72013-07-23 19:13:54 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkScaledImageCache.h"
reed@google.comd94697c2013-07-24 14:31:33 +00009#include "SkMipMap.h"
commit-bot@chromium.org75854792013-10-29 19:55:00 +000010#include "SkOnce.h"
reed@google.com602a1d72013-07-23 19:13:54 +000011#include "SkPixelRef.h"
12#include "SkRect.h"
13
reed@google.come4eb1222013-12-09 22:29:30 +000014// This can be defined by the caller's build system
15//#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
16
17#ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT
18# define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 1024
19#endif
20
reed@google.com602a1d72013-07-23 19:13:54 +000021#ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
22 #define SK_DEFAULT_IMAGE_CACHE_LIMIT (2 * 1024 * 1024)
23#endif
24
commit-bot@chromium.org75854792013-10-29 19:55:00 +000025static inline SkScaledImageCache::ID* rec_to_id(SkScaledImageCache::Rec* rec) {
26 return reinterpret_cast<SkScaledImageCache::ID*>(rec);
27}
28
29static inline SkScaledImageCache::Rec* id_to_rec(SkScaledImageCache::ID* id) {
30 return reinterpret_cast<SkScaledImageCache::Rec*>(id);
31}
reed@google.com5d1e5582013-07-25 14:36:15 +000032
reed@google.com602a1d72013-07-23 19:13:54 +000033 // Implemented from en.wikipedia.org/wiki/MurmurHash.
34static uint32_t compute_hash(const uint32_t data[], int count) {
35 uint32_t hash = 0;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +000036
reed@google.com602a1d72013-07-23 19:13:54 +000037 for (int i = 0; i < count; ++i) {
38 uint32_t k = data[i];
39 k *= 0xcc9e2d51;
40 k = (k << 15) | (k >> 17);
41 k *= 0x1b873593;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +000042
reed@google.com602a1d72013-07-23 19:13:54 +000043 hash ^= k;
44 hash = (hash << 13) | (hash >> 19);
45 hash *= 5;
46 hash += 0xe6546b64;
47 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +000048
reed@google.com602a1d72013-07-23 19:13:54 +000049 // hash ^= size;
50 hash ^= hash >> 16;
51 hash *= 0x85ebca6b;
52 hash ^= hash >> 13;
53 hash *= 0xc2b2ae35;
54 hash ^= hash >> 16;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +000055
reed@google.com602a1d72013-07-23 19:13:54 +000056 return hash;
57}
reed@google.com602a1d72013-07-23 19:13:54 +000058
rmistry@google.comd6bab022013-12-02 13:50:38 +000059struct SkScaledImageCache::Key {
commit-bot@chromium.org75854792013-10-29 19:55:00 +000060 Key(uint32_t genID,
61 SkScalar scaleX,
62 SkScalar scaleY,
63 SkIRect bounds)
64 : fGenID(genID)
65 , fScaleX(scaleX)
66 , fScaleY(scaleY)
67 , fBounds(bounds) {
reed@google.com602a1d72013-07-23 19:13:54 +000068 fHash = compute_hash(&fGenID, 7);
reed@google.com602a1d72013-07-23 19:13:54 +000069 }
70
reed@google.com5d1e5582013-07-25 14:36:15 +000071 bool operator<(const Key& other) const {
reed@google.com602a1d72013-07-23 19:13:54 +000072 const uint32_t* a = &fGenID;
73 const uint32_t* b = &other.fGenID;
74 for (int i = 0; i < 7; ++i) {
75 if (a[i] < b[i]) {
76 return true;
77 }
78 if (a[i] > b[i]) {
79 return false;
80 }
81 }
82 return false;
83 }
84
reed@google.com5d1e5582013-07-25 14:36:15 +000085 bool operator==(const Key& other) const {
reed@google.com602a1d72013-07-23 19:13:54 +000086 const uint32_t* a = &fHash;
87 const uint32_t* b = &other.fHash;
88 for (int i = 0; i < 8; ++i) {
89 if (a[i] != b[i]) {
90 return false;
91 }
92 }
93 return true;
94 }
95
96 uint32_t fHash;
97 uint32_t fGenID;
98 float fScaleX;
99 float fScaleY;
100 SkIRect fBounds;
101};
102
103struct SkScaledImageCache::Rec {
104 Rec(const Key& key, const SkBitmap& bm) : fKey(key), fBitmap(bm) {
105 fLockCount = 1;
reed@google.comd94697c2013-07-24 14:31:33 +0000106 fMip = NULL;
reed@google.com602a1d72013-07-23 19:13:54 +0000107 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000108
reed@google.comd94697c2013-07-24 14:31:33 +0000109 Rec(const Key& key, const SkMipMap* mip) : fKey(key) {
110 fLockCount = 1;
111 fMip = mip;
112 mip->ref();
113 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000114
reed@google.comd94697c2013-07-24 14:31:33 +0000115 ~Rec() {
116 SkSafeUnref(fMip);
117 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000118
reed@google.com602a1d72013-07-23 19:13:54 +0000119 size_t bytesUsed() const {
reed@google.comd94697c2013-07-24 14:31:33 +0000120 return fMip ? fMip->getSize() : fBitmap.getSize();
reed@google.com602a1d72013-07-23 19:13:54 +0000121 }
122
123 Rec* fNext;
124 Rec* fPrev;
125
126 // this guy wants to be 64bit aligned
127 Key fKey;
128
129 int32_t fLockCount;
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000130
reed@google.comd94697c2013-07-24 14:31:33 +0000131 // we use either fBitmap or fMip, but not both
reed@google.com602a1d72013-07-23 19:13:54 +0000132 SkBitmap fBitmap;
reed@google.comd94697c2013-07-24 14:31:33 +0000133 const SkMipMap* fMip;
reed@google.com602a1d72013-07-23 19:13:54 +0000134};
135
reed@google.com5d1e5582013-07-25 14:36:15 +0000136#include "SkTDynamicHash.h"
137
reed@google.com56172672013-07-25 14:46:22 +0000138namespace { // can't use static functions w/ template parameters
rmistry@google.comd6bab022013-12-02 13:50:38 +0000139const SkScaledImageCache::Key& key_from_rec(const SkScaledImageCache::Rec& rec) {
reed@google.com5d1e5582013-07-25 14:36:15 +0000140 return rec.fKey;
141}
142
rmistry@google.comd6bab022013-12-02 13:50:38 +0000143uint32_t hash_from_key(const SkScaledImageCache::Key& key) {
reed@google.com5d1e5582013-07-25 14:36:15 +0000144 return key.fHash;
145}
146
rmistry@google.comd6bab022013-12-02 13:50:38 +0000147bool eq_rec_key(const SkScaledImageCache::Rec& rec, const SkScaledImageCache::Key& key) {
reed@google.com5d1e5582013-07-25 14:36:15 +0000148 return rec.fKey == key;
149}
reed@google.com56172672013-07-25 14:46:22 +0000150}
reed@google.com5d1e5582013-07-25 14:36:15 +0000151
152class SkScaledImageCache::Hash : public SkTDynamicHash<SkScaledImageCache::Rec,
rmistry@google.comd6bab022013-12-02 13:50:38 +0000153 SkScaledImageCache::Key,
154 key_from_rec,
155 hash_from_key,
156 eq_rec_key> {};
reed@google.com5d1e5582013-07-25 14:36:15 +0000157
158///////////////////////////////////////////////////////////////////////////////
159
reed@google.comb8d17fe2013-07-25 17:42:24 +0000160// experimental hash to speed things up
reed@google.comea348cb2013-07-25 17:50:37 +0000161#define USE_HASH
reed@google.com5d1e5582013-07-25 14:36:15 +0000162
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000163#if !defined(USE_HASH)
164static inline SkScaledImageCache::Rec* find_rec_in_list(
165 SkScaledImageCache::Rec* head, const Key & key) {
166 SkScaledImageCache::Rec* rec = head;
167 while ((rec != NULL) && (rec->fKey != key)) {
168 rec = rec->fNext;
169 }
170 return rec;
171}
172#endif
173
reed@google.come4eb1222013-12-09 22:29:30 +0000174void SkScaledImageCache::init() {
reed@google.com602a1d72013-07-23 19:13:54 +0000175 fHead = NULL;
176 fTail = NULL;
reed@google.com5d1e5582013-07-25 14:36:15 +0000177#ifdef USE_HASH
178 fHash = new Hash;
179#else
180 fHash = NULL;
181#endif
reed@google.com602a1d72013-07-23 19:13:54 +0000182 fBytesUsed = 0;
reed@google.com602a1d72013-07-23 19:13:54 +0000183 fCount = 0;
reed@google.come4eb1222013-12-09 22:29:30 +0000184 fAllocator = NULL;
185
186 // One of these should be explicit set by the caller after we return.
187 fByteLimit = 0;
188 fDiscardableFactory = NULL;
189}
190
191#include "SkDiscardableMemory.h"
192
193class SkOneShotDiscardablePixelRef : public SkPixelRef {
194public:
195 // Ownership of the discardablememory is transfered to the pixelref
196 SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes);
197 ~SkOneShotDiscardablePixelRef();
198
199 SK_DECLARE_UNFLATTENABLE_OBJECT()
200
201protected:
202 virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
203 virtual void onUnlockPixels() SK_OVERRIDE;
204 virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
205
206private:
207 SkImageInfo fInfo; // remove when SkPixelRef gets this in baseclass
208
209 SkDiscardableMemory* fDM;
210 size_t fRB;
211 bool fFirstTime;
212
213 typedef SkPixelRef INHERITED;
214};
215
216SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info,
217 SkDiscardableMemory* dm,
218 size_t rowBytes)
219 : INHERITED(info)
220 , fDM(dm)
221 , fRB(rowBytes)
222{
223 fInfo = info; // remove this redundant field when SkPixelRef has info
224
225 SkASSERT(dm->data());
226 fFirstTime = true;
227}
228
229SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
230 SkDELETE(fDM);
231}
232
233void* SkOneShotDiscardablePixelRef::onLockPixels(SkColorTable** ctable) {
234 if (fFirstTime) {
235 // we're already locked
236 fFirstTime = false;
237 return fDM->data();
238 }
239 return fDM->lock() ? fDM->data() : NULL;
240}
241
242void SkOneShotDiscardablePixelRef::onUnlockPixels() {
243 SkASSERT(!fFirstTime);
244 fDM->unlock();
245}
246
247size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
248 return fInfo.fHeight * fRB;
249}
250
251class SkScaledImageCacheDiscardableAllocator : public SkBitmap::Allocator {
252public:
253 SkScaledImageCacheDiscardableAllocator(
254 SkScaledImageCache::DiscardableFactory factory) {
255 SkASSERT(factory);
256 fFactory = factory;
257 }
258
259 virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
260
261private:
262 SkScaledImageCache::DiscardableFactory fFactory;
263};
264
265bool SkScaledImageCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap,
266 SkColorTable* ctable) {
267 size_t size = bitmap->getSize();
268 if (0 == size) {
269 return false;
270 }
271
272 SkDiscardableMemory* dm = fFactory(size);
273 if (NULL == dm) {
274 return false;
275 }
276
277 // can relax when we have bitmap::asImageInfo
278 if (SkBitmap::kARGB_8888_Config != bitmap->config()) {
279 return false;
280 }
281
282 SkImageInfo info = {
283 bitmap->width(),
284 bitmap->height(),
285 kPMColor_SkColorType,
286 bitmap->alphaType()
287 };
288
289 bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef,
290 (info, dm, bitmap->rowBytes())))->unref();
291 bitmap->lockPixels();
292 return bitmap->readyToDraw();
293}
294
295SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) {
296 this->init();
297 fDiscardableFactory = factory;
298
299 fAllocator = SkNEW_ARGS(SkScaledImageCacheDiscardableAllocator, (factory));
300}
301
302SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
303 this->init();
304 fByteLimit = byteLimit;
reed@google.com602a1d72013-07-23 19:13:54 +0000305}
306
307SkScaledImageCache::~SkScaledImageCache() {
reed@google.come4eb1222013-12-09 22:29:30 +0000308 SkSafeUnref(fAllocator);
309
reed@google.com602a1d72013-07-23 19:13:54 +0000310 Rec* rec = fHead;
311 while (rec) {
312 Rec* next = rec->fNext;
313 SkDELETE(rec);
314 rec = next;
315 }
reed@google.com5d1e5582013-07-25 14:36:15 +0000316 delete fHash;
reed@google.com602a1d72013-07-23 19:13:54 +0000317}
318
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000319////////////////////////////////////////////////////////////////////////////////
320
rmistry@google.comd6bab022013-12-02 13:50:38 +0000321
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000322SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(uint32_t genID,
reed@google.com602a1d72013-07-23 19:13:54 +0000323 SkScalar scaleX,
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000324 SkScalar scaleY,
325 const SkIRect& bounds) {
rmistry@google.comd6bab022013-12-02 13:50:38 +0000326 const Key key(genID, scaleX, scaleY, bounds);
327 return this->findAndLock(key);
328}
329
330/**
331 This private method is the fully general record finder. All other
332 record finders should call this function or the one above. */
333SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(const SkScaledImageCache::Key& key) {
334 if (key.fBounds.isEmpty()) {
reed@google.com602a1d72013-07-23 19:13:54 +0000335 return NULL;
336 }
reed@google.com5d1e5582013-07-25 14:36:15 +0000337#ifdef USE_HASH
338 Rec* rec = fHash->find(key);
339#else
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000340 Rec* rec = find_rec_in_list(fHead, key);
reed@google.com5d1e5582013-07-25 14:36:15 +0000341#endif
reed@google.com5d1e5582013-07-25 14:36:15 +0000342 if (rec) {
343 this->moveToHead(rec); // for our LRU
344 rec->fLockCount += 1;
345 }
346 return rec;
reed@google.com602a1d72013-07-23 19:13:54 +0000347}
348
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000349/**
350 This function finds the bounds of the bitmap *within its pixelRef*.
351 If the bitmap lacks a pixelRef, it will return an empty rect, since
352 that doesn't make sense. This may be a useful enough function that
353 it should be somewhere else (in SkBitmap?). */
354static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
355 if (!(bm.pixelRef())) {
356 return SkIRect::MakeEmpty();
357 }
358 size_t x, y;
359 SkTDivMod(bm.pixelRefOffset(), bm.rowBytes(), &y, &x);
360 x >>= bm.shiftPerPixel();
361 return SkIRect::MakeXYWH(x, y, bm.width(), bm.height());
362}
363
364
365SkScaledImageCache::ID* SkScaledImageCache::findAndLock(uint32_t genID,
366 int32_t width,
367 int32_t height,
368 SkBitmap* bitmap) {
369 Rec* rec = this->findAndLock(genID, SK_Scalar1, SK_Scalar1,
370 SkIRect::MakeWH(width, height));
371 if (rec) {
372 SkASSERT(NULL == rec->fMip);
373 SkASSERT(rec->fBitmap.pixelRef());
374 *bitmap = rec->fBitmap;
375 }
376 return rec_to_id(rec);
377}
378
reed@google.comd94697c2013-07-24 14:31:33 +0000379SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig,
380 SkScalar scaleX,
381 SkScalar scaleY,
382 SkBitmap* scaled) {
383 if (0 == scaleX || 0 == scaleY) {
384 // degenerate, and the key we use for mipmaps
385 return NULL;
386 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000387 Rec* rec = this->findAndLock(orig.getGenerationID(), scaleX,
388 scaleY, get_bounds_from_bitmap(orig));
reed@google.comd94697c2013-07-24 14:31:33 +0000389 if (rec) {
390 SkASSERT(NULL == rec->fMip);
391 SkASSERT(rec->fBitmap.pixelRef());
392 *scaled = rec->fBitmap;
393 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000394 return rec_to_id(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000395}
396
397SkScaledImageCache::ID* SkScaledImageCache::findAndLockMip(const SkBitmap& orig,
398 SkMipMap const ** mip) {
skia.committer@gmail.comb77f0f42013-10-30 07:01:56 +0000399 Rec* rec = this->findAndLock(orig.getGenerationID(), 0, 0,
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000400 get_bounds_from_bitmap(orig));
reed@google.comd94697c2013-07-24 14:31:33 +0000401 if (rec) {
402 SkASSERT(rec->fMip);
403 SkASSERT(NULL == rec->fBitmap.pixelRef());
404 *mip = rec->fMip;
405 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000406 return rec_to_id(rec);
407}
408
409
410////////////////////////////////////////////////////////////////////////////////
411/**
412 This private method is the fully general record adder. All other
413 record adders should call this funtion. */
rmistry@google.comd6bab022013-12-02 13:50:38 +0000414SkScaledImageCache::ID* SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000415 SkASSERT(rec);
rmistry@google.comd6bab022013-12-02 13:50:38 +0000416 // See if we already have this key (racy inserts, etc.)
417 Rec* existing = this->findAndLock(rec->fKey);
418 if (existing != NULL) {
419 return rec_to_id(existing);
420 }
421
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000422 this->addToHead(rec);
423 SkASSERT(1 == rec->fLockCount);
424#ifdef USE_HASH
425 SkASSERT(fHash);
426 fHash->add(rec);
427#endif
428 // We may (now) be overbudget, so see if we need to purge something.
429 this->purgeAsNeeded();
rmistry@google.comd6bab022013-12-02 13:50:38 +0000430 return rec_to_id(rec);
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000431}
432
433SkScaledImageCache::ID* SkScaledImageCache::addAndLock(uint32_t genID,
434 int32_t width,
435 int32_t height,
436 const SkBitmap& bitmap) {
437 Key key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeWH(width, height));
438 Rec* rec = SkNEW_ARGS(Rec, (key, bitmap));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000439 return this->addAndLock(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000440}
441
reed@google.com602a1d72013-07-23 19:13:54 +0000442SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig,
443 SkScalar scaleX,
444 SkScalar scaleY,
445 const SkBitmap& scaled) {
reed@google.comd94697c2013-07-24 14:31:33 +0000446 if (0 == scaleX || 0 == scaleY) {
447 // degenerate, and the key we use for mipmaps
448 return NULL;
449 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000450 SkIRect bounds = get_bounds_from_bitmap(orig);
451 if (bounds.isEmpty()) {
reed@google.com602a1d72013-07-23 19:13:54 +0000452 return NULL;
453 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000454 Key key(orig.getGenerationID(), scaleX, scaleY, bounds);
reed@google.com602a1d72013-07-23 19:13:54 +0000455 Rec* rec = SkNEW_ARGS(Rec, (key, scaled));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000456 return this->addAndLock(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000457}
reed@google.com602a1d72013-07-23 19:13:54 +0000458
reed@google.comd94697c2013-07-24 14:31:33 +0000459SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig,
460 const SkMipMap* mip) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000461 SkIRect bounds = get_bounds_from_bitmap(orig);
462 if (bounds.isEmpty()) {
reed@google.comd94697c2013-07-24 14:31:33 +0000463 return NULL;
464 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000465 Key key(orig.getGenerationID(), 0, 0, bounds);
reed@google.comd94697c2013-07-24 14:31:33 +0000466 Rec* rec = SkNEW_ARGS(Rec, (key, mip));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000467 return this->addAndLock(rec);
reed@google.com602a1d72013-07-23 19:13:54 +0000468}
469
470void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) {
471 SkASSERT(id);
472
473#ifdef SK_DEBUG
474 {
475 bool found = false;
476 Rec* rec = fHead;
477 while (rec != NULL) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000478 if (rec == id_to_rec(id)) {
reed@google.com602a1d72013-07-23 19:13:54 +0000479 found = true;
480 break;
481 }
482 rec = rec->fNext;
483 }
484 SkASSERT(found);
485 }
486#endif
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000487 Rec* rec = id_to_rec(id);
reed@google.com602a1d72013-07-23 19:13:54 +0000488 SkASSERT(rec->fLockCount > 0);
489 rec->fLockCount -= 1;
490
reed@google.com602a1d72013-07-23 19:13:54 +0000491 // we may have been over-budget, but now have released something, so check
492 // if we should purge.
493 if (0 == rec->fLockCount) {
494 this->purgeAsNeeded();
495 }
496}
497
498void SkScaledImageCache::purgeAsNeeded() {
reed@google.come4eb1222013-12-09 22:29:30 +0000499 size_t byteLimit;
500 int countLimit;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000501
reed@google.come4eb1222013-12-09 22:29:30 +0000502 if (fDiscardableFactory) {
503 countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
504 byteLimit = SK_MaxU32; // no limit based on bytes
505 } else {
506 countLimit = SK_MaxS32; // no limit based on count
507 byteLimit = fByteLimit;
508 }
509
510 size_t bytesUsed = fBytesUsed;
511 int countUsed = fCount;
512
reed@google.com602a1d72013-07-23 19:13:54 +0000513 Rec* rec = fTail;
514 while (rec) {
reed@google.come4eb1222013-12-09 22:29:30 +0000515 if (bytesUsed < byteLimit && countUsed < countLimit) {
reed@google.com602a1d72013-07-23 19:13:54 +0000516 break;
517 }
reed@google.come4eb1222013-12-09 22:29:30 +0000518
reed@google.com602a1d72013-07-23 19:13:54 +0000519 Rec* prev = rec->fPrev;
520 if (0 == rec->fLockCount) {
reed@google.com602a1d72013-07-23 19:13:54 +0000521 size_t used = rec->bytesUsed();
522 SkASSERT(used <= bytesUsed);
reed@google.com602a1d72013-07-23 19:13:54 +0000523 this->detach(rec);
reed@google.com5d1e5582013-07-25 14:36:15 +0000524#ifdef USE_HASH
525 fHash->remove(rec->fKey);
526#endif
skia.committer@gmail.com956b3102013-07-26 07:00:58 +0000527
reed@google.com602a1d72013-07-23 19:13:54 +0000528 SkDELETE(rec);
reed@google.come4eb1222013-12-09 22:29:30 +0000529
530 bytesUsed -= used;
531 countUsed -= 1;
reed@google.com602a1d72013-07-23 19:13:54 +0000532 }
533 rec = prev;
534 }
reed@google.come4eb1222013-12-09 22:29:30 +0000535
reed@google.com602a1d72013-07-23 19:13:54 +0000536 fBytesUsed = bytesUsed;
reed@google.come4eb1222013-12-09 22:29:30 +0000537 fCount = countUsed;
reed@google.com602a1d72013-07-23 19:13:54 +0000538}
539
540size_t SkScaledImageCache::setByteLimit(size_t newLimit) {
541 size_t prevLimit = fByteLimit;
542 fByteLimit = newLimit;
543 if (newLimit < prevLimit) {
544 this->purgeAsNeeded();
545 }
546 return prevLimit;
547}
548
549///////////////////////////////////////////////////////////////////////////////
550
551void SkScaledImageCache::detach(Rec* rec) {
552 Rec* prev = rec->fPrev;
553 Rec* next = rec->fNext;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000554
reed@google.com602a1d72013-07-23 19:13:54 +0000555 if (!prev) {
556 SkASSERT(fHead == rec);
557 fHead = next;
558 } else {
559 prev->fNext = next;
560 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000561
reed@google.com602a1d72013-07-23 19:13:54 +0000562 if (!next) {
563 fTail = prev;
564 } else {
565 next->fPrev = prev;
566 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000567
reed@google.com602a1d72013-07-23 19:13:54 +0000568 rec->fNext = rec->fPrev = NULL;
569}
570
571void SkScaledImageCache::moveToHead(Rec* rec) {
572 if (fHead == rec) {
573 return;
574 }
575
576 SkASSERT(fHead);
577 SkASSERT(fTail);
578
579 this->validate();
580
581 this->detach(rec);
582
583 fHead->fPrev = rec;
584 rec->fNext = fHead;
585 fHead = rec;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000586
reed@google.com602a1d72013-07-23 19:13:54 +0000587 this->validate();
588}
589
590void SkScaledImageCache::addToHead(Rec* rec) {
591 this->validate();
592
593 rec->fPrev = NULL;
594 rec->fNext = fHead;
595 if (fHead) {
596 fHead->fPrev = rec;
597 }
598 fHead = rec;
599 if (!fTail) {
600 fTail = rec;
601 }
602 fBytesUsed += rec->bytesUsed();
603 fCount += 1;
604
605 this->validate();
606}
607
608#ifdef SK_DEBUG
609void SkScaledImageCache::validate() const {
610 if (NULL == fHead) {
611 SkASSERT(NULL == fTail);
612 SkASSERT(0 == fBytesUsed);
613 return;
614 }
615
616 if (fHead == fTail) {
617 SkASSERT(NULL == fHead->fPrev);
618 SkASSERT(NULL == fHead->fNext);
619 SkASSERT(fHead->bytesUsed() == fBytesUsed);
620 return;
621 }
622
623 SkASSERT(NULL == fHead->fPrev);
624 SkASSERT(NULL != fHead->fNext);
625 SkASSERT(NULL == fTail->fNext);
626 SkASSERT(NULL != fTail->fPrev);
627
628 size_t used = 0;
629 int count = 0;
630 const Rec* rec = fHead;
631 while (rec) {
632 count += 1;
633 used += rec->bytesUsed();
634 SkASSERT(used <= fBytesUsed);
635 rec = rec->fNext;
636 }
637 SkASSERT(fCount == count);
638
639 rec = fTail;
640 while (rec) {
641 SkASSERT(count > 0);
642 count -= 1;
643 SkASSERT(used >= rec->bytesUsed());
644 used -= rec->bytesUsed();
645 rec = rec->fPrev;
646 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000647
reed@google.com602a1d72013-07-23 19:13:54 +0000648 SkASSERT(0 == count);
649 SkASSERT(0 == used);
650}
651#endif
652
653///////////////////////////////////////////////////////////////////////////////
654
655#include "SkThread.h"
656
reed@google.combe19dbe2013-07-24 15:06:34 +0000657SK_DECLARE_STATIC_MUTEX(gMutex);
reed@google.com602a1d72013-07-23 19:13:54 +0000658
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000659static void create_cache(SkScaledImageCache** cache) {
reed@google.come4eb1222013-12-09 22:29:30 +0000660#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
661 *cache = SkNEW_ARGS(SkScaledImageCache, (SkDiscardableMemory::Create));
662#else
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000663 *cache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
reed@google.come4eb1222013-12-09 22:29:30 +0000664#endif
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000665}
666
reed@google.com602a1d72013-07-23 19:13:54 +0000667static SkScaledImageCache* get_cache() {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000668 static SkScaledImageCache* gCache(NULL);
669 SK_DECLARE_STATIC_ONCE(create_cache_once);
commit-bot@chromium.org1b2c1b82013-12-05 19:20:49 +0000670 SkOnce(&create_cache_once, create_cache, &gCache);
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000671 SkASSERT(NULL != gCache);
reed@google.com602a1d72013-07-23 19:13:54 +0000672 return gCache;
673}
674
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000675
676SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(
677 uint32_t pixelGenerationID,
678 int32_t width,
679 int32_t height,
680 SkBitmap* scaled) {
681 SkAutoMutexAcquire am(gMutex);
682 return get_cache()->findAndLock(pixelGenerationID, width, height, scaled);
683}
684
685SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(
686 uint32_t pixelGenerationID,
687 int32_t width,
688 int32_t height,
689 const SkBitmap& scaled) {
690 SkAutoMutexAcquire am(gMutex);
691 return get_cache()->addAndLock(pixelGenerationID, width, height, scaled);
692}
693
694
reed@google.com602a1d72013-07-23 19:13:54 +0000695SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(const SkBitmap& orig,
696 SkScalar scaleX,
697 SkScalar scaleY,
698 SkBitmap* scaled) {
699 SkAutoMutexAcquire am(gMutex);
700 return get_cache()->findAndLock(orig, scaleX, scaleY, scaled);
701}
702
reed@google.comd94697c2013-07-24 14:31:33 +0000703SkScaledImageCache::ID* SkScaledImageCache::FindAndLockMip(const SkBitmap& orig,
704 SkMipMap const ** mip) {
705 SkAutoMutexAcquire am(gMutex);
706 return get_cache()->findAndLockMip(orig, mip);
707}
708
reed@google.com602a1d72013-07-23 19:13:54 +0000709SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(const SkBitmap& orig,
710 SkScalar scaleX,
711 SkScalar scaleY,
712 const SkBitmap& scaled) {
713 SkAutoMutexAcquire am(gMutex);
714 return get_cache()->addAndLock(orig, scaleX, scaleY, scaled);
715}
716
reed@google.comd94697c2013-07-24 14:31:33 +0000717SkScaledImageCache::ID* SkScaledImageCache::AddAndLockMip(const SkBitmap& orig,
718 const SkMipMap* mip) {
719 SkAutoMutexAcquire am(gMutex);
720 return get_cache()->addAndLockMip(orig, mip);
721}
722
reed@google.com602a1d72013-07-23 19:13:54 +0000723void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) {
724 SkAutoMutexAcquire am(gMutex);
725 return get_cache()->unlock(id);
726}
727
728size_t SkScaledImageCache::GetBytesUsed() {
729 SkAutoMutexAcquire am(gMutex);
730 return get_cache()->getBytesUsed();
731}
732
733size_t SkScaledImageCache::GetByteLimit() {
734 SkAutoMutexAcquire am(gMutex);
735 return get_cache()->getByteLimit();
736}
737
738size_t SkScaledImageCache::SetByteLimit(size_t newLimit) {
739 SkAutoMutexAcquire am(gMutex);
740 return get_cache()->setByteLimit(newLimit);
741}
742
reed@google.come4eb1222013-12-09 22:29:30 +0000743SkBitmap::Allocator* SkScaledImageCache::GetAllocator() {
744 SkAutoMutexAcquire am(gMutex);
745 return get_cache()->allocator();
746}
747
reed@google.com602a1d72013-07-23 19:13:54 +0000748///////////////////////////////////////////////////////////////////////////////
749
750#include "SkGraphics.h"
751
752size_t SkGraphics::GetImageCacheBytesUsed() {
753 return SkScaledImageCache::GetBytesUsed();
754}
755
756size_t SkGraphics::GetImageCacheByteLimit() {
757 return SkScaledImageCache::GetByteLimit();
758}
759
760size_t SkGraphics::SetImageCacheByteLimit(size_t newLimit) {
761 return SkScaledImageCache::SetByteLimit(newLimit);
762}