Index: skia/sgl/SkGlyphCache.cpp |
=================================================================== |
--- skia/sgl/SkGlyphCache.cpp (revision 16859) |
+++ skia/sgl/SkGlyphCache.cpp (working copy) |
@@ -1,662 +0,0 @@ |
-/* libs/graphics/sgl/SkGlyphCache.cpp |
-** |
-** Copyright 2006, The Android Open Source Project |
-** |
-** Licensed under the Apache License, Version 2.0 (the "License"); |
-** you may not use this file except in compliance with the License. |
-** You may obtain a copy of the License at |
-** |
-** http://www.apache.org/licenses/LICENSE-2.0 |
-** |
-** Unless required by applicable law or agreed to in writing, software |
-** distributed under the License is distributed on an "AS IS" BASIS, |
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-** See the License for the specific language governing permissions and |
-** limitations under the License. |
-*/ |
- |
-#include "SkGlyphCache.h" |
-#include "SkFontHost.h" |
-#include "SkPaint.h" |
-#include "SkTemplates.h" |
- |
-#define SPEW_PURGE_STATUS |
-//#define USE_CACHE_HASH |
-//#define RECORD_HASH_EFFICIENCY |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-#ifdef RECORD_HASH_EFFICIENCY |
- static uint32_t gHashSuccess; |
- static uint32_t gHashCollision; |
- |
- static void RecordHashSuccess() { |
- gHashSuccess += 1; |
- } |
- |
- static void RecordHashCollisionIf(bool pred) { |
- if (pred) { |
- gHashCollision += 1; |
- |
- uint32_t total = gHashSuccess + gHashCollision; |
- SkDebugf("Font Cache Hash success rate: %d%%\n", |
- 100 * gHashSuccess / total); |
- } |
- } |
-#else |
- #define RecordHashSuccess() (void)0 |
- #define RecordHashCollisionIf(pred) (void)0 |
-#endif |
-#define RecordHashCollision() RecordHashCollisionIf(true) |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-#define kMinGlphAlloc (sizeof(SkGlyph) * 64) |
-#define kMinImageAlloc (24 * 64) // should be pointsize-dependent |
- |
-#define METRICS_RESERVE_COUNT 128 // so we don't grow this array a lot |
- |
-SkGlyphCache::SkGlyphCache(const SkDescriptor* desc) |
- : fGlyphAlloc(kMinGlphAlloc), fImageAlloc(kMinImageAlloc) { |
- fPrev = fNext = NULL; |
- |
- fDesc = desc->copy(); |
- fScalerContext = SkScalerContext::Create(desc); |
- fScalerContext->getFontMetrics(NULL, &fFontMetricsY); |
- |
- // init to 0 so that all of the pointers will be null |
- memset(fGlyphHash, 0, sizeof(fGlyphHash)); |
- // init with 0xFF so that the charCode field will be -1, which is invalid |
- memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash)); |
- |
- fMemoryUsed = sizeof(*this) + kMinGlphAlloc + kMinImageAlloc; |
- |
- fGlyphArray.setReserve(METRICS_RESERVE_COUNT); |
- |
- fMetricsCount = 0; |
- fAdvanceCount = 0; |
- fAuxProcList = NULL; |
-} |
- |
-SkGlyphCache::~SkGlyphCache() { |
- SkGlyph** gptr = fGlyphArray.begin(); |
- SkGlyph** stop = fGlyphArray.end(); |
- while (gptr < stop) { |
- SkPath* path = (*gptr)->fPath; |
- if (path) { |
- SkDELETE(path); |
- } |
- gptr += 1; |
- } |
- SkDescriptor::Free(fDesc); |
- SkDELETE(fScalerContext); |
- this->invokeAndRemoveAuxProcs(); |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-#ifdef SK_DEBUG |
-class AutoCheckForNull { |
-public: |
- AutoCheckForNull(const SkTDArray<SkGlyph*>& array) : fArray(array) { |
- for (int i = 0; i < array.count(); i++) |
- SkASSERT(array[i]); |
- } |
- ~AutoCheckForNull() { |
- const SkTDArray<SkGlyph*>& array = fArray; |
- for (int i = 0; i < array.count(); i++) { |
- SkASSERT(array[i]); |
- } |
- } |
-private: |
- const SkTDArray<SkGlyph*>& fArray; |
-}; |
-#define VALIDATE() AutoCheckForNull acfn(fGlyphArray) |
-#else |
-#define VALIDATE() |
-#endif |
- |
-uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) { |
- VALIDATE(); |
- uint32_t id = SkGlyph::MakeID(charCode); |
- const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)]; |
- |
- if (rec.fID == id) { |
- return rec.fGlyph->getGlyphID(); |
- } else { |
- return fScalerContext->charToGlyphID(charCode); |
- } |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) { |
- VALIDATE(); |
- uint32_t id = SkGlyph::MakeID(charCode); |
- CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; |
- |
- if (rec->fID != id) { |
- // this ID is based on the UniChar |
- rec->fID = id; |
- // this ID is based on the glyph index |
- id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode)); |
- rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType); |
- } |
- return *rec->fGlyph; |
-} |
- |
-const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) { |
- VALIDATE(); |
- uint32_t id = SkGlyph::MakeID(glyphID); |
- unsigned index = ID2HashIndex(id); |
- SkGlyph* glyph = fGlyphHash[index]; |
- |
- if (NULL == glyph || glyph->fID != id) { |
- glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType); |
- fGlyphHash[index] = glyph; |
- } |
- return *glyph; |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) { |
- VALIDATE(); |
- uint32_t id = SkGlyph::MakeID(charCode); |
- CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; |
- |
- if (rec->fID != id) { |
- RecordHashCollisionIf(rec->fGlyph != NULL); |
- // this ID is based on the UniChar |
- rec->fID = id; |
- // this ID is based on the glyph index |
- id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode)); |
- rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType); |
- } else { |
- RecordHashSuccess(); |
- if (rec->fGlyph->isJustAdvance()) { |
- fScalerContext->getMetrics(rec->fGlyph); |
- } |
- } |
- SkASSERT(rec->fGlyph->isFullMetrics()); |
- return *rec->fGlyph; |
-} |
- |
-const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode, |
- SkFixed x, SkFixed y) { |
- VALIDATE(); |
- uint32_t id = SkGlyph::MakeID(charCode, x, y); |
- CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; |
- |
- if (rec->fID != id) { |
- RecordHashCollisionIf(rec->fGlyph != NULL); |
- // this ID is based on the UniChar |
- rec->fID = id; |
- // this ID is based on the glyph index |
- id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y); |
- rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType); |
- } else { |
- RecordHashSuccess(); |
- if (rec->fGlyph->isJustAdvance()) { |
- fScalerContext->getMetrics(rec->fGlyph); |
- } |
- } |
- SkASSERT(rec->fGlyph->isFullMetrics()); |
- return *rec->fGlyph; |
-} |
- |
-const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) { |
- VALIDATE(); |
- uint32_t id = SkGlyph::MakeID(glyphID); |
- unsigned index = ID2HashIndex(id); |
- SkGlyph* glyph = fGlyphHash[index]; |
- |
- if (NULL == glyph || glyph->fID != id) { |
- RecordHashCollisionIf(glyph != NULL); |
- glyph = this->lookupMetrics(glyphID, kFull_MetricsType); |
- fGlyphHash[index] = glyph; |
- } else { |
- RecordHashSuccess(); |
- if (glyph->isJustAdvance()) { |
- fScalerContext->getMetrics(glyph); |
- } |
- } |
- SkASSERT(glyph->isFullMetrics()); |
- return *glyph; |
-} |
- |
-const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, |
- SkFixed x, SkFixed y) { |
- VALIDATE(); |
- uint32_t id = SkGlyph::MakeID(glyphID, x, y); |
- unsigned index = ID2HashIndex(id); |
- SkGlyph* glyph = fGlyphHash[index]; |
- |
- if (NULL == glyph || glyph->fID != id) { |
- RecordHashCollisionIf(glyph != NULL); |
- glyph = this->lookupMetrics(id, kFull_MetricsType); |
- fGlyphHash[index] = glyph; |
- } else { |
- RecordHashSuccess(); |
- if (glyph->isJustAdvance()) { |
- fScalerContext->getMetrics(glyph); |
- } |
- } |
- SkASSERT(glyph->isFullMetrics()); |
- return *glyph; |
-} |
- |
-SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) { |
- SkGlyph* glyph; |
- |
- int hi = 0; |
- int count = fGlyphArray.count(); |
- |
- if (count) { |
- SkGlyph** gptr = fGlyphArray.begin(); |
- int lo = 0; |
- |
- hi = count - 1; |
- while (lo < hi) { |
- int mid = (hi + lo) >> 1; |
- if (gptr[mid]->fID < id) { |
- lo = mid + 1; |
- } else { |
- hi = mid; |
- } |
- } |
- glyph = gptr[hi]; |
- if (glyph->fID == id) { |
- if (kFull_MetricsType == mtype && glyph->isJustAdvance()) { |
- fScalerContext->getMetrics(glyph); |
- } |
- return glyph; |
- } |
- |
- // check if we need to bump hi before falling though to the allocator |
- if (glyph->fID < id) { |
- hi += 1; |
- } |
- } |
- |
- // not found, but hi tells us where to inser the new glyph |
- fMemoryUsed += sizeof(SkGlyph); |
- |
- glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph), |
- SkChunkAlloc::kThrow_AllocFailType); |
- glyph->fID = id; |
- glyph->fImage = NULL; |
- glyph->fPath = NULL; |
- *fGlyphArray.insert(hi) = glyph; |
- |
- if (kJustAdvance_MetricsType == mtype) { |
- fScalerContext->getAdvance(glyph); |
- fAdvanceCount += 1; |
- } else { |
- SkASSERT(kFull_MetricsType == mtype); |
- fScalerContext->getMetrics(glyph); |
- fMetricsCount += 1; |
- } |
- |
- return glyph; |
-} |
- |
-const void* SkGlyphCache::findImage(const SkGlyph& glyph) { |
- if (glyph.fWidth) { |
- if (glyph.fImage == NULL) { |
- size_t size = glyph.computeImageSize(); |
- const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size, |
- SkChunkAlloc::kReturnNil_AllocFailType); |
- fScalerContext->getImage(glyph); |
- fMemoryUsed += size; |
- } |
- } |
- return glyph.fImage; |
-} |
- |
-const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { |
- if (glyph.fWidth) { |
- if (glyph.fPath == NULL) { |
- const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath); |
- fScalerContext->getPath(glyph, glyph.fPath); |
- fMemoryUsed += sizeof(SkPath) + |
- glyph.fPath->getPoints(NULL, 0x7FFFFFFF) * sizeof(SkPoint); |
- } |
- } |
- return glyph.fPath; |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const { |
- const AuxProcRec* rec = fAuxProcList; |
- while (rec) { |
- if (rec->fProc == proc) { |
- if (dataPtr) { |
- *dataPtr = rec->fData; |
- } |
- return true; |
- } |
- rec = rec->fNext; |
- } |
- return false; |
-} |
- |
-void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) { |
- if (proc == NULL) { |
- return; |
- } |
- |
- AuxProcRec* rec = fAuxProcList; |
- while (rec) { |
- if (rec->fProc == proc) { |
- rec->fData = data; |
- return; |
- } |
- rec = rec->fNext; |
- } |
- // not found, create a new rec |
- rec = SkNEW(AuxProcRec); |
- rec->fProc = proc; |
- rec->fData = data; |
- rec->fNext = fAuxProcList; |
- fAuxProcList = rec; |
-} |
- |
-void SkGlyphCache::removeAuxProc(void (*proc)(void*)) { |
- AuxProcRec* rec = fAuxProcList; |
- AuxProcRec* prev = NULL; |
- while (rec) { |
- AuxProcRec* next = rec->fNext; |
- if (rec->fProc == proc) { |
- if (prev) { |
- prev->fNext = next; |
- } else { |
- fAuxProcList = next; |
- } |
- SkDELETE(rec); |
- return; |
- } |
- prev = rec; |
- rec = next; |
- } |
-} |
- |
-void SkGlyphCache::invokeAndRemoveAuxProcs() { |
- AuxProcRec* rec = fAuxProcList; |
- while (rec) { |
- rec->fProc(rec->fData); |
- AuxProcRec* next = rec->fNext; |
- SkDELETE(rec); |
- rec = next; |
- } |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-#include "SkGlobals.h" |
-#include "SkThread.h" |
- |
-#define SkGlyphCache_GlobalsTag SkSetFourByteTag('g', 'l', 'f', 'c') |
- |
-#ifdef USE_CACHE_HASH |
- #define HASH_BITCOUNT 6 |
- #define HASH_COUNT (1 << HASH_BITCOUNT) |
- #define HASH_MASK (HASH_COUNT - 1) |
- |
- static unsigned desc_to_hashindex(const SkDescriptor* desc) |
- { |
- SkASSERT(HASH_MASK < 256); // since our munging reduces to 8 bits |
- |
- uint32_t n = *(const uint32_t*)desc; //desc->getChecksum(); |
- SkASSERT(n == desc->getChecksum()); |
- |
- // don't trust that the low bits of checksum vary enough, so... |
- n ^= (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ (n >> 30); |
- |
- return n & HASH_MASK; |
- } |
-#endif |
- |
-class SkGlyphCache_Globals : public SkGlobals::Rec { |
-public: |
- SkMutex fMutex; |
- SkGlyphCache* fHead; |
- size_t fTotalMemoryUsed; |
-#ifdef USE_CACHE_HASH |
- SkGlyphCache* fHash[HASH_COUNT]; |
-#endif |
- |
-#ifdef SK_DEBUG |
- void validate() const; |
-#else |
- void validate() const {} |
-#endif |
-}; |
- |
-#ifdef SK_USE_RUNTIME_GLOBALS |
- static SkGlobals::Rec* create_globals() { |
- SkGlyphCache_Globals* rec = SkNEW(SkGlyphCache_Globals); |
- rec->fHead = NULL; |
- rec->fTotalMemoryUsed = 0; |
-#ifdef USE_CACHE_HASH |
- memset(rec->fHash, 0, sizeof(rec->fHash)); |
-#endif |
- return rec; |
- } |
- |
- #define FIND_GC_GLOBALS() *(SkGlyphCache_Globals*)SkGlobals::Find(SkGlyphCache_GlobalsTag, create_globals) |
- #define GET_GC_GLOBALS() *(SkGlyphCache_Globals*)SkGlobals::Get(SkGlyphCache_GlobalsTag) |
-#else |
- static SkGlyphCache_Globals gGCGlobals; |
- #define FIND_GC_GLOBALS() gGCGlobals |
- #define GET_GC_GLOBALS() gGCGlobals |
-#endif |
- |
-void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), |
- void* context) { |
- SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); |
- SkAutoMutexAcquire ac(globals.fMutex); |
- SkGlyphCache* cache; |
- |
- globals.validate(); |
- |
- for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { |
- if (proc(cache, context)) { |
- break; |
- } |
- } |
- |
- globals.validate(); |
-} |
- |
-/* This guy calls the visitor from within the mutext lock, so the visitor |
- cannot: |
- - take too much time |
- - try to acquire the mutext again |
- - call a fontscaler (which might call into the cache) |
-*/ |
-SkGlyphCache* SkGlyphCache::VisitCache(const SkDescriptor* desc, |
- bool (*proc)(const SkGlyphCache*, void*), |
- void* context) { |
- SkASSERT(desc); |
- |
- SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); |
- SkAutoMutexAcquire ac(globals.fMutex); |
- SkGlyphCache* cache; |
- bool insideMutex = true; |
- |
- globals.validate(); |
- |
-#ifdef USE_CACHE_HASH |
- SkGlyphCache** hash = globals.fHash; |
- unsigned index = desc_to_hashindex(desc); |
- cache = hash[index]; |
- if (cache && *cache->fDesc == *desc) { |
- cache->detach(&globals.fHead); |
- goto FOUND_IT; |
- } |
-#endif |
- |
- for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { |
- if (cache->fDesc->equals(*desc)) { |
- cache->detach(&globals.fHead); |
- goto FOUND_IT; |
- } |
- } |
- |
- /* Release the mutex now, before we create a new entry (which might have |
- side-effects like trying to access the cache/mutex (yikes!) |
- */ |
- ac.release(); // release the mutex now |
- insideMutex = false; // can't use globals anymore |
- |
- cache = SkNEW_ARGS(SkGlyphCache, (desc)); |
- |
-FOUND_IT: |
- if (proc(cache, context)) { // stay detached |
- if (insideMutex) { |
- SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed); |
- globals.fTotalMemoryUsed -= cache->fMemoryUsed; |
-#ifdef USE_CACHE_HASH |
- hash[index] = NULL; |
-#endif |
- } |
- } else { // reattach |
- if (insideMutex) { |
- cache->attachToHead(&globals.fHead); |
-#ifdef USE_CACHE_HASH |
- hash[index] = cache; |
-#endif |
- } else { |
- AttachCache(cache); |
- } |
- cache = NULL; |
- } |
- return cache; |
-} |
- |
-void SkGlyphCache::AttachCache(SkGlyphCache* cache) { |
- SkASSERT(cache); |
- SkASSERT(cache->fNext == NULL); |
- |
- SkGlyphCache_Globals& globals = GET_GC_GLOBALS(); |
- SkAutoMutexAcquire ac(globals.fMutex); |
- |
- globals.validate(); |
- |
- // if we have a fixed budget for our cache, do a purge here |
- { |
- size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed; |
- size_t amountToFree = SkFontHost::ShouldPurgeFontCache(allocated); |
- if (amountToFree) |
- (void)InternalFreeCache(&globals, amountToFree); |
- } |
- |
- cache->attachToHead(&globals.fHead); |
- globals.fTotalMemoryUsed += cache->fMemoryUsed; |
- |
-#ifdef USE_CACHE_HASH |
- unsigned index = desc_to_hashindex(cache->fDesc); |
- SkASSERT(globals.fHash[index] != cache); |
- globals.fHash[index] = cache; |
-#endif |
- |
- globals.validate(); |
-} |
- |
-size_t SkGlyphCache::GetCacheUsed() { |
- SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); |
- SkAutoMutexAcquire ac(globals.fMutex); |
- |
- return SkGlyphCache::ComputeMemoryUsed(globals.fHead); |
-} |
- |
-bool SkGlyphCache::SetCacheUsed(size_t bytesUsed) { |
- size_t curr = SkGlyphCache::GetCacheUsed(); |
- |
- if (curr > bytesUsed) { |
- SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); |
- SkAutoMutexAcquire ac(globals.fMutex); |
- |
- return InternalFreeCache(&globals, curr - bytesUsed) > 0; |
- } |
- return false; |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) { |
- if (cache) { |
- while (cache->fNext) { |
- cache = cache->fNext; |
- } |
- } |
- return cache; |
-} |
- |
-size_t SkGlyphCache::ComputeMemoryUsed(const SkGlyphCache* head) { |
- size_t size = 0; |
- |
- while (head != NULL) { |
- size += head->fMemoryUsed; |
- head = head->fNext; |
- } |
- return size; |
-} |
- |
-#ifdef SK_DEBUG |
-void SkGlyphCache_Globals::validate() const { |
- size_t computed = SkGlyphCache::ComputeMemoryUsed(fHead); |
- if (fTotalMemoryUsed != computed) { |
- printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed); |
- } |
- SkASSERT(fTotalMemoryUsed == computed); |
-} |
-#endif |
- |
-size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals, |
- size_t bytesNeeded) { |
- globals->validate(); |
- |
- size_t bytesFreed = 0; |
- int count = 0; |
- |
- // don't do any "small" purges |
- size_t minToPurge = globals->fTotalMemoryUsed >> 2; |
- if (bytesNeeded < minToPurge) |
- bytesNeeded = minToPurge; |
- |
- SkGlyphCache* cache = FindTail(globals->fHead); |
- while (cache != NULL && bytesFreed < bytesNeeded) { |
- SkGlyphCache* prev = cache->fPrev; |
- bytesFreed += cache->fMemoryUsed; |
- |
-#ifdef USE_CACHE_HASH |
- unsigned index = desc_to_hashindex(cache->fDesc); |
- if (cache == globals->fHash[index]) { |
- globals->fHash[index] = NULL; |
- } |
-#endif |
- |
- cache->detach(&globals->fHead); |
- SkDELETE(cache); |
- cache = prev; |
- count += 1; |
- } |
- |
- SkASSERT(bytesFreed <= globals->fTotalMemoryUsed); |
- globals->fTotalMemoryUsed -= bytesFreed; |
- globals->validate(); |
- |
-#ifdef SPEW_PURGE_STATUS |
- if (count) { |
- SkDebugf("purging %dK from font cache [%d entries]\n", |
- (int)(bytesFreed >> 10), count); |
- } |
-#endif |
- |
- return bytesFreed; |
-} |
- |