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
29/**
30 This function finds the bounds of the bitmap *within its pixelRef*.
31 If the bitmap lacks a pixelRef, it will return an empty rect, since
32 that doesn't make sense.  This may be a useful enough function that
33 it should be somewhere else (in SkBitmap?).
34 */
35static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
36    if (!(bm.pixelRef())) {
37        return SkIRect::MakeEmpty();
38    }
39    SkIPoint origin = bm.pixelRefOrigin();
40    return SkIRect::MakeXYWH(origin.fX, origin.fY, bm.width(), bm.height());
41}
42
43/**
44 *  This function finds the bounds of the image. Today this is just the entire bounds,
45 *  but in the future we may support subsets within an image, in which case this should
46 *  return that subset (see get_bounds_from_bitmap).
47 */
48static SkIRect get_bounds_from_image(const SkImage* image) {
49    SkASSERT(image->width() > 0 && image->height() > 0);
50    return SkIRect::MakeWH(image->width(), image->height());
51}
52
53SkBitmapCacheDesc SkBitmapCacheDesc::Make(uint32_t imageID, int origWidth, int origHeight) {
54    SkASSERT(imageID);
55    SkASSERT(origWidth > 0 && origHeight > 0);
56    return { imageID, 0, 0, {0, 0, origWidth, origHeight} };
57}
58
59SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkBitmap& bm, int scaledWidth, int scaledHeight) {
60    SkASSERT(bm.width() > 0 && bm.height() > 0);
61    SkASSERT(scaledWidth > 0 && scaledHeight > 0);
62    SkASSERT(scaledWidth != bm.width() || scaledHeight != bm.height());
63
64    return { bm.getGenerationID(), scaledWidth, scaledHeight, get_bounds_from_bitmap(bm) };
65}
66
67SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkBitmap& bm) {
68    SkASSERT(bm.width() > 0 && bm.height() > 0);
69    SkASSERT(bm.pixelRefOrigin() == SkIPoint::Make(0, 0));
70
71    return { bm.getGenerationID(), 0, 0, get_bounds_from_bitmap(bm) };
72}
73
74SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image, int scaledWidth, int scaledHeight) {
75    SkASSERT(image->width() > 0 && image->height() > 0);
76    SkASSERT(scaledWidth > 0 && scaledHeight > 0);
77
78    // If the dimensions are the same, should we set them to 0,0?
79    //SkASSERT(scaledWidth != image->width() || scaledHeight != image->height());
80
81    return { image->uniqueID(), scaledWidth, scaledHeight, get_bounds_from_image(image) };
82}
83
84SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image) {
85    SkASSERT(image->width() > 0 && image->height() > 0);
86
87    return { image->uniqueID(), 0, 0, get_bounds_from_image(image) };
88}
89
90namespace {
91static unsigned gBitmapKeyNamespaceLabel;
92
93struct BitmapKey : public SkResourceCache::Key {
94public:
95    BitmapKey(const SkBitmapCacheDesc& desc) : fDesc(desc) {
96        this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fDesc.fImageID),
97                   sizeof(fDesc));
98    }
99
100    void dump() const {
101        SkDebugf("-- add [%d %d] %d [%d %d %d %d]\n",
102                 fDesc.fScaledWidth, fDesc.fScaledHeight, fDesc.fImageID,
103             fDesc.fSubset.x(), fDesc.fSubset.y(), fDesc.fSubset.width(), fDesc.fSubset.height());
104    }
105
106    const SkBitmapCacheDesc fDesc;
107};
108}
109
110//////////////////////
111#include "SkDiscardableMemory.h"
112#include "SkNextID.h"
113
114void SkBitmapCache_setImmutableWithID(SkPixelRef* pr, uint32_t id) {
115    pr->setImmutableWithID(id);
116}
117
118//#define REC_TRACE   SkDebugf
119static void REC_TRACE(const char format[], ...) {}
120
121// for diagnostics
122static int32_t gRecCounter;
123
124class SkBitmapCache::Rec : public SkResourceCache::Rec {
125public:
126    Rec(const SkBitmapCacheDesc& desc, const SkImageInfo& info, size_t rowBytes,
127        std::unique_ptr<SkDiscardableMemory> dm, void* block)
128        : fKey(desc)
129        , fDM(std::move(dm))
130        , fMalloc(block)
131        , fInfo(info)
132        , fRowBytes(rowBytes)
133        , fExternalCounter(kBeforeFirstInstall_ExternalCounter)
134    {
135        SkASSERT(!(fDM && fMalloc));    // can't have both
136
137        // We need an ID to return with the bitmap/pixelref.
138        // If they are not scaling, we can return the same ID as the key/desc
139        // If they are scaling, we need a new ID
140        if (desc.fScaledWidth == 0 && desc.fScaledHeight == 0) {
141            fPrUniqueID = desc.fImageID;
142        } else {
143            fPrUniqueID = SkNextID::ImageID();
144        }
145        REC_TRACE(" Rec(%d): [%d %d] %d\n",
146                  sk_atomic_inc(&gRecCounter), fInfo.width(), fInfo.height(), fPrUniqueID);
147    }
148
149    ~Rec() override {
150        SkASSERT(0 == fExternalCounter || kBeforeFirstInstall_ExternalCounter == fExternalCounter);
151        if (fDM && kBeforeFirstInstall_ExternalCounter == fExternalCounter) {
152            // we never installed, so we need to unlock before we destroy the DM
153            SkASSERT(fDM->data());
154            fDM->unlock();
155        }
156        REC_TRACE("~Rec(%d): [%d %d] %d\n",
157                  sk_atomic_dec(&gRecCounter) - 1, fInfo.width(), fInfo.height(), fPrUniqueID);
158        sk_free(fMalloc);   // may be null
159    }
160
161    const Key& getKey() const override { return fKey; }
162    size_t bytesUsed() const override {
163        return sizeof(fKey) + fInfo.computeByteSize(fRowBytes);
164    }
165    bool canBePurged() override {
166        SkAutoMutexAcquire ama(fMutex);
167        return fExternalCounter == 0;
168    }
169    void postAddInstall(void* payload) override {
170        SkAssertResult(this->install(static_cast<SkBitmap*>(payload)));
171    }
172
173    const char* getCategory() const override { return "bitmap"; }
174    SkDiscardableMemory* diagnostic_only_getDiscardable() const override {
175        return fDM.get();
176    }
177
178    static void ReleaseProc(void* addr, void* ctx) {
179        Rec* rec = static_cast<Rec*>(ctx);
180        SkAutoMutexAcquire ama(rec->fMutex);
181
182        REC_TRACE(" Rec: [%d] releaseproc\n", rec->fPrUniqueID);
183
184        SkASSERT(rec->fExternalCounter > 0);
185        rec->fExternalCounter -= 1;
186        if (rec->fDM) {
187            SkASSERT(rec->fMalloc == nullptr);
188            if (rec->fExternalCounter == 0) {
189                REC_TRACE(" Rec [%d] unlock\n", rec->fPrUniqueID);
190                rec->fDM->unlock();
191            }
192        } else {
193            SkASSERT(rec->fMalloc != nullptr);
194        }
195    }
196
197    bool install(SkBitmap* bitmap) {
198        SkAutoMutexAcquire ama(fMutex);
199
200        // are we still valid
201        if (!fDM && !fMalloc) {
202            REC_TRACE(" Rec: [%d] invalid\n", fPrUniqueID);
203            return false;
204        }
205
206        /*
207            constructor      fExternalCount < 0     fDM->data()
208            after install    fExternalCount > 0     fDM->data()
209            after Release    fExternalCount == 0    !fDM->data()
210        */
211        if (fDM) {
212            if (kBeforeFirstInstall_ExternalCounter == fExternalCounter) {
213                SkASSERT(fDM->data());
214            } else if (fExternalCounter > 0) {
215                SkASSERT(fDM->data());
216            } else {
217                SkASSERT(fExternalCounter == 0);
218                if (!fDM->lock()) {
219                    REC_TRACE(" Rec [%d] re-lock failed\n", fPrUniqueID);
220                    fDM.reset(nullptr);
221                    return false;
222                }
223                REC_TRACE(" Rec [%d] re-lock succeeded\n", fPrUniqueID);
224            }
225            SkASSERT(fDM->data());
226        }
227
228        bitmap->installPixels(fInfo, fDM ? fDM->data() : fMalloc, fRowBytes, ReleaseProc, this);
229        SkBitmapCache_setImmutableWithID(bitmap->pixelRef(), fPrUniqueID);
230
231        REC_TRACE(" Rec: [%d] install new pr\n", fPrUniqueID);
232
233        if (kBeforeFirstInstall_ExternalCounter == fExternalCounter) {
234            fExternalCounter = 1;
235        } else {
236            fExternalCounter += 1;
237        }
238        SkASSERT(fExternalCounter > 0);
239        return true;
240    }
241
242    static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) {
243        Rec* rec = (Rec*)&baseRec;
244        SkBitmap* result = (SkBitmap*)contextBitmap;
245        REC_TRACE(" Rec: [%d] found\n", rec->fPrUniqueID);
246        return rec->install(result);
247    }
248
249private:
250    BitmapKey   fKey;
251
252    SkMutex     fMutex;
253
254    // either fDM or fMalloc can be non-null, but not both
255    std::unique_ptr<SkDiscardableMemory> fDM;
256    void*       fMalloc;
257
258    SkImageInfo fInfo;
259    size_t      fRowBytes;
260    uint32_t    fPrUniqueID;
261
262    // This field counts the number of external pixelrefs we have created. They notify us when
263    // they are destroyed so we can decrement this.
264    //
265    //  > 0     we have outstanding pixelrefs
266    // == 0     we have no outstanding pixelrefs, and can be safely purged
267    //  < 0     we have been created, but not yet "installed" the first time.
268    //
269    int         fExternalCounter;
270
271    enum {
272        kBeforeFirstInstall_ExternalCounter = -1
273    };
274};
275
276void SkBitmapCache::PrivateDeleteRec(Rec* rec) { delete rec; }
277
278SkBitmapCache::RecPtr SkBitmapCache::Alloc(const SkBitmapCacheDesc& desc, const SkImageInfo& info,
279                                           SkPixmap* pmap) {
280    // Ensure that the caller is self-consistent:
281    //  - if they are scaling, the info matches the scaled size
282    //  - if they are not, the info matches the subset (i.e. the subset is the entire image)
283    if (desc.fScaledWidth == 0 && desc.fScaledHeight == 0) {
284        SkASSERT(info.width() == desc.fSubset.width());
285        SkASSERT(info.height() == desc.fSubset.height());
286    } else {
287        SkASSERT(info.width() == desc.fScaledWidth);
288        SkASSERT(info.height() == desc.fScaledHeight);
289    }
290
291    const size_t rb = info.minRowBytes();
292    size_t size = info.computeByteSize(rb);
293    if (SkImageInfo::ByteSizeOverflowed(size)) {
294        return nullptr;
295    }
296
297    std::unique_ptr<SkDiscardableMemory> dm;
298    void* block = nullptr;
299
300    auto factory = SkResourceCache::GetDiscardableFactory();
301    if (factory) {
302        dm.reset(factory(size));
303    } else {
304        block = sk_malloc_canfail(size);
305    }
306    if (!dm && !block) {
307        return nullptr;
308    }
309    *pmap = SkPixmap(info, dm ? dm->data() : block, rb);
310    return RecPtr(new Rec(desc, info, rb, std::move(dm), block));
311}
312
313void SkBitmapCache::Add(RecPtr rec, SkBitmap* bitmap) {
314    SkResourceCache::Add(rec.release(), bitmap);
315}
316
317bool SkBitmapCache::Find(const SkBitmapCacheDesc& desc, SkBitmap* result) {
318    desc.validate();
319    return SkResourceCache::Find(BitmapKey(desc), SkBitmapCache::Rec::Finder, result);
320}
321
322//////////////////////////////////////////////////////////////////////////////////////////
323//////////////////////////////////////////////////////////////////////////////////////////
324
325#define CHECK_LOCAL(localCache, localName, globalName, ...) \
326    ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__))
327
328namespace {
329static unsigned gMipMapKeyNamespaceLabel;
330
331struct MipMapKey : public SkResourceCache::Key {
332public:
333    MipMapKey(uint32_t imageID, const SkIRect& subset, SkDestinationSurfaceColorMode colorMode)
334        : fImageID(imageID)
335        , fColorMode(static_cast<uint32_t>(colorMode))
336        , fSubset(subset)
337    {
338        SkASSERT(fImageID);
339        SkASSERT(!subset.isEmpty());
340        this->init(&gMipMapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fImageID),
341                   sizeof(fImageID) + sizeof(fColorMode) + sizeof(fSubset));
342    }
343
344    uint32_t    fImageID;
345    uint32_t    fColorMode;
346    SkIRect     fSubset;
347};
348
349struct MipMapRec : public SkResourceCache::Rec {
350    MipMapRec(uint32_t imageID, const SkIRect& subset, SkDestinationSurfaceColorMode colorMode,
351              const SkMipMap* result)
352        : fKey(imageID, subset, colorMode)
353        , fMipMap(result)
354    {
355        fMipMap->attachToCacheAndRef();
356    }
357
358    ~MipMapRec() override {
359        fMipMap->detachFromCacheAndUnref();
360    }
361
362    const Key& getKey() const override { return fKey; }
363    size_t bytesUsed() const override { return sizeof(fKey) + fMipMap->size(); }
364    const char* getCategory() const override { return "mipmap"; }
365    SkDiscardableMemory* diagnostic_only_getDiscardable() const override {
366        return fMipMap->diagnostic_only_getDiscardable();
367    }
368
369    static bool Finder(const SkResourceCache::Rec& baseRec, void* contextMip) {
370        const MipMapRec& rec = static_cast<const MipMapRec&>(baseRec);
371        const SkMipMap* mm = SkRef(rec.fMipMap);
372        // the call to ref() above triggers a "lock" in the case of discardable memory,
373        // which means we can now check for null (in case the lock failed).
374        if (nullptr == mm->data()) {
375            mm->unref();    // balance our call to ref()
376            return false;
377        }
378        // the call must call unref() when they are done.
379        *(const SkMipMap**)contextMip = mm;
380        return true;
381    }
382
383private:
384    MipMapKey       fKey;
385    const SkMipMap* fMipMap;
386};
387}
388
389const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmapCacheDesc& desc,
390                                          SkDestinationSurfaceColorMode colorMode,
391                                          SkResourceCache* localCache) {
392    SkASSERT(desc.fScaledWidth == 0);
393    SkASSERT(desc.fScaledHeight == 0);
394    MipMapKey key(desc.fImageID, desc.fSubset, colorMode);
395    const SkMipMap* result;
396
397    if (!CHECK_LOCAL(localCache, find, Find, key, MipMapRec::Finder, &result)) {
398        result = nullptr;
399    }
400    return result;
401}
402
403static SkResourceCache::DiscardableFactory get_fact(SkResourceCache* localCache) {
404    return localCache ? localCache->GetDiscardableFactory()
405                      : SkResourceCache::GetDiscardableFactory();
406}
407
408const SkMipMap* SkMipMapCache::AddAndRef(const SkBitmap& src,
409                                         SkDestinationSurfaceColorMode colorMode,
410                                         SkResourceCache* localCache) {
411    SkMipMap* mipmap = SkMipMap::Build(src, colorMode, get_fact(localCache));
412    if (mipmap) {
413        MipMapRec* rec = new MipMapRec(src.getGenerationID(), get_bounds_from_bitmap(src),
414                                       colorMode, mipmap);
415        CHECK_LOCAL(localCache, add, Add, rec);
416        src.pixelRef()->notifyAddedToCache();
417    }
418    return mipmap;
419}
420