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 "SkResourceCache.h"
10#include "SkMipMap.h"
11#include "SkPixelRef.h"
12#include "SkRect.h"
13
14/**
15 *  Use this for bitmapcache and mipmapcache entries.
16 */
17uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID) {
18    uint64_t sharedID = SkSetFourByteTag('b', 'm', 'a', 'p');
19    return (sharedID << 32) | bitmapGenID;
20}
21
22void SkNotifyBitmapGenIDIsStale(uint32_t bitmapGenID) {
23    SkResourceCache::PostPurgeSharedID(SkMakeResourceCacheSharedIDForBitmap(bitmapGenID));
24}
25
26///////////////////////////////////////////////////////////////////////////////////////////////////
27
28SkBitmap::Allocator* SkBitmapCache::GetAllocator() {
29    return SkResourceCache::GetAllocator();
30}
31
32/**
33 This function finds the bounds of the bitmap *within its pixelRef*.
34 If the bitmap lacks a pixelRef, it will return an empty rect, since
35 that doesn't make sense.  This may be a useful enough function that
36 it should be somewhere else (in SkBitmap?).
37 */
38static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
39    if (!(bm.pixelRef())) {
40        return SkIRect::MakeEmpty();
41    }
42    SkIPoint origin = bm.pixelRefOrigin();
43    return SkIRect::MakeXYWH(origin.fX, origin.fY, bm.width(), bm.height());
44}
45
46namespace {
47static unsigned gBitmapKeyNamespaceLabel;
48
49struct BitmapKey : public SkResourceCache::Key {
50public:
51    BitmapKey(uint32_t genID, SkScalar sx, SkScalar sy, const SkIRect& bounds)
52        : fGenID(genID)
53        , fScaleX(sx)
54        , fScaleY(sy)
55        , fBounds(bounds)
56    {
57        this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(genID),
58                   sizeof(fGenID) + sizeof(fScaleX) + sizeof(fScaleY) + sizeof(fBounds));
59    }
60
61    uint32_t    fGenID;
62    SkScalar    fScaleX;
63    SkScalar    fScaleY;
64    SkIRect     fBounds;
65};
66
67struct BitmapRec : public SkResourceCache::Rec {
68    BitmapRec(uint32_t genID, SkScalar scaleX, SkScalar scaleY, const SkIRect& bounds,
69              const SkBitmap& result)
70        : fKey(genID, scaleX, scaleY, bounds)
71        , fBitmap(result)
72    {}
73
74    const Key& getKey() const override { return fKey; }
75    size_t bytesUsed() const override { return sizeof(fKey) + fBitmap.getSize(); }
76
77    static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) {
78        const BitmapRec& rec = static_cast<const BitmapRec&>(baseRec);
79        SkBitmap* result = (SkBitmap*)contextBitmap;
80
81        *result = rec.fBitmap;
82        result->lockPixels();
83        return SkToBool(result->getPixels());
84    }
85
86private:
87    BitmapKey   fKey;
88    SkBitmap    fBitmap;
89};
90} // namespace
91
92#define CHECK_LOCAL(localCache, localName, globalName, ...) \
93    ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__))
94
95bool SkBitmapCache::Find(const SkBitmap& src, SkScalar invScaleX, SkScalar invScaleY, SkBitmap* result,
96                         SkResourceCache* localCache) {
97    if (0 == invScaleX || 0 == invScaleY) {
98        // degenerate, and the key we use for mipmaps
99        return false;
100    }
101    BitmapKey key(src.getGenerationID(), invScaleX, invScaleY, get_bounds_from_bitmap(src));
102
103    return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Finder, result);
104}
105
106void SkBitmapCache::Add(const SkBitmap& src, SkScalar invScaleX, SkScalar invScaleY,
107                        const SkBitmap& result, SkResourceCache* localCache) {
108    if (0 == invScaleX || 0 == invScaleY) {
109        // degenerate, and the key we use for mipmaps
110        return;
111    }
112    SkASSERT(result.isImmutable());
113    BitmapRec* rec = SkNEW_ARGS(BitmapRec, (src.getGenerationID(), invScaleX, invScaleY,
114                                            get_bounds_from_bitmap(src), result));
115    CHECK_LOCAL(localCache, add, Add, rec);
116    src.pixelRef()->notifyAddedToCache();
117}
118
119bool SkBitmapCache::Find(uint32_t genID, const SkIRect& subset, SkBitmap* result,
120                         SkResourceCache* localCache) {
121    BitmapKey key(genID, SK_Scalar1, SK_Scalar1, subset);
122
123    return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Finder, result);
124}
125
126bool SkBitmapCache::Add(SkPixelRef* pr, const SkIRect& subset, const SkBitmap& result,
127                        SkResourceCache* localCache) {
128    SkASSERT(result.isImmutable());
129
130    if (subset.isEmpty()
131        || subset.top() < 0
132        || subset.left() < 0
133        || result.width() != subset.width()
134        || result.height() != subset.height()) {
135        return false;
136    } else {
137        BitmapRec* rec = SkNEW_ARGS(BitmapRec, (pr->getGenerationID(), 1, 1, subset, result));
138
139        CHECK_LOCAL(localCache, add, Add, rec);
140        pr->notifyAddedToCache();
141        return true;
142    }
143}
144
145//////////////////////////////////////////////////////////////////////////////////////////
146//////////////////////////////////////////////////////////////////////////////////////////
147
148namespace {
149static unsigned gMipMapKeyNamespaceLabel;
150
151struct MipMapKey : public SkResourceCache::Key {
152public:
153    MipMapKey(uint32_t genID, const SkIRect& bounds) : fGenID(genID), fBounds(bounds) {
154        this->init(&gMipMapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(genID),
155                   sizeof(fGenID) + sizeof(fBounds));
156    }
157
158    uint32_t    fGenID;
159    SkIRect     fBounds;
160};
161
162struct MipMapRec : public SkResourceCache::Rec {
163    MipMapRec(const SkBitmap& src, const SkMipMap* result)
164        : fKey(src.getGenerationID(), get_bounds_from_bitmap(src))
165        , fMipMap(result)
166    {
167        fMipMap->attachToCacheAndRef();
168    }
169
170    virtual ~MipMapRec() {
171        fMipMap->detachFromCacheAndUnref();
172    }
173
174    const Key& getKey() const override { return fKey; }
175    size_t bytesUsed() const override { return sizeof(fKey) + fMipMap->size(); }
176
177    static bool Finder(const SkResourceCache::Rec& baseRec, void* contextMip) {
178        const MipMapRec& rec = static_cast<const MipMapRec&>(baseRec);
179        const SkMipMap* mm = SkRef(rec.fMipMap);
180        // the call to ref() above triggers a "lock" in the case of discardable memory,
181        // which means we can now check for null (in case the lock failed).
182        if (NULL == mm->data()) {
183            mm->unref();    // balance our call to ref()
184            return false;
185        }
186        // the call must call unref() when they are done.
187        *(const SkMipMap**)contextMip = mm;
188        return true;
189    }
190
191private:
192    MipMapKey       fKey;
193    const SkMipMap* fMipMap;
194};
195}
196
197const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmap& src, SkResourceCache* localCache) {
198    MipMapKey key(src.getGenerationID(), get_bounds_from_bitmap(src));
199    const SkMipMap* result;
200
201    if (!CHECK_LOCAL(localCache, find, Find, key, MipMapRec::Finder, &result)) {
202        result = NULL;
203    }
204    return result;
205}
206
207static SkResourceCache::DiscardableFactory get_fact(SkResourceCache* localCache) {
208    return localCache ? localCache->GetDiscardableFactory()
209                      : SkResourceCache::GetDiscardableFactory();
210}
211
212const SkMipMap* SkMipMapCache::AddAndRef(const SkBitmap& src, SkResourceCache* localCache) {
213    SkMipMap* mipmap = SkMipMap::Build(src, get_fact(localCache));
214    if (mipmap) {
215        MipMapRec* rec = SkNEW_ARGS(MipMapRec, (src, mipmap));
216        CHECK_LOCAL(localCache, add, Add, rec);
217        src.pixelRef()->notifyAddedToCache();
218    }
219    return mipmap;
220}
221