Chromium Code Reviews| 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 |