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 "include/core/SkImage.h"
9 #include "include/core/SkPixelRef.h"
10 #include "include/core/SkRect.h"
11 #include "src/core/SkBitmapCache.h"
12 #include "src/core/SkMipMap.h"
13 #include "src/core/SkResourceCache.h"
14 #include "src/image/SkImage_Base.h"
15
16 /**
17 * Use this for bitmapcache and mipmapcache entries.
18 */
SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID)19 uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID) {
20 uint64_t sharedID = SkSetFourByteTag('b', 'm', 'a', 'p');
21 return (sharedID << 32) | bitmapGenID;
22 }
23
SkNotifyBitmapGenIDIsStale(uint32_t bitmapGenID)24 void SkNotifyBitmapGenIDIsStale(uint32_t bitmapGenID) {
25 SkResourceCache::PostPurgeSharedID(SkMakeResourceCacheSharedIDForBitmap(bitmapGenID));
26 }
27
28 ///////////////////////////////////////////////////////////////////////////////////////////////////
29
Make(uint32_t imageID,const SkIRect & subset)30 SkBitmapCacheDesc SkBitmapCacheDesc::Make(uint32_t imageID, const SkIRect& subset) {
31 SkASSERT(imageID);
32 SkASSERT(subset.width() > 0 && subset.height() > 0);
33 return { imageID, subset };
34 }
35
Make(const SkImage * image)36 SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image) {
37 SkIRect bounds = SkIRect::MakeWH(image->width(), image->height());
38 return Make(image->uniqueID(), bounds);
39 }
40
41 namespace {
42 static unsigned gBitmapKeyNamespaceLabel;
43
44 struct BitmapKey : public SkResourceCache::Key {
45 public:
BitmapKey__anon7c6590bd0111::BitmapKey46 BitmapKey(const SkBitmapCacheDesc& desc) : fDesc(desc) {
47 this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fDesc.fImageID),
48 sizeof(fDesc));
49 }
50
51 const SkBitmapCacheDesc fDesc;
52 };
53 }
54
55 //////////////////////
56 #include "src/core/SkDiscardableMemory.h"
57 #include "src/core/SkNextID.h"
58
SkBitmapCache_setImmutableWithID(SkPixelRef * pr,uint32_t id)59 void SkBitmapCache_setImmutableWithID(SkPixelRef* pr, uint32_t id) {
60 pr->setImmutableWithID(id);
61 }
62
63 class SkBitmapCache::Rec : public SkResourceCache::Rec {
64 public:
Rec(const SkBitmapCacheDesc & desc,const SkImageInfo & info,size_t rowBytes,std::unique_ptr<SkDiscardableMemory> dm,void * block)65 Rec(const SkBitmapCacheDesc& desc, const SkImageInfo& info, size_t rowBytes,
66 std::unique_ptr<SkDiscardableMemory> dm, void* block)
67 : fKey(desc)
68 , fDM(std::move(dm))
69 , fMalloc(block)
70 , fInfo(info)
71 , fRowBytes(rowBytes)
72 {
73 SkASSERT(!(fDM && fMalloc)); // can't have both
74
75 // We need an ID to return with the bitmap/pixelref. We can't necessarily use the key/desc
76 // ID - lazy images cache the same ID with multiple keys (in different color types).
77 fPrUniqueID = SkNextID::ImageID();
78 }
79
~Rec()80 ~Rec() override {
81 SkASSERT(0 == fExternalCounter);
82 if (fDM && fDiscardableIsLocked) {
83 SkASSERT(fDM->data());
84 fDM->unlock();
85 }
86 sk_free(fMalloc); // may be null
87 }
88
getKey() const89 const Key& getKey() const override { return fKey; }
bytesUsed() const90 size_t bytesUsed() const override {
91 return sizeof(fKey) + fInfo.computeByteSize(fRowBytes);
92 }
canBePurged()93 bool canBePurged() override {
94 SkAutoMutexExclusive ama(fMutex);
95 return fExternalCounter == 0;
96 }
postAddInstall(void * payload)97 void postAddInstall(void* payload) override {
98 SkAssertResult(this->install(static_cast<SkBitmap*>(payload)));
99 }
100
getCategory() const101 const char* getCategory() const override { return "bitmap"; }
diagnostic_only_getDiscardable() const102 SkDiscardableMemory* diagnostic_only_getDiscardable() const override {
103 return fDM.get();
104 }
105
ReleaseProc(void * addr,void * ctx)106 static void ReleaseProc(void* addr, void* ctx) {
107 Rec* rec = static_cast<Rec*>(ctx);
108 SkAutoMutexExclusive ama(rec->fMutex);
109
110 SkASSERT(rec->fExternalCounter > 0);
111 rec->fExternalCounter -= 1;
112 if (rec->fDM) {
113 SkASSERT(rec->fMalloc == nullptr);
114 if (rec->fExternalCounter == 0) {
115 rec->fDM->unlock();
116 rec->fDiscardableIsLocked = false;
117 }
118 } else {
119 SkASSERT(rec->fMalloc != nullptr);
120 }
121 }
122
install(SkBitmap * bitmap)123 bool install(SkBitmap* bitmap) {
124 SkAutoMutexExclusive ama(fMutex);
125
126 if (!fDM && !fMalloc) {
127 return false;
128 }
129
130 if (fDM) {
131 if (!fDiscardableIsLocked) {
132 SkASSERT(fExternalCounter == 0);
133 if (!fDM->lock()) {
134 fDM.reset(nullptr);
135 return false;
136 }
137 fDiscardableIsLocked = true;
138 }
139 SkASSERT(fDM->data());
140 }
141
142 bitmap->installPixels(fInfo, fDM ? fDM->data() : fMalloc, fRowBytes, ReleaseProc, this);
143 SkBitmapCache_setImmutableWithID(bitmap->pixelRef(), fPrUniqueID);
144 fExternalCounter++;
145
146 return true;
147 }
148
Finder(const SkResourceCache::Rec & baseRec,void * contextBitmap)149 static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) {
150 Rec* rec = (Rec*)&baseRec;
151 SkBitmap* result = (SkBitmap*)contextBitmap;
152 return rec->install(result);
153 }
154
155 private:
156 BitmapKey fKey;
157
158 SkMutex fMutex;
159
160 // either fDM or fMalloc can be non-null, but not both
161 std::unique_ptr<SkDiscardableMemory> fDM;
162 void* fMalloc;
163
164 SkImageInfo fInfo;
165 size_t fRowBytes;
166 uint32_t fPrUniqueID;
167
168 // This field counts the number of external pixelrefs we have created.
169 // They notify us when they are destroyed so we can decrement this.
170 int fExternalCounter = 0;
171 bool fDiscardableIsLocked = true;
172 };
173
PrivateDeleteRec(Rec * rec)174 void SkBitmapCache::PrivateDeleteRec(Rec* rec) { delete rec; }
175
Alloc(const SkBitmapCacheDesc & desc,const SkImageInfo & info,SkPixmap * pmap)176 SkBitmapCache::RecPtr SkBitmapCache::Alloc(const SkBitmapCacheDesc& desc, const SkImageInfo& info,
177 SkPixmap* pmap) {
178 // Ensure that the info matches the subset (i.e. the subset is the entire image)
179 SkASSERT(info.width() == desc.fSubset.width());
180 SkASSERT(info.height() == desc.fSubset.height());
181
182 const size_t rb = info.minRowBytes();
183 size_t size = info.computeByteSize(rb);
184 if (SkImageInfo::ByteSizeOverflowed(size)) {
185 return nullptr;
186 }
187
188 std::unique_ptr<SkDiscardableMemory> dm;
189 void* block = nullptr;
190
191 auto factory = SkResourceCache::GetDiscardableFactory();
192 if (factory) {
193 dm.reset(factory(size));
194 } else {
195 block = sk_malloc_canfail(size);
196 }
197 if (!dm && !block) {
198 return nullptr;
199 }
200 *pmap = SkPixmap(info, dm ? dm->data() : block, rb);
201 return RecPtr(new Rec(desc, info, rb, std::move(dm), block));
202 }
203
Add(RecPtr rec,SkBitmap * bitmap)204 void SkBitmapCache::Add(RecPtr rec, SkBitmap* bitmap) {
205 SkResourceCache::Add(rec.release(), bitmap);
206 }
207
Find(const SkBitmapCacheDesc & desc,SkBitmap * result)208 bool SkBitmapCache::Find(const SkBitmapCacheDesc& desc, SkBitmap* result) {
209 desc.validate();
210 return SkResourceCache::Find(BitmapKey(desc), SkBitmapCache::Rec::Finder, result);
211 }
212
213 //////////////////////////////////////////////////////////////////////////////////////////
214 //////////////////////////////////////////////////////////////////////////////////////////
215
216 #define CHECK_LOCAL(localCache, localName, globalName, ...) \
217 ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__))
218
219 namespace {
220 static unsigned gMipMapKeyNamespaceLabel;
221
222 struct MipMapKey : public SkResourceCache::Key {
223 public:
MipMapKey__anon7c6590bd0211::MipMapKey224 MipMapKey(const SkBitmapCacheDesc& desc) : fDesc(desc) {
225 this->init(&gMipMapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fDesc.fImageID),
226 sizeof(fDesc));
227 }
228
229 const SkBitmapCacheDesc fDesc;
230 };
231
232 struct MipMapRec : public SkResourceCache::Rec {
MipMapRec__anon7c6590bd0211::MipMapRec233 MipMapRec(const SkBitmapCacheDesc& desc, const SkMipMap* result)
234 : fKey(desc)
235 , fMipMap(result)
236 {
237 fMipMap->attachToCacheAndRef();
238 }
239
~MipMapRec__anon7c6590bd0211::MipMapRec240 ~MipMapRec() override {
241 fMipMap->detachFromCacheAndUnref();
242 }
243
getKey__anon7c6590bd0211::MipMapRec244 const Key& getKey() const override { return fKey; }
bytesUsed__anon7c6590bd0211::MipMapRec245 size_t bytesUsed() const override { return sizeof(fKey) + fMipMap->size(); }
getCategory__anon7c6590bd0211::MipMapRec246 const char* getCategory() const override { return "mipmap"; }
diagnostic_only_getDiscardable__anon7c6590bd0211::MipMapRec247 SkDiscardableMemory* diagnostic_only_getDiscardable() const override {
248 return fMipMap->diagnostic_only_getDiscardable();
249 }
250
Finder__anon7c6590bd0211::MipMapRec251 static bool Finder(const SkResourceCache::Rec& baseRec, void* contextMip) {
252 const MipMapRec& rec = static_cast<const MipMapRec&>(baseRec);
253 const SkMipMap* mm = SkRef(rec.fMipMap);
254 // the call to ref() above triggers a "lock" in the case of discardable memory,
255 // which means we can now check for null (in case the lock failed).
256 if (nullptr == mm->data()) {
257 mm->unref(); // balance our call to ref()
258 return false;
259 }
260 // the call must call unref() when they are done.
261 *(const SkMipMap**)contextMip = mm;
262 return true;
263 }
264
265 private:
266 MipMapKey fKey;
267 const SkMipMap* fMipMap;
268 };
269 }
270
FindAndRef(const SkBitmapCacheDesc & desc,SkResourceCache * localCache)271 const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmapCacheDesc& desc,
272 SkResourceCache* localCache) {
273 MipMapKey key(desc);
274 const SkMipMap* result;
275
276 if (!CHECK_LOCAL(localCache, find, Find, key, MipMapRec::Finder, &result)) {
277 result = nullptr;
278 }
279 return result;
280 }
281
get_fact(SkResourceCache * localCache)282 static SkResourceCache::DiscardableFactory get_fact(SkResourceCache* localCache) {
283 return localCache ? localCache->GetDiscardableFactory()
284 : SkResourceCache::GetDiscardableFactory();
285 }
286
AddAndRef(const SkImage_Base * image,SkResourceCache * localCache)287 const SkMipMap* SkMipMapCache::AddAndRef(const SkImage_Base* image, SkResourceCache* localCache) {
288 SkBitmap src;
289 if (!image->getROPixels(&src)) {
290 return nullptr;
291 }
292
293 SkMipMap* mipmap = SkMipMap::Build(src, get_fact(localCache));
294 if (mipmap) {
295 MipMapRec* rec = new MipMapRec(SkBitmapCacheDesc::Make(image), mipmap);
296 CHECK_LOCAL(localCache, add, Add, rec);
297 image->notifyAddedToRasterCache();
298 }
299 return mipmap;
300 }
301