blob: 5a772a7cd502681ea558acfd1dba98b4b86a289b [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:
reed@google.comd0419b12014-01-06 17:08:27 +0000202 virtual bool onNewLockPixels(LockRec*) SK_OVERRIDE;
reed@google.come4eb1222013-12-09 22:29:30 +0000203 virtual void onUnlockPixels() SK_OVERRIDE;
204 virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
205
206private:
reed@google.come4eb1222013-12-09 22:29:30 +0000207 SkDiscardableMemory* fDM;
208 size_t fRB;
209 bool fFirstTime;
210
211 typedef SkPixelRef INHERITED;
212};
213
214SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info,
215 SkDiscardableMemory* dm,
216 size_t rowBytes)
217 : INHERITED(info)
218 , fDM(dm)
219 , fRB(rowBytes)
220{
reed@google.come4eb1222013-12-09 22:29:30 +0000221 SkASSERT(dm->data());
222 fFirstTime = true;
223}
224
225SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
226 SkDELETE(fDM);
227}
228
reed@google.comd0419b12014-01-06 17:08:27 +0000229bool SkOneShotDiscardablePixelRef::onNewLockPixels(LockRec* rec) {
reed@google.come4eb1222013-12-09 22:29:30 +0000230 if (fFirstTime) {
231 // we're already locked
reed@google.comc83a91f2013-12-13 13:41:14 +0000232 SkASSERT(fDM->data());
reed@google.come4eb1222013-12-09 22:29:30 +0000233 fFirstTime = false;
reed@google.comd0419b12014-01-06 17:08:27 +0000234 goto SUCCESS;
reed@google.come4eb1222013-12-09 22:29:30 +0000235 }
commit-bot@chromium.org281713e2013-12-12 18:08:08 +0000236
reed@google.comfa7fd802013-12-12 21:37:25 +0000237 // A previous call to onUnlock may have deleted our DM, so check for that
238 if (NULL == fDM) {
reed@google.comd0419b12014-01-06 17:08:27 +0000239 return false;
reed@google.comfa7fd802013-12-12 21:37:25 +0000240 }
241
242 if (!fDM->lock()) {
243 // since it failed, we delete it now, to free-up the resource
244 delete fDM;
245 fDM = NULL;
reed@google.comd0419b12014-01-06 17:08:27 +0000246 return false;
reed@google.comfa7fd802013-12-12 21:37:25 +0000247 }
reed@google.comd0419b12014-01-06 17:08:27 +0000248
249SUCCESS:
250 rec->fPixels = fDM->data();
251 rec->fColorTable = NULL;
252 rec->fRowBytes = fRB;
253 return true;
reed@google.come4eb1222013-12-09 22:29:30 +0000254}
255
256void SkOneShotDiscardablePixelRef::onUnlockPixels() {
257 SkASSERT(!fFirstTime);
reed@google.comc83a91f2013-12-13 13:41:14 +0000258 fDM->unlock();
reed@google.come4eb1222013-12-09 22:29:30 +0000259}
260
261size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
reed@google.comd0419b12014-01-06 17:08:27 +0000262 return this->info().getSafeSize(fRB);
reed@google.come4eb1222013-12-09 22:29:30 +0000263}
264
265class SkScaledImageCacheDiscardableAllocator : public SkBitmap::Allocator {
266public:
267 SkScaledImageCacheDiscardableAllocator(
268 SkScaledImageCache::DiscardableFactory factory) {
269 SkASSERT(factory);
270 fFactory = factory;
271 }
272
273 virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
skia.committer@gmail.comcf0803b2013-12-10 07:02:03 +0000274
reed@google.come4eb1222013-12-09 22:29:30 +0000275private:
276 SkScaledImageCache::DiscardableFactory fFactory;
277};
278
279bool SkScaledImageCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap,
280 SkColorTable* ctable) {
281 size_t size = bitmap->getSize();
282 if (0 == size) {
283 return false;
284 }
285
286 SkDiscardableMemory* dm = fFactory(size);
287 if (NULL == dm) {
288 return false;
289 }
290
291 // can relax when we have bitmap::asImageInfo
292 if (SkBitmap::kARGB_8888_Config != bitmap->config()) {
293 return false;
294 }
295
296 SkImageInfo info = {
297 bitmap->width(),
298 bitmap->height(),
299 kPMColor_SkColorType,
300 bitmap->alphaType()
301 };
302
303 bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef,
304 (info, dm, bitmap->rowBytes())))->unref();
305 bitmap->lockPixels();
306 return bitmap->readyToDraw();
307}
308
309SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) {
310 this->init();
311 fDiscardableFactory = factory;
312
313 fAllocator = SkNEW_ARGS(SkScaledImageCacheDiscardableAllocator, (factory));
314}
315
316SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
317 this->init();
318 fByteLimit = byteLimit;
reed@google.com602a1d72013-07-23 19:13:54 +0000319}
320
321SkScaledImageCache::~SkScaledImageCache() {
reed@google.come4eb1222013-12-09 22:29:30 +0000322 SkSafeUnref(fAllocator);
323
reed@google.com602a1d72013-07-23 19:13:54 +0000324 Rec* rec = fHead;
325 while (rec) {
326 Rec* next = rec->fNext;
327 SkDELETE(rec);
328 rec = next;
329 }
reed@google.com5d1e5582013-07-25 14:36:15 +0000330 delete fHash;
reed@google.com602a1d72013-07-23 19:13:54 +0000331}
332
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000333////////////////////////////////////////////////////////////////////////////////
334
rmistry@google.comd6bab022013-12-02 13:50:38 +0000335
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000336SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(uint32_t genID,
reed@google.com602a1d72013-07-23 19:13:54 +0000337 SkScalar scaleX,
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000338 SkScalar scaleY,
339 const SkIRect& bounds) {
rmistry@google.comd6bab022013-12-02 13:50:38 +0000340 const Key key(genID, scaleX, scaleY, bounds);
341 return this->findAndLock(key);
342}
343
344/**
345 This private method is the fully general record finder. All other
346 record finders should call this function or the one above. */
347SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(const SkScaledImageCache::Key& key) {
348 if (key.fBounds.isEmpty()) {
reed@google.com602a1d72013-07-23 19:13:54 +0000349 return NULL;
350 }
reed@google.com5d1e5582013-07-25 14:36:15 +0000351#ifdef USE_HASH
352 Rec* rec = fHash->find(key);
353#else
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000354 Rec* rec = find_rec_in_list(fHead, key);
reed@google.com5d1e5582013-07-25 14:36:15 +0000355#endif
reed@google.com5d1e5582013-07-25 14:36:15 +0000356 if (rec) {
357 this->moveToHead(rec); // for our LRU
358 rec->fLockCount += 1;
359 }
360 return rec;
reed@google.com602a1d72013-07-23 19:13:54 +0000361}
362
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000363/**
364 This function finds the bounds of the bitmap *within its pixelRef*.
365 If the bitmap lacks a pixelRef, it will return an empty rect, since
366 that doesn't make sense. This may be a useful enough function that
367 it should be somewhere else (in SkBitmap?). */
368static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
369 if (!(bm.pixelRef())) {
370 return SkIRect::MakeEmpty();
371 }
372 size_t x, y;
373 SkTDivMod(bm.pixelRefOffset(), bm.rowBytes(), &y, &x);
374 x >>= bm.shiftPerPixel();
375 return SkIRect::MakeXYWH(x, y, bm.width(), bm.height());
376}
377
378
379SkScaledImageCache::ID* SkScaledImageCache::findAndLock(uint32_t genID,
380 int32_t width,
381 int32_t height,
382 SkBitmap* bitmap) {
383 Rec* rec = this->findAndLock(genID, SK_Scalar1, SK_Scalar1,
384 SkIRect::MakeWH(width, height));
385 if (rec) {
386 SkASSERT(NULL == rec->fMip);
387 SkASSERT(rec->fBitmap.pixelRef());
388 *bitmap = rec->fBitmap;
389 }
390 return rec_to_id(rec);
391}
392
reed@google.comd94697c2013-07-24 14:31:33 +0000393SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig,
394 SkScalar scaleX,
395 SkScalar scaleY,
396 SkBitmap* scaled) {
397 if (0 == scaleX || 0 == scaleY) {
398 // degenerate, and the key we use for mipmaps
399 return NULL;
400 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000401 Rec* rec = this->findAndLock(orig.getGenerationID(), scaleX,
402 scaleY, get_bounds_from_bitmap(orig));
reed@google.comd94697c2013-07-24 14:31:33 +0000403 if (rec) {
404 SkASSERT(NULL == rec->fMip);
405 SkASSERT(rec->fBitmap.pixelRef());
406 *scaled = rec->fBitmap;
407 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000408 return rec_to_id(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000409}
410
411SkScaledImageCache::ID* SkScaledImageCache::findAndLockMip(const SkBitmap& orig,
412 SkMipMap const ** mip) {
skia.committer@gmail.comb77f0f42013-10-30 07:01:56 +0000413 Rec* rec = this->findAndLock(orig.getGenerationID(), 0, 0,
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000414 get_bounds_from_bitmap(orig));
reed@google.comd94697c2013-07-24 14:31:33 +0000415 if (rec) {
416 SkASSERT(rec->fMip);
417 SkASSERT(NULL == rec->fBitmap.pixelRef());
418 *mip = rec->fMip;
419 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000420 return rec_to_id(rec);
421}
422
423
424////////////////////////////////////////////////////////////////////////////////
425/**
426 This private method is the fully general record adder. All other
427 record adders should call this funtion. */
rmistry@google.comd6bab022013-12-02 13:50:38 +0000428SkScaledImageCache::ID* SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000429 SkASSERT(rec);
rmistry@google.comd6bab022013-12-02 13:50:38 +0000430 // See if we already have this key (racy inserts, etc.)
431 Rec* existing = this->findAndLock(rec->fKey);
robertphillips@google.com5db04df2013-12-18 18:48:08 +0000432 if (NULL != existing) {
433 // Since we already have a matching entry, just delete the new one and return.
434 // Call sites cannot assume the passed in object will live past this call.
435 SkDELETE(rec);
rmistry@google.comd6bab022013-12-02 13:50:38 +0000436 return rec_to_id(existing);
437 }
438
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000439 this->addToHead(rec);
440 SkASSERT(1 == rec->fLockCount);
441#ifdef USE_HASH
442 SkASSERT(fHash);
443 fHash->add(rec);
444#endif
445 // We may (now) be overbudget, so see if we need to purge something.
446 this->purgeAsNeeded();
rmistry@google.comd6bab022013-12-02 13:50:38 +0000447 return rec_to_id(rec);
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000448}
449
450SkScaledImageCache::ID* SkScaledImageCache::addAndLock(uint32_t genID,
451 int32_t width,
452 int32_t height,
453 const SkBitmap& bitmap) {
454 Key key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeWH(width, height));
455 Rec* rec = SkNEW_ARGS(Rec, (key, bitmap));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000456 return this->addAndLock(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000457}
458
reed@google.com602a1d72013-07-23 19:13:54 +0000459SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig,
460 SkScalar scaleX,
461 SkScalar scaleY,
462 const SkBitmap& scaled) {
reed@google.comd94697c2013-07-24 14:31:33 +0000463 if (0 == scaleX || 0 == scaleY) {
464 // degenerate, and the key we use for mipmaps
465 return NULL;
466 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000467 SkIRect bounds = get_bounds_from_bitmap(orig);
468 if (bounds.isEmpty()) {
reed@google.com602a1d72013-07-23 19:13:54 +0000469 return NULL;
470 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000471 Key key(orig.getGenerationID(), scaleX, scaleY, bounds);
reed@google.com602a1d72013-07-23 19:13:54 +0000472 Rec* rec = SkNEW_ARGS(Rec, (key, scaled));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000473 return this->addAndLock(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000474}
reed@google.com602a1d72013-07-23 19:13:54 +0000475
reed@google.comd94697c2013-07-24 14:31:33 +0000476SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig,
477 const SkMipMap* mip) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000478 SkIRect bounds = get_bounds_from_bitmap(orig);
479 if (bounds.isEmpty()) {
reed@google.comd94697c2013-07-24 14:31:33 +0000480 return NULL;
481 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000482 Key key(orig.getGenerationID(), 0, 0, bounds);
reed@google.comd94697c2013-07-24 14:31:33 +0000483 Rec* rec = SkNEW_ARGS(Rec, (key, mip));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000484 return this->addAndLock(rec);
reed@google.com602a1d72013-07-23 19:13:54 +0000485}
486
487void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) {
488 SkASSERT(id);
489
490#ifdef SK_DEBUG
491 {
492 bool found = false;
493 Rec* rec = fHead;
494 while (rec != NULL) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000495 if (rec == id_to_rec(id)) {
reed@google.com602a1d72013-07-23 19:13:54 +0000496 found = true;
497 break;
498 }
499 rec = rec->fNext;
500 }
501 SkASSERT(found);
502 }
503#endif
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000504 Rec* rec = id_to_rec(id);
reed@google.com602a1d72013-07-23 19:13:54 +0000505 SkASSERT(rec->fLockCount > 0);
506 rec->fLockCount -= 1;
507
reed@google.com602a1d72013-07-23 19:13:54 +0000508 // we may have been over-budget, but now have released something, so check
509 // if we should purge.
510 if (0 == rec->fLockCount) {
511 this->purgeAsNeeded();
512 }
513}
514
515void SkScaledImageCache::purgeAsNeeded() {
reed@google.come4eb1222013-12-09 22:29:30 +0000516 size_t byteLimit;
517 int countLimit;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000518
reed@google.come4eb1222013-12-09 22:29:30 +0000519 if (fDiscardableFactory) {
520 countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
521 byteLimit = SK_MaxU32; // no limit based on bytes
522 } else {
523 countLimit = SK_MaxS32; // no limit based on count
524 byteLimit = fByteLimit;
525 }
526
527 size_t bytesUsed = fBytesUsed;
528 int countUsed = fCount;
skia.committer@gmail.comcf0803b2013-12-10 07:02:03 +0000529
reed@google.com602a1d72013-07-23 19:13:54 +0000530 Rec* rec = fTail;
531 while (rec) {
reed@google.come4eb1222013-12-09 22:29:30 +0000532 if (bytesUsed < byteLimit && countUsed < countLimit) {
reed@google.com602a1d72013-07-23 19:13:54 +0000533 break;
534 }
reed@google.come4eb1222013-12-09 22:29:30 +0000535
reed@google.com602a1d72013-07-23 19:13:54 +0000536 Rec* prev = rec->fPrev;
537 if (0 == rec->fLockCount) {
reed@google.com602a1d72013-07-23 19:13:54 +0000538 size_t used = rec->bytesUsed();
539 SkASSERT(used <= bytesUsed);
reed@google.com602a1d72013-07-23 19:13:54 +0000540 this->detach(rec);
reed@google.com5d1e5582013-07-25 14:36:15 +0000541#ifdef USE_HASH
542 fHash->remove(rec->fKey);
543#endif
skia.committer@gmail.com956b3102013-07-26 07:00:58 +0000544
reed@google.com602a1d72013-07-23 19:13:54 +0000545 SkDELETE(rec);
reed@google.come4eb1222013-12-09 22:29:30 +0000546
547 bytesUsed -= used;
548 countUsed -= 1;
reed@google.com602a1d72013-07-23 19:13:54 +0000549 }
550 rec = prev;
551 }
reed@google.come4eb1222013-12-09 22:29:30 +0000552
reed@google.com602a1d72013-07-23 19:13:54 +0000553 fBytesUsed = bytesUsed;
reed@google.come4eb1222013-12-09 22:29:30 +0000554 fCount = countUsed;
reed@google.com602a1d72013-07-23 19:13:54 +0000555}
556
557size_t SkScaledImageCache::setByteLimit(size_t newLimit) {
558 size_t prevLimit = fByteLimit;
559 fByteLimit = newLimit;
560 if (newLimit < prevLimit) {
561 this->purgeAsNeeded();
562 }
563 return prevLimit;
564}
565
566///////////////////////////////////////////////////////////////////////////////
567
568void SkScaledImageCache::detach(Rec* rec) {
569 Rec* prev = rec->fPrev;
570 Rec* next = rec->fNext;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000571
reed@google.com602a1d72013-07-23 19:13:54 +0000572 if (!prev) {
573 SkASSERT(fHead == rec);
574 fHead = next;
575 } else {
576 prev->fNext = next;
577 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000578
reed@google.com602a1d72013-07-23 19:13:54 +0000579 if (!next) {
580 fTail = prev;
581 } else {
582 next->fPrev = prev;
583 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000584
reed@google.com602a1d72013-07-23 19:13:54 +0000585 rec->fNext = rec->fPrev = NULL;
586}
587
588void SkScaledImageCache::moveToHead(Rec* rec) {
589 if (fHead == rec) {
590 return;
591 }
592
593 SkASSERT(fHead);
594 SkASSERT(fTail);
595
596 this->validate();
597
598 this->detach(rec);
599
600 fHead->fPrev = rec;
601 rec->fNext = fHead;
602 fHead = rec;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000603
reed@google.com602a1d72013-07-23 19:13:54 +0000604 this->validate();
605}
606
607void SkScaledImageCache::addToHead(Rec* rec) {
608 this->validate();
609
610 rec->fPrev = NULL;
611 rec->fNext = fHead;
612 if (fHead) {
613 fHead->fPrev = rec;
614 }
615 fHead = rec;
616 if (!fTail) {
617 fTail = rec;
618 }
619 fBytesUsed += rec->bytesUsed();
620 fCount += 1;
621
622 this->validate();
623}
624
reed@google.comfa7fd802013-12-12 21:37:25 +0000625///////////////////////////////////////////////////////////////////////////////
626
reed@google.com602a1d72013-07-23 19:13:54 +0000627#ifdef SK_DEBUG
628void SkScaledImageCache::validate() const {
629 if (NULL == fHead) {
630 SkASSERT(NULL == fTail);
631 SkASSERT(0 == fBytesUsed);
632 return;
633 }
634
635 if (fHead == fTail) {
636 SkASSERT(NULL == fHead->fPrev);
637 SkASSERT(NULL == fHead->fNext);
638 SkASSERT(fHead->bytesUsed() == fBytesUsed);
639 return;
640 }
641
642 SkASSERT(NULL == fHead->fPrev);
643 SkASSERT(NULL != fHead->fNext);
644 SkASSERT(NULL == fTail->fNext);
645 SkASSERT(NULL != fTail->fPrev);
646
647 size_t used = 0;
648 int count = 0;
649 const Rec* rec = fHead;
650 while (rec) {
651 count += 1;
652 used += rec->bytesUsed();
653 SkASSERT(used <= fBytesUsed);
654 rec = rec->fNext;
655 }
656 SkASSERT(fCount == count);
657
658 rec = fTail;
659 while (rec) {
660 SkASSERT(count > 0);
661 count -= 1;
662 SkASSERT(used >= rec->bytesUsed());
663 used -= rec->bytesUsed();
664 rec = rec->fPrev;
665 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000666
reed@google.com602a1d72013-07-23 19:13:54 +0000667 SkASSERT(0 == count);
668 SkASSERT(0 == used);
669}
670#endif
671
reed@google.comfa7fd802013-12-12 21:37:25 +0000672void SkScaledImageCache::dump() const {
673 this->validate();
674
675 const Rec* rec = fHead;
676 int locked = 0;
677 while (rec) {
678 locked += rec->fLockCount > 0;
679 rec = rec->fNext;
680 }
681
682 SkDebugf("SkScaledImageCache: count=%d bytes=%d locked=%d %s\n",
683 fCount, fBytesUsed, locked,
684 fDiscardableFactory ? "discardable" : "malloc");
685}
686
reed@google.com602a1d72013-07-23 19:13:54 +0000687///////////////////////////////////////////////////////////////////////////////
688
689#include "SkThread.h"
690
reed@google.combe19dbe2013-07-24 15:06:34 +0000691SK_DECLARE_STATIC_MUTEX(gMutex);
reed@google.com602a1d72013-07-23 19:13:54 +0000692
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000693static void create_cache(SkScaledImageCache** cache) {
reed@google.come4eb1222013-12-09 22:29:30 +0000694#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
695 *cache = SkNEW_ARGS(SkScaledImageCache, (SkDiscardableMemory::Create));
696#else
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000697 *cache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
reed@google.come4eb1222013-12-09 22:29:30 +0000698#endif
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000699}
700
reed@google.com602a1d72013-07-23 19:13:54 +0000701static SkScaledImageCache* get_cache() {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000702 static SkScaledImageCache* gCache(NULL);
703 SK_DECLARE_STATIC_ONCE(create_cache_once);
commit-bot@chromium.org1b2c1b82013-12-05 19:20:49 +0000704 SkOnce(&create_cache_once, create_cache, &gCache);
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000705 SkASSERT(NULL != gCache);
reed@google.com602a1d72013-07-23 19:13:54 +0000706 return gCache;
707}
708
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000709
710SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(
711 uint32_t pixelGenerationID,
712 int32_t width,
713 int32_t height,
714 SkBitmap* scaled) {
715 SkAutoMutexAcquire am(gMutex);
716 return get_cache()->findAndLock(pixelGenerationID, width, height, scaled);
717}
718
719SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(
720 uint32_t pixelGenerationID,
721 int32_t width,
722 int32_t height,
723 const SkBitmap& scaled) {
724 SkAutoMutexAcquire am(gMutex);
725 return get_cache()->addAndLock(pixelGenerationID, width, height, scaled);
726}
727
728
reed@google.com602a1d72013-07-23 19:13:54 +0000729SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(const SkBitmap& orig,
730 SkScalar scaleX,
731 SkScalar scaleY,
732 SkBitmap* scaled) {
733 SkAutoMutexAcquire am(gMutex);
734 return get_cache()->findAndLock(orig, scaleX, scaleY, scaled);
735}
736
reed@google.comd94697c2013-07-24 14:31:33 +0000737SkScaledImageCache::ID* SkScaledImageCache::FindAndLockMip(const SkBitmap& orig,
738 SkMipMap const ** mip) {
739 SkAutoMutexAcquire am(gMutex);
740 return get_cache()->findAndLockMip(orig, mip);
741}
742
reed@google.com602a1d72013-07-23 19:13:54 +0000743SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(const SkBitmap& orig,
744 SkScalar scaleX,
745 SkScalar scaleY,
746 const SkBitmap& scaled) {
747 SkAutoMutexAcquire am(gMutex);
748 return get_cache()->addAndLock(orig, scaleX, scaleY, scaled);
749}
750
reed@google.comd94697c2013-07-24 14:31:33 +0000751SkScaledImageCache::ID* SkScaledImageCache::AddAndLockMip(const SkBitmap& orig,
752 const SkMipMap* mip) {
753 SkAutoMutexAcquire am(gMutex);
754 return get_cache()->addAndLockMip(orig, mip);
755}
756
reed@google.com602a1d72013-07-23 19:13:54 +0000757void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) {
758 SkAutoMutexAcquire am(gMutex);
reed@google.comfa7fd802013-12-12 21:37:25 +0000759 get_cache()->unlock(id);
760
761// get_cache()->dump();
reed@google.com602a1d72013-07-23 19:13:54 +0000762}
763
764size_t SkScaledImageCache::GetBytesUsed() {
765 SkAutoMutexAcquire am(gMutex);
766 return get_cache()->getBytesUsed();
767}
768
769size_t SkScaledImageCache::GetByteLimit() {
770 SkAutoMutexAcquire am(gMutex);
771 return get_cache()->getByteLimit();
772}
773
774size_t SkScaledImageCache::SetByteLimit(size_t newLimit) {
775 SkAutoMutexAcquire am(gMutex);
776 return get_cache()->setByteLimit(newLimit);
777}
778
reed@google.come4eb1222013-12-09 22:29:30 +0000779SkBitmap::Allocator* SkScaledImageCache::GetAllocator() {
780 SkAutoMutexAcquire am(gMutex);
781 return get_cache()->allocator();
782}
783
reed@google.comfa7fd802013-12-12 21:37:25 +0000784void SkScaledImageCache::Dump() {
785 SkAutoMutexAcquire am(gMutex);
786 get_cache()->dump();
787}
788
reed@google.com602a1d72013-07-23 19:13:54 +0000789///////////////////////////////////////////////////////////////////////////////
790
791#include "SkGraphics.h"
792
793size_t SkGraphics::GetImageCacheBytesUsed() {
794 return SkScaledImageCache::GetBytesUsed();
795}
796
797size_t SkGraphics::GetImageCacheByteLimit() {
798 return SkScaledImageCache::GetByteLimit();
799}
800
801size_t SkGraphics::SetImageCacheByteLimit(size_t newLimit) {
802 return SkScaledImageCache::SetByteLimit(newLimit);
803}