SkBitmapCache.cpp revision 6644d9353f3f0c09914385fd762e073f98d54205
1/*
2 * Copyright 2014 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 "SkBitmapCache.h"
9#include "SkImage.h"
10#include "SkResourceCache.h"
11#include "SkMipMap.h"
12#include "SkPixelRef.h"
13#include "SkRect.h"
14
15/**
16 *  Use this for bitmapcache and mipmapcache entries.
17 */
18uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID) {
19    uint64_t sharedID = SkSetFourByteTag('b', 'm', 'a', 'p');
20    return (sharedID << 32) | bitmapGenID;
21}
22
23void SkNotifyBitmapGenIDIsStale(uint32_t bitmapGenID) {
24    SkResourceCache::PostPurgeSharedID(SkMakeResourceCacheSharedIDForBitmap(bitmapGenID));
25}
26
27///////////////////////////////////////////////////////////////////////////////////////////////////
28
29SkBitmap::Allocator* SkBitmapCache::GetAllocator() {
30    return SkResourceCache::GetAllocator();
31}
32
33/**
34 This function finds the bounds of the bitmap *within its pixelRef*.
35 If the bitmap lacks a pixelRef, it will return an empty rect, since
36 that doesn't make sense.  This may be a useful enough function that
37 it should be somewhere else (in SkBitmap?).
38 */
39static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
40    if (!(bm.pixelRef())) {
41        return SkIRect::MakeEmpty();
42    }
43    SkIPoint origin = bm.pixelRefOrigin();
44    return SkIRect::MakeXYWH(origin.fX, origin.fY, bm.width(), bm.height());
45}
46
47/**
48 *  This function finds the bounds of the image. Today this is just the entire bounds,
49 *  but in the future we may support subsets within an image, in which case this should
50 *  return that subset (see get_bounds_from_bitmap).
51 */
52static SkIRect get_bounds_from_image(const SkImage* image) {
53    return SkIRect::MakeWH(image->width(), image->height());
54}
55
56SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkBitmap& bm, int width, int height) {
57    SkBitmapCacheDesc desc;
58    desc.fImageID = bm.getGenerationID();
59    desc.fWidth = width;
60    desc.fHeight = height;
61    desc.fBounds = get_bounds_from_bitmap(bm);
62    return desc;
63}
64
65SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkBitmap& bm) {
66    return Make(bm, bm.width(), bm.height());
67}
68
69SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image, int width, int height) {
70    SkBitmapCacheDesc desc;
71    desc.fImageID = image->uniqueID();
72    desc.fWidth = width;
73    desc.fHeight = height;
74    desc.fBounds = get_bounds_from_image(image);
75    return desc;
76}
77
78SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image) {
79    return Make(image, image->width(), image->height());
80}
81
82namespace {
83static unsigned gBitmapKeyNamespaceLabel;
84
85struct BitmapKey : public SkResourceCache::Key {
86public:
87    BitmapKey(uint32_t genID, int width, int height, const SkIRect& bounds)
88        : fGenID(genID)
89        , fWidth(width)
90        , fHeight(height)
91        , fBounds(bounds)
92    {
93        this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fGenID),
94                   sizeof(fGenID) + sizeof(fWidth) + sizeof(fHeight) + sizeof(fBounds));
95    }
96
97    BitmapKey(const SkBitmapCacheDesc& desc)
98        : fGenID(desc.fImageID)
99        , fWidth(desc.fWidth)
100        , fHeight(desc.fHeight)
101        , fBounds(desc.fBounds)
102    {
103        this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fGenID),
104                   sizeof(fGenID) + sizeof(fWidth) + sizeof(fHeight) + sizeof(fBounds));
105    }
106
107    void dump() const {
108        SkDebugf("-- add [%d %d] %d [%d %d %d %d]\n", fWidth, fHeight, fGenID,
109                 fBounds.x(), fBounds.y(), fBounds.width(), fBounds.height());
110    }
111
112    const uint32_t  fGenID;
113    const int       fWidth;
114    const int       fHeight;
115    const SkIRect   fBounds;
116};
117
118struct BitmapRec : public SkResourceCache::Rec {
119    BitmapRec(uint32_t genID, int width, int height, const SkIRect& bounds,
120              const SkBitmap& result)
121        : fKey(genID, width, height, bounds)
122        , fBitmap(result)
123    {
124#ifdef TRACE_NEW_BITMAP_CACHE_RECS
125        fKey.dump();
126#endif
127    }
128
129    BitmapRec(const SkBitmapCacheDesc& desc, const SkBitmap& result)
130        : fKey(desc)
131        , fBitmap(result)
132    {
133#ifdef TRACE_NEW_BITMAP_CACHE_RECS
134        fKey.dump();
135#endif
136    }
137
138    const Key& getKey() const override { return fKey; }
139    size_t bytesUsed() const override { return sizeof(fKey) + fBitmap.getSize(); }
140
141    const char* getCategory() const override { return "bitmap"; }
142    SkDiscardableMemory* diagnostic_only_getDiscardable() const override {
143        return fBitmap.pixelRef()->diagnostic_only_getDiscardable();
144    }
145
146    static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) {
147        const BitmapRec& rec = static_cast<const BitmapRec&>(baseRec);
148        SkBitmap* result = (SkBitmap*)contextBitmap;
149
150        *result = rec.fBitmap;
151        result->lockPixels();
152        return SkToBool(result->getPixels());
153    }
154
155private:
156    BitmapKey   fKey;
157    SkBitmap    fBitmap;
158};
159} // namespace
160
161#define CHECK_LOCAL(localCache, localName, globalName, ...) \
162    ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__))
163
164bool SkBitmapCache::FindWH(const SkBitmapCacheDesc& desc, SkBitmap* result,
165                           SkResourceCache* localCache) {
166    if (0 == desc.fWidth || 0 == desc.fHeight) {
167        // degenerate
168        return false;
169    }
170    return CHECK_LOCAL(localCache, find, Find, BitmapKey(desc), BitmapRec::Finder, result);
171}
172
173bool SkBitmapCache::AddWH(const SkBitmapCacheDesc& desc, const SkBitmap& result,
174                          SkResourceCache* localCache) {
175    if (0 == desc.fWidth || 0 == desc.fHeight) {
176        // degenerate, and the key we use for mipmaps
177        return false;
178    }
179    SkASSERT(result.isImmutable());
180    BitmapRec* rec = new BitmapRec(desc, result);
181    CHECK_LOCAL(localCache, add, Add, rec);
182    return true;
183}
184
185bool SkBitmapCache::Find(uint32_t genID, const SkIRect& subset, SkBitmap* result,
186                         SkResourceCache* localCache) {
187    BitmapKey key(genID, SK_Scalar1, SK_Scalar1, subset);
188
189    return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Finder, result);
190}
191
192bool SkBitmapCache::Add(SkPixelRef* pr, const SkIRect& subset, const SkBitmap& result,
193                        SkResourceCache* localCache) {
194    SkASSERT(result.isImmutable());
195
196    if (subset.isEmpty()
197        || subset.top() < 0
198        || subset.left() < 0
199        || result.width() != subset.width()
200        || result.height() != subset.height()) {
201        return false;
202    } else {
203        BitmapRec* rec = new BitmapRec(pr->getGenerationID(), 1, 1, subset, result);
204
205        CHECK_LOCAL(localCache, add, Add, rec);
206        pr->notifyAddedToCache();
207        return true;
208    }
209}
210
211bool SkBitmapCache::Find(uint32_t genID, SkBitmap* result, SkResourceCache* localCache) {
212    BitmapKey key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeEmpty());
213
214    return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Finder, result);
215}
216
217void SkBitmapCache::Add(uint32_t genID, const SkBitmap& result, SkResourceCache* localCache) {
218    SkASSERT(result.isImmutable());
219
220    BitmapRec* rec = new BitmapRec(genID, 1, 1, SkIRect::MakeEmpty(), result);
221
222    CHECK_LOCAL(localCache, add, Add, rec);
223}
224
225//////////////////////////////////////////////////////////////////////////////////////////
226//////////////////////////////////////////////////////////////////////////////////////////
227
228namespace {
229static unsigned gMipMapKeyNamespaceLabel;
230
231struct MipMapKey : public SkResourceCache::Key {
232public:
233    MipMapKey(uint32_t genID, SkSourceGammaTreatment treatment, const SkIRect& bounds)
234        : fGenID(genID), fSrcGammaTreatment(static_cast<uint32_t>(treatment)), fBounds(bounds)
235    {
236        this->init(&gMipMapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(genID),
237                   sizeof(fGenID) + sizeof(fSrcGammaTreatment) + sizeof(fBounds));
238    }
239
240    uint32_t    fGenID;
241    uint32_t    fSrcGammaTreatment;
242    SkIRect     fBounds;
243};
244
245struct MipMapRec : public SkResourceCache::Rec {
246    MipMapRec(const SkBitmap& src, SkSourceGammaTreatment treatment, const SkMipMap* result)
247        : fKey(src.getGenerationID(), treatment, get_bounds_from_bitmap(src))
248        , fMipMap(result)
249    {
250        fMipMap->attachToCacheAndRef();
251    }
252
253    virtual ~MipMapRec() {
254        fMipMap->detachFromCacheAndUnref();
255    }
256
257    const Key& getKey() const override { return fKey; }
258    size_t bytesUsed() const override { return sizeof(fKey) + fMipMap->size(); }
259    const char* getCategory() const override { return "mipmap"; }
260    SkDiscardableMemory* diagnostic_only_getDiscardable() const override {
261        return fMipMap->diagnostic_only_getDiscardable();
262    }
263
264    static bool Finder(const SkResourceCache::Rec& baseRec, void* contextMip) {
265        const MipMapRec& rec = static_cast<const MipMapRec&>(baseRec);
266        const SkMipMap* mm = SkRef(rec.fMipMap);
267        // the call to ref() above triggers a "lock" in the case of discardable memory,
268        // which means we can now check for null (in case the lock failed).
269        if (nullptr == mm->data()) {
270            mm->unref();    // balance our call to ref()
271            return false;
272        }
273        // the call must call unref() when they are done.
274        *(const SkMipMap**)contextMip = mm;
275        return true;
276    }
277
278private:
279    MipMapKey       fKey;
280    const SkMipMap* fMipMap;
281};
282}
283
284const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmapCacheDesc& desc,
285                                          SkSourceGammaTreatment treatment,
286                                          SkResourceCache* localCache) {
287    // Note: we ignore width/height from desc, just need id and bounds
288    MipMapKey key(desc.fImageID, treatment, desc.fBounds);
289    const SkMipMap* result;
290
291    if (!CHECK_LOCAL(localCache, find, Find, key, MipMapRec::Finder, &result)) {
292        result = nullptr;
293    }
294    return result;
295}
296
297static SkResourceCache::DiscardableFactory get_fact(SkResourceCache* localCache) {
298    return localCache ? localCache->GetDiscardableFactory()
299                      : SkResourceCache::GetDiscardableFactory();
300}
301
302const SkMipMap* SkMipMapCache::AddAndRef(const SkBitmap& src, SkSourceGammaTreatment treatment,
303                                         SkResourceCache* localCache) {
304    SkMipMap* mipmap = SkMipMap::Build(src, treatment, get_fact(localCache));
305    if (mipmap) {
306        MipMapRec* rec = new MipMapRec(src, treatment, mipmap);
307        CHECK_LOCAL(localCache, add, Add, rec);
308        src.pixelRef()->notifyAddedToCache();
309    }
310    return mipmap;
311}
312