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