SkScaledImageCache.cpp revision 27f890219b09e4774da75e6a11ec82849eadae5a
1/*
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"
9#include "SkMipMap.h"
10#include "SkOnce.h"
11#include "SkPixelRef.h"
12#include "SkRect.h"
13
14// 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
21#ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
22    #define SK_DEFAULT_IMAGE_CACHE_LIMIT     (2 * 1024 * 1024)
23#endif
24
25static 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}
32
33 // Implemented from en.wikipedia.org/wiki/MurmurHash.
34static uint32_t compute_hash(const uint32_t data[], int count) {
35    uint32_t hash = 0;
36
37    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;
42
43        hash ^= k;
44        hash = (hash << 13) | (hash >> 19);
45        hash *= 5;
46        hash += 0xe6546b64;
47    }
48
49    //    hash ^= size;
50    hash ^= hash >> 16;
51    hash *= 0x85ebca6b;
52    hash ^= hash >> 13;
53    hash *= 0xc2b2ae35;
54    hash ^= hash >> 16;
55
56    return hash;
57}
58
59struct SkScaledImageCache::Key {
60    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) {
68        fHash = compute_hash(&fGenID, 7);
69    }
70
71    bool operator<(const Key& other) const {
72        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
85    bool operator==(const Key& other) const {
86        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;
106        fMip = NULL;
107    }
108
109    Rec(const Key& key, const SkMipMap* mip) : fKey(key) {
110        fLockCount = 1;
111        fMip = mip;
112        mip->ref();
113    }
114
115    ~Rec() {
116        SkSafeUnref(fMip);
117    }
118
119    size_t bytesUsed() const {
120        return fMip ? fMip->getSize() : fBitmap.getSize();
121    }
122
123    Rec*    fNext;
124    Rec*    fPrev;
125
126    // this guy wants to be 64bit aligned
127    Key     fKey;
128
129    int32_t fLockCount;
130
131    // we use either fBitmap or fMip, but not both
132    SkBitmap fBitmap;
133    const SkMipMap* fMip;
134};
135
136#include "SkTDynamicHash.h"
137
138namespace { // can't use static functions w/ template parameters
139const SkScaledImageCache::Key& key_from_rec(const SkScaledImageCache::Rec& rec) {
140    return rec.fKey;
141}
142
143uint32_t hash_from_key(const SkScaledImageCache::Key& key) {
144    return key.fHash;
145}
146
147bool eq_rec_key(const SkScaledImageCache::Rec& rec, const SkScaledImageCache::Key& key) {
148    return rec.fKey == key;
149}
150}
151
152class SkScaledImageCache::Hash : public SkTDynamicHash<SkScaledImageCache::Rec,
153                                                       SkScaledImageCache::Key,
154                                                       key_from_rec,
155                                                       hash_from_key,
156                                                       eq_rec_key> {};
157
158///////////////////////////////////////////////////////////////////////////////
159
160// experimental hash to speed things up
161#define USE_HASH
162
163#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
174void SkScaledImageCache::init() {
175    fHead = NULL;
176    fTail = NULL;
177#ifdef USE_HASH
178    fHash = new Hash;
179#else
180    fHash = NULL;
181#endif
182    fBytesUsed = 0;
183    fCount = 0;
184    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        SkASSERT(fDM->data());
237        fFirstTime = false;
238        return fDM->data();
239    }
240
241    // A previous call to onUnlock may have deleted our DM, so check for that
242    if (NULL == fDM) {
243        return NULL;
244    }
245
246    if (!fDM->lock()) {
247        // since it failed, we delete it now, to free-up the resource
248        delete fDM;
249        fDM = NULL;
250        return NULL;
251    }
252    return fDM->data();
253}
254
255void SkOneShotDiscardablePixelRef::onUnlockPixels() {
256    SkASSERT(!fFirstTime);
257    fDM->unlock();
258}
259
260size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
261    return fInfo.fHeight * fRB;
262}
263
264class SkScaledImageCacheDiscardableAllocator : public SkBitmap::Allocator {
265public:
266    SkScaledImageCacheDiscardableAllocator(
267                            SkScaledImageCache::DiscardableFactory factory) {
268        SkASSERT(factory);
269        fFactory = factory;
270    }
271
272    virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
273
274private:
275    SkScaledImageCache::DiscardableFactory fFactory;
276};
277
278bool SkScaledImageCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap,
279                                                       SkColorTable* ctable) {
280    size_t size = bitmap->getSize();
281    if (0 == size) {
282        return false;
283    }
284
285    SkDiscardableMemory* dm = fFactory(size);
286    if (NULL == dm) {
287        return false;
288    }
289
290    // can relax when we have bitmap::asImageInfo
291    if (SkBitmap::kARGB_8888_Config != bitmap->config()) {
292        return false;
293    }
294
295    SkImageInfo info = {
296        bitmap->width(),
297        bitmap->height(),
298        kPMColor_SkColorType,
299        bitmap->alphaType()
300    };
301
302    bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef,
303                                   (info, dm, bitmap->rowBytes())))->unref();
304    bitmap->lockPixels();
305    return bitmap->readyToDraw();
306}
307
308SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) {
309    this->init();
310    fDiscardableFactory = factory;
311
312    fAllocator = SkNEW_ARGS(SkScaledImageCacheDiscardableAllocator, (factory));
313}
314
315SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
316    this->init();
317    fByteLimit = byteLimit;
318}
319
320SkScaledImageCache::~SkScaledImageCache() {
321    SkSafeUnref(fAllocator);
322
323    Rec* rec = fHead;
324    while (rec) {
325        Rec* next = rec->fNext;
326        SkDELETE(rec);
327        rec = next;
328    }
329    delete fHash;
330}
331
332////////////////////////////////////////////////////////////////////////////////
333
334
335SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(uint32_t genID,
336                                                        SkScalar scaleX,
337                                                        SkScalar scaleY,
338                                                        const SkIRect& bounds) {
339    const Key key(genID, scaleX, scaleY, bounds);
340    return this->findAndLock(key);
341}
342
343/**
344   This private method is the fully general record finder. All other
345   record finders should call this function or the one above. */
346SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(const SkScaledImageCache::Key& key) {
347    if (key.fBounds.isEmpty()) {
348        return NULL;
349    }
350#ifdef USE_HASH
351    Rec* rec = fHash->find(key);
352#else
353    Rec* rec = find_rec_in_list(fHead, key);
354#endif
355    if (rec) {
356        this->moveToHead(rec);  // for our LRU
357        rec->fLockCount += 1;
358    }
359    return rec;
360}
361
362/**
363   This function finds the bounds of the bitmap *within its pixelRef*.
364   If the bitmap lacks a pixelRef, it will return an empty rect, since
365   that doesn't make sense.  This may be a useful enough function that
366   it should be somewhere else (in SkBitmap?). */
367static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
368    if (!(bm.pixelRef())) {
369        return SkIRect::MakeEmpty();
370    }
371    size_t x, y;
372    SkTDivMod(bm.pixelRefOffset(), bm.rowBytes(), &y, &x);
373    x >>= bm.shiftPerPixel();
374    return SkIRect::MakeXYWH(x, y, bm.width(), bm.height());
375}
376
377
378SkScaledImageCache::ID* SkScaledImageCache::findAndLock(uint32_t genID,
379                                                        int32_t width,
380                                                        int32_t height,
381                                                        SkBitmap* bitmap) {
382    Rec* rec = this->findAndLock(genID, SK_Scalar1, SK_Scalar1,
383                                 SkIRect::MakeWH(width, height));
384    if (rec) {
385        SkASSERT(NULL == rec->fMip);
386        SkASSERT(rec->fBitmap.pixelRef());
387        *bitmap = rec->fBitmap;
388    }
389    return rec_to_id(rec);
390}
391
392SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig,
393                                                        SkScalar scaleX,
394                                                        SkScalar scaleY,
395                                                        SkBitmap* scaled) {
396    if (0 == scaleX || 0 == scaleY) {
397        // degenerate, and the key we use for mipmaps
398        return NULL;
399    }
400    Rec* rec = this->findAndLock(orig.getGenerationID(), scaleX,
401                                 scaleY, get_bounds_from_bitmap(orig));
402    if (rec) {
403        SkASSERT(NULL == rec->fMip);
404        SkASSERT(rec->fBitmap.pixelRef());
405        *scaled = rec->fBitmap;
406    }
407    return rec_to_id(rec);
408}
409
410SkScaledImageCache::ID* SkScaledImageCache::findAndLockMip(const SkBitmap& orig,
411                                                           SkMipMap const ** mip) {
412    Rec* rec = this->findAndLock(orig.getGenerationID(), 0, 0,
413                                 get_bounds_from_bitmap(orig));
414    if (rec) {
415        SkASSERT(rec->fMip);
416        SkASSERT(NULL == rec->fBitmap.pixelRef());
417        *mip = rec->fMip;
418    }
419    return rec_to_id(rec);
420}
421
422
423////////////////////////////////////////////////////////////////////////////////
424/**
425   This private method is the fully general record adder. All other
426   record adders should call this funtion. */
427SkScaledImageCache::ID* SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) {
428    SkASSERT(rec);
429    // See if we already have this key (racy inserts, etc.)
430    Rec* existing = this->findAndLock(rec->fKey);
431    if (NULL != existing) {
432        // Since we already have a matching entry, just delete the new one and return.
433        // Call sites cannot assume the passed in object will live past this call.
434        SkDELETE(rec);
435        return rec_to_id(existing);
436    }
437
438    this->addToHead(rec);
439    SkASSERT(1 == rec->fLockCount);
440#ifdef USE_HASH
441    SkASSERT(fHash);
442    fHash->add(rec);
443#endif
444    // We may (now) be overbudget, so see if we need to purge something.
445    this->purgeAsNeeded();
446    return rec_to_id(rec);
447}
448
449SkScaledImageCache::ID* SkScaledImageCache::addAndLock(uint32_t genID,
450                                                       int32_t width,
451                                                       int32_t height,
452                                                       const SkBitmap& bitmap) {
453    Key key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeWH(width, height));
454    Rec* rec = SkNEW_ARGS(Rec, (key, bitmap));
455    return this->addAndLock(rec);
456}
457
458SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig,
459                                                       SkScalar scaleX,
460                                                       SkScalar scaleY,
461                                                       const SkBitmap& scaled) {
462    if (0 == scaleX || 0 == scaleY) {
463        // degenerate, and the key we use for mipmaps
464        return NULL;
465    }
466    SkIRect bounds = get_bounds_from_bitmap(orig);
467    if (bounds.isEmpty()) {
468        return NULL;
469    }
470    Key key(orig.getGenerationID(), scaleX, scaleY, bounds);
471    Rec* rec = SkNEW_ARGS(Rec, (key, scaled));
472    return this->addAndLock(rec);
473}
474
475SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig,
476                                                          const SkMipMap* mip) {
477    SkIRect bounds = get_bounds_from_bitmap(orig);
478    if (bounds.isEmpty()) {
479        return NULL;
480    }
481    Key key(orig.getGenerationID(), 0, 0, bounds);
482    Rec* rec = SkNEW_ARGS(Rec, (key, mip));
483    return this->addAndLock(rec);
484}
485
486void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) {
487    SkASSERT(id);
488
489#ifdef SK_DEBUG
490    {
491        bool found = false;
492        Rec* rec = fHead;
493        while (rec != NULL) {
494            if (rec == id_to_rec(id)) {
495                found = true;
496                break;
497            }
498            rec = rec->fNext;
499        }
500        SkASSERT(found);
501    }
502#endif
503    Rec* rec = id_to_rec(id);
504    SkASSERT(rec->fLockCount > 0);
505    rec->fLockCount -= 1;
506
507    // we may have been over-budget, but now have released something, so check
508    // if we should purge.
509    if (0 == rec->fLockCount) {
510        this->purgeAsNeeded();
511    }
512}
513
514void SkScaledImageCache::purgeAsNeeded() {
515    size_t byteLimit;
516    int    countLimit;
517
518    if (fDiscardableFactory) {
519        countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
520        byteLimit = SK_MaxU32;  // no limit based on bytes
521    } else {
522        countLimit = SK_MaxS32; // no limit based on count
523        byteLimit = fByteLimit;
524    }
525
526    size_t bytesUsed = fBytesUsed;
527    int    countUsed = fCount;
528
529    Rec* rec = fTail;
530    while (rec) {
531        if (bytesUsed < byteLimit && countUsed < countLimit) {
532            break;
533        }
534
535        Rec* prev = rec->fPrev;
536        if (0 == rec->fLockCount) {
537            size_t used = rec->bytesUsed();
538            SkASSERT(used <= bytesUsed);
539            this->detach(rec);
540#ifdef USE_HASH
541            fHash->remove(rec->fKey);
542#endif
543
544            SkDELETE(rec);
545
546            bytesUsed -= used;
547            countUsed -= 1;
548        }
549        rec = prev;
550    }
551
552    fBytesUsed = bytesUsed;
553    fCount = countUsed;
554}
555
556size_t SkScaledImageCache::setByteLimit(size_t newLimit) {
557    size_t prevLimit = fByteLimit;
558    fByteLimit = newLimit;
559    if (newLimit < prevLimit) {
560        this->purgeAsNeeded();
561    }
562    return prevLimit;
563}
564
565///////////////////////////////////////////////////////////////////////////////
566
567void SkScaledImageCache::detach(Rec* rec) {
568    Rec* prev = rec->fPrev;
569    Rec* next = rec->fNext;
570
571    if (!prev) {
572        SkASSERT(fHead == rec);
573        fHead = next;
574    } else {
575        prev->fNext = next;
576    }
577
578    if (!next) {
579        fTail = prev;
580    } else {
581        next->fPrev = prev;
582    }
583
584    rec->fNext = rec->fPrev = NULL;
585}
586
587void SkScaledImageCache::moveToHead(Rec* rec) {
588    if (fHead == rec) {
589        return;
590    }
591
592    SkASSERT(fHead);
593    SkASSERT(fTail);
594
595    this->validate();
596
597    this->detach(rec);
598
599    fHead->fPrev = rec;
600    rec->fNext = fHead;
601    fHead = rec;
602
603    this->validate();
604}
605
606void SkScaledImageCache::addToHead(Rec* rec) {
607    this->validate();
608
609    rec->fPrev = NULL;
610    rec->fNext = fHead;
611    if (fHead) {
612        fHead->fPrev = rec;
613    }
614    fHead = rec;
615    if (!fTail) {
616        fTail = rec;
617    }
618    fBytesUsed += rec->bytesUsed();
619    fCount += 1;
620
621    this->validate();
622}
623
624///////////////////////////////////////////////////////////////////////////////
625
626#ifdef SK_DEBUG
627void SkScaledImageCache::validate() const {
628    if (NULL == fHead) {
629        SkASSERT(NULL == fTail);
630        SkASSERT(0 == fBytesUsed);
631        return;
632    }
633
634    if (fHead == fTail) {
635        SkASSERT(NULL == fHead->fPrev);
636        SkASSERT(NULL == fHead->fNext);
637        SkASSERT(fHead->bytesUsed() == fBytesUsed);
638        return;
639    }
640
641    SkASSERT(NULL == fHead->fPrev);
642    SkASSERT(NULL != fHead->fNext);
643    SkASSERT(NULL == fTail->fNext);
644    SkASSERT(NULL != fTail->fPrev);
645
646    size_t used = 0;
647    int count = 0;
648    const Rec* rec = fHead;
649    while (rec) {
650        count += 1;
651        used += rec->bytesUsed();
652        SkASSERT(used <= fBytesUsed);
653        rec = rec->fNext;
654    }
655    SkASSERT(fCount == count);
656
657    rec = fTail;
658    while (rec) {
659        SkASSERT(count > 0);
660        count -= 1;
661        SkASSERT(used >= rec->bytesUsed());
662        used -= rec->bytesUsed();
663        rec = rec->fPrev;
664    }
665
666    SkASSERT(0 == count);
667    SkASSERT(0 == used);
668}
669#endif
670
671void SkScaledImageCache::dump() const {
672    this->validate();
673
674    const Rec* rec = fHead;
675    int locked = 0;
676    while (rec) {
677        locked += rec->fLockCount > 0;
678        rec = rec->fNext;
679    }
680
681    SkDebugf("SkScaledImageCache: count=%d bytes=%d locked=%d %s\n",
682             fCount, fBytesUsed, locked,
683             fDiscardableFactory ? "discardable" : "malloc");
684}
685
686///////////////////////////////////////////////////////////////////////////////
687
688#include "SkThread.h"
689
690SK_DECLARE_STATIC_MUTEX(gMutex);
691
692static void create_cache(SkScaledImageCache** cache) {
693#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
694    *cache = SkNEW_ARGS(SkScaledImageCache, (SkDiscardableMemory::Create));
695#else
696    *cache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
697#endif
698}
699
700static SkScaledImageCache* get_cache() {
701    static SkScaledImageCache* gCache(NULL);
702    SK_DECLARE_STATIC_ONCE(create_cache_once);
703    SkOnce(&create_cache_once, create_cache, &gCache);
704    SkASSERT(NULL != gCache);
705    return gCache;
706}
707
708
709SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(
710                                uint32_t pixelGenerationID,
711                                int32_t width,
712                                int32_t height,
713                                SkBitmap* scaled) {
714    SkAutoMutexAcquire am(gMutex);
715    return get_cache()->findAndLock(pixelGenerationID, width, height, scaled);
716}
717
718SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(
719                               uint32_t pixelGenerationID,
720                               int32_t width,
721                               int32_t height,
722                               const SkBitmap& scaled) {
723    SkAutoMutexAcquire am(gMutex);
724    return get_cache()->addAndLock(pixelGenerationID, width, height, scaled);
725}
726
727
728SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(const SkBitmap& orig,
729                                                        SkScalar scaleX,
730                                                        SkScalar scaleY,
731                                                        SkBitmap* scaled) {
732    SkAutoMutexAcquire am(gMutex);
733    return get_cache()->findAndLock(orig, scaleX, scaleY, scaled);
734}
735
736SkScaledImageCache::ID* SkScaledImageCache::FindAndLockMip(const SkBitmap& orig,
737                                                       SkMipMap const ** mip) {
738    SkAutoMutexAcquire am(gMutex);
739    return get_cache()->findAndLockMip(orig, mip);
740}
741
742SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(const SkBitmap& orig,
743                                                       SkScalar scaleX,
744                                                       SkScalar scaleY,
745                                                       const SkBitmap& scaled) {
746    SkAutoMutexAcquire am(gMutex);
747    return get_cache()->addAndLock(orig, scaleX, scaleY, scaled);
748}
749
750SkScaledImageCache::ID* SkScaledImageCache::AddAndLockMip(const SkBitmap& orig,
751                                                          const SkMipMap* mip) {
752    SkAutoMutexAcquire am(gMutex);
753    return get_cache()->addAndLockMip(orig, mip);
754}
755
756void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) {
757    SkAutoMutexAcquire am(gMutex);
758    get_cache()->unlock(id);
759
760//    get_cache()->dump();
761}
762
763size_t SkScaledImageCache::GetBytesUsed() {
764    SkAutoMutexAcquire am(gMutex);
765    return get_cache()->getBytesUsed();
766}
767
768size_t SkScaledImageCache::GetByteLimit() {
769    SkAutoMutexAcquire am(gMutex);
770    return get_cache()->getByteLimit();
771}
772
773size_t SkScaledImageCache::SetByteLimit(size_t newLimit) {
774    SkAutoMutexAcquire am(gMutex);
775    return get_cache()->setByteLimit(newLimit);
776}
777
778SkBitmap::Allocator* SkScaledImageCache::GetAllocator() {
779    SkAutoMutexAcquire am(gMutex);
780    return get_cache()->allocator();
781}
782
783void SkScaledImageCache::Dump() {
784    SkAutoMutexAcquire am(gMutex);
785    get_cache()->dump();
786}
787
788///////////////////////////////////////////////////////////////////////////////
789
790#include "SkGraphics.h"
791
792size_t SkGraphics::GetImageCacheBytesUsed() {
793    return SkScaledImageCache::GetBytesUsed();
794}
795
796size_t SkGraphics::GetImageCacheByteLimit() {
797    return SkScaledImageCache::GetByteLimit();
798}
799
800size_t SkGraphics::SetImageCacheByteLimit(size_t newLimit) {
801    return SkScaledImageCache::SetByteLimit(newLimit);
802}
803