gfx/skia/trunk/src/core/SkScaledImageCache.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /*
michael@0 2 * Copyright 2013 Google Inc.
michael@0 3 *
michael@0 4 * Use of this source code is governed by a BSD-style license that can be
michael@0 5 * found in the LICENSE file.
michael@0 6 */
michael@0 7
michael@0 8 #include "SkScaledImageCache.h"
michael@0 9 #include "SkMipMap.h"
michael@0 10 #include "SkOnce.h"
michael@0 11 #include "SkPixelRef.h"
michael@0 12 #include "SkRect.h"
michael@0 13
michael@0 14 // This can be defined by the caller's build system
michael@0 15 //#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
michael@0 16
michael@0 17 #ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT
michael@0 18 # define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 1024
michael@0 19 #endif
michael@0 20
michael@0 21 #ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
michael@0 22 #define SK_DEFAULT_IMAGE_CACHE_LIMIT (2 * 1024 * 1024)
michael@0 23 #endif
michael@0 24
michael@0 25 static inline SkScaledImageCache::ID* rec_to_id(SkScaledImageCache::Rec* rec) {
michael@0 26 return reinterpret_cast<SkScaledImageCache::ID*>(rec);
michael@0 27 }
michael@0 28
michael@0 29 static inline SkScaledImageCache::Rec* id_to_rec(SkScaledImageCache::ID* id) {
michael@0 30 return reinterpret_cast<SkScaledImageCache::Rec*>(id);
michael@0 31 }
michael@0 32
michael@0 33 // Implemented from en.wikipedia.org/wiki/MurmurHash.
michael@0 34 static uint32_t compute_hash(const uint32_t data[], int count) {
michael@0 35 uint32_t hash = 0;
michael@0 36
michael@0 37 for (int i = 0; i < count; ++i) {
michael@0 38 uint32_t k = data[i];
michael@0 39 k *= 0xcc9e2d51;
michael@0 40 k = (k << 15) | (k >> 17);
michael@0 41 k *= 0x1b873593;
michael@0 42
michael@0 43 hash ^= k;
michael@0 44 hash = (hash << 13) | (hash >> 19);
michael@0 45 hash *= 5;
michael@0 46 hash += 0xe6546b64;
michael@0 47 }
michael@0 48
michael@0 49 // hash ^= size;
michael@0 50 hash ^= hash >> 16;
michael@0 51 hash *= 0x85ebca6b;
michael@0 52 hash ^= hash >> 13;
michael@0 53 hash *= 0xc2b2ae35;
michael@0 54 hash ^= hash >> 16;
michael@0 55
michael@0 56 return hash;
michael@0 57 }
michael@0 58
michael@0 59 struct SkScaledImageCache::Key {
michael@0 60 Key(uint32_t genID,
michael@0 61 SkScalar scaleX,
michael@0 62 SkScalar scaleY,
michael@0 63 SkIRect bounds)
michael@0 64 : fGenID(genID)
michael@0 65 , fScaleX(scaleX)
michael@0 66 , fScaleY(scaleY)
michael@0 67 , fBounds(bounds) {
michael@0 68 fHash = compute_hash(&fGenID, 7);
michael@0 69 }
michael@0 70
michael@0 71 bool operator<(const Key& other) const {
michael@0 72 const uint32_t* a = &fGenID;
michael@0 73 const uint32_t* b = &other.fGenID;
michael@0 74 for (int i = 0; i < 7; ++i) {
michael@0 75 if (a[i] < b[i]) {
michael@0 76 return true;
michael@0 77 }
michael@0 78 if (a[i] > b[i]) {
michael@0 79 return false;
michael@0 80 }
michael@0 81 }
michael@0 82 return false;
michael@0 83 }
michael@0 84
michael@0 85 bool operator==(const Key& other) const {
michael@0 86 const uint32_t* a = &fHash;
michael@0 87 const uint32_t* b = &other.fHash;
michael@0 88 for (int i = 0; i < 8; ++i) {
michael@0 89 if (a[i] != b[i]) {
michael@0 90 return false;
michael@0 91 }
michael@0 92 }
michael@0 93 return true;
michael@0 94 }
michael@0 95
michael@0 96 uint32_t fHash;
michael@0 97 uint32_t fGenID;
michael@0 98 float fScaleX;
michael@0 99 float fScaleY;
michael@0 100 SkIRect fBounds;
michael@0 101 };
michael@0 102
michael@0 103 struct SkScaledImageCache::Rec {
michael@0 104 Rec(const Key& key, const SkBitmap& bm) : fKey(key), fBitmap(bm) {
michael@0 105 fLockCount = 1;
michael@0 106 fMip = NULL;
michael@0 107 }
michael@0 108
michael@0 109 Rec(const Key& key, const SkMipMap* mip) : fKey(key) {
michael@0 110 fLockCount = 1;
michael@0 111 fMip = mip;
michael@0 112 mip->ref();
michael@0 113 }
michael@0 114
michael@0 115 ~Rec() {
michael@0 116 SkSafeUnref(fMip);
michael@0 117 }
michael@0 118
michael@0 119 size_t bytesUsed() const {
michael@0 120 return fMip ? fMip->getSize() : fBitmap.getSize();
michael@0 121 }
michael@0 122
michael@0 123 Rec* fNext;
michael@0 124 Rec* fPrev;
michael@0 125
michael@0 126 // this guy wants to be 64bit aligned
michael@0 127 Key fKey;
michael@0 128
michael@0 129 int32_t fLockCount;
michael@0 130
michael@0 131 // we use either fBitmap or fMip, but not both
michael@0 132 SkBitmap fBitmap;
michael@0 133 const SkMipMap* fMip;
michael@0 134 };
michael@0 135
michael@0 136 #include "SkTDynamicHash.h"
michael@0 137
michael@0 138 namespace { // can't use static functions w/ template parameters
michael@0 139 const SkScaledImageCache::Key& key_from_rec(const SkScaledImageCache::Rec& rec) {
michael@0 140 return rec.fKey;
michael@0 141 }
michael@0 142
michael@0 143 uint32_t hash_from_key(const SkScaledImageCache::Key& key) {
michael@0 144 return key.fHash;
michael@0 145 }
michael@0 146
michael@0 147 bool eq_rec_key(const SkScaledImageCache::Rec& rec, const SkScaledImageCache::Key& key) {
michael@0 148 return rec.fKey == key;
michael@0 149 }
michael@0 150 }
michael@0 151
michael@0 152 class SkScaledImageCache::Hash : public SkTDynamicHash<SkScaledImageCache::Rec,
michael@0 153 SkScaledImageCache::Key,
michael@0 154 key_from_rec,
michael@0 155 hash_from_key,
michael@0 156 eq_rec_key> {};
michael@0 157
michael@0 158 ///////////////////////////////////////////////////////////////////////////////
michael@0 159
michael@0 160 // experimental hash to speed things up
michael@0 161 #define USE_HASH
michael@0 162
michael@0 163 #if !defined(USE_HASH)
michael@0 164 static inline SkScaledImageCache::Rec* find_rec_in_list(
michael@0 165 SkScaledImageCache::Rec* head, const Key & key) {
michael@0 166 SkScaledImageCache::Rec* rec = head;
michael@0 167 while ((rec != NULL) && (rec->fKey != key)) {
michael@0 168 rec = rec->fNext;
michael@0 169 }
michael@0 170 return rec;
michael@0 171 }
michael@0 172 #endif
michael@0 173
michael@0 174 void SkScaledImageCache::init() {
michael@0 175 fHead = NULL;
michael@0 176 fTail = NULL;
michael@0 177 #ifdef USE_HASH
michael@0 178 fHash = new Hash;
michael@0 179 #else
michael@0 180 fHash = NULL;
michael@0 181 #endif
michael@0 182 fBytesUsed = 0;
michael@0 183 fCount = 0;
michael@0 184 fAllocator = NULL;
michael@0 185
michael@0 186 // One of these should be explicit set by the caller after we return.
michael@0 187 fByteLimit = 0;
michael@0 188 fDiscardableFactory = NULL;
michael@0 189 }
michael@0 190
michael@0 191 #include "SkDiscardableMemory.h"
michael@0 192
michael@0 193 class SkOneShotDiscardablePixelRef : public SkPixelRef {
michael@0 194 public:
michael@0 195 SK_DECLARE_INST_COUNT(SkOneShotDiscardablePixelRef)
michael@0 196 // Ownership of the discardablememory is transfered to the pixelref
michael@0 197 SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes);
michael@0 198 ~SkOneShotDiscardablePixelRef();
michael@0 199
michael@0 200 SK_DECLARE_UNFLATTENABLE_OBJECT()
michael@0 201
michael@0 202 protected:
michael@0 203 virtual bool onNewLockPixels(LockRec*) SK_OVERRIDE;
michael@0 204 virtual void onUnlockPixels() SK_OVERRIDE;
michael@0 205 virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
michael@0 206
michael@0 207 private:
michael@0 208 SkDiscardableMemory* fDM;
michael@0 209 size_t fRB;
michael@0 210 bool fFirstTime;
michael@0 211
michael@0 212 typedef SkPixelRef INHERITED;
michael@0 213 };
michael@0 214
michael@0 215 SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info,
michael@0 216 SkDiscardableMemory* dm,
michael@0 217 size_t rowBytes)
michael@0 218 : INHERITED(info)
michael@0 219 , fDM(dm)
michael@0 220 , fRB(rowBytes)
michael@0 221 {
michael@0 222 SkASSERT(dm->data());
michael@0 223 fFirstTime = true;
michael@0 224 }
michael@0 225
michael@0 226 SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
michael@0 227 SkDELETE(fDM);
michael@0 228 }
michael@0 229
michael@0 230 bool SkOneShotDiscardablePixelRef::onNewLockPixels(LockRec* rec) {
michael@0 231 if (fFirstTime) {
michael@0 232 // we're already locked
michael@0 233 SkASSERT(fDM->data());
michael@0 234 fFirstTime = false;
michael@0 235 goto SUCCESS;
michael@0 236 }
michael@0 237
michael@0 238 // A previous call to onUnlock may have deleted our DM, so check for that
michael@0 239 if (NULL == fDM) {
michael@0 240 return false;
michael@0 241 }
michael@0 242
michael@0 243 if (!fDM->lock()) {
michael@0 244 // since it failed, we delete it now, to free-up the resource
michael@0 245 delete fDM;
michael@0 246 fDM = NULL;
michael@0 247 return false;
michael@0 248 }
michael@0 249
michael@0 250 SUCCESS:
michael@0 251 rec->fPixels = fDM->data();
michael@0 252 rec->fColorTable = NULL;
michael@0 253 rec->fRowBytes = fRB;
michael@0 254 return true;
michael@0 255 }
michael@0 256
michael@0 257 void SkOneShotDiscardablePixelRef::onUnlockPixels() {
michael@0 258 SkASSERT(!fFirstTime);
michael@0 259 fDM->unlock();
michael@0 260 }
michael@0 261
michael@0 262 size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
michael@0 263 return this->info().getSafeSize(fRB);
michael@0 264 }
michael@0 265
michael@0 266 class SkScaledImageCacheDiscardableAllocator : public SkBitmap::Allocator {
michael@0 267 public:
michael@0 268 SkScaledImageCacheDiscardableAllocator(
michael@0 269 SkScaledImageCache::DiscardableFactory factory) {
michael@0 270 SkASSERT(factory);
michael@0 271 fFactory = factory;
michael@0 272 }
michael@0 273
michael@0 274 virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
michael@0 275
michael@0 276 private:
michael@0 277 SkScaledImageCache::DiscardableFactory fFactory;
michael@0 278 };
michael@0 279
michael@0 280 bool SkScaledImageCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap,
michael@0 281 SkColorTable* ctable) {
michael@0 282 size_t size = bitmap->getSize();
michael@0 283 if (0 == size) {
michael@0 284 return false;
michael@0 285 }
michael@0 286
michael@0 287 SkDiscardableMemory* dm = fFactory(size);
michael@0 288 if (NULL == dm) {
michael@0 289 return false;
michael@0 290 }
michael@0 291
michael@0 292 // can we relax this?
michael@0 293 if (kPMColor_SkColorType != bitmap->colorType()) {
michael@0 294 return false;
michael@0 295 }
michael@0 296
michael@0 297 SkImageInfo info = bitmap->info();
michael@0 298 bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef,
michael@0 299 (info, dm, bitmap->rowBytes())))->unref();
michael@0 300 bitmap->lockPixels();
michael@0 301 return bitmap->readyToDraw();
michael@0 302 }
michael@0 303
michael@0 304 SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) {
michael@0 305 this->init();
michael@0 306 fDiscardableFactory = factory;
michael@0 307
michael@0 308 fAllocator = SkNEW_ARGS(SkScaledImageCacheDiscardableAllocator, (factory));
michael@0 309 }
michael@0 310
michael@0 311 SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
michael@0 312 this->init();
michael@0 313 fByteLimit = byteLimit;
michael@0 314 }
michael@0 315
michael@0 316 SkScaledImageCache::~SkScaledImageCache() {
michael@0 317 SkSafeUnref(fAllocator);
michael@0 318
michael@0 319 Rec* rec = fHead;
michael@0 320 while (rec) {
michael@0 321 Rec* next = rec->fNext;
michael@0 322 SkDELETE(rec);
michael@0 323 rec = next;
michael@0 324 }
michael@0 325 delete fHash;
michael@0 326 }
michael@0 327
michael@0 328 ////////////////////////////////////////////////////////////////////////////////
michael@0 329
michael@0 330
michael@0 331 SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(uint32_t genID,
michael@0 332 SkScalar scaleX,
michael@0 333 SkScalar scaleY,
michael@0 334 const SkIRect& bounds) {
michael@0 335 const Key key(genID, scaleX, scaleY, bounds);
michael@0 336 return this->findAndLock(key);
michael@0 337 }
michael@0 338
michael@0 339 /**
michael@0 340 This private method is the fully general record finder. All other
michael@0 341 record finders should call this function or the one above. */
michael@0 342 SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(const SkScaledImageCache::Key& key) {
michael@0 343 if (key.fBounds.isEmpty()) {
michael@0 344 return NULL;
michael@0 345 }
michael@0 346 #ifdef USE_HASH
michael@0 347 Rec* rec = fHash->find(key);
michael@0 348 #else
michael@0 349 Rec* rec = find_rec_in_list(fHead, key);
michael@0 350 #endif
michael@0 351 if (rec) {
michael@0 352 this->moveToHead(rec); // for our LRU
michael@0 353 rec->fLockCount += 1;
michael@0 354 }
michael@0 355 return rec;
michael@0 356 }
michael@0 357
michael@0 358 /**
michael@0 359 This function finds the bounds of the bitmap *within its pixelRef*.
michael@0 360 If the bitmap lacks a pixelRef, it will return an empty rect, since
michael@0 361 that doesn't make sense. This may be a useful enough function that
michael@0 362 it should be somewhere else (in SkBitmap?). */
michael@0 363 static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
michael@0 364 if (!(bm.pixelRef())) {
michael@0 365 return SkIRect::MakeEmpty();
michael@0 366 }
michael@0 367 SkIPoint origin = bm.pixelRefOrigin();
michael@0 368 return SkIRect::MakeXYWH(origin.fX, origin.fY, bm.width(), bm.height());
michael@0 369 }
michael@0 370
michael@0 371
michael@0 372 SkScaledImageCache::ID* SkScaledImageCache::findAndLock(uint32_t genID,
michael@0 373 int32_t width,
michael@0 374 int32_t height,
michael@0 375 SkBitmap* bitmap) {
michael@0 376 Rec* rec = this->findAndLock(genID, SK_Scalar1, SK_Scalar1,
michael@0 377 SkIRect::MakeWH(width, height));
michael@0 378 if (rec) {
michael@0 379 SkASSERT(NULL == rec->fMip);
michael@0 380 SkASSERT(rec->fBitmap.pixelRef());
michael@0 381 *bitmap = rec->fBitmap;
michael@0 382 }
michael@0 383 return rec_to_id(rec);
michael@0 384 }
michael@0 385
michael@0 386 SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig,
michael@0 387 SkScalar scaleX,
michael@0 388 SkScalar scaleY,
michael@0 389 SkBitmap* scaled) {
michael@0 390 if (0 == scaleX || 0 == scaleY) {
michael@0 391 // degenerate, and the key we use for mipmaps
michael@0 392 return NULL;
michael@0 393 }
michael@0 394 Rec* rec = this->findAndLock(orig.getGenerationID(), scaleX,
michael@0 395 scaleY, get_bounds_from_bitmap(orig));
michael@0 396 if (rec) {
michael@0 397 SkASSERT(NULL == rec->fMip);
michael@0 398 SkASSERT(rec->fBitmap.pixelRef());
michael@0 399 *scaled = rec->fBitmap;
michael@0 400 }
michael@0 401 return rec_to_id(rec);
michael@0 402 }
michael@0 403
michael@0 404 SkScaledImageCache::ID* SkScaledImageCache::findAndLockMip(const SkBitmap& orig,
michael@0 405 SkMipMap const ** mip) {
michael@0 406 Rec* rec = this->findAndLock(orig.getGenerationID(), 0, 0,
michael@0 407 get_bounds_from_bitmap(orig));
michael@0 408 if (rec) {
michael@0 409 SkASSERT(rec->fMip);
michael@0 410 SkASSERT(NULL == rec->fBitmap.pixelRef());
michael@0 411 *mip = rec->fMip;
michael@0 412 }
michael@0 413 return rec_to_id(rec);
michael@0 414 }
michael@0 415
michael@0 416
michael@0 417 ////////////////////////////////////////////////////////////////////////////////
michael@0 418 /**
michael@0 419 This private method is the fully general record adder. All other
michael@0 420 record adders should call this funtion. */
michael@0 421 SkScaledImageCache::ID* SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) {
michael@0 422 SkASSERT(rec);
michael@0 423 // See if we already have this key (racy inserts, etc.)
michael@0 424 Rec* existing = this->findAndLock(rec->fKey);
michael@0 425 if (NULL != existing) {
michael@0 426 // Since we already have a matching entry, just delete the new one and return.
michael@0 427 // Call sites cannot assume the passed in object will live past this call.
michael@0 428 existing->fBitmap = rec->fBitmap;
michael@0 429 SkDELETE(rec);
michael@0 430 return rec_to_id(existing);
michael@0 431 }
michael@0 432
michael@0 433 this->addToHead(rec);
michael@0 434 SkASSERT(1 == rec->fLockCount);
michael@0 435 #ifdef USE_HASH
michael@0 436 SkASSERT(fHash);
michael@0 437 fHash->add(rec);
michael@0 438 #endif
michael@0 439 // We may (now) be overbudget, so see if we need to purge something.
michael@0 440 this->purgeAsNeeded();
michael@0 441 return rec_to_id(rec);
michael@0 442 }
michael@0 443
michael@0 444 SkScaledImageCache::ID* SkScaledImageCache::addAndLock(uint32_t genID,
michael@0 445 int32_t width,
michael@0 446 int32_t height,
michael@0 447 const SkBitmap& bitmap) {
michael@0 448 Key key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeWH(width, height));
michael@0 449 Rec* rec = SkNEW_ARGS(Rec, (key, bitmap));
michael@0 450 return this->addAndLock(rec);
michael@0 451 }
michael@0 452
michael@0 453 SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig,
michael@0 454 SkScalar scaleX,
michael@0 455 SkScalar scaleY,
michael@0 456 const SkBitmap& scaled) {
michael@0 457 if (0 == scaleX || 0 == scaleY) {
michael@0 458 // degenerate, and the key we use for mipmaps
michael@0 459 return NULL;
michael@0 460 }
michael@0 461 SkIRect bounds = get_bounds_from_bitmap(orig);
michael@0 462 if (bounds.isEmpty()) {
michael@0 463 return NULL;
michael@0 464 }
michael@0 465 Key key(orig.getGenerationID(), scaleX, scaleY, bounds);
michael@0 466 Rec* rec = SkNEW_ARGS(Rec, (key, scaled));
michael@0 467 return this->addAndLock(rec);
michael@0 468 }
michael@0 469
michael@0 470 SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig,
michael@0 471 const SkMipMap* mip) {
michael@0 472 SkIRect bounds = get_bounds_from_bitmap(orig);
michael@0 473 if (bounds.isEmpty()) {
michael@0 474 return NULL;
michael@0 475 }
michael@0 476 Key key(orig.getGenerationID(), 0, 0, bounds);
michael@0 477 Rec* rec = SkNEW_ARGS(Rec, (key, mip));
michael@0 478 return this->addAndLock(rec);
michael@0 479 }
michael@0 480
michael@0 481 void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) {
michael@0 482 SkASSERT(id);
michael@0 483
michael@0 484 #ifdef SK_DEBUG
michael@0 485 {
michael@0 486 bool found = false;
michael@0 487 Rec* rec = fHead;
michael@0 488 while (rec != NULL) {
michael@0 489 if (rec == id_to_rec(id)) {
michael@0 490 found = true;
michael@0 491 break;
michael@0 492 }
michael@0 493 rec = rec->fNext;
michael@0 494 }
michael@0 495 SkASSERT(found);
michael@0 496 }
michael@0 497 #endif
michael@0 498 Rec* rec = id_to_rec(id);
michael@0 499 SkASSERT(rec->fLockCount > 0);
michael@0 500 rec->fLockCount -= 1;
michael@0 501
michael@0 502 // we may have been over-budget, but now have released something, so check
michael@0 503 // if we should purge.
michael@0 504 if (0 == rec->fLockCount) {
michael@0 505 this->purgeAsNeeded();
michael@0 506 }
michael@0 507 }
michael@0 508
michael@0 509 void SkScaledImageCache::purgeAsNeeded() {
michael@0 510 size_t byteLimit;
michael@0 511 int countLimit;
michael@0 512
michael@0 513 if (fDiscardableFactory) {
michael@0 514 countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
michael@0 515 byteLimit = SK_MaxU32; // no limit based on bytes
michael@0 516 } else {
michael@0 517 countLimit = SK_MaxS32; // no limit based on count
michael@0 518 byteLimit = fByteLimit;
michael@0 519 }
michael@0 520
michael@0 521 size_t bytesUsed = fBytesUsed;
michael@0 522 int countUsed = fCount;
michael@0 523
michael@0 524 Rec* rec = fTail;
michael@0 525 while (rec) {
michael@0 526 if (bytesUsed < byteLimit && countUsed < countLimit) {
michael@0 527 break;
michael@0 528 }
michael@0 529
michael@0 530 Rec* prev = rec->fPrev;
michael@0 531 if (0 == rec->fLockCount) {
michael@0 532 size_t used = rec->bytesUsed();
michael@0 533 SkASSERT(used <= bytesUsed);
michael@0 534 this->detach(rec);
michael@0 535 #ifdef USE_HASH
michael@0 536 fHash->remove(rec->fKey);
michael@0 537 #endif
michael@0 538
michael@0 539 SkDELETE(rec);
michael@0 540
michael@0 541 bytesUsed -= used;
michael@0 542 countUsed -= 1;
michael@0 543 }
michael@0 544 rec = prev;
michael@0 545 }
michael@0 546
michael@0 547 fBytesUsed = bytesUsed;
michael@0 548 fCount = countUsed;
michael@0 549 }
michael@0 550
michael@0 551 size_t SkScaledImageCache::setByteLimit(size_t newLimit) {
michael@0 552 size_t prevLimit = fByteLimit;
michael@0 553 fByteLimit = newLimit;
michael@0 554 if (newLimit < prevLimit) {
michael@0 555 this->purgeAsNeeded();
michael@0 556 }
michael@0 557 return prevLimit;
michael@0 558 }
michael@0 559
michael@0 560 ///////////////////////////////////////////////////////////////////////////////
michael@0 561
michael@0 562 void SkScaledImageCache::detach(Rec* rec) {
michael@0 563 Rec* prev = rec->fPrev;
michael@0 564 Rec* next = rec->fNext;
michael@0 565
michael@0 566 if (!prev) {
michael@0 567 SkASSERT(fHead == rec);
michael@0 568 fHead = next;
michael@0 569 } else {
michael@0 570 prev->fNext = next;
michael@0 571 }
michael@0 572
michael@0 573 if (!next) {
michael@0 574 fTail = prev;
michael@0 575 } else {
michael@0 576 next->fPrev = prev;
michael@0 577 }
michael@0 578
michael@0 579 rec->fNext = rec->fPrev = NULL;
michael@0 580 }
michael@0 581
michael@0 582 void SkScaledImageCache::moveToHead(Rec* rec) {
michael@0 583 if (fHead == rec) {
michael@0 584 return;
michael@0 585 }
michael@0 586
michael@0 587 SkASSERT(fHead);
michael@0 588 SkASSERT(fTail);
michael@0 589
michael@0 590 this->validate();
michael@0 591
michael@0 592 this->detach(rec);
michael@0 593
michael@0 594 fHead->fPrev = rec;
michael@0 595 rec->fNext = fHead;
michael@0 596 fHead = rec;
michael@0 597
michael@0 598 this->validate();
michael@0 599 }
michael@0 600
michael@0 601 void SkScaledImageCache::addToHead(Rec* rec) {
michael@0 602 this->validate();
michael@0 603
michael@0 604 rec->fPrev = NULL;
michael@0 605 rec->fNext = fHead;
michael@0 606 if (fHead) {
michael@0 607 fHead->fPrev = rec;
michael@0 608 }
michael@0 609 fHead = rec;
michael@0 610 if (!fTail) {
michael@0 611 fTail = rec;
michael@0 612 }
michael@0 613 fBytesUsed += rec->bytesUsed();
michael@0 614 fCount += 1;
michael@0 615
michael@0 616 this->validate();
michael@0 617 }
michael@0 618
michael@0 619 ///////////////////////////////////////////////////////////////////////////////
michael@0 620
michael@0 621 #ifdef SK_DEBUG
michael@0 622 void SkScaledImageCache::validate() const {
michael@0 623 if (NULL == fHead) {
michael@0 624 SkASSERT(NULL == fTail);
michael@0 625 SkASSERT(0 == fBytesUsed);
michael@0 626 return;
michael@0 627 }
michael@0 628
michael@0 629 if (fHead == fTail) {
michael@0 630 SkASSERT(NULL == fHead->fPrev);
michael@0 631 SkASSERT(NULL == fHead->fNext);
michael@0 632 SkASSERT(fHead->bytesUsed() == fBytesUsed);
michael@0 633 return;
michael@0 634 }
michael@0 635
michael@0 636 SkASSERT(NULL == fHead->fPrev);
michael@0 637 SkASSERT(NULL != fHead->fNext);
michael@0 638 SkASSERT(NULL == fTail->fNext);
michael@0 639 SkASSERT(NULL != fTail->fPrev);
michael@0 640
michael@0 641 size_t used = 0;
michael@0 642 int count = 0;
michael@0 643 const Rec* rec = fHead;
michael@0 644 while (rec) {
michael@0 645 count += 1;
michael@0 646 used += rec->bytesUsed();
michael@0 647 SkASSERT(used <= fBytesUsed);
michael@0 648 rec = rec->fNext;
michael@0 649 }
michael@0 650 SkASSERT(fCount == count);
michael@0 651
michael@0 652 rec = fTail;
michael@0 653 while (rec) {
michael@0 654 SkASSERT(count > 0);
michael@0 655 count -= 1;
michael@0 656 SkASSERT(used >= rec->bytesUsed());
michael@0 657 used -= rec->bytesUsed();
michael@0 658 rec = rec->fPrev;
michael@0 659 }
michael@0 660
michael@0 661 SkASSERT(0 == count);
michael@0 662 SkASSERT(0 == used);
michael@0 663 }
michael@0 664 #endif
michael@0 665
michael@0 666 void SkScaledImageCache::dump() const {
michael@0 667 this->validate();
michael@0 668
michael@0 669 const Rec* rec = fHead;
michael@0 670 int locked = 0;
michael@0 671 while (rec) {
michael@0 672 locked += rec->fLockCount > 0;
michael@0 673 rec = rec->fNext;
michael@0 674 }
michael@0 675
michael@0 676 SkDebugf("SkScaledImageCache: count=%d bytes=%d locked=%d %s\n",
michael@0 677 fCount, fBytesUsed, locked,
michael@0 678 fDiscardableFactory ? "discardable" : "malloc");
michael@0 679 }
michael@0 680
michael@0 681 ///////////////////////////////////////////////////////////////////////////////
michael@0 682
michael@0 683 #include "SkThread.h"
michael@0 684
michael@0 685 SK_DECLARE_STATIC_MUTEX(gMutex);
michael@0 686 static SkScaledImageCache* gScaledImageCache = NULL;
michael@0 687 static void cleanup_gScaledImageCache() { SkDELETE(gScaledImageCache); }
michael@0 688
michael@0 689 static void create_cache(int) {
michael@0 690 #ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
michael@0 691 gScaledImageCache = SkNEW_ARGS(SkScaledImageCache, (SkDiscardableMemory::Create));
michael@0 692 #else
michael@0 693 gScaledImageCache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
michael@0 694 #endif
michael@0 695 }
michael@0 696
michael@0 697 static SkScaledImageCache* get_cache() {
michael@0 698 SK_DECLARE_STATIC_ONCE(once);
michael@0 699 SkOnce(&once, create_cache, 0, cleanup_gScaledImageCache);
michael@0 700 SkASSERT(NULL != gScaledImageCache);
michael@0 701 return gScaledImageCache;
michael@0 702 }
michael@0 703
michael@0 704
michael@0 705 SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(
michael@0 706 uint32_t pixelGenerationID,
michael@0 707 int32_t width,
michael@0 708 int32_t height,
michael@0 709 SkBitmap* scaled) {
michael@0 710 SkAutoMutexAcquire am(gMutex);
michael@0 711 return get_cache()->findAndLock(pixelGenerationID, width, height, scaled);
michael@0 712 }
michael@0 713
michael@0 714 SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(
michael@0 715 uint32_t pixelGenerationID,
michael@0 716 int32_t width,
michael@0 717 int32_t height,
michael@0 718 const SkBitmap& scaled) {
michael@0 719 SkAutoMutexAcquire am(gMutex);
michael@0 720 return get_cache()->addAndLock(pixelGenerationID, width, height, scaled);
michael@0 721 }
michael@0 722
michael@0 723
michael@0 724 SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(const SkBitmap& orig,
michael@0 725 SkScalar scaleX,
michael@0 726 SkScalar scaleY,
michael@0 727 SkBitmap* scaled) {
michael@0 728 SkAutoMutexAcquire am(gMutex);
michael@0 729 return get_cache()->findAndLock(orig, scaleX, scaleY, scaled);
michael@0 730 }
michael@0 731
michael@0 732 SkScaledImageCache::ID* SkScaledImageCache::FindAndLockMip(const SkBitmap& orig,
michael@0 733 SkMipMap const ** mip) {
michael@0 734 SkAutoMutexAcquire am(gMutex);
michael@0 735 return get_cache()->findAndLockMip(orig, mip);
michael@0 736 }
michael@0 737
michael@0 738 SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(const SkBitmap& orig,
michael@0 739 SkScalar scaleX,
michael@0 740 SkScalar scaleY,
michael@0 741 const SkBitmap& scaled) {
michael@0 742 SkAutoMutexAcquire am(gMutex);
michael@0 743 return get_cache()->addAndLock(orig, scaleX, scaleY, scaled);
michael@0 744 }
michael@0 745
michael@0 746 SkScaledImageCache::ID* SkScaledImageCache::AddAndLockMip(const SkBitmap& orig,
michael@0 747 const SkMipMap* mip) {
michael@0 748 SkAutoMutexAcquire am(gMutex);
michael@0 749 return get_cache()->addAndLockMip(orig, mip);
michael@0 750 }
michael@0 751
michael@0 752 void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) {
michael@0 753 SkAutoMutexAcquire am(gMutex);
michael@0 754 get_cache()->unlock(id);
michael@0 755
michael@0 756 // get_cache()->dump();
michael@0 757 }
michael@0 758
michael@0 759 size_t SkScaledImageCache::GetBytesUsed() {
michael@0 760 SkAutoMutexAcquire am(gMutex);
michael@0 761 return get_cache()->getBytesUsed();
michael@0 762 }
michael@0 763
michael@0 764 size_t SkScaledImageCache::GetByteLimit() {
michael@0 765 SkAutoMutexAcquire am(gMutex);
michael@0 766 return get_cache()->getByteLimit();
michael@0 767 }
michael@0 768
michael@0 769 size_t SkScaledImageCache::SetByteLimit(size_t newLimit) {
michael@0 770 SkAutoMutexAcquire am(gMutex);
michael@0 771 return get_cache()->setByteLimit(newLimit);
michael@0 772 }
michael@0 773
michael@0 774 SkBitmap::Allocator* SkScaledImageCache::GetAllocator() {
michael@0 775 SkAutoMutexAcquire am(gMutex);
michael@0 776 return get_cache()->allocator();
michael@0 777 }
michael@0 778
michael@0 779 void SkScaledImageCache::Dump() {
michael@0 780 SkAutoMutexAcquire am(gMutex);
michael@0 781 get_cache()->dump();
michael@0 782 }
michael@0 783
michael@0 784 ///////////////////////////////////////////////////////////////////////////////
michael@0 785
michael@0 786 #include "SkGraphics.h"
michael@0 787
michael@0 788 size_t SkGraphics::GetImageCacheBytesUsed() {
michael@0 789 return SkScaledImageCache::GetBytesUsed();
michael@0 790 }
michael@0 791
michael@0 792 size_t SkGraphics::GetImageCacheByteLimit() {
michael@0 793 return SkScaledImageCache::GetByteLimit();
michael@0 794 }
michael@0 795
michael@0 796 size_t SkGraphics::SetImageCacheByteLimit(size_t newLimit) {
michael@0 797 return SkScaledImageCache::SetByteLimit(newLimit);
michael@0 798 }

mercurial