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 "SkPixelRef.h"
11#include "SkRect.h"
12
13// This can be defined by the caller's build system
14//#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
15
16#ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT
17#   define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT   1024
18#endif
19
20#ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
21    #define SK_DEFAULT_IMAGE_CACHE_LIMIT     (2 * 1024 * 1024)
22#endif
23
24static inline SkScaledImageCache::ID* rec_to_id(SkScaledImageCache::Rec* rec) {
25    return reinterpret_cast<SkScaledImageCache::ID*>(rec);
26}
27
28static inline SkScaledImageCache::Rec* id_to_rec(SkScaledImageCache::ID* id) {
29    return reinterpret_cast<SkScaledImageCache::Rec*>(id);
30}
31
32 // Implemented from en.wikipedia.org/wiki/MurmurHash.
33static uint32_t compute_hash(const uint32_t data[], int count) {
34    uint32_t hash = 0;
35
36    for (int i = 0; i < count; ++i) {
37        uint32_t k = data[i];
38        k *= 0xcc9e2d51;
39        k = (k << 15) | (k >> 17);
40        k *= 0x1b873593;
41
42        hash ^= k;
43        hash = (hash << 13) | (hash >> 19);
44        hash *= 5;
45        hash += 0xe6546b64;
46    }
47
48    //    hash ^= size;
49    hash ^= hash >> 16;
50    hash *= 0x85ebca6b;
51    hash ^= hash >> 13;
52    hash *= 0xc2b2ae35;
53    hash ^= hash >> 16;
54
55    return hash;
56}
57
58struct SkScaledImageCache::Key {
59    Key(uint32_t genID,
60        SkScalar scaleX,
61        SkScalar scaleY,
62        SkIRect  bounds)
63        : fGenID(genID)
64        , fScaleX(scaleX)
65        , fScaleY(scaleY)
66        , fBounds(bounds) {
67        fHash = compute_hash(&fGenID, 7);
68    }
69
70    bool operator<(const Key& other) const {
71        const uint32_t* a = &fGenID;
72        const uint32_t* b = &other.fGenID;
73        for (int i = 0; i < 7; ++i) {
74            if (a[i] < b[i]) {
75                return true;
76            }
77            if (a[i] > b[i]) {
78                return false;
79            }
80        }
81        return false;
82    }
83
84    bool operator==(const Key& other) const {
85        const uint32_t* a = &fHash;
86        const uint32_t* b = &other.fHash;
87        for (int i = 0; i < 8; ++i) {
88            if (a[i] != b[i]) {
89                return false;
90            }
91        }
92        return true;
93    }
94
95    uint32_t    fHash;
96    uint32_t    fGenID;
97    float       fScaleX;
98    float       fScaleY;
99    SkIRect     fBounds;
100};
101
102struct SkScaledImageCache::Rec {
103    Rec(const Key& key, const SkBitmap& bm) : fKey(key), fBitmap(bm) {
104        fLockCount = 1;
105        fMip = NULL;
106    }
107
108    Rec(const Key& key, const SkMipMap* mip) : fKey(key) {
109        fLockCount = 1;
110        fMip = mip;
111        mip->ref();
112    }
113
114    ~Rec() {
115        SkSafeUnref(fMip);
116    }
117
118    static const Key& GetKey(const Rec& rec) { return rec.fKey; }
119    static uint32_t Hash(const Key& key) { return key.fHash; }
120
121    size_t bytesUsed() const {
122        return fMip ? fMip->getSize() : fBitmap.getSize();
123    }
124
125    Rec*    fNext;
126    Rec*    fPrev;
127
128    // this guy wants to be 64bit aligned
129    Key     fKey;
130
131    int32_t fLockCount;
132
133    // we use either fBitmap or fMip, but not both
134    SkBitmap fBitmap;
135    const SkMipMap* fMip;
136};
137
138#include "SkTDynamicHash.h"
139
140class SkScaledImageCache::Hash :
141    public SkTDynamicHash<SkScaledImageCache::Rec, SkScaledImageCache::Key> {};
142
143
144///////////////////////////////////////////////////////////////////////////////
145
146// experimental hash to speed things up
147#define USE_HASH
148
149#if !defined(USE_HASH)
150static inline SkScaledImageCache::Rec* find_rec_in_list(
151        SkScaledImageCache::Rec* head, const Key & key) {
152    SkScaledImageCache::Rec* rec = head;
153    while ((rec != NULL) && (rec->fKey != key)) {
154        rec = rec->fNext;
155    }
156    return rec;
157}
158#endif
159
160void SkScaledImageCache::init() {
161    fHead = NULL;
162    fTail = NULL;
163#ifdef USE_HASH
164    fHash = new Hash;
165#else
166    fHash = NULL;
167#endif
168    fTotalBytesUsed = 0;
169    fCount = 0;
170    fSingleAllocationByteLimit = 0;
171    fAllocator = NULL;
172
173    // One of these should be explicit set by the caller after we return.
174    fTotalByteLimit = 0;
175    fDiscardableFactory = NULL;
176}
177
178#include "SkDiscardableMemory.h"
179
180class SkOneShotDiscardablePixelRef : public SkPixelRef {
181public:
182    SK_DECLARE_INST_COUNT(SkOneShotDiscardablePixelRef)
183    // Ownership of the discardablememory is transfered to the pixelref
184    SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes);
185    ~SkOneShotDiscardablePixelRef();
186
187    SK_DECLARE_UNFLATTENABLE_OBJECT()
188
189protected:
190    virtual bool onNewLockPixels(LockRec*) SK_OVERRIDE;
191    virtual void onUnlockPixels() SK_OVERRIDE;
192    virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
193
194private:
195    SkDiscardableMemory* fDM;
196    size_t               fRB;
197    bool                 fFirstTime;
198
199    typedef SkPixelRef INHERITED;
200};
201
202SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info,
203                                             SkDiscardableMemory* dm,
204                                             size_t rowBytes)
205    : INHERITED(info)
206    , fDM(dm)
207    , fRB(rowBytes)
208{
209    SkASSERT(dm->data());
210    fFirstTime = true;
211}
212
213SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
214    SkDELETE(fDM);
215}
216
217bool SkOneShotDiscardablePixelRef::onNewLockPixels(LockRec* rec) {
218    if (fFirstTime) {
219        // we're already locked
220        SkASSERT(fDM->data());
221        fFirstTime = false;
222        goto SUCCESS;
223    }
224
225    // A previous call to onUnlock may have deleted our DM, so check for that
226    if (NULL == fDM) {
227        return false;
228    }
229
230    if (!fDM->lock()) {
231        // since it failed, we delete it now, to free-up the resource
232        delete fDM;
233        fDM = NULL;
234        return false;
235    }
236
237SUCCESS:
238    rec->fPixels = fDM->data();
239    rec->fColorTable = NULL;
240    rec->fRowBytes = fRB;
241    return true;
242}
243
244void SkOneShotDiscardablePixelRef::onUnlockPixels() {
245    SkASSERT(!fFirstTime);
246    fDM->unlock();
247}
248
249size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
250    return this->info().getSafeSize(fRB);
251}
252
253class SkScaledImageCacheDiscardableAllocator : public SkBitmap::Allocator {
254public:
255    SkScaledImageCacheDiscardableAllocator(
256                            SkScaledImageCache::DiscardableFactory factory) {
257        SkASSERT(factory);
258        fFactory = factory;
259    }
260
261    virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
262
263private:
264    SkScaledImageCache::DiscardableFactory fFactory;
265};
266
267bool SkScaledImageCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap,
268                                                       SkColorTable* ctable) {
269    size_t size = bitmap->getSize();
270    uint64_t size64 = bitmap->computeSize64();
271    if (0 == size || size64 > (uint64_t)size) {
272        return false;
273    }
274
275    SkDiscardableMemory* dm = fFactory(size);
276    if (NULL == dm) {
277        return false;
278    }
279
280    // can we relax this?
281    if (kN32_SkColorType != bitmap->colorType()) {
282        return false;
283    }
284
285    SkImageInfo info = bitmap->info();
286    bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef,
287                                   (info, dm, bitmap->rowBytes())))->unref();
288    bitmap->lockPixels();
289    return bitmap->readyToDraw();
290}
291
292SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) {
293    this->init();
294    fDiscardableFactory = factory;
295
296    fAllocator = SkNEW_ARGS(SkScaledImageCacheDiscardableAllocator, (factory));
297}
298
299SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
300    this->init();
301    fTotalByteLimit = byteLimit;
302}
303
304SkScaledImageCache::~SkScaledImageCache() {
305    SkSafeUnref(fAllocator);
306
307    Rec* rec = fHead;
308    while (rec) {
309        Rec* next = rec->fNext;
310        SkDELETE(rec);
311        rec = next;
312    }
313    delete fHash;
314}
315
316////////////////////////////////////////////////////////////////////////////////
317
318
319SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(uint32_t genID,
320                                                        SkScalar scaleX,
321                                                        SkScalar scaleY,
322                                                        const SkIRect& bounds) {
323    const Key key(genID, scaleX, scaleY, bounds);
324    return this->findAndLock(key);
325}
326
327/**
328   This private method is the fully general record finder. All other
329   record finders should call this function or the one above. */
330SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(const SkScaledImageCache::Key& key) {
331    if (key.fBounds.isEmpty()) {
332        return NULL;
333    }
334#ifdef USE_HASH
335    Rec* rec = fHash->find(key);
336#else
337    Rec* rec = find_rec_in_list(fHead, key);
338#endif
339    if (rec) {
340        this->moveToHead(rec);  // for our LRU
341        rec->fLockCount += 1;
342    }
343    return rec;
344}
345
346/**
347   This function finds the bounds of the bitmap *within its pixelRef*.
348   If the bitmap lacks a pixelRef, it will return an empty rect, since
349   that doesn't make sense.  This may be a useful enough function that
350   it should be somewhere else (in SkBitmap?). */
351static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
352    if (!(bm.pixelRef())) {
353        return SkIRect::MakeEmpty();
354    }
355    SkIPoint origin = bm.pixelRefOrigin();
356    return SkIRect::MakeXYWH(origin.fX, origin.fY, bm.width(), bm.height());
357}
358
359
360SkScaledImageCache::ID* SkScaledImageCache::findAndLock(uint32_t genID,
361                                                        int32_t width,
362                                                        int32_t height,
363                                                        SkBitmap* bitmap) {
364    Rec* rec = this->findAndLock(genID, SK_Scalar1, SK_Scalar1,
365                                 SkIRect::MakeWH(width, height));
366    if (rec) {
367        SkASSERT(NULL == rec->fMip);
368        SkASSERT(rec->fBitmap.pixelRef());
369        *bitmap = rec->fBitmap;
370    }
371    return rec_to_id(rec);
372}
373
374SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig,
375                                                        SkScalar scaleX,
376                                                        SkScalar scaleY,
377                                                        SkBitmap* scaled) {
378    if (0 == scaleX || 0 == scaleY) {
379        // degenerate, and the key we use for mipmaps
380        return NULL;
381    }
382    Rec* rec = this->findAndLock(orig.getGenerationID(), scaleX,
383                                 scaleY, get_bounds_from_bitmap(orig));
384    if (rec) {
385        SkASSERT(NULL == rec->fMip);
386        SkASSERT(rec->fBitmap.pixelRef());
387        *scaled = rec->fBitmap;
388    }
389    return rec_to_id(rec);
390}
391
392SkScaledImageCache::ID* SkScaledImageCache::findAndLockMip(const SkBitmap& orig,
393                                                           SkMipMap const ** mip) {
394    Rec* rec = this->findAndLock(orig.getGenerationID(), 0, 0,
395                                 get_bounds_from_bitmap(orig));
396    if (rec) {
397        SkASSERT(rec->fMip);
398        SkASSERT(NULL == rec->fBitmap.pixelRef());
399        *mip = rec->fMip;
400    }
401    return rec_to_id(rec);
402}
403
404
405////////////////////////////////////////////////////////////////////////////////
406/**
407   This private method is the fully general record adder. All other
408   record adders should call this funtion. */
409SkScaledImageCache::ID* SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) {
410    SkASSERT(rec);
411    // See if we already have this key (racy inserts, etc.)
412    Rec* existing = this->findAndLock(rec->fKey);
413    if (NULL != existing) {
414        // Since we already have a matching entry, just delete the new one and return.
415        // Call sites cannot assume the passed in object will live past this call.
416        existing->fBitmap = rec->fBitmap;
417        SkDELETE(rec);
418        return rec_to_id(existing);
419    }
420
421    this->addToHead(rec);
422    SkASSERT(1 == rec->fLockCount);
423#ifdef USE_HASH
424    SkASSERT(fHash);
425    fHash->add(rec);
426#endif
427    // We may (now) be overbudget, so see if we need to purge something.
428    this->purgeAsNeeded();
429    return rec_to_id(rec);
430}
431
432SkScaledImageCache::ID* SkScaledImageCache::addAndLock(uint32_t genID,
433                                                       int32_t width,
434                                                       int32_t height,
435                                                       const SkBitmap& bitmap) {
436    Key key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeWH(width, height));
437    Rec* rec = SkNEW_ARGS(Rec, (key, bitmap));
438    return this->addAndLock(rec);
439}
440
441SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig,
442                                                       SkScalar scaleX,
443                                                       SkScalar scaleY,
444                                                       const SkBitmap& scaled) {
445    if (0 == scaleX || 0 == scaleY) {
446        // degenerate, and the key we use for mipmaps
447        return NULL;
448    }
449    SkIRect bounds = get_bounds_from_bitmap(orig);
450    if (bounds.isEmpty()) {
451        return NULL;
452    }
453    Key key(orig.getGenerationID(), scaleX, scaleY, bounds);
454    Rec* rec = SkNEW_ARGS(Rec, (key, scaled));
455    return this->addAndLock(rec);
456}
457
458SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig,
459                                                          const SkMipMap* mip) {
460    SkIRect bounds = get_bounds_from_bitmap(orig);
461    if (bounds.isEmpty()) {
462        return NULL;
463    }
464    Key key(orig.getGenerationID(), 0, 0, bounds);
465    Rec* rec = SkNEW_ARGS(Rec, (key, mip));
466    return this->addAndLock(rec);
467}
468
469void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) {
470    SkASSERT(id);
471
472#ifdef SK_DEBUG
473    {
474        bool found = false;
475        Rec* rec = fHead;
476        while (rec != NULL) {
477            if (rec == id_to_rec(id)) {
478                found = true;
479                break;
480            }
481            rec = rec->fNext;
482        }
483        SkASSERT(found);
484    }
485#endif
486    Rec* rec = id_to_rec(id);
487    SkASSERT(rec->fLockCount > 0);
488    rec->fLockCount -= 1;
489
490    // we may have been over-budget, but now have released something, so check
491    // if we should purge.
492    if (0 == rec->fLockCount) {
493        this->purgeAsNeeded();
494    }
495}
496
497void SkScaledImageCache::purgeAsNeeded() {
498    size_t byteLimit;
499    int    countLimit;
500
501    if (fDiscardableFactory) {
502        countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
503        byteLimit = SK_MaxU32;  // no limit based on bytes
504    } else {
505        countLimit = SK_MaxS32; // no limit based on count
506        byteLimit = fTotalByteLimit;
507    }
508
509    size_t bytesUsed = fTotalBytesUsed;
510    int    countUsed = fCount;
511
512    Rec* rec = fTail;
513    while (rec) {
514        if (bytesUsed < byteLimit && countUsed < countLimit) {
515            break;
516        }
517
518        Rec* prev = rec->fPrev;
519        if (0 == rec->fLockCount) {
520            size_t used = rec->bytesUsed();
521            SkASSERT(used <= bytesUsed);
522            this->detach(rec);
523#ifdef USE_HASH
524            fHash->remove(rec->fKey);
525#endif
526
527            SkDELETE(rec);
528
529            bytesUsed -= used;
530            countUsed -= 1;
531        }
532        rec = prev;
533    }
534
535    fTotalBytesUsed = bytesUsed;
536    fCount = countUsed;
537}
538
539size_t SkScaledImageCache::setTotalByteLimit(size_t newLimit) {
540    size_t prevLimit = fTotalByteLimit;
541    fTotalByteLimit = newLimit;
542    if (newLimit < prevLimit) {
543        this->purgeAsNeeded();
544    }
545    return prevLimit;
546}
547
548///////////////////////////////////////////////////////////////////////////////
549
550void SkScaledImageCache::detach(Rec* rec) {
551    Rec* prev = rec->fPrev;
552    Rec* next = rec->fNext;
553
554    if (!prev) {
555        SkASSERT(fHead == rec);
556        fHead = next;
557    } else {
558        prev->fNext = next;
559    }
560
561    if (!next) {
562        fTail = prev;
563    } else {
564        next->fPrev = prev;
565    }
566
567    rec->fNext = rec->fPrev = NULL;
568}
569
570void SkScaledImageCache::moveToHead(Rec* rec) {
571    if (fHead == rec) {
572        return;
573    }
574
575    SkASSERT(fHead);
576    SkASSERT(fTail);
577
578    this->validate();
579
580    this->detach(rec);
581
582    fHead->fPrev = rec;
583    rec->fNext = fHead;
584    fHead = rec;
585
586    this->validate();
587}
588
589void SkScaledImageCache::addToHead(Rec* rec) {
590    this->validate();
591
592    rec->fPrev = NULL;
593    rec->fNext = fHead;
594    if (fHead) {
595        fHead->fPrev = rec;
596    }
597    fHead = rec;
598    if (!fTail) {
599        fTail = rec;
600    }
601    fTotalBytesUsed += rec->bytesUsed();
602    fCount += 1;
603
604    this->validate();
605}
606
607///////////////////////////////////////////////////////////////////////////////
608
609#ifdef SK_DEBUG
610void SkScaledImageCache::validate() const {
611    if (NULL == fHead) {
612        SkASSERT(NULL == fTail);
613        SkASSERT(0 == fTotalBytesUsed);
614        return;
615    }
616
617    if (fHead == fTail) {
618        SkASSERT(NULL == fHead->fPrev);
619        SkASSERT(NULL == fHead->fNext);
620        SkASSERT(fHead->bytesUsed() == fTotalBytesUsed);
621        return;
622    }
623
624    SkASSERT(NULL == fHead->fPrev);
625    SkASSERT(NULL != fHead->fNext);
626    SkASSERT(NULL == fTail->fNext);
627    SkASSERT(NULL != fTail->fPrev);
628
629    size_t used = 0;
630    int count = 0;
631    const Rec* rec = fHead;
632    while (rec) {
633        count += 1;
634        used += rec->bytesUsed();
635        SkASSERT(used <= fTotalBytesUsed);
636        rec = rec->fNext;
637    }
638    SkASSERT(fCount == count);
639
640    rec = fTail;
641    while (rec) {
642        SkASSERT(count > 0);
643        count -= 1;
644        SkASSERT(used >= rec->bytesUsed());
645        used -= rec->bytesUsed();
646        rec = rec->fPrev;
647    }
648
649    SkASSERT(0 == count);
650    SkASSERT(0 == used);
651}
652#endif
653
654void SkScaledImageCache::dump() const {
655    this->validate();
656
657    const Rec* rec = fHead;
658    int locked = 0;
659    while (rec) {
660        locked += rec->fLockCount > 0;
661        rec = rec->fNext;
662    }
663
664    SkDebugf("SkScaledImageCache: count=%d bytes=%d locked=%d %s\n",
665             fCount, fTotalBytesUsed, locked,
666             fDiscardableFactory ? "discardable" : "malloc");
667}
668
669size_t SkScaledImageCache::setSingleAllocationByteLimit(size_t newLimit) {
670    size_t oldLimit = fSingleAllocationByteLimit;
671    fSingleAllocationByteLimit = newLimit;
672    return oldLimit;
673}
674
675size_t SkScaledImageCache::getSingleAllocationByteLimit() const {
676    return fSingleAllocationByteLimit;
677}
678
679///////////////////////////////////////////////////////////////////////////////
680
681#include "SkThread.h"
682
683SK_DECLARE_STATIC_MUTEX(gMutex);
684static SkScaledImageCache* gScaledImageCache = NULL;
685static void cleanup_gScaledImageCache() {
686    // We'll clean this up in our own tests, but disable for clients.
687    // Chrome seems to have funky multi-process things going on in unit tests that
688    // makes this unsafe to delete when the main process atexit()s.
689    // SkLazyPtr does the same sort of thing.
690#if SK_DEVELOPER
691    SkDELETE(gScaledImageCache);
692#endif
693}
694
695/** Must hold gMutex when calling. */
696static SkScaledImageCache* get_cache() {
697    // gMutex is always held when this is called, so we don't need to be fancy in here.
698    gMutex.assertHeld();
699    if (NULL == gScaledImageCache) {
700#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
701        gScaledImageCache = SkNEW_ARGS(SkScaledImageCache, (SkDiscardableMemory::Create));
702#else
703        gScaledImageCache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
704#endif
705        atexit(cleanup_gScaledImageCache);
706    }
707    return gScaledImageCache;
708}
709
710
711SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(
712                                uint32_t pixelGenerationID,
713                                int32_t width,
714                                int32_t height,
715                                SkBitmap* scaled) {
716    SkAutoMutexAcquire am(gMutex);
717    return get_cache()->findAndLock(pixelGenerationID, width, height, scaled);
718}
719
720SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(
721                               uint32_t pixelGenerationID,
722                               int32_t width,
723                               int32_t height,
724                               const SkBitmap& scaled) {
725    SkAutoMutexAcquire am(gMutex);
726    return get_cache()->addAndLock(pixelGenerationID, width, height, scaled);
727}
728
729
730SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(const SkBitmap& orig,
731                                                        SkScalar scaleX,
732                                                        SkScalar scaleY,
733                                                        SkBitmap* scaled) {
734    SkAutoMutexAcquire am(gMutex);
735    return get_cache()->findAndLock(orig, scaleX, scaleY, scaled);
736}
737
738SkScaledImageCache::ID* SkScaledImageCache::FindAndLockMip(const SkBitmap& orig,
739                                                       SkMipMap const ** mip) {
740    SkAutoMutexAcquire am(gMutex);
741    return get_cache()->findAndLockMip(orig, mip);
742}
743
744SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(const SkBitmap& orig,
745                                                       SkScalar scaleX,
746                                                       SkScalar scaleY,
747                                                       const SkBitmap& scaled) {
748    SkAutoMutexAcquire am(gMutex);
749    return get_cache()->addAndLock(orig, scaleX, scaleY, scaled);
750}
751
752SkScaledImageCache::ID* SkScaledImageCache::AddAndLockMip(const SkBitmap& orig,
753                                                          const SkMipMap* mip) {
754    SkAutoMutexAcquire am(gMutex);
755    return get_cache()->addAndLockMip(orig, mip);
756}
757
758void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) {
759    SkAutoMutexAcquire am(gMutex);
760    get_cache()->unlock(id);
761
762//    get_cache()->dump();
763}
764
765size_t SkScaledImageCache::GetTotalBytesUsed() {
766    SkAutoMutexAcquire am(gMutex);
767    return get_cache()->getTotalBytesUsed();
768}
769
770size_t SkScaledImageCache::GetTotalByteLimit() {
771    SkAutoMutexAcquire am(gMutex);
772    return get_cache()->getTotalByteLimit();
773}
774
775size_t SkScaledImageCache::SetTotalByteLimit(size_t newLimit) {
776    SkAutoMutexAcquire am(gMutex);
777    return get_cache()->setTotalByteLimit(newLimit);
778}
779
780SkBitmap::Allocator* SkScaledImageCache::GetAllocator() {
781    SkAutoMutexAcquire am(gMutex);
782    return get_cache()->allocator();
783}
784
785void SkScaledImageCache::Dump() {
786    SkAutoMutexAcquire am(gMutex);
787    get_cache()->dump();
788}
789
790size_t SkScaledImageCache::SetSingleAllocationByteLimit(size_t size) {
791    SkAutoMutexAcquire am(gMutex);
792    return get_cache()->setSingleAllocationByteLimit(size);
793}
794
795size_t SkScaledImageCache::GetSingleAllocationByteLimit() {
796    SkAutoMutexAcquire am(gMutex);
797    return get_cache()->getSingleAllocationByteLimit();
798}
799
800///////////////////////////////////////////////////////////////////////////////
801
802#include "SkGraphics.h"
803
804size_t SkGraphics::GetImageCacheTotalBytesUsed() {
805    return SkScaledImageCache::GetTotalBytesUsed();
806}
807
808size_t SkGraphics::GetImageCacheTotalByteLimit() {
809    return SkScaledImageCache::GetTotalByteLimit();
810}
811
812size_t SkGraphics::SetImageCacheTotalByteLimit(size_t newLimit) {
813    return SkScaledImageCache::SetTotalByteLimit(newLimit);
814}
815
816size_t SkGraphics::GetImageCacheSingleAllocationByteLimit() {
817    return SkScaledImageCache::GetSingleAllocationByteLimit();
818}
819
820size_t SkGraphics::SetImageCacheSingleAllocationByteLimit(size_t newLimit) {
821    return SkScaledImageCache::SetSingleAllocationByteLimit(newLimit);
822}
823
824