OLD | NEW |
---|---|
(Empty) | |
1 #include "SkScaledImageCache.h" | |
2 #include "SkPixelRef.h" | |
3 #include "SkRect.h" | |
4 | |
5 static const size_t DEF_BYTE_LIMIT = 2 * 1024 * 1024; | |
ernstm
2013/07/23 06:36:50
Two megabyte is quite low. Should we set this to a
reed1
2013/07/23 14:50:29
There is now a define that chrome can set in skia.
| |
6 | |
7 struct Key { | |
8 bool init(const SkBitmap& bm, SkScalar scaleX, SkScalar scaleY) { | |
9 SkPixelRef* pr = bm.pixelRef(); | |
10 if (!pr) { | |
11 return false; | |
12 } | |
13 | |
14 size_t offset = bm.pixelRefOffset(); | |
15 size_t rowBytes = bm.rowBytes(); | |
16 int x = (offset % rowBytes) >> 2; | |
17 int y = offset / rowBytes; | |
18 | |
19 fGenID = pr->getGenerationID(); | |
20 fBounds.set(x, y, x + bm.width(), y + bm.height()); | |
21 fScaleX = scaleX; | |
22 fScaleY = scaleY; | |
23 return true; | |
24 } | |
25 | |
26 bool operator<(const Key& other) { | |
27 const uint32_t* a = &fGenID; | |
28 const uint32_t* b = &other.fGenID; | |
29 for (int i = 0; i < 7; ++i) { | |
30 if (a[i] < b[i]) { | |
31 return true; | |
32 } | |
33 if (a[i] > b[i]) { | |
34 return false; | |
35 } | |
36 } | |
37 return false; | |
38 } | |
39 | |
40 bool operator==(const Key& other) { | |
41 const uint32_t* a = &fGenID; | |
42 const uint32_t* b = &other.fGenID; | |
43 for (int i = 0; i < 7; ++i) { | |
44 if (a[i] != b[i]) { | |
45 return false; | |
46 } | |
47 } | |
48 return true; | |
49 } | |
50 | |
51 uint32_t fGenID; | |
52 SkIRect fBounds; | |
53 float fScaleX; | |
54 float fScaleY; | |
55 }; | |
56 | |
57 struct SkScaledImageCache::Rec { | |
58 Rec(const Key& key, const SkBitmap& bm) : fKey(key), fBitmap(bm) { | |
59 fLockCount = 1; | |
60 } | |
61 | |
62 size_t bytesUsed() const { | |
63 return fBitmap.getSize(); | |
64 } | |
65 | |
66 Rec* fNext; | |
67 Rec* fPrev; | |
68 | |
69 int32_t fLockCount; | |
70 Key fKey; | |
71 SkBitmap fBitmap; | |
72 }; | |
73 | |
74 SkScaledImageCache::SkScaledImageCache() { | |
75 fHead = NULL; | |
76 fTail = NULL; | |
77 fBytesUsed = 0; | |
78 fByteLimit = DEF_BYTE_LIMIT; | |
79 fCount = 0; | |
80 } | |
81 | |
82 SkScaledImageCache::~SkScaledImageCache() { | |
83 Rec* rec = fHead; | |
84 while (rec) { | |
85 Rec* next = rec->fNext; | |
86 SkDELETE(rec); | |
87 rec = next; | |
88 } | |
89 } | |
90 | |
91 SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig, | |
humper
2013/07/23 01:25:44
Possibly worth a comment here saying that any nece
reed1
2013/07/23 14:50:29
Done.
| |
92 SkScalar scaleX, | |
93 SkScalar scaleY, | |
94 SkBitmap* scaled) { | |
95 Key key; | |
96 if (!key.init(orig, scaleX, scaleY)) { | |
97 return NULL; | |
98 } | |
99 | |
100 Rec* rec = fHead; | |
101 while (rec != NULL) { | |
102 if (rec->fKey == key) { | |
103 this->moveToHead(rec); // for our LRU | |
104 rec->fLockCount += 1; | |
105 *scaled = rec->fBitmap; | |
106 // SkDebugf("Found: [%d %d] %d\n", rec->fBitmap.width(), rec->fBitmap .height(), rec->fLockCount); | |
107 return (ID*)rec; | |
108 } | |
109 rec = rec->fNext; | |
110 } | |
111 return NULL; | |
112 } | |
113 | |
114 SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig, | |
humper
2013/07/23 01:25:44
same as above re: comment
reed1
2013/07/23 14:50:29
Done.
| |
115 SkScalar scaleX, | |
116 SkScalar scaleY, | |
117 const SkBitmap& scaled) { | |
118 Key key; | |
119 if (!key.init(orig, scaleX, scaleY)) { | |
120 return NULL; | |
121 } | |
122 | |
123 Rec* rec = SkNEW_ARGS(Rec, (key, scaled)); | |
124 this->addToHead(rec); | |
125 SkASSERT(1 == rec->fLockCount); | |
126 | |
127 // SkDebugf("Added: [%d %d]\n", rec->fBitmap.width(), rec->fBitmap.height()); | |
128 | |
129 // We may (now) be overbudget, so see if we need to purge something. | |
130 this->purgeAsNeeded(); | |
131 return (ID*)rec; | |
132 } | |
133 | |
134 void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) { | |
humper
2013/07/23 01:25:44
ditto above re: comment
reed1
2013/07/23 14:50:29
Done.
| |
135 SkASSERT(id); | |
136 | |
137 #ifdef SK_DEBUG | |
138 { | |
139 bool found = false; | |
140 Rec* rec = fHead; | |
141 while (rec != NULL) { | |
142 if ((ID*)rec == id) { | |
143 found = true; | |
144 break; | |
145 } | |
146 rec = rec->fNext; | |
147 } | |
148 SkASSERT(found); | |
149 } | |
150 #endif | |
151 Rec* rec = (Rec*)id; | |
152 SkASSERT(rec->fLockCount > 0); | |
153 rec->fLockCount -= 1; | |
154 | |
155 // SkDebugf("Unlock: [%d %d] %d\n", rec->fBitmap.width(), rec->fBitmap.height (), rec->fLockCount); | |
156 | |
157 // we may have been over-budget, but now have released something, so check | |
158 // if we should purge. | |
159 if (0 == rec->fLockCount) { | |
160 this->purgeAsNeeded(); | |
161 } | |
162 } | |
163 | |
164 void SkScaledImageCache::purgeAsNeeded() { | |
165 size_t byteLimit = fByteLimit; | |
166 size_t bytesUsed = fBytesUsed; | |
167 | |
168 Rec* rec = fTail; | |
169 while (rec) { | |
170 if (bytesUsed < byteLimit) { | |
171 break; | |
172 } | |
173 Rec* prev = rec->fPrev; | |
174 if (0 == rec->fLockCount) { | |
175 // SkDebugf("Purge: [%d %d] %d\n", rec->fBitmap.width(), rec->fBitmap .height(), fCount); | |
176 size_t used = rec->bytesUsed(); | |
177 SkASSERT(used <= bytesUsed); | |
178 bytesUsed -= used; | |
179 this->detach(rec); | |
180 SkDELETE(rec); | |
181 fCount -= 1; | |
182 } | |
183 rec = prev; | |
184 } | |
185 fBytesUsed = bytesUsed; | |
186 } | |
187 | |
188 /////////////////////////////////////////////////////////////////////////////// | |
189 | |
190 void SkScaledImageCache::detach(Rec* rec) { | |
191 Rec* prev = rec->fPrev; | |
192 Rec* next = rec->fNext; | |
193 | |
194 if (!prev) { | |
195 SkASSERT(fHead == rec); | |
196 fHead = next; | |
197 } else { | |
198 prev->fNext = next; | |
199 } | |
200 | |
201 if (!next) { | |
202 fTail = prev; | |
203 } else { | |
204 next->fPrev = prev; | |
205 } | |
206 | |
207 rec->fNext = rec->fPrev = NULL; | |
208 } | |
209 | |
210 void SkScaledImageCache::moveToHead(Rec* rec) { | |
211 if (fHead == rec) { | |
212 return; | |
213 } | |
214 | |
215 SkASSERT(fHead); | |
216 SkASSERT(fTail); | |
217 | |
218 this->validate(); | |
219 | |
220 if (3 == fCount) | |
221 SkDebugf(""); | |
humper
2013/07/23 01:25:44
Obviously the debug statements are intended to be
reed1
2013/07/23 14:50:29
Done.
| |
222 this->detach(rec); | |
223 | |
224 fHead->fPrev = rec; | |
225 rec->fNext = fHead; | |
226 fHead = rec; | |
227 | |
228 this->validate(); | |
229 } | |
230 | |
231 void SkScaledImageCache::addToHead(Rec* rec) { | |
232 this->validate(); | |
233 | |
234 rec->fPrev = NULL; | |
235 rec->fNext = fHead; | |
236 if (fHead) { | |
237 fHead->fPrev = rec; | |
238 } | |
239 fHead = rec; | |
240 if (!fTail) { | |
241 fTail = rec; | |
242 } | |
243 fBytesUsed += rec->bytesUsed(); | |
244 fCount += 1; | |
245 | |
246 this->validate(); | |
247 } | |
248 | |
249 #ifdef SK_DEBUG | |
250 void SkScaledImageCache::validate() const { | |
251 if (NULL == fHead) { | |
252 SkASSERT(NULL == fTail); | |
253 SkASSERT(0 == fBytesUsed); | |
254 return; | |
255 } | |
256 | |
257 if (fHead == fTail) { | |
258 SkASSERT(NULL == fHead->fPrev); | |
259 SkASSERT(NULL == fHead->fNext); | |
260 SkASSERT(fHead->bytesUsed() == fBytesUsed); | |
261 return; | |
262 } | |
263 | |
264 SkASSERT(NULL == fHead->fPrev); | |
265 SkASSERT(NULL != fHead->fNext); | |
266 SkASSERT(NULL == fTail->fNext); | |
267 SkASSERT(NULL != fTail->fPrev); | |
268 | |
269 size_t used = 0; | |
270 int count = 0; | |
271 const Rec* rec = fHead; | |
272 while (rec) { | |
273 count += 1; | |
274 used += rec->bytesUsed(); | |
275 SkASSERT(used <= fBytesUsed); | |
276 rec = rec->fNext; | |
277 } | |
278 SkASSERT(fCount == count); | |
279 | |
280 rec = fTail; | |
281 while (rec) { | |
282 SkASSERT(count > 0); | |
283 count -= 1; | |
284 SkASSERT(used >= rec->bytesUsed()); | |
285 used -= rec->bytesUsed(); | |
286 rec = rec->fPrev; | |
287 } | |
288 | |
289 SkASSERT(0 == count); | |
290 SkASSERT(0 == used); | |
291 } | |
292 #endif | |
293 | |
294 /////////////////////////////////////////////////////////////////////////////// | |
295 | |
296 #include "SkThread.h" | |
297 | |
298 static SkMutex gMutex; | |
299 | |
300 static SkScaledImageCache* get_cache() { | |
301 static SkScaledImageCache* gCache; | |
humper
2013/07/23 01:25:44
I know the standard says that this will be initial
reed1
2013/07/23 14:50:29
I don't know if anything is written in the doc, bu
| |
302 if (!gCache) { | |
303 gCache = SkNEW(SkScaledImageCache); | |
304 } | |
305 return gCache; | |
306 } | |
307 | |
308 SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(const SkBitmap& orig, | |
309 SkScalar scaleX, | |
310 SkScalar scaleY, | |
311 SkBitmap* scaled) { | |
312 SkAutoMutexAcquire am(gMutex); | |
313 return get_cache()->findAndLock(orig, scaleX, scaleY, scaled); | |
314 } | |
315 | |
316 SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(const SkBitmap& orig, | |
317 SkScalar scaleX, | |
318 SkScalar scaleY, | |
319 const SkBitmap& scaled) { | |
320 SkAutoMutexAcquire am(gMutex); | |
321 return get_cache()->addAndLock(orig, scaleX, scaleY, scaled); | |
322 } | |
323 | |
324 void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) { | |
325 SkAutoMutexAcquire am(gMutex); | |
326 return get_cache()->unlock(id); | |
327 } | |
328 | |
329 size_t SkScaledImageCache::GetByteLimit() { | |
330 SkAutoMutexAcquire am(gMutex); | |
331 return get_cache()->fByteLimit; | |
332 } | |
333 | |
334 size_t SkScaledImageCache::SetByteLimit(size_t newLimit) { | |
335 SkAutoMutexAcquire am(gMutex); | |
336 SkScaledImageCache* cache = get_cache(); | |
337 size_t prevLimit = cache->fByteLimit; | |
338 cache->fByteLimit = newLimit; | |
339 cache->purgeAsNeeded(); | |
340 return prevLimit; | |
341 } | |
342 | |
343 size_t SkScaledImageCache::GetBytesUsed() { | |
344 SkAutoMutexAcquire am(gMutex); | |
345 return get_cache()->fBytesUsed; | |
346 } | |
347 | |
OLD | NEW |