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