Sat, 03 Jan 2015 20:18:00 +0100
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 | /* |
michael@0 | 3 | * Copyright 2006 The Android Open Source Project |
michael@0 | 4 | * |
michael@0 | 5 | * Use of this source code is governed by a BSD-style license that can be |
michael@0 | 6 | * found in the LICENSE file. |
michael@0 | 7 | */ |
michael@0 | 8 | |
michael@0 | 9 | |
michael@0 | 10 | #include "SkGlyphCache.h" |
michael@0 | 11 | #include "SkGlyphCache_Globals.h" |
michael@0 | 12 | #include "SkGraphics.h" |
michael@0 | 13 | #include "SkPaint.h" |
michael@0 | 14 | #include "SkPath.h" |
michael@0 | 15 | #include "SkTemplates.h" |
michael@0 | 16 | #include "SkTLS.h" |
michael@0 | 17 | #include "SkTypeface.h" |
michael@0 | 18 | |
michael@0 | 19 | //#define SPEW_PURGE_STATUS |
michael@0 | 20 | //#define RECORD_HASH_EFFICIENCY |
michael@0 | 21 | |
michael@0 | 22 | bool gSkSuppressFontCachePurgeSpew; |
michael@0 | 23 | |
michael@0 | 24 | // Returns the shared globals |
michael@0 | 25 | static SkGlyphCache_Globals& getSharedGlobals() { |
michael@0 | 26 | // we leak this, so we don't incur any shutdown cost of the destructor |
michael@0 | 27 | static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals, |
michael@0 | 28 | (SkGlyphCache_Globals::kYes_UseMutex)); |
michael@0 | 29 | return *gGlobals; |
michael@0 | 30 | } |
michael@0 | 31 | |
michael@0 | 32 | // Returns the TLS globals (if set), or the shared globals |
michael@0 | 33 | static SkGlyphCache_Globals& getGlobals() { |
michael@0 | 34 | SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS(); |
michael@0 | 35 | return tls ? *tls : getSharedGlobals(); |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 39 | |
michael@0 | 40 | #ifdef RECORD_HASH_EFFICIENCY |
michael@0 | 41 | static uint32_t gHashSuccess; |
michael@0 | 42 | static uint32_t gHashCollision; |
michael@0 | 43 | |
michael@0 | 44 | static void RecordHashSuccess() { |
michael@0 | 45 | gHashSuccess += 1; |
michael@0 | 46 | } |
michael@0 | 47 | |
michael@0 | 48 | static void RecordHashCollisionIf(bool pred) { |
michael@0 | 49 | if (pred) { |
michael@0 | 50 | gHashCollision += 1; |
michael@0 | 51 | |
michael@0 | 52 | uint32_t total = gHashSuccess + gHashCollision; |
michael@0 | 53 | SkDebugf("Font Cache Hash success rate: %d%%\n", |
michael@0 | 54 | 100 * gHashSuccess / total); |
michael@0 | 55 | } |
michael@0 | 56 | } |
michael@0 | 57 | #else |
michael@0 | 58 | #define RecordHashSuccess() (void)0 |
michael@0 | 59 | #define RecordHashCollisionIf(pred) (void)0 |
michael@0 | 60 | #endif |
michael@0 | 61 | #define RecordHashCollision() RecordHashCollisionIf(true) |
michael@0 | 62 | |
michael@0 | 63 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 64 | |
michael@0 | 65 | // so we don't grow our arrays a lot |
michael@0 | 66 | #define kMinGlyphCount 16 |
michael@0 | 67 | #define kMinGlyphImageSize (16*2) |
michael@0 | 68 | #define kMinAllocAmount ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount) |
michael@0 | 69 | |
michael@0 | 70 | SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkScalerContext* ctx) |
michael@0 | 71 | : fScalerContext(ctx), fGlyphAlloc(kMinAllocAmount) { |
michael@0 | 72 | SkASSERT(typeface); |
michael@0 | 73 | SkASSERT(desc); |
michael@0 | 74 | SkASSERT(ctx); |
michael@0 | 75 | |
michael@0 | 76 | fPrev = fNext = NULL; |
michael@0 | 77 | |
michael@0 | 78 | fDesc = desc->copy(); |
michael@0 | 79 | fScalerContext->getFontMetrics(&fFontMetrics); |
michael@0 | 80 | |
michael@0 | 81 | // init to 0 so that all of the pointers will be null |
michael@0 | 82 | memset(fGlyphHash, 0, sizeof(fGlyphHash)); |
michael@0 | 83 | // init with 0xFF so that the charCode field will be -1, which is invalid |
michael@0 | 84 | memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash)); |
michael@0 | 85 | |
michael@0 | 86 | fMemoryUsed = sizeof(*this); |
michael@0 | 87 | |
michael@0 | 88 | fGlyphArray.setReserve(kMinGlyphCount); |
michael@0 | 89 | |
michael@0 | 90 | fAuxProcList = NULL; |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | SkGlyphCache::~SkGlyphCache() { |
michael@0 | 94 | #if 0 |
michael@0 | 95 | { |
michael@0 | 96 | size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*); |
michael@0 | 97 | size_t glyphAlloc = fGlyphAlloc.totalCapacity(); |
michael@0 | 98 | size_t glyphHashUsed = 0; |
michael@0 | 99 | size_t uniHashUsed = 0; |
michael@0 | 100 | for (int i = 0; i < kHashCount; ++i) { |
michael@0 | 101 | glyphHashUsed += fGlyphHash[i] ? sizeof(fGlyphHash[0]) : 0; |
michael@0 | 102 | uniHashUsed += fCharToGlyphHash[i].fID != 0xFFFFFFFF ? sizeof(fCharToGlyphHash[0]) : 0; |
michael@0 | 103 | } |
michael@0 | 104 | size_t glyphUsed = fGlyphArray.count() * sizeof(SkGlyph); |
michael@0 | 105 | size_t imageUsed = 0; |
michael@0 | 106 | for (int i = 0; i < fGlyphArray.count(); ++i) { |
michael@0 | 107 | const SkGlyph& g = *fGlyphArray[i]; |
michael@0 | 108 | if (g.fImage) { |
michael@0 | 109 | imageUsed += g.fHeight * g.rowBytes(); |
michael@0 | 110 | } |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | printf("glyphPtrArray,%zu, Alloc,%zu, imageUsed,%zu, glyphUsed,%zu, glyphHashAlloc,%zu, glyphHashUsed,%zu, unicharHashAlloc,%zu, unicharHashUsed,%zu\n", |
michael@0 | 114 | ptrMem, glyphAlloc, imageUsed, glyphUsed, sizeof(fGlyphHash), glyphHashUsed, sizeof(fCharToGlyphHash), uniHashUsed); |
michael@0 | 115 | |
michael@0 | 116 | } |
michael@0 | 117 | #endif |
michael@0 | 118 | SkGlyph** gptr = fGlyphArray.begin(); |
michael@0 | 119 | SkGlyph** stop = fGlyphArray.end(); |
michael@0 | 120 | while (gptr < stop) { |
michael@0 | 121 | SkPath* path = (*gptr)->fPath; |
michael@0 | 122 | if (path) { |
michael@0 | 123 | SkDELETE(path); |
michael@0 | 124 | } |
michael@0 | 125 | gptr += 1; |
michael@0 | 126 | } |
michael@0 | 127 | SkDescriptor::Free(fDesc); |
michael@0 | 128 | SkDELETE(fScalerContext); |
michael@0 | 129 | this->invokeAndRemoveAuxProcs(); |
michael@0 | 130 | } |
michael@0 | 131 | |
michael@0 | 132 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 133 | |
michael@0 | 134 | #ifdef SK_DEBUG |
michael@0 | 135 | #define VALIDATE() AutoValidate av(this) |
michael@0 | 136 | #else |
michael@0 | 137 | #define VALIDATE() |
michael@0 | 138 | #endif |
michael@0 | 139 | |
michael@0 | 140 | uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) { |
michael@0 | 141 | VALIDATE(); |
michael@0 | 142 | uint32_t id = SkGlyph::MakeID(charCode); |
michael@0 | 143 | const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)]; |
michael@0 | 144 | |
michael@0 | 145 | if (rec.fID == id) { |
michael@0 | 146 | return rec.fGlyph->getGlyphID(); |
michael@0 | 147 | } else { |
michael@0 | 148 | return fScalerContext->charToGlyphID(charCode); |
michael@0 | 149 | } |
michael@0 | 150 | } |
michael@0 | 151 | |
michael@0 | 152 | SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) { |
michael@0 | 153 | return fScalerContext->glyphIDToChar(glyphID); |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | unsigned SkGlyphCache::getGlyphCount() { |
michael@0 | 157 | return fScalerContext->getGlyphCount(); |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 161 | |
michael@0 | 162 | const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) { |
michael@0 | 163 | VALIDATE(); |
michael@0 | 164 | uint32_t id = SkGlyph::MakeID(charCode); |
michael@0 | 165 | CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; |
michael@0 | 166 | |
michael@0 | 167 | if (rec->fID != id) { |
michael@0 | 168 | // this ID is based on the UniChar |
michael@0 | 169 | rec->fID = id; |
michael@0 | 170 | // this ID is based on the glyph index |
michael@0 | 171 | id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode)); |
michael@0 | 172 | rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType); |
michael@0 | 173 | } |
michael@0 | 174 | return *rec->fGlyph; |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) { |
michael@0 | 178 | VALIDATE(); |
michael@0 | 179 | uint32_t id = SkGlyph::MakeID(glyphID); |
michael@0 | 180 | unsigned index = ID2HashIndex(id); |
michael@0 | 181 | SkGlyph* glyph = fGlyphHash[index]; |
michael@0 | 182 | |
michael@0 | 183 | if (NULL == glyph || glyph->fID != id) { |
michael@0 | 184 | glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType); |
michael@0 | 185 | fGlyphHash[index] = glyph; |
michael@0 | 186 | } |
michael@0 | 187 | return *glyph; |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 191 | |
michael@0 | 192 | const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) { |
michael@0 | 193 | VALIDATE(); |
michael@0 | 194 | uint32_t id = SkGlyph::MakeID(charCode); |
michael@0 | 195 | CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; |
michael@0 | 196 | |
michael@0 | 197 | if (rec->fID != id) { |
michael@0 | 198 | RecordHashCollisionIf(rec->fGlyph != NULL); |
michael@0 | 199 | // this ID is based on the UniChar |
michael@0 | 200 | rec->fID = id; |
michael@0 | 201 | // this ID is based on the glyph index |
michael@0 | 202 | id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode)); |
michael@0 | 203 | rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType); |
michael@0 | 204 | } else { |
michael@0 | 205 | RecordHashSuccess(); |
michael@0 | 206 | if (rec->fGlyph->isJustAdvance()) { |
michael@0 | 207 | fScalerContext->getMetrics(rec->fGlyph); |
michael@0 | 208 | } |
michael@0 | 209 | } |
michael@0 | 210 | SkASSERT(rec->fGlyph->isFullMetrics()); |
michael@0 | 211 | return *rec->fGlyph; |
michael@0 | 212 | } |
michael@0 | 213 | |
michael@0 | 214 | const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode, |
michael@0 | 215 | SkFixed x, SkFixed y) { |
michael@0 | 216 | VALIDATE(); |
michael@0 | 217 | uint32_t id = SkGlyph::MakeID(charCode, x, y); |
michael@0 | 218 | CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; |
michael@0 | 219 | |
michael@0 | 220 | if (rec->fID != id) { |
michael@0 | 221 | RecordHashCollisionIf(rec->fGlyph != NULL); |
michael@0 | 222 | // this ID is based on the UniChar |
michael@0 | 223 | rec->fID = id; |
michael@0 | 224 | // this ID is based on the glyph index |
michael@0 | 225 | id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y); |
michael@0 | 226 | rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType); |
michael@0 | 227 | } else { |
michael@0 | 228 | RecordHashSuccess(); |
michael@0 | 229 | if (rec->fGlyph->isJustAdvance()) { |
michael@0 | 230 | fScalerContext->getMetrics(rec->fGlyph); |
michael@0 | 231 | } |
michael@0 | 232 | } |
michael@0 | 233 | SkASSERT(rec->fGlyph->isFullMetrics()); |
michael@0 | 234 | return *rec->fGlyph; |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) { |
michael@0 | 238 | VALIDATE(); |
michael@0 | 239 | uint32_t id = SkGlyph::MakeID(glyphID); |
michael@0 | 240 | unsigned index = ID2HashIndex(id); |
michael@0 | 241 | SkGlyph* glyph = fGlyphHash[index]; |
michael@0 | 242 | |
michael@0 | 243 | if (NULL == glyph || glyph->fID != id) { |
michael@0 | 244 | RecordHashCollisionIf(glyph != NULL); |
michael@0 | 245 | glyph = this->lookupMetrics(glyphID, kFull_MetricsType); |
michael@0 | 246 | fGlyphHash[index] = glyph; |
michael@0 | 247 | } else { |
michael@0 | 248 | RecordHashSuccess(); |
michael@0 | 249 | if (glyph->isJustAdvance()) { |
michael@0 | 250 | fScalerContext->getMetrics(glyph); |
michael@0 | 251 | } |
michael@0 | 252 | } |
michael@0 | 253 | SkASSERT(glyph->isFullMetrics()); |
michael@0 | 254 | return *glyph; |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, |
michael@0 | 258 | SkFixed x, SkFixed y) { |
michael@0 | 259 | VALIDATE(); |
michael@0 | 260 | uint32_t id = SkGlyph::MakeID(glyphID, x, y); |
michael@0 | 261 | unsigned index = ID2HashIndex(id); |
michael@0 | 262 | SkGlyph* glyph = fGlyphHash[index]; |
michael@0 | 263 | |
michael@0 | 264 | if (NULL == glyph || glyph->fID != id) { |
michael@0 | 265 | RecordHashCollisionIf(glyph != NULL); |
michael@0 | 266 | glyph = this->lookupMetrics(id, kFull_MetricsType); |
michael@0 | 267 | fGlyphHash[index] = glyph; |
michael@0 | 268 | } else { |
michael@0 | 269 | RecordHashSuccess(); |
michael@0 | 270 | if (glyph->isJustAdvance()) { |
michael@0 | 271 | fScalerContext->getMetrics(glyph); |
michael@0 | 272 | } |
michael@0 | 273 | } |
michael@0 | 274 | SkASSERT(glyph->isFullMetrics()); |
michael@0 | 275 | return *glyph; |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) { |
michael@0 | 279 | SkGlyph* glyph; |
michael@0 | 280 | |
michael@0 | 281 | int hi = 0; |
michael@0 | 282 | int count = fGlyphArray.count(); |
michael@0 | 283 | |
michael@0 | 284 | if (count) { |
michael@0 | 285 | SkGlyph** gptr = fGlyphArray.begin(); |
michael@0 | 286 | int lo = 0; |
michael@0 | 287 | |
michael@0 | 288 | hi = count - 1; |
michael@0 | 289 | while (lo < hi) { |
michael@0 | 290 | int mid = (hi + lo) >> 1; |
michael@0 | 291 | if (gptr[mid]->fID < id) { |
michael@0 | 292 | lo = mid + 1; |
michael@0 | 293 | } else { |
michael@0 | 294 | hi = mid; |
michael@0 | 295 | } |
michael@0 | 296 | } |
michael@0 | 297 | glyph = gptr[hi]; |
michael@0 | 298 | if (glyph->fID == id) { |
michael@0 | 299 | if (kFull_MetricsType == mtype && glyph->isJustAdvance()) { |
michael@0 | 300 | fScalerContext->getMetrics(glyph); |
michael@0 | 301 | } |
michael@0 | 302 | return glyph; |
michael@0 | 303 | } |
michael@0 | 304 | |
michael@0 | 305 | // check if we need to bump hi before falling though to the allocator |
michael@0 | 306 | if (glyph->fID < id) { |
michael@0 | 307 | hi += 1; |
michael@0 | 308 | } |
michael@0 | 309 | } |
michael@0 | 310 | |
michael@0 | 311 | // not found, but hi tells us where to inser the new glyph |
michael@0 | 312 | fMemoryUsed += sizeof(SkGlyph); |
michael@0 | 313 | |
michael@0 | 314 | glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph), |
michael@0 | 315 | SkChunkAlloc::kThrow_AllocFailType); |
michael@0 | 316 | glyph->init(id); |
michael@0 | 317 | *fGlyphArray.insert(hi) = glyph; |
michael@0 | 318 | |
michael@0 | 319 | if (kJustAdvance_MetricsType == mtype) { |
michael@0 | 320 | fScalerContext->getAdvance(glyph); |
michael@0 | 321 | } else { |
michael@0 | 322 | SkASSERT(kFull_MetricsType == mtype); |
michael@0 | 323 | fScalerContext->getMetrics(glyph); |
michael@0 | 324 | } |
michael@0 | 325 | |
michael@0 | 326 | return glyph; |
michael@0 | 327 | } |
michael@0 | 328 | |
michael@0 | 329 | const void* SkGlyphCache::findImage(const SkGlyph& glyph) { |
michael@0 | 330 | if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) { |
michael@0 | 331 | if (glyph.fImage == NULL) { |
michael@0 | 332 | size_t size = glyph.computeImageSize(); |
michael@0 | 333 | const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size, |
michael@0 | 334 | SkChunkAlloc::kReturnNil_AllocFailType); |
michael@0 | 335 | // check that alloc() actually succeeded |
michael@0 | 336 | if (glyph.fImage) { |
michael@0 | 337 | fScalerContext->getImage(glyph); |
michael@0 | 338 | // TODO: the scaler may have changed the maskformat during |
michael@0 | 339 | // getImage (e.g. from AA or LCD to BW) which means we may have |
michael@0 | 340 | // overallocated the buffer. Check if the new computedImageSize |
michael@0 | 341 | // is smaller, and if so, strink the alloc size in fImageAlloc. |
michael@0 | 342 | fMemoryUsed += size; |
michael@0 | 343 | } |
michael@0 | 344 | } |
michael@0 | 345 | } |
michael@0 | 346 | return glyph.fImage; |
michael@0 | 347 | } |
michael@0 | 348 | |
michael@0 | 349 | const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { |
michael@0 | 350 | if (glyph.fWidth) { |
michael@0 | 351 | if (glyph.fPath == NULL) { |
michael@0 | 352 | const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath); |
michael@0 | 353 | fScalerContext->getPath(glyph, glyph.fPath); |
michael@0 | 354 | fMemoryUsed += sizeof(SkPath) + |
michael@0 | 355 | glyph.fPath->countPoints() * sizeof(SkPoint); |
michael@0 | 356 | } |
michael@0 | 357 | } |
michael@0 | 358 | return glyph.fPath; |
michael@0 | 359 | } |
michael@0 | 360 | |
michael@0 | 361 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 362 | |
michael@0 | 363 | bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const { |
michael@0 | 364 | const AuxProcRec* rec = fAuxProcList; |
michael@0 | 365 | while (rec) { |
michael@0 | 366 | if (rec->fProc == proc) { |
michael@0 | 367 | if (dataPtr) { |
michael@0 | 368 | *dataPtr = rec->fData; |
michael@0 | 369 | } |
michael@0 | 370 | return true; |
michael@0 | 371 | } |
michael@0 | 372 | rec = rec->fNext; |
michael@0 | 373 | } |
michael@0 | 374 | return false; |
michael@0 | 375 | } |
michael@0 | 376 | |
michael@0 | 377 | void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) { |
michael@0 | 378 | if (proc == NULL) { |
michael@0 | 379 | return; |
michael@0 | 380 | } |
michael@0 | 381 | |
michael@0 | 382 | AuxProcRec* rec = fAuxProcList; |
michael@0 | 383 | while (rec) { |
michael@0 | 384 | if (rec->fProc == proc) { |
michael@0 | 385 | rec->fData = data; |
michael@0 | 386 | return; |
michael@0 | 387 | } |
michael@0 | 388 | rec = rec->fNext; |
michael@0 | 389 | } |
michael@0 | 390 | // not found, create a new rec |
michael@0 | 391 | rec = SkNEW(AuxProcRec); |
michael@0 | 392 | rec->fProc = proc; |
michael@0 | 393 | rec->fData = data; |
michael@0 | 394 | rec->fNext = fAuxProcList; |
michael@0 | 395 | fAuxProcList = rec; |
michael@0 | 396 | } |
michael@0 | 397 | |
michael@0 | 398 | void SkGlyphCache::invokeAndRemoveAuxProcs() { |
michael@0 | 399 | AuxProcRec* rec = fAuxProcList; |
michael@0 | 400 | while (rec) { |
michael@0 | 401 | rec->fProc(rec->fData); |
michael@0 | 402 | AuxProcRec* next = rec->fNext; |
michael@0 | 403 | SkDELETE(rec); |
michael@0 | 404 | rec = next; |
michael@0 | 405 | } |
michael@0 | 406 | } |
michael@0 | 407 | |
michael@0 | 408 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 409 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 410 | |
michael@0 | 411 | #include "SkThread.h" |
michael@0 | 412 | |
michael@0 | 413 | size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) { |
michael@0 | 414 | static const size_t minLimit = 256 * 1024; |
michael@0 | 415 | if (newLimit < minLimit) { |
michael@0 | 416 | newLimit = minLimit; |
michael@0 | 417 | } |
michael@0 | 418 | |
michael@0 | 419 | SkAutoMutexAcquire ac(fMutex); |
michael@0 | 420 | |
michael@0 | 421 | size_t prevLimit = fCacheSizeLimit; |
michael@0 | 422 | fCacheSizeLimit = newLimit; |
michael@0 | 423 | this->internalPurge(); |
michael@0 | 424 | return prevLimit; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | int SkGlyphCache_Globals::setCacheCountLimit(int newCount) { |
michael@0 | 428 | if (newCount < 0) { |
michael@0 | 429 | newCount = 0; |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | SkAutoMutexAcquire ac(fMutex); |
michael@0 | 433 | |
michael@0 | 434 | int prevCount = fCacheCountLimit; |
michael@0 | 435 | fCacheCountLimit = newCount; |
michael@0 | 436 | this->internalPurge(); |
michael@0 | 437 | return prevCount; |
michael@0 | 438 | } |
michael@0 | 439 | |
michael@0 | 440 | void SkGlyphCache_Globals::purgeAll() { |
michael@0 | 441 | SkAutoMutexAcquire ac(fMutex); |
michael@0 | 442 | this->internalPurge(fTotalMemoryUsed); |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), |
michael@0 | 446 | void* context) { |
michael@0 | 447 | SkGlyphCache_Globals& globals = getGlobals(); |
michael@0 | 448 | SkAutoMutexAcquire ac(globals.fMutex); |
michael@0 | 449 | SkGlyphCache* cache; |
michael@0 | 450 | |
michael@0 | 451 | globals.validate(); |
michael@0 | 452 | |
michael@0 | 453 | for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) { |
michael@0 | 454 | if (proc(cache, context)) { |
michael@0 | 455 | break; |
michael@0 | 456 | } |
michael@0 | 457 | } |
michael@0 | 458 | |
michael@0 | 459 | globals.validate(); |
michael@0 | 460 | } |
michael@0 | 461 | |
michael@0 | 462 | /* This guy calls the visitor from within the mutext lock, so the visitor |
michael@0 | 463 | cannot: |
michael@0 | 464 | - take too much time |
michael@0 | 465 | - try to acquire the mutext again |
michael@0 | 466 | - call a fontscaler (which might call into the cache) |
michael@0 | 467 | */ |
michael@0 | 468 | SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface, |
michael@0 | 469 | const SkDescriptor* desc, |
michael@0 | 470 | bool (*proc)(const SkGlyphCache*, void*), |
michael@0 | 471 | void* context) { |
michael@0 | 472 | if (!typeface) { |
michael@0 | 473 | typeface = SkTypeface::GetDefaultTypeface(); |
michael@0 | 474 | } |
michael@0 | 475 | SkASSERT(desc); |
michael@0 | 476 | |
michael@0 | 477 | SkGlyphCache_Globals& globals = getGlobals(); |
michael@0 | 478 | SkAutoMutexAcquire ac(globals.fMutex); |
michael@0 | 479 | SkGlyphCache* cache; |
michael@0 | 480 | bool insideMutex = true; |
michael@0 | 481 | |
michael@0 | 482 | globals.validate(); |
michael@0 | 483 | |
michael@0 | 484 | for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) { |
michael@0 | 485 | if (cache->fDesc->equals(*desc)) { |
michael@0 | 486 | globals.internalDetachCache(cache); |
michael@0 | 487 | goto FOUND_IT; |
michael@0 | 488 | } |
michael@0 | 489 | } |
michael@0 | 490 | |
michael@0 | 491 | /* Release the mutex now, before we create a new entry (which might have |
michael@0 | 492 | side-effects like trying to access the cache/mutex (yikes!) |
michael@0 | 493 | */ |
michael@0 | 494 | ac.release(); // release the mutex now |
michael@0 | 495 | insideMutex = false; // can't use globals anymore |
michael@0 | 496 | |
michael@0 | 497 | // Check if we can create a scaler-context before creating the glyphcache. |
michael@0 | 498 | // If not, we may have exhausted OS/font resources, so try purging the |
michael@0 | 499 | // cache once and try again. |
michael@0 | 500 | { |
michael@0 | 501 | // pass true the first time, to notice if the scalercontext failed, |
michael@0 | 502 | // so we can try the purge. |
michael@0 | 503 | SkScalerContext* ctx = typeface->createScalerContext(desc, true); |
michael@0 | 504 | if (!ctx) { |
michael@0 | 505 | getSharedGlobals().purgeAll(); |
michael@0 | 506 | ctx = typeface->createScalerContext(desc, false); |
michael@0 | 507 | SkASSERT(ctx); |
michael@0 | 508 | } |
michael@0 | 509 | cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx)); |
michael@0 | 510 | } |
michael@0 | 511 | |
michael@0 | 512 | FOUND_IT: |
michael@0 | 513 | |
michael@0 | 514 | AutoValidate av(cache); |
michael@0 | 515 | |
michael@0 | 516 | if (!proc(cache, context)) { // need to reattach |
michael@0 | 517 | if (insideMutex) { |
michael@0 | 518 | globals.internalAttachCacheToHead(cache); |
michael@0 | 519 | } else { |
michael@0 | 520 | globals.attachCacheToHead(cache); |
michael@0 | 521 | } |
michael@0 | 522 | cache = NULL; |
michael@0 | 523 | } |
michael@0 | 524 | return cache; |
michael@0 | 525 | } |
michael@0 | 526 | |
michael@0 | 527 | void SkGlyphCache::AttachCache(SkGlyphCache* cache) { |
michael@0 | 528 | SkASSERT(cache); |
michael@0 | 529 | SkASSERT(cache->fNext == NULL); |
michael@0 | 530 | |
michael@0 | 531 | getGlobals().attachCacheToHead(cache); |
michael@0 | 532 | } |
michael@0 | 533 | |
michael@0 | 534 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 535 | |
michael@0 | 536 | void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) { |
michael@0 | 537 | SkAutoMutexAcquire ac(fMutex); |
michael@0 | 538 | |
michael@0 | 539 | this->validate(); |
michael@0 | 540 | cache->validate(); |
michael@0 | 541 | |
michael@0 | 542 | this->internalAttachCacheToHead(cache); |
michael@0 | 543 | this->internalPurge(); |
michael@0 | 544 | } |
michael@0 | 545 | |
michael@0 | 546 | SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const { |
michael@0 | 547 | SkGlyphCache* cache = fHead; |
michael@0 | 548 | if (cache) { |
michael@0 | 549 | while (cache->fNext) { |
michael@0 | 550 | cache = cache->fNext; |
michael@0 | 551 | } |
michael@0 | 552 | } |
michael@0 | 553 | return cache; |
michael@0 | 554 | } |
michael@0 | 555 | |
michael@0 | 556 | size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) { |
michael@0 | 557 | this->validate(); |
michael@0 | 558 | |
michael@0 | 559 | size_t bytesNeeded = 0; |
michael@0 | 560 | if (fTotalMemoryUsed > fCacheSizeLimit) { |
michael@0 | 561 | bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit; |
michael@0 | 562 | } |
michael@0 | 563 | bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded); |
michael@0 | 564 | if (bytesNeeded) { |
michael@0 | 565 | // no small purges! |
michael@0 | 566 | bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2); |
michael@0 | 567 | } |
michael@0 | 568 | |
michael@0 | 569 | int countNeeded = 0; |
michael@0 | 570 | if (fCacheCount > fCacheCountLimit) { |
michael@0 | 571 | countNeeded = fCacheCount - fCacheCountLimit; |
michael@0 | 572 | // no small purges! |
michael@0 | 573 | countNeeded = SkMax32(countNeeded, fCacheCount >> 2); |
michael@0 | 574 | } |
michael@0 | 575 | |
michael@0 | 576 | // early exit |
michael@0 | 577 | if (!countNeeded && !bytesNeeded) { |
michael@0 | 578 | return 0; |
michael@0 | 579 | } |
michael@0 | 580 | |
michael@0 | 581 | size_t bytesFreed = 0; |
michael@0 | 582 | int countFreed = 0; |
michael@0 | 583 | |
michael@0 | 584 | // we start at the tail and proceed backwards, as the linklist is in LRU |
michael@0 | 585 | // order, with unimportant entries at the tail. |
michael@0 | 586 | SkGlyphCache* cache = this->internalGetTail(); |
michael@0 | 587 | while (cache != NULL && |
michael@0 | 588 | (bytesFreed < bytesNeeded || countFreed < countNeeded)) { |
michael@0 | 589 | SkGlyphCache* prev = cache->fPrev; |
michael@0 | 590 | bytesFreed += cache->fMemoryUsed; |
michael@0 | 591 | countFreed += 1; |
michael@0 | 592 | |
michael@0 | 593 | this->internalDetachCache(cache); |
michael@0 | 594 | SkDELETE(cache); |
michael@0 | 595 | cache = prev; |
michael@0 | 596 | } |
michael@0 | 597 | |
michael@0 | 598 | this->validate(); |
michael@0 | 599 | |
michael@0 | 600 | #ifdef SPEW_PURGE_STATUS |
michael@0 | 601 | if (countFreed && !gSkSuppressFontCachePurgeSpew) { |
michael@0 | 602 | SkDebugf("purging %dK from font cache [%d entries]\n", |
michael@0 | 603 | (int)(bytesFreed >> 10), countFreed); |
michael@0 | 604 | } |
michael@0 | 605 | #endif |
michael@0 | 606 | |
michael@0 | 607 | return bytesFreed; |
michael@0 | 608 | } |
michael@0 | 609 | |
michael@0 | 610 | void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) { |
michael@0 | 611 | SkASSERT(NULL == cache->fPrev && NULL == cache->fNext); |
michael@0 | 612 | if (fHead) { |
michael@0 | 613 | fHead->fPrev = cache; |
michael@0 | 614 | cache->fNext = fHead; |
michael@0 | 615 | } |
michael@0 | 616 | fHead = cache; |
michael@0 | 617 | |
michael@0 | 618 | fCacheCount += 1; |
michael@0 | 619 | fTotalMemoryUsed += cache->fMemoryUsed; |
michael@0 | 620 | } |
michael@0 | 621 | |
michael@0 | 622 | void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) { |
michael@0 | 623 | SkASSERT(fCacheCount > 0); |
michael@0 | 624 | fCacheCount -= 1; |
michael@0 | 625 | fTotalMemoryUsed -= cache->fMemoryUsed; |
michael@0 | 626 | |
michael@0 | 627 | if (cache->fPrev) { |
michael@0 | 628 | cache->fPrev->fNext = cache->fNext; |
michael@0 | 629 | } else { |
michael@0 | 630 | fHead = cache->fNext; |
michael@0 | 631 | } |
michael@0 | 632 | if (cache->fNext) { |
michael@0 | 633 | cache->fNext->fPrev = cache->fPrev; |
michael@0 | 634 | } |
michael@0 | 635 | cache->fPrev = cache->fNext = NULL; |
michael@0 | 636 | } |
michael@0 | 637 | |
michael@0 | 638 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 639 | |
michael@0 | 640 | #ifdef SK_DEBUG |
michael@0 | 641 | |
michael@0 | 642 | void SkGlyphCache::validate() const { |
michael@0 | 643 | #ifdef SK_DEBUG_GLYPH_CACHE |
michael@0 | 644 | int count = fGlyphArray.count(); |
michael@0 | 645 | for (int i = 0; i < count; i++) { |
michael@0 | 646 | const SkGlyph* glyph = fGlyphArray[i]; |
michael@0 | 647 | SkASSERT(glyph); |
michael@0 | 648 | SkASSERT(fGlyphAlloc.contains(glyph)); |
michael@0 | 649 | if (glyph->fImage) { |
michael@0 | 650 | SkASSERT(fGlyphAlloc.contains(glyph->fImage)); |
michael@0 | 651 | } |
michael@0 | 652 | } |
michael@0 | 653 | #endif |
michael@0 | 654 | } |
michael@0 | 655 | |
michael@0 | 656 | void SkGlyphCache_Globals::validate() const { |
michael@0 | 657 | size_t computedBytes = 0; |
michael@0 | 658 | int computedCount = 0; |
michael@0 | 659 | |
michael@0 | 660 | const SkGlyphCache* head = fHead; |
michael@0 | 661 | while (head != NULL) { |
michael@0 | 662 | computedBytes += head->fMemoryUsed; |
michael@0 | 663 | computedCount += 1; |
michael@0 | 664 | head = head->fNext; |
michael@0 | 665 | } |
michael@0 | 666 | |
michael@0 | 667 | SkASSERT(fTotalMemoryUsed == computedBytes); |
michael@0 | 668 | SkASSERT(fCacheCount == computedCount); |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | #endif |
michael@0 | 672 | |
michael@0 | 673 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 674 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 675 | |
michael@0 | 676 | #include "SkTypefaceCache.h" |
michael@0 | 677 | |
michael@0 | 678 | size_t SkGraphics::GetFontCacheLimit() { |
michael@0 | 679 | return getSharedGlobals().getCacheSizeLimit(); |
michael@0 | 680 | } |
michael@0 | 681 | |
michael@0 | 682 | size_t SkGraphics::SetFontCacheLimit(size_t bytes) { |
michael@0 | 683 | return getSharedGlobals().setCacheSizeLimit(bytes); |
michael@0 | 684 | } |
michael@0 | 685 | |
michael@0 | 686 | size_t SkGraphics::GetFontCacheUsed() { |
michael@0 | 687 | return getSharedGlobals().getTotalMemoryUsed(); |
michael@0 | 688 | } |
michael@0 | 689 | |
michael@0 | 690 | int SkGraphics::GetFontCacheCountLimit() { |
michael@0 | 691 | return getSharedGlobals().getCacheCountLimit(); |
michael@0 | 692 | } |
michael@0 | 693 | |
michael@0 | 694 | int SkGraphics::SetFontCacheCountLimit(int count) { |
michael@0 | 695 | return getSharedGlobals().setCacheCountLimit(count); |
michael@0 | 696 | } |
michael@0 | 697 | |
michael@0 | 698 | int SkGraphics::GetFontCacheCountUsed() { |
michael@0 | 699 | return getSharedGlobals().getCacheCountUsed(); |
michael@0 | 700 | } |
michael@0 | 701 | |
michael@0 | 702 | void SkGraphics::PurgeFontCache() { |
michael@0 | 703 | getSharedGlobals().purgeAll(); |
michael@0 | 704 | SkTypefaceCache::PurgeAll(); |
michael@0 | 705 | } |
michael@0 | 706 | |
michael@0 | 707 | size_t SkGraphics::GetTLSFontCacheLimit() { |
michael@0 | 708 | const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS(); |
michael@0 | 709 | return tls ? tls->getCacheSizeLimit() : 0; |
michael@0 | 710 | } |
michael@0 | 711 | |
michael@0 | 712 | void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { |
michael@0 | 713 | if (0 == bytes) { |
michael@0 | 714 | SkGlyphCache_Globals::DeleteTLS(); |
michael@0 | 715 | } else { |
michael@0 | 716 | SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes); |
michael@0 | 717 | } |
michael@0 | 718 | } |