michael@0: michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "SkGlyphCache.h" michael@0: #include "SkGlyphCache_Globals.h" michael@0: #include "SkGraphics.h" michael@0: #include "SkPaint.h" michael@0: #include "SkPath.h" michael@0: #include "SkTemplates.h" michael@0: #include "SkTLS.h" michael@0: #include "SkTypeface.h" michael@0: michael@0: //#define SPEW_PURGE_STATUS michael@0: //#define RECORD_HASH_EFFICIENCY michael@0: michael@0: bool gSkSuppressFontCachePurgeSpew; michael@0: michael@0: // Returns the shared globals michael@0: static SkGlyphCache_Globals& getSharedGlobals() { michael@0: // we leak this, so we don't incur any shutdown cost of the destructor michael@0: static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals, michael@0: (SkGlyphCache_Globals::kYes_UseMutex)); michael@0: return *gGlobals; michael@0: } michael@0: michael@0: // Returns the TLS globals (if set), or the shared globals michael@0: static SkGlyphCache_Globals& getGlobals() { michael@0: SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS(); michael@0: return tls ? *tls : getSharedGlobals(); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #ifdef RECORD_HASH_EFFICIENCY michael@0: static uint32_t gHashSuccess; michael@0: static uint32_t gHashCollision; michael@0: michael@0: static void RecordHashSuccess() { michael@0: gHashSuccess += 1; michael@0: } michael@0: michael@0: static void RecordHashCollisionIf(bool pred) { michael@0: if (pred) { michael@0: gHashCollision += 1; michael@0: michael@0: uint32_t total = gHashSuccess + gHashCollision; michael@0: SkDebugf("Font Cache Hash success rate: %d%%\n", michael@0: 100 * gHashSuccess / total); michael@0: } michael@0: } michael@0: #else michael@0: #define RecordHashSuccess() (void)0 michael@0: #define RecordHashCollisionIf(pred) (void)0 michael@0: #endif michael@0: #define RecordHashCollision() RecordHashCollisionIf(true) michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: // so we don't grow our arrays a lot michael@0: #define kMinGlyphCount 16 michael@0: #define kMinGlyphImageSize (16*2) michael@0: #define kMinAllocAmount ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount) michael@0: michael@0: SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkScalerContext* ctx) michael@0: : fScalerContext(ctx), fGlyphAlloc(kMinAllocAmount) { michael@0: SkASSERT(typeface); michael@0: SkASSERT(desc); michael@0: SkASSERT(ctx); michael@0: michael@0: fPrev = fNext = NULL; michael@0: michael@0: fDesc = desc->copy(); michael@0: fScalerContext->getFontMetrics(&fFontMetrics); michael@0: michael@0: // init to 0 so that all of the pointers will be null michael@0: memset(fGlyphHash, 0, sizeof(fGlyphHash)); michael@0: // init with 0xFF so that the charCode field will be -1, which is invalid michael@0: memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash)); michael@0: michael@0: fMemoryUsed = sizeof(*this); michael@0: michael@0: fGlyphArray.setReserve(kMinGlyphCount); michael@0: michael@0: fAuxProcList = NULL; michael@0: } michael@0: michael@0: SkGlyphCache::~SkGlyphCache() { michael@0: #if 0 michael@0: { michael@0: size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*); michael@0: size_t glyphAlloc = fGlyphAlloc.totalCapacity(); michael@0: size_t glyphHashUsed = 0; michael@0: size_t uniHashUsed = 0; michael@0: for (int i = 0; i < kHashCount; ++i) { michael@0: glyphHashUsed += fGlyphHash[i] ? sizeof(fGlyphHash[0]) : 0; michael@0: uniHashUsed += fCharToGlyphHash[i].fID != 0xFFFFFFFF ? sizeof(fCharToGlyphHash[0]) : 0; michael@0: } michael@0: size_t glyphUsed = fGlyphArray.count() * sizeof(SkGlyph); michael@0: size_t imageUsed = 0; michael@0: for (int i = 0; i < fGlyphArray.count(); ++i) { michael@0: const SkGlyph& g = *fGlyphArray[i]; michael@0: if (g.fImage) { michael@0: imageUsed += g.fHeight * g.rowBytes(); michael@0: } michael@0: } michael@0: michael@0: printf("glyphPtrArray,%zu, Alloc,%zu, imageUsed,%zu, glyphUsed,%zu, glyphHashAlloc,%zu, glyphHashUsed,%zu, unicharHashAlloc,%zu, unicharHashUsed,%zu\n", michael@0: ptrMem, glyphAlloc, imageUsed, glyphUsed, sizeof(fGlyphHash), glyphHashUsed, sizeof(fCharToGlyphHash), uniHashUsed); michael@0: michael@0: } michael@0: #endif michael@0: SkGlyph** gptr = fGlyphArray.begin(); michael@0: SkGlyph** stop = fGlyphArray.end(); michael@0: while (gptr < stop) { michael@0: SkPath* path = (*gptr)->fPath; michael@0: if (path) { michael@0: SkDELETE(path); michael@0: } michael@0: gptr += 1; michael@0: } michael@0: SkDescriptor::Free(fDesc); michael@0: SkDELETE(fScalerContext); michael@0: this->invokeAndRemoveAuxProcs(); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #ifdef SK_DEBUG michael@0: #define VALIDATE() AutoValidate av(this) michael@0: #else michael@0: #define VALIDATE() michael@0: #endif michael@0: michael@0: uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) { michael@0: VALIDATE(); michael@0: uint32_t id = SkGlyph::MakeID(charCode); michael@0: const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)]; michael@0: michael@0: if (rec.fID == id) { michael@0: return rec.fGlyph->getGlyphID(); michael@0: } else { michael@0: return fScalerContext->charToGlyphID(charCode); michael@0: } michael@0: } michael@0: michael@0: SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) { michael@0: return fScalerContext->glyphIDToChar(glyphID); michael@0: } michael@0: michael@0: unsigned SkGlyphCache::getGlyphCount() { michael@0: return fScalerContext->getGlyphCount(); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) { michael@0: VALIDATE(); michael@0: uint32_t id = SkGlyph::MakeID(charCode); michael@0: CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; michael@0: michael@0: if (rec->fID != id) { michael@0: // this ID is based on the UniChar michael@0: rec->fID = id; michael@0: // this ID is based on the glyph index michael@0: id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode)); michael@0: rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType); michael@0: } michael@0: return *rec->fGlyph; michael@0: } michael@0: michael@0: const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) { michael@0: VALIDATE(); michael@0: uint32_t id = SkGlyph::MakeID(glyphID); michael@0: unsigned index = ID2HashIndex(id); michael@0: SkGlyph* glyph = fGlyphHash[index]; michael@0: michael@0: if (NULL == glyph || glyph->fID != id) { michael@0: glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType); michael@0: fGlyphHash[index] = glyph; michael@0: } michael@0: return *glyph; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) { michael@0: VALIDATE(); michael@0: uint32_t id = SkGlyph::MakeID(charCode); michael@0: CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; michael@0: michael@0: if (rec->fID != id) { michael@0: RecordHashCollisionIf(rec->fGlyph != NULL); michael@0: // this ID is based on the UniChar michael@0: rec->fID = id; michael@0: // this ID is based on the glyph index michael@0: id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode)); michael@0: rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType); michael@0: } else { michael@0: RecordHashSuccess(); michael@0: if (rec->fGlyph->isJustAdvance()) { michael@0: fScalerContext->getMetrics(rec->fGlyph); michael@0: } michael@0: } michael@0: SkASSERT(rec->fGlyph->isFullMetrics()); michael@0: return *rec->fGlyph; michael@0: } michael@0: michael@0: const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode, michael@0: SkFixed x, SkFixed y) { michael@0: VALIDATE(); michael@0: uint32_t id = SkGlyph::MakeID(charCode, x, y); michael@0: CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; michael@0: michael@0: if (rec->fID != id) { michael@0: RecordHashCollisionIf(rec->fGlyph != NULL); michael@0: // this ID is based on the UniChar michael@0: rec->fID = id; michael@0: // this ID is based on the glyph index michael@0: id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y); michael@0: rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType); michael@0: } else { michael@0: RecordHashSuccess(); michael@0: if (rec->fGlyph->isJustAdvance()) { michael@0: fScalerContext->getMetrics(rec->fGlyph); michael@0: } michael@0: } michael@0: SkASSERT(rec->fGlyph->isFullMetrics()); michael@0: return *rec->fGlyph; michael@0: } michael@0: michael@0: const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) { michael@0: VALIDATE(); michael@0: uint32_t id = SkGlyph::MakeID(glyphID); michael@0: unsigned index = ID2HashIndex(id); michael@0: SkGlyph* glyph = fGlyphHash[index]; michael@0: michael@0: if (NULL == glyph || glyph->fID != id) { michael@0: RecordHashCollisionIf(glyph != NULL); michael@0: glyph = this->lookupMetrics(glyphID, kFull_MetricsType); michael@0: fGlyphHash[index] = glyph; michael@0: } else { michael@0: RecordHashSuccess(); michael@0: if (glyph->isJustAdvance()) { michael@0: fScalerContext->getMetrics(glyph); michael@0: } michael@0: } michael@0: SkASSERT(glyph->isFullMetrics()); michael@0: return *glyph; michael@0: } michael@0: michael@0: const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, michael@0: SkFixed x, SkFixed y) { michael@0: VALIDATE(); michael@0: uint32_t id = SkGlyph::MakeID(glyphID, x, y); michael@0: unsigned index = ID2HashIndex(id); michael@0: SkGlyph* glyph = fGlyphHash[index]; michael@0: michael@0: if (NULL == glyph || glyph->fID != id) { michael@0: RecordHashCollisionIf(glyph != NULL); michael@0: glyph = this->lookupMetrics(id, kFull_MetricsType); michael@0: fGlyphHash[index] = glyph; michael@0: } else { michael@0: RecordHashSuccess(); michael@0: if (glyph->isJustAdvance()) { michael@0: fScalerContext->getMetrics(glyph); michael@0: } michael@0: } michael@0: SkASSERT(glyph->isFullMetrics()); michael@0: return *glyph; michael@0: } michael@0: michael@0: SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) { michael@0: SkGlyph* glyph; michael@0: michael@0: int hi = 0; michael@0: int count = fGlyphArray.count(); michael@0: michael@0: if (count) { michael@0: SkGlyph** gptr = fGlyphArray.begin(); michael@0: int lo = 0; michael@0: michael@0: hi = count - 1; michael@0: while (lo < hi) { michael@0: int mid = (hi + lo) >> 1; michael@0: if (gptr[mid]->fID < id) { michael@0: lo = mid + 1; michael@0: } else { michael@0: hi = mid; michael@0: } michael@0: } michael@0: glyph = gptr[hi]; michael@0: if (glyph->fID == id) { michael@0: if (kFull_MetricsType == mtype && glyph->isJustAdvance()) { michael@0: fScalerContext->getMetrics(glyph); michael@0: } michael@0: return glyph; michael@0: } michael@0: michael@0: // check if we need to bump hi before falling though to the allocator michael@0: if (glyph->fID < id) { michael@0: hi += 1; michael@0: } michael@0: } michael@0: michael@0: // not found, but hi tells us where to inser the new glyph michael@0: fMemoryUsed += sizeof(SkGlyph); michael@0: michael@0: glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph), michael@0: SkChunkAlloc::kThrow_AllocFailType); michael@0: glyph->init(id); michael@0: *fGlyphArray.insert(hi) = glyph; michael@0: michael@0: if (kJustAdvance_MetricsType == mtype) { michael@0: fScalerContext->getAdvance(glyph); michael@0: } else { michael@0: SkASSERT(kFull_MetricsType == mtype); michael@0: fScalerContext->getMetrics(glyph); michael@0: } michael@0: michael@0: return glyph; michael@0: } michael@0: michael@0: const void* SkGlyphCache::findImage(const SkGlyph& glyph) { michael@0: if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) { michael@0: if (glyph.fImage == NULL) { michael@0: size_t size = glyph.computeImageSize(); michael@0: const_cast(glyph).fImage = fGlyphAlloc.alloc(size, michael@0: SkChunkAlloc::kReturnNil_AllocFailType); michael@0: // check that alloc() actually succeeded michael@0: if (glyph.fImage) { michael@0: fScalerContext->getImage(glyph); michael@0: // TODO: the scaler may have changed the maskformat during michael@0: // getImage (e.g. from AA or LCD to BW) which means we may have michael@0: // overallocated the buffer. Check if the new computedImageSize michael@0: // is smaller, and if so, strink the alloc size in fImageAlloc. michael@0: fMemoryUsed += size; michael@0: } michael@0: } michael@0: } michael@0: return glyph.fImage; michael@0: } michael@0: michael@0: const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { michael@0: if (glyph.fWidth) { michael@0: if (glyph.fPath == NULL) { michael@0: const_cast(glyph).fPath = SkNEW(SkPath); michael@0: fScalerContext->getPath(glyph, glyph.fPath); michael@0: fMemoryUsed += sizeof(SkPath) + michael@0: glyph.fPath->countPoints() * sizeof(SkPoint); michael@0: } michael@0: } michael@0: return glyph.fPath; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const { michael@0: const AuxProcRec* rec = fAuxProcList; michael@0: while (rec) { michael@0: if (rec->fProc == proc) { michael@0: if (dataPtr) { michael@0: *dataPtr = rec->fData; michael@0: } michael@0: return true; michael@0: } michael@0: rec = rec->fNext; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) { michael@0: if (proc == NULL) { michael@0: return; michael@0: } michael@0: michael@0: AuxProcRec* rec = fAuxProcList; michael@0: while (rec) { michael@0: if (rec->fProc == proc) { michael@0: rec->fData = data; michael@0: return; michael@0: } michael@0: rec = rec->fNext; michael@0: } michael@0: // not found, create a new rec michael@0: rec = SkNEW(AuxProcRec); michael@0: rec->fProc = proc; michael@0: rec->fData = data; michael@0: rec->fNext = fAuxProcList; michael@0: fAuxProcList = rec; michael@0: } michael@0: michael@0: void SkGlyphCache::invokeAndRemoveAuxProcs() { michael@0: AuxProcRec* rec = fAuxProcList; michael@0: while (rec) { michael@0: rec->fProc(rec->fData); michael@0: AuxProcRec* next = rec->fNext; michael@0: SkDELETE(rec); michael@0: rec = next; michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #include "SkThread.h" michael@0: michael@0: size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) { michael@0: static const size_t minLimit = 256 * 1024; michael@0: if (newLimit < minLimit) { michael@0: newLimit = minLimit; michael@0: } michael@0: michael@0: SkAutoMutexAcquire ac(fMutex); michael@0: michael@0: size_t prevLimit = fCacheSizeLimit; michael@0: fCacheSizeLimit = newLimit; michael@0: this->internalPurge(); michael@0: return prevLimit; michael@0: } michael@0: michael@0: int SkGlyphCache_Globals::setCacheCountLimit(int newCount) { michael@0: if (newCount < 0) { michael@0: newCount = 0; michael@0: } michael@0: michael@0: SkAutoMutexAcquire ac(fMutex); michael@0: michael@0: int prevCount = fCacheCountLimit; michael@0: fCacheCountLimit = newCount; michael@0: this->internalPurge(); michael@0: return prevCount; michael@0: } michael@0: michael@0: void SkGlyphCache_Globals::purgeAll() { michael@0: SkAutoMutexAcquire ac(fMutex); michael@0: this->internalPurge(fTotalMemoryUsed); michael@0: } michael@0: michael@0: void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), michael@0: void* context) { michael@0: SkGlyphCache_Globals& globals = getGlobals(); michael@0: SkAutoMutexAcquire ac(globals.fMutex); michael@0: SkGlyphCache* cache; michael@0: michael@0: globals.validate(); michael@0: michael@0: for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) { michael@0: if (proc(cache, context)) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: globals.validate(); michael@0: } michael@0: michael@0: /* This guy calls the visitor from within the mutext lock, so the visitor michael@0: cannot: michael@0: - take too much time michael@0: - try to acquire the mutext again michael@0: - call a fontscaler (which might call into the cache) michael@0: */ michael@0: SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface, michael@0: const SkDescriptor* desc, michael@0: bool (*proc)(const SkGlyphCache*, void*), michael@0: void* context) { michael@0: if (!typeface) { michael@0: typeface = SkTypeface::GetDefaultTypeface(); michael@0: } michael@0: SkASSERT(desc); michael@0: michael@0: SkGlyphCache_Globals& globals = getGlobals(); michael@0: SkAutoMutexAcquire ac(globals.fMutex); michael@0: SkGlyphCache* cache; michael@0: bool insideMutex = true; michael@0: michael@0: globals.validate(); michael@0: michael@0: for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) { michael@0: if (cache->fDesc->equals(*desc)) { michael@0: globals.internalDetachCache(cache); michael@0: goto FOUND_IT; michael@0: } michael@0: } michael@0: michael@0: /* Release the mutex now, before we create a new entry (which might have michael@0: side-effects like trying to access the cache/mutex (yikes!) michael@0: */ michael@0: ac.release(); // release the mutex now michael@0: insideMutex = false; // can't use globals anymore michael@0: michael@0: // Check if we can create a scaler-context before creating the glyphcache. michael@0: // If not, we may have exhausted OS/font resources, so try purging the michael@0: // cache once and try again. michael@0: { michael@0: // pass true the first time, to notice if the scalercontext failed, michael@0: // so we can try the purge. michael@0: SkScalerContext* ctx = typeface->createScalerContext(desc, true); michael@0: if (!ctx) { michael@0: getSharedGlobals().purgeAll(); michael@0: ctx = typeface->createScalerContext(desc, false); michael@0: SkASSERT(ctx); michael@0: } michael@0: cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx)); michael@0: } michael@0: michael@0: FOUND_IT: michael@0: michael@0: AutoValidate av(cache); michael@0: michael@0: if (!proc(cache, context)) { // need to reattach michael@0: if (insideMutex) { michael@0: globals.internalAttachCacheToHead(cache); michael@0: } else { michael@0: globals.attachCacheToHead(cache); michael@0: } michael@0: cache = NULL; michael@0: } michael@0: return cache; michael@0: } michael@0: michael@0: void SkGlyphCache::AttachCache(SkGlyphCache* cache) { michael@0: SkASSERT(cache); michael@0: SkASSERT(cache->fNext == NULL); michael@0: michael@0: getGlobals().attachCacheToHead(cache); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) { michael@0: SkAutoMutexAcquire ac(fMutex); michael@0: michael@0: this->validate(); michael@0: cache->validate(); michael@0: michael@0: this->internalAttachCacheToHead(cache); michael@0: this->internalPurge(); michael@0: } michael@0: michael@0: SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const { michael@0: SkGlyphCache* cache = fHead; michael@0: if (cache) { michael@0: while (cache->fNext) { michael@0: cache = cache->fNext; michael@0: } michael@0: } michael@0: return cache; michael@0: } michael@0: michael@0: size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) { michael@0: this->validate(); michael@0: michael@0: size_t bytesNeeded = 0; michael@0: if (fTotalMemoryUsed > fCacheSizeLimit) { michael@0: bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit; michael@0: } michael@0: bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded); michael@0: if (bytesNeeded) { michael@0: // no small purges! michael@0: bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2); michael@0: } michael@0: michael@0: int countNeeded = 0; michael@0: if (fCacheCount > fCacheCountLimit) { michael@0: countNeeded = fCacheCount - fCacheCountLimit; michael@0: // no small purges! michael@0: countNeeded = SkMax32(countNeeded, fCacheCount >> 2); michael@0: } michael@0: michael@0: // early exit michael@0: if (!countNeeded && !bytesNeeded) { michael@0: return 0; michael@0: } michael@0: michael@0: size_t bytesFreed = 0; michael@0: int countFreed = 0; michael@0: michael@0: // we start at the tail and proceed backwards, as the linklist is in LRU michael@0: // order, with unimportant entries at the tail. michael@0: SkGlyphCache* cache = this->internalGetTail(); michael@0: while (cache != NULL && michael@0: (bytesFreed < bytesNeeded || countFreed < countNeeded)) { michael@0: SkGlyphCache* prev = cache->fPrev; michael@0: bytesFreed += cache->fMemoryUsed; michael@0: countFreed += 1; michael@0: michael@0: this->internalDetachCache(cache); michael@0: SkDELETE(cache); michael@0: cache = prev; michael@0: } michael@0: michael@0: this->validate(); michael@0: michael@0: #ifdef SPEW_PURGE_STATUS michael@0: if (countFreed && !gSkSuppressFontCachePurgeSpew) { michael@0: SkDebugf("purging %dK from font cache [%d entries]\n", michael@0: (int)(bytesFreed >> 10), countFreed); michael@0: } michael@0: #endif michael@0: michael@0: return bytesFreed; michael@0: } michael@0: michael@0: void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) { michael@0: SkASSERT(NULL == cache->fPrev && NULL == cache->fNext); michael@0: if (fHead) { michael@0: fHead->fPrev = cache; michael@0: cache->fNext = fHead; michael@0: } michael@0: fHead = cache; michael@0: michael@0: fCacheCount += 1; michael@0: fTotalMemoryUsed += cache->fMemoryUsed; michael@0: } michael@0: michael@0: void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) { michael@0: SkASSERT(fCacheCount > 0); michael@0: fCacheCount -= 1; michael@0: fTotalMemoryUsed -= cache->fMemoryUsed; michael@0: michael@0: if (cache->fPrev) { michael@0: cache->fPrev->fNext = cache->fNext; michael@0: } else { michael@0: fHead = cache->fNext; michael@0: } michael@0: if (cache->fNext) { michael@0: cache->fNext->fPrev = cache->fPrev; michael@0: } michael@0: cache->fPrev = cache->fNext = NULL; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #ifdef SK_DEBUG michael@0: michael@0: void SkGlyphCache::validate() const { michael@0: #ifdef SK_DEBUG_GLYPH_CACHE michael@0: int count = fGlyphArray.count(); michael@0: for (int i = 0; i < count; i++) { michael@0: const SkGlyph* glyph = fGlyphArray[i]; michael@0: SkASSERT(glyph); michael@0: SkASSERT(fGlyphAlloc.contains(glyph)); michael@0: if (glyph->fImage) { michael@0: SkASSERT(fGlyphAlloc.contains(glyph->fImage)); michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void SkGlyphCache_Globals::validate() const { michael@0: size_t computedBytes = 0; michael@0: int computedCount = 0; michael@0: michael@0: const SkGlyphCache* head = fHead; michael@0: while (head != NULL) { michael@0: computedBytes += head->fMemoryUsed; michael@0: computedCount += 1; michael@0: head = head->fNext; michael@0: } michael@0: michael@0: SkASSERT(fTotalMemoryUsed == computedBytes); michael@0: SkASSERT(fCacheCount == computedCount); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #include "SkTypefaceCache.h" michael@0: michael@0: size_t SkGraphics::GetFontCacheLimit() { michael@0: return getSharedGlobals().getCacheSizeLimit(); michael@0: } michael@0: michael@0: size_t SkGraphics::SetFontCacheLimit(size_t bytes) { michael@0: return getSharedGlobals().setCacheSizeLimit(bytes); michael@0: } michael@0: michael@0: size_t SkGraphics::GetFontCacheUsed() { michael@0: return getSharedGlobals().getTotalMemoryUsed(); michael@0: } michael@0: michael@0: int SkGraphics::GetFontCacheCountLimit() { michael@0: return getSharedGlobals().getCacheCountLimit(); michael@0: } michael@0: michael@0: int SkGraphics::SetFontCacheCountLimit(int count) { michael@0: return getSharedGlobals().setCacheCountLimit(count); michael@0: } michael@0: michael@0: int SkGraphics::GetFontCacheCountUsed() { michael@0: return getSharedGlobals().getCacheCountUsed(); michael@0: } michael@0: michael@0: void SkGraphics::PurgeFontCache() { michael@0: getSharedGlobals().purgeAll(); michael@0: SkTypefaceCache::PurgeAll(); michael@0: } michael@0: michael@0: size_t SkGraphics::GetTLSFontCacheLimit() { michael@0: const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS(); michael@0: return tls ? tls->getCacheSizeLimit() : 0; michael@0: } michael@0: michael@0: void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { michael@0: if (0 == bytes) { michael@0: SkGlyphCache_Globals::DeleteTLS(); michael@0: } else { michael@0: SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes); michael@0: } michael@0: }