blob: 8320b921816c6fe526455abf26776a9888283cf5 [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
commit-bot@chromium.org158f6462014-04-02 17:03:09 +0000147} // namespace
reed@google.com5d1e5582013-07-25 14:36:15 +0000148
149class SkScaledImageCache::Hash : public SkTDynamicHash<SkScaledImageCache::Rec,
rmistry@google.comd6bab022013-12-02 13:50:38 +0000150 SkScaledImageCache::Key,
151 key_from_rec,
commit-bot@chromium.org158f6462014-04-02 17:03:09 +0000152 hash_from_key> {};
153
reed@google.com5d1e5582013-07-25 14:36:15 +0000154
155///////////////////////////////////////////////////////////////////////////////
156
reed@google.comb8d17fe2013-07-25 17:42:24 +0000157// experimental hash to speed things up
reed@google.comea348cb2013-07-25 17:50:37 +0000158#define USE_HASH
reed@google.com5d1e5582013-07-25 14:36:15 +0000159
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000160#if !defined(USE_HASH)
161static inline SkScaledImageCache::Rec* find_rec_in_list(
162 SkScaledImageCache::Rec* head, const Key & key) {
163 SkScaledImageCache::Rec* rec = head;
164 while ((rec != NULL) && (rec->fKey != key)) {
165 rec = rec->fNext;
166 }
167 return rec;
168}
169#endif
170
reed@google.come4eb1222013-12-09 22:29:30 +0000171void SkScaledImageCache::init() {
reed@google.com602a1d72013-07-23 19:13:54 +0000172 fHead = NULL;
173 fTail = NULL;
reed@google.com5d1e5582013-07-25 14:36:15 +0000174#ifdef USE_HASH
175 fHash = new Hash;
176#else
177 fHash = NULL;
178#endif
reed@google.com602a1d72013-07-23 19:13:54 +0000179 fBytesUsed = 0;
reed@google.com602a1d72013-07-23 19:13:54 +0000180 fCount = 0;
reed@google.come4eb1222013-12-09 22:29:30 +0000181 fAllocator = NULL;
182
183 // One of these should be explicit set by the caller after we return.
184 fByteLimit = 0;
185 fDiscardableFactory = NULL;
186}
187
188#include "SkDiscardableMemory.h"
189
190class SkOneShotDiscardablePixelRef : public SkPixelRef {
191public:
commit-bot@chromium.org227c2462014-01-24 18:33:07 +0000192 SK_DECLARE_INST_COUNT(SkOneShotDiscardablePixelRef)
reed@google.come4eb1222013-12-09 22:29:30 +0000193 // Ownership of the discardablememory is transfered to the pixelref
194 SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes);
195 ~SkOneShotDiscardablePixelRef();
196
197 SK_DECLARE_UNFLATTENABLE_OBJECT()
198
199protected:
reed@google.comd0419b12014-01-06 17:08:27 +0000200 virtual bool onNewLockPixels(LockRec*) SK_OVERRIDE;
reed@google.come4eb1222013-12-09 22:29:30 +0000201 virtual void onUnlockPixels() SK_OVERRIDE;
202 virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
203
204private:
reed@google.come4eb1222013-12-09 22:29:30 +0000205 SkDiscardableMemory* fDM;
206 size_t fRB;
207 bool fFirstTime;
208
209 typedef SkPixelRef INHERITED;
210};
211
212SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info,
213 SkDiscardableMemory* dm,
214 size_t rowBytes)
215 : INHERITED(info)
216 , fDM(dm)
217 , fRB(rowBytes)
218{
reed@google.come4eb1222013-12-09 22:29:30 +0000219 SkASSERT(dm->data());
220 fFirstTime = true;
221}
222
223SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
224 SkDELETE(fDM);
225}
226
reed@google.comd0419b12014-01-06 17:08:27 +0000227bool SkOneShotDiscardablePixelRef::onNewLockPixels(LockRec* rec) {
reed@google.come4eb1222013-12-09 22:29:30 +0000228 if (fFirstTime) {
229 // we're already locked
reed@google.comc83a91f2013-12-13 13:41:14 +0000230 SkASSERT(fDM->data());
reed@google.come4eb1222013-12-09 22:29:30 +0000231 fFirstTime = false;
reed@google.comd0419b12014-01-06 17:08:27 +0000232 goto SUCCESS;
reed@google.come4eb1222013-12-09 22:29:30 +0000233 }
commit-bot@chromium.org281713e2013-12-12 18:08:08 +0000234
reed@google.comfa7fd802013-12-12 21:37:25 +0000235 // A previous call to onUnlock may have deleted our DM, so check for that
236 if (NULL == fDM) {
reed@google.comd0419b12014-01-06 17:08:27 +0000237 return false;
reed@google.comfa7fd802013-12-12 21:37:25 +0000238 }
239
240 if (!fDM->lock()) {
241 // since it failed, we delete it now, to free-up the resource
242 delete fDM;
243 fDM = NULL;
reed@google.comd0419b12014-01-06 17:08:27 +0000244 return false;
reed@google.comfa7fd802013-12-12 21:37:25 +0000245 }
reed@google.comd0419b12014-01-06 17:08:27 +0000246
247SUCCESS:
248 rec->fPixels = fDM->data();
249 rec->fColorTable = NULL;
250 rec->fRowBytes = fRB;
251 return true;
reed@google.come4eb1222013-12-09 22:29:30 +0000252}
253
254void SkOneShotDiscardablePixelRef::onUnlockPixels() {
255 SkASSERT(!fFirstTime);
reed@google.comc83a91f2013-12-13 13:41:14 +0000256 fDM->unlock();
reed@google.come4eb1222013-12-09 22:29:30 +0000257}
258
259size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
reed@google.comd0419b12014-01-06 17:08:27 +0000260 return this->info().getSafeSize(fRB);
reed@google.come4eb1222013-12-09 22:29:30 +0000261}
262
263class SkScaledImageCacheDiscardableAllocator : public SkBitmap::Allocator {
264public:
265 SkScaledImageCacheDiscardableAllocator(
266 SkScaledImageCache::DiscardableFactory factory) {
267 SkASSERT(factory);
268 fFactory = factory;
269 }
270
271 virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
skia.committer@gmail.comcf0803b2013-12-10 07:02:03 +0000272
reed@google.come4eb1222013-12-09 22:29:30 +0000273private:
274 SkScaledImageCache::DiscardableFactory fFactory;
275};
276
277bool SkScaledImageCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap,
278 SkColorTable* ctable) {
279 size_t size = bitmap->getSize();
280 if (0 == size) {
281 return false;
282 }
283
284 SkDiscardableMemory* dm = fFactory(size);
285 if (NULL == dm) {
286 return false;
287 }
288
reed@google.com900ecf22014-02-20 20:55:37 +0000289 // can we relax this?
290 if (kPMColor_SkColorType != bitmap->colorType()) {
reed@google.come4eb1222013-12-09 22:29:30 +0000291 return false;
292 }
293
reed@google.com900ecf22014-02-20 20:55:37 +0000294 SkImageInfo info = bitmap->info();
commit-bot@chromium.orge13af712014-01-13 20:39:14 +0000295 bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef,
reed@google.come4eb1222013-12-09 22:29:30 +0000296 (info, dm, bitmap->rowBytes())))->unref();
297 bitmap->lockPixels();
298 return bitmap->readyToDraw();
299}
300
301SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) {
302 this->init();
303 fDiscardableFactory = factory;
304
305 fAllocator = SkNEW_ARGS(SkScaledImageCacheDiscardableAllocator, (factory));
306}
307
308SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
309 this->init();
310 fByteLimit = byteLimit;
reed@google.com602a1d72013-07-23 19:13:54 +0000311}
312
313SkScaledImageCache::~SkScaledImageCache() {
reed@google.come4eb1222013-12-09 22:29:30 +0000314 SkSafeUnref(fAllocator);
315
reed@google.com602a1d72013-07-23 19:13:54 +0000316 Rec* rec = fHead;
317 while (rec) {
318 Rec* next = rec->fNext;
319 SkDELETE(rec);
320 rec = next;
321 }
reed@google.com5d1e5582013-07-25 14:36:15 +0000322 delete fHash;
reed@google.com602a1d72013-07-23 19:13:54 +0000323}
324
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000325////////////////////////////////////////////////////////////////////////////////
326
rmistry@google.comd6bab022013-12-02 13:50:38 +0000327
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000328SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(uint32_t genID,
reed@google.com602a1d72013-07-23 19:13:54 +0000329 SkScalar scaleX,
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000330 SkScalar scaleY,
331 const SkIRect& bounds) {
rmistry@google.comd6bab022013-12-02 13:50:38 +0000332 const Key key(genID, scaleX, scaleY, bounds);
333 return this->findAndLock(key);
334}
335
336/**
337 This private method is the fully general record finder. All other
338 record finders should call this function or the one above. */
339SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(const SkScaledImageCache::Key& key) {
340 if (key.fBounds.isEmpty()) {
reed@google.com602a1d72013-07-23 19:13:54 +0000341 return NULL;
342 }
reed@google.com5d1e5582013-07-25 14:36:15 +0000343#ifdef USE_HASH
344 Rec* rec = fHash->find(key);
345#else
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000346 Rec* rec = find_rec_in_list(fHead, key);
reed@google.com5d1e5582013-07-25 14:36:15 +0000347#endif
reed@google.com5d1e5582013-07-25 14:36:15 +0000348 if (rec) {
349 this->moveToHead(rec); // for our LRU
350 rec->fLockCount += 1;
351 }
352 return rec;
reed@google.com602a1d72013-07-23 19:13:54 +0000353}
354
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000355/**
356 This function finds the bounds of the bitmap *within its pixelRef*.
357 If the bitmap lacks a pixelRef, it will return an empty rect, since
358 that doesn't make sense. This may be a useful enough function that
359 it should be somewhere else (in SkBitmap?). */
360static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
361 if (!(bm.pixelRef())) {
362 return SkIRect::MakeEmpty();
363 }
reed@google.com672588b2014-01-08 15:42:01 +0000364 SkIPoint origin = bm.pixelRefOrigin();
365 return SkIRect::MakeXYWH(origin.fX, origin.fY, bm.width(), bm.height());
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000366}
367
368
369SkScaledImageCache::ID* SkScaledImageCache::findAndLock(uint32_t genID,
370 int32_t width,
371 int32_t height,
372 SkBitmap* bitmap) {
373 Rec* rec = this->findAndLock(genID, SK_Scalar1, SK_Scalar1,
374 SkIRect::MakeWH(width, height));
375 if (rec) {
376 SkASSERT(NULL == rec->fMip);
377 SkASSERT(rec->fBitmap.pixelRef());
378 *bitmap = rec->fBitmap;
379 }
380 return rec_to_id(rec);
381}
382
reed@google.comd94697c2013-07-24 14:31:33 +0000383SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig,
384 SkScalar scaleX,
385 SkScalar scaleY,
386 SkBitmap* scaled) {
387 if (0 == scaleX || 0 == scaleY) {
388 // degenerate, and the key we use for mipmaps
389 return NULL;
390 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000391 Rec* rec = this->findAndLock(orig.getGenerationID(), scaleX,
392 scaleY, get_bounds_from_bitmap(orig));
reed@google.comd94697c2013-07-24 14:31:33 +0000393 if (rec) {
394 SkASSERT(NULL == rec->fMip);
395 SkASSERT(rec->fBitmap.pixelRef());
396 *scaled = rec->fBitmap;
397 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000398 return rec_to_id(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000399}
400
401SkScaledImageCache::ID* SkScaledImageCache::findAndLockMip(const SkBitmap& orig,
402 SkMipMap const ** mip) {
skia.committer@gmail.comb77f0f42013-10-30 07:01:56 +0000403 Rec* rec = this->findAndLock(orig.getGenerationID(), 0, 0,
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000404 get_bounds_from_bitmap(orig));
reed@google.comd94697c2013-07-24 14:31:33 +0000405 if (rec) {
406 SkASSERT(rec->fMip);
407 SkASSERT(NULL == rec->fBitmap.pixelRef());
408 *mip = rec->fMip;
409 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000410 return rec_to_id(rec);
411}
412
413
414////////////////////////////////////////////////////////////////////////////////
415/**
416 This private method is the fully general record adder. All other
417 record adders should call this funtion. */
rmistry@google.comd6bab022013-12-02 13:50:38 +0000418SkScaledImageCache::ID* SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000419 SkASSERT(rec);
rmistry@google.comd6bab022013-12-02 13:50:38 +0000420 // See if we already have this key (racy inserts, etc.)
421 Rec* existing = this->findAndLock(rec->fKey);
robertphillips@google.com5db04df2013-12-18 18:48:08 +0000422 if (NULL != existing) {
423 // Since we already have a matching entry, just delete the new one and return.
424 // Call sites cannot assume the passed in object will live past this call.
commit-bot@chromium.org5e4112b2014-03-05 13:44:18 +0000425 existing->fBitmap = rec->fBitmap;
robertphillips@google.com5db04df2013-12-18 18:48:08 +0000426 SkDELETE(rec);
rmistry@google.comd6bab022013-12-02 13:50:38 +0000427 return rec_to_id(existing);
428 }
429
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000430 this->addToHead(rec);
431 SkASSERT(1 == rec->fLockCount);
432#ifdef USE_HASH
433 SkASSERT(fHash);
434 fHash->add(rec);
435#endif
436 // We may (now) be overbudget, so see if we need to purge something.
437 this->purgeAsNeeded();
rmistry@google.comd6bab022013-12-02 13:50:38 +0000438 return rec_to_id(rec);
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000439}
440
441SkScaledImageCache::ID* SkScaledImageCache::addAndLock(uint32_t genID,
442 int32_t width,
443 int32_t height,
444 const SkBitmap& bitmap) {
445 Key key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeWH(width, height));
446 Rec* rec = SkNEW_ARGS(Rec, (key, bitmap));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000447 return this->addAndLock(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000448}
449
reed@google.com602a1d72013-07-23 19:13:54 +0000450SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig,
451 SkScalar scaleX,
452 SkScalar scaleY,
453 const SkBitmap& scaled) {
reed@google.comd94697c2013-07-24 14:31:33 +0000454 if (0 == scaleX || 0 == scaleY) {
455 // degenerate, and the key we use for mipmaps
456 return NULL;
457 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000458 SkIRect bounds = get_bounds_from_bitmap(orig);
459 if (bounds.isEmpty()) {
reed@google.com602a1d72013-07-23 19:13:54 +0000460 return NULL;
461 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000462 Key key(orig.getGenerationID(), scaleX, scaleY, bounds);
reed@google.com602a1d72013-07-23 19:13:54 +0000463 Rec* rec = SkNEW_ARGS(Rec, (key, scaled));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000464 return this->addAndLock(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000465}
reed@google.com602a1d72013-07-23 19:13:54 +0000466
reed@google.comd94697c2013-07-24 14:31:33 +0000467SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig,
468 const SkMipMap* mip) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000469 SkIRect bounds = get_bounds_from_bitmap(orig);
470 if (bounds.isEmpty()) {
reed@google.comd94697c2013-07-24 14:31:33 +0000471 return NULL;
472 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000473 Key key(orig.getGenerationID(), 0, 0, bounds);
reed@google.comd94697c2013-07-24 14:31:33 +0000474 Rec* rec = SkNEW_ARGS(Rec, (key, mip));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000475 return this->addAndLock(rec);
reed@google.com602a1d72013-07-23 19:13:54 +0000476}
477
478void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) {
479 SkASSERT(id);
480
481#ifdef SK_DEBUG
482 {
483 bool found = false;
484 Rec* rec = fHead;
485 while (rec != NULL) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000486 if (rec == id_to_rec(id)) {
reed@google.com602a1d72013-07-23 19:13:54 +0000487 found = true;
488 break;
489 }
490 rec = rec->fNext;
491 }
492 SkASSERT(found);
493 }
494#endif
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000495 Rec* rec = id_to_rec(id);
reed@google.com602a1d72013-07-23 19:13:54 +0000496 SkASSERT(rec->fLockCount > 0);
497 rec->fLockCount -= 1;
498
reed@google.com602a1d72013-07-23 19:13:54 +0000499 // we may have been over-budget, but now have released something, so check
500 // if we should purge.
501 if (0 == rec->fLockCount) {
502 this->purgeAsNeeded();
503 }
504}
505
506void SkScaledImageCache::purgeAsNeeded() {
reed@google.come4eb1222013-12-09 22:29:30 +0000507 size_t byteLimit;
508 int countLimit;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000509
reed@google.come4eb1222013-12-09 22:29:30 +0000510 if (fDiscardableFactory) {
511 countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
512 byteLimit = SK_MaxU32; // no limit based on bytes
513 } else {
514 countLimit = SK_MaxS32; // no limit based on count
515 byteLimit = fByteLimit;
516 }
517
518 size_t bytesUsed = fBytesUsed;
519 int countUsed = fCount;
skia.committer@gmail.comcf0803b2013-12-10 07:02:03 +0000520
reed@google.com602a1d72013-07-23 19:13:54 +0000521 Rec* rec = fTail;
522 while (rec) {
reed@google.come4eb1222013-12-09 22:29:30 +0000523 if (bytesUsed < byteLimit && countUsed < countLimit) {
reed@google.com602a1d72013-07-23 19:13:54 +0000524 break;
525 }
reed@google.come4eb1222013-12-09 22:29:30 +0000526
reed@google.com602a1d72013-07-23 19:13:54 +0000527 Rec* prev = rec->fPrev;
528 if (0 == rec->fLockCount) {
reed@google.com602a1d72013-07-23 19:13:54 +0000529 size_t used = rec->bytesUsed();
530 SkASSERT(used <= bytesUsed);
reed@google.com602a1d72013-07-23 19:13:54 +0000531 this->detach(rec);
reed@google.com5d1e5582013-07-25 14:36:15 +0000532#ifdef USE_HASH
533 fHash->remove(rec->fKey);
534#endif
skia.committer@gmail.com956b3102013-07-26 07:00:58 +0000535
reed@google.com602a1d72013-07-23 19:13:54 +0000536 SkDELETE(rec);
reed@google.come4eb1222013-12-09 22:29:30 +0000537
538 bytesUsed -= used;
539 countUsed -= 1;
reed@google.com602a1d72013-07-23 19:13:54 +0000540 }
541 rec = prev;
542 }
reed@google.come4eb1222013-12-09 22:29:30 +0000543
reed@google.com602a1d72013-07-23 19:13:54 +0000544 fBytesUsed = bytesUsed;
reed@google.come4eb1222013-12-09 22:29:30 +0000545 fCount = countUsed;
reed@google.com602a1d72013-07-23 19:13:54 +0000546}
547
548size_t SkScaledImageCache::setByteLimit(size_t newLimit) {
549 size_t prevLimit = fByteLimit;
550 fByteLimit = newLimit;
551 if (newLimit < prevLimit) {
552 this->purgeAsNeeded();
553 }
554 return prevLimit;
555}
556
557///////////////////////////////////////////////////////////////////////////////
558
559void SkScaledImageCache::detach(Rec* rec) {
560 Rec* prev = rec->fPrev;
561 Rec* next = rec->fNext;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000562
reed@google.com602a1d72013-07-23 19:13:54 +0000563 if (!prev) {
564 SkASSERT(fHead == rec);
565 fHead = next;
566 } else {
567 prev->fNext = next;
568 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000569
reed@google.com602a1d72013-07-23 19:13:54 +0000570 if (!next) {
571 fTail = prev;
572 } else {
573 next->fPrev = prev;
574 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000575
reed@google.com602a1d72013-07-23 19:13:54 +0000576 rec->fNext = rec->fPrev = NULL;
577}
578
579void SkScaledImageCache::moveToHead(Rec* rec) {
580 if (fHead == rec) {
581 return;
582 }
583
584 SkASSERT(fHead);
585 SkASSERT(fTail);
586
587 this->validate();
588
589 this->detach(rec);
590
591 fHead->fPrev = rec;
592 rec->fNext = fHead;
593 fHead = rec;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000594
reed@google.com602a1d72013-07-23 19:13:54 +0000595 this->validate();
596}
597
598void SkScaledImageCache::addToHead(Rec* rec) {
599 this->validate();
600
601 rec->fPrev = NULL;
602 rec->fNext = fHead;
603 if (fHead) {
604 fHead->fPrev = rec;
605 }
606 fHead = rec;
607 if (!fTail) {
608 fTail = rec;
609 }
610 fBytesUsed += rec->bytesUsed();
611 fCount += 1;
612
613 this->validate();
614}
615
reed@google.comfa7fd802013-12-12 21:37:25 +0000616///////////////////////////////////////////////////////////////////////////////
617
reed@google.com602a1d72013-07-23 19:13:54 +0000618#ifdef SK_DEBUG
619void SkScaledImageCache::validate() const {
620 if (NULL == fHead) {
621 SkASSERT(NULL == fTail);
622 SkASSERT(0 == fBytesUsed);
623 return;
624 }
625
626 if (fHead == fTail) {
627 SkASSERT(NULL == fHead->fPrev);
628 SkASSERT(NULL == fHead->fNext);
629 SkASSERT(fHead->bytesUsed() == fBytesUsed);
630 return;
631 }
632
633 SkASSERT(NULL == fHead->fPrev);
634 SkASSERT(NULL != fHead->fNext);
635 SkASSERT(NULL == fTail->fNext);
636 SkASSERT(NULL != fTail->fPrev);
637
638 size_t used = 0;
639 int count = 0;
640 const Rec* rec = fHead;
641 while (rec) {
642 count += 1;
643 used += rec->bytesUsed();
644 SkASSERT(used <= fBytesUsed);
645 rec = rec->fNext;
646 }
647 SkASSERT(fCount == count);
648
649 rec = fTail;
650 while (rec) {
651 SkASSERT(count > 0);
652 count -= 1;
653 SkASSERT(used >= rec->bytesUsed());
654 used -= rec->bytesUsed();
655 rec = rec->fPrev;
656 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000657
reed@google.com602a1d72013-07-23 19:13:54 +0000658 SkASSERT(0 == count);
659 SkASSERT(0 == used);
660}
661#endif
662
reed@google.comfa7fd802013-12-12 21:37:25 +0000663void SkScaledImageCache::dump() const {
664 this->validate();
665
666 const Rec* rec = fHead;
667 int locked = 0;
668 while (rec) {
669 locked += rec->fLockCount > 0;
670 rec = rec->fNext;
671 }
672
673 SkDebugf("SkScaledImageCache: count=%d bytes=%d locked=%d %s\n",
674 fCount, fBytesUsed, locked,
675 fDiscardableFactory ? "discardable" : "malloc");
676}
677
reed@google.com602a1d72013-07-23 19:13:54 +0000678///////////////////////////////////////////////////////////////////////////////
679
680#include "SkThread.h"
681
reed@google.combe19dbe2013-07-24 15:06:34 +0000682SK_DECLARE_STATIC_MUTEX(gMutex);
commit-bot@chromium.org709ca752014-01-24 22:38:39 +0000683static SkScaledImageCache* gScaledImageCache = NULL;
684static void cleanup_gScaledImageCache() { SkDELETE(gScaledImageCache); }
reed@google.com602a1d72013-07-23 19:13:54 +0000685
commit-bot@chromium.org709ca752014-01-24 22:38:39 +0000686static void create_cache(int) {
reed@google.come4eb1222013-12-09 22:29:30 +0000687#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
commit-bot@chromium.org709ca752014-01-24 22:38:39 +0000688 gScaledImageCache = SkNEW_ARGS(SkScaledImageCache, (SkDiscardableMemory::Create));
reed@google.come4eb1222013-12-09 22:29:30 +0000689#else
commit-bot@chromium.org709ca752014-01-24 22:38:39 +0000690 gScaledImageCache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
reed@google.come4eb1222013-12-09 22:29:30 +0000691#endif
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000692}
693
reed@google.com602a1d72013-07-23 19:13:54 +0000694static SkScaledImageCache* get_cache() {
commit-bot@chromium.org709ca752014-01-24 22:38:39 +0000695 SK_DECLARE_STATIC_ONCE(once);
696 SkOnce(&once, create_cache, 0, cleanup_gScaledImageCache);
697 SkASSERT(NULL != gScaledImageCache);
698 return gScaledImageCache;
reed@google.com602a1d72013-07-23 19:13:54 +0000699}
700
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000701
702SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(
703 uint32_t pixelGenerationID,
704 int32_t width,
705 int32_t height,
706 SkBitmap* scaled) {
707 SkAutoMutexAcquire am(gMutex);
708 return get_cache()->findAndLock(pixelGenerationID, width, height, scaled);
709}
710
711SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(
712 uint32_t pixelGenerationID,
713 int32_t width,
714 int32_t height,
715 const SkBitmap& scaled) {
716 SkAutoMutexAcquire am(gMutex);
717 return get_cache()->addAndLock(pixelGenerationID, width, height, scaled);
718}
719
720
reed@google.com602a1d72013-07-23 19:13:54 +0000721SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(const SkBitmap& orig,
722 SkScalar scaleX,
723 SkScalar scaleY,
724 SkBitmap* scaled) {
725 SkAutoMutexAcquire am(gMutex);
726 return get_cache()->findAndLock(orig, scaleX, scaleY, scaled);
727}
728
reed@google.comd94697c2013-07-24 14:31:33 +0000729SkScaledImageCache::ID* SkScaledImageCache::FindAndLockMip(const SkBitmap& orig,
730 SkMipMap const ** mip) {
731 SkAutoMutexAcquire am(gMutex);
732 return get_cache()->findAndLockMip(orig, mip);
733}
734
reed@google.com602a1d72013-07-23 19:13:54 +0000735SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(const SkBitmap& orig,
736 SkScalar scaleX,
737 SkScalar scaleY,
738 const SkBitmap& scaled) {
739 SkAutoMutexAcquire am(gMutex);
740 return get_cache()->addAndLock(orig, scaleX, scaleY, scaled);
741}
742
reed@google.comd94697c2013-07-24 14:31:33 +0000743SkScaledImageCache::ID* SkScaledImageCache::AddAndLockMip(const SkBitmap& orig,
744 const SkMipMap* mip) {
745 SkAutoMutexAcquire am(gMutex);
746 return get_cache()->addAndLockMip(orig, mip);
747}
748
reed@google.com602a1d72013-07-23 19:13:54 +0000749void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) {
750 SkAutoMutexAcquire am(gMutex);
reed@google.comfa7fd802013-12-12 21:37:25 +0000751 get_cache()->unlock(id);
752
753// get_cache()->dump();
reed@google.com602a1d72013-07-23 19:13:54 +0000754}
755
756size_t SkScaledImageCache::GetBytesUsed() {
757 SkAutoMutexAcquire am(gMutex);
758 return get_cache()->getBytesUsed();
759}
760
761size_t SkScaledImageCache::GetByteLimit() {
762 SkAutoMutexAcquire am(gMutex);
763 return get_cache()->getByteLimit();
764}
765
766size_t SkScaledImageCache::SetByteLimit(size_t newLimit) {
767 SkAutoMutexAcquire am(gMutex);
768 return get_cache()->setByteLimit(newLimit);
769}
770
reed@google.come4eb1222013-12-09 22:29:30 +0000771SkBitmap::Allocator* SkScaledImageCache::GetAllocator() {
772 SkAutoMutexAcquire am(gMutex);
773 return get_cache()->allocator();
774}
775
reed@google.comfa7fd802013-12-12 21:37:25 +0000776void SkScaledImageCache::Dump() {
777 SkAutoMutexAcquire am(gMutex);
778 get_cache()->dump();
779}
780
reed@google.com602a1d72013-07-23 19:13:54 +0000781///////////////////////////////////////////////////////////////////////////////
782
783#include "SkGraphics.h"
784
785size_t SkGraphics::GetImageCacheBytesUsed() {
786 return SkScaledImageCache::GetBytesUsed();
787}
788
789size_t SkGraphics::GetImageCacheByteLimit() {
790 return SkScaledImageCache::GetByteLimit();
791}
792
793size_t SkGraphics::SetImageCacheByteLimit(size_t newLimit) {
794 return SkScaledImageCache::SetByteLimit(newLimit);
795}