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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/core/SkGlyphCache.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,718 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2006 The Android Open Source Project
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +
    1.13 +#include "SkGlyphCache.h"
    1.14 +#include "SkGlyphCache_Globals.h"
    1.15 +#include "SkGraphics.h"
    1.16 +#include "SkPaint.h"
    1.17 +#include "SkPath.h"
    1.18 +#include "SkTemplates.h"
    1.19 +#include "SkTLS.h"
    1.20 +#include "SkTypeface.h"
    1.21 +
    1.22 +//#define SPEW_PURGE_STATUS
    1.23 +//#define RECORD_HASH_EFFICIENCY
    1.24 +
    1.25 +bool gSkSuppressFontCachePurgeSpew;
    1.26 +
    1.27 +// Returns the shared globals
    1.28 +static SkGlyphCache_Globals& getSharedGlobals() {
    1.29 +    // we leak this, so we don't incur any shutdown cost of the destructor
    1.30 +    static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals,
    1.31 +                                                       (SkGlyphCache_Globals::kYes_UseMutex));
    1.32 +    return *gGlobals;
    1.33 +}
    1.34 +
    1.35 +// Returns the TLS globals (if set), or the shared globals
    1.36 +static SkGlyphCache_Globals& getGlobals() {
    1.37 +    SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
    1.38 +    return tls ? *tls : getSharedGlobals();
    1.39 +}
    1.40 +
    1.41 +///////////////////////////////////////////////////////////////////////////////
    1.42 +
    1.43 +#ifdef RECORD_HASH_EFFICIENCY
    1.44 +    static uint32_t gHashSuccess;
    1.45 +    static uint32_t gHashCollision;
    1.46 +
    1.47 +    static void RecordHashSuccess() {
    1.48 +        gHashSuccess += 1;
    1.49 +    }
    1.50 +
    1.51 +    static void RecordHashCollisionIf(bool pred) {
    1.52 +        if (pred) {
    1.53 +            gHashCollision += 1;
    1.54 +
    1.55 +            uint32_t total = gHashSuccess + gHashCollision;
    1.56 +            SkDebugf("Font Cache Hash success rate: %d%%\n",
    1.57 +                     100 * gHashSuccess / total);
    1.58 +        }
    1.59 +    }
    1.60 +#else
    1.61 +    #define RecordHashSuccess() (void)0
    1.62 +    #define RecordHashCollisionIf(pred) (void)0
    1.63 +#endif
    1.64 +#define RecordHashCollision() RecordHashCollisionIf(true)
    1.65 +
    1.66 +///////////////////////////////////////////////////////////////////////////////
    1.67 +
    1.68 +// so we don't grow our arrays a lot
    1.69 +#define kMinGlyphCount      16
    1.70 +#define kMinGlyphImageSize  (16*2)
    1.71 +#define kMinAllocAmount     ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount)
    1.72 +
    1.73 +SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkScalerContext* ctx)
    1.74 +        : fScalerContext(ctx), fGlyphAlloc(kMinAllocAmount) {
    1.75 +    SkASSERT(typeface);
    1.76 +    SkASSERT(desc);
    1.77 +    SkASSERT(ctx);
    1.78 +
    1.79 +    fPrev = fNext = NULL;
    1.80 +
    1.81 +    fDesc = desc->copy();
    1.82 +    fScalerContext->getFontMetrics(&fFontMetrics);
    1.83 +
    1.84 +    // init to 0 so that all of the pointers will be null
    1.85 +    memset(fGlyphHash, 0, sizeof(fGlyphHash));
    1.86 +    // init with 0xFF so that the charCode field will be -1, which is invalid
    1.87 +    memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
    1.88 +
    1.89 +    fMemoryUsed = sizeof(*this);
    1.90 +
    1.91 +    fGlyphArray.setReserve(kMinGlyphCount);
    1.92 +
    1.93 +    fAuxProcList = NULL;
    1.94 +}
    1.95 +
    1.96 +SkGlyphCache::~SkGlyphCache() {
    1.97 +#if 0
    1.98 +    {
    1.99 +        size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*);
   1.100 +        size_t glyphAlloc = fGlyphAlloc.totalCapacity();
   1.101 +        size_t glyphHashUsed = 0;
   1.102 +        size_t uniHashUsed = 0;
   1.103 +        for (int i = 0; i < kHashCount; ++i) {
   1.104 +            glyphHashUsed += fGlyphHash[i] ? sizeof(fGlyphHash[0]) : 0;
   1.105 +            uniHashUsed += fCharToGlyphHash[i].fID != 0xFFFFFFFF ? sizeof(fCharToGlyphHash[0]) : 0;
   1.106 +        }
   1.107 +        size_t glyphUsed = fGlyphArray.count() * sizeof(SkGlyph);
   1.108 +        size_t imageUsed = 0;
   1.109 +        for (int i = 0; i < fGlyphArray.count(); ++i) {
   1.110 +            const SkGlyph& g = *fGlyphArray[i];
   1.111 +            if (g.fImage) {
   1.112 +                imageUsed += g.fHeight * g.rowBytes();
   1.113 +            }
   1.114 +        }
   1.115 +
   1.116 +        printf("glyphPtrArray,%zu, Alloc,%zu, imageUsed,%zu, glyphUsed,%zu, glyphHashAlloc,%zu, glyphHashUsed,%zu, unicharHashAlloc,%zu, unicharHashUsed,%zu\n",
   1.117 +                 ptrMem, glyphAlloc, imageUsed, glyphUsed, sizeof(fGlyphHash), glyphHashUsed, sizeof(fCharToGlyphHash), uniHashUsed);
   1.118 +
   1.119 +    }
   1.120 +#endif
   1.121 +    SkGlyph**   gptr = fGlyphArray.begin();
   1.122 +    SkGlyph**   stop = fGlyphArray.end();
   1.123 +    while (gptr < stop) {
   1.124 +        SkPath* path = (*gptr)->fPath;
   1.125 +        if (path) {
   1.126 +            SkDELETE(path);
   1.127 +        }
   1.128 +        gptr += 1;
   1.129 +    }
   1.130 +    SkDescriptor::Free(fDesc);
   1.131 +    SkDELETE(fScalerContext);
   1.132 +    this->invokeAndRemoveAuxProcs();
   1.133 +}
   1.134 +
   1.135 +///////////////////////////////////////////////////////////////////////////////
   1.136 +
   1.137 +#ifdef SK_DEBUG
   1.138 +#define VALIDATE()  AutoValidate av(this)
   1.139 +#else
   1.140 +#define VALIDATE()
   1.141 +#endif
   1.142 +
   1.143 +uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
   1.144 +    VALIDATE();
   1.145 +    uint32_t id = SkGlyph::MakeID(charCode);
   1.146 +    const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
   1.147 +
   1.148 +    if (rec.fID == id) {
   1.149 +        return rec.fGlyph->getGlyphID();
   1.150 +    } else {
   1.151 +        return fScalerContext->charToGlyphID(charCode);
   1.152 +    }
   1.153 +}
   1.154 +
   1.155 +SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
   1.156 +    return fScalerContext->glyphIDToChar(glyphID);
   1.157 +}
   1.158 +
   1.159 +unsigned SkGlyphCache::getGlyphCount() {
   1.160 +    return fScalerContext->getGlyphCount();
   1.161 +}
   1.162 +
   1.163 +///////////////////////////////////////////////////////////////////////////////
   1.164 +
   1.165 +const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
   1.166 +    VALIDATE();
   1.167 +    uint32_t id = SkGlyph::MakeID(charCode);
   1.168 +    CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
   1.169 +
   1.170 +    if (rec->fID != id) {
   1.171 +        // this ID is based on the UniChar
   1.172 +        rec->fID = id;
   1.173 +        // this ID is based on the glyph index
   1.174 +        id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
   1.175 +        rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
   1.176 +    }
   1.177 +    return *rec->fGlyph;
   1.178 +}
   1.179 +
   1.180 +const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
   1.181 +    VALIDATE();
   1.182 +    uint32_t id = SkGlyph::MakeID(glyphID);
   1.183 +    unsigned index = ID2HashIndex(id);
   1.184 +    SkGlyph* glyph = fGlyphHash[index];
   1.185 +
   1.186 +    if (NULL == glyph || glyph->fID != id) {
   1.187 +        glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
   1.188 +        fGlyphHash[index] = glyph;
   1.189 +    }
   1.190 +    return *glyph;
   1.191 +}
   1.192 +
   1.193 +///////////////////////////////////////////////////////////////////////////////
   1.194 +
   1.195 +const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
   1.196 +    VALIDATE();
   1.197 +    uint32_t id = SkGlyph::MakeID(charCode);
   1.198 +    CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
   1.199 +
   1.200 +    if (rec->fID != id) {
   1.201 +        RecordHashCollisionIf(rec->fGlyph != NULL);
   1.202 +        // this ID is based on the UniChar
   1.203 +        rec->fID = id;
   1.204 +        // this ID is based on the glyph index
   1.205 +        id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
   1.206 +        rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
   1.207 +    } else {
   1.208 +        RecordHashSuccess();
   1.209 +        if (rec->fGlyph->isJustAdvance()) {
   1.210 +            fScalerContext->getMetrics(rec->fGlyph);
   1.211 +        }
   1.212 +    }
   1.213 +    SkASSERT(rec->fGlyph->isFullMetrics());
   1.214 +    return *rec->fGlyph;
   1.215 +}
   1.216 +
   1.217 +const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
   1.218 +                                               SkFixed x, SkFixed y) {
   1.219 +    VALIDATE();
   1.220 +    uint32_t id = SkGlyph::MakeID(charCode, x, y);
   1.221 +    CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
   1.222 +
   1.223 +    if (rec->fID != id) {
   1.224 +        RecordHashCollisionIf(rec->fGlyph != NULL);
   1.225 +        // this ID is based on the UniChar
   1.226 +        rec->fID = id;
   1.227 +        // this ID is based on the glyph index
   1.228 +        id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
   1.229 +        rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
   1.230 +    } else {
   1.231 +        RecordHashSuccess();
   1.232 +        if (rec->fGlyph->isJustAdvance()) {
   1.233 +            fScalerContext->getMetrics(rec->fGlyph);
   1.234 +        }
   1.235 +    }
   1.236 +    SkASSERT(rec->fGlyph->isFullMetrics());
   1.237 +    return *rec->fGlyph;
   1.238 +}
   1.239 +
   1.240 +const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
   1.241 +    VALIDATE();
   1.242 +    uint32_t id = SkGlyph::MakeID(glyphID);
   1.243 +    unsigned index = ID2HashIndex(id);
   1.244 +    SkGlyph* glyph = fGlyphHash[index];
   1.245 +
   1.246 +    if (NULL == glyph || glyph->fID != id) {
   1.247 +        RecordHashCollisionIf(glyph != NULL);
   1.248 +        glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
   1.249 +        fGlyphHash[index] = glyph;
   1.250 +    } else {
   1.251 +        RecordHashSuccess();
   1.252 +        if (glyph->isJustAdvance()) {
   1.253 +            fScalerContext->getMetrics(glyph);
   1.254 +        }
   1.255 +    }
   1.256 +    SkASSERT(glyph->isFullMetrics());
   1.257 +    return *glyph;
   1.258 +}
   1.259 +
   1.260 +const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
   1.261 +                                               SkFixed x, SkFixed y) {
   1.262 +    VALIDATE();
   1.263 +    uint32_t id = SkGlyph::MakeID(glyphID, x, y);
   1.264 +    unsigned index = ID2HashIndex(id);
   1.265 +    SkGlyph* glyph = fGlyphHash[index];
   1.266 +
   1.267 +    if (NULL == glyph || glyph->fID != id) {
   1.268 +        RecordHashCollisionIf(glyph != NULL);
   1.269 +        glyph = this->lookupMetrics(id, kFull_MetricsType);
   1.270 +        fGlyphHash[index] = glyph;
   1.271 +    } else {
   1.272 +        RecordHashSuccess();
   1.273 +        if (glyph->isJustAdvance()) {
   1.274 +            fScalerContext->getMetrics(glyph);
   1.275 +        }
   1.276 +    }
   1.277 +    SkASSERT(glyph->isFullMetrics());
   1.278 +    return *glyph;
   1.279 +}
   1.280 +
   1.281 +SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
   1.282 +    SkGlyph* glyph;
   1.283 +
   1.284 +    int     hi = 0;
   1.285 +    int     count = fGlyphArray.count();
   1.286 +
   1.287 +    if (count) {
   1.288 +        SkGlyph**   gptr = fGlyphArray.begin();
   1.289 +        int     lo = 0;
   1.290 +
   1.291 +        hi = count - 1;
   1.292 +        while (lo < hi) {
   1.293 +            int mid = (hi + lo) >> 1;
   1.294 +            if (gptr[mid]->fID < id) {
   1.295 +                lo = mid + 1;
   1.296 +            } else {
   1.297 +                hi = mid;
   1.298 +            }
   1.299 +        }
   1.300 +        glyph = gptr[hi];
   1.301 +        if (glyph->fID == id) {
   1.302 +            if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
   1.303 +                fScalerContext->getMetrics(glyph);
   1.304 +            }
   1.305 +            return glyph;
   1.306 +        }
   1.307 +
   1.308 +        // check if we need to bump hi before falling though to the allocator
   1.309 +        if (glyph->fID < id) {
   1.310 +            hi += 1;
   1.311 +        }
   1.312 +    }
   1.313 +
   1.314 +    // not found, but hi tells us where to inser the new glyph
   1.315 +    fMemoryUsed += sizeof(SkGlyph);
   1.316 +
   1.317 +    glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
   1.318 +                                        SkChunkAlloc::kThrow_AllocFailType);
   1.319 +    glyph->init(id);
   1.320 +    *fGlyphArray.insert(hi) = glyph;
   1.321 +
   1.322 +    if (kJustAdvance_MetricsType == mtype) {
   1.323 +        fScalerContext->getAdvance(glyph);
   1.324 +    } else {
   1.325 +        SkASSERT(kFull_MetricsType == mtype);
   1.326 +        fScalerContext->getMetrics(glyph);
   1.327 +    }
   1.328 +
   1.329 +    return glyph;
   1.330 +}
   1.331 +
   1.332 +const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
   1.333 +    if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
   1.334 +        if (glyph.fImage == NULL) {
   1.335 +            size_t  size = glyph.computeImageSize();
   1.336 +            const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size,
   1.337 +                                        SkChunkAlloc::kReturnNil_AllocFailType);
   1.338 +            // check that alloc() actually succeeded
   1.339 +            if (glyph.fImage) {
   1.340 +                fScalerContext->getImage(glyph);
   1.341 +                // TODO: the scaler may have changed the maskformat during
   1.342 +                // getImage (e.g. from AA or LCD to BW) which means we may have
   1.343 +                // overallocated the buffer. Check if the new computedImageSize
   1.344 +                // is smaller, and if so, strink the alloc size in fImageAlloc.
   1.345 +                fMemoryUsed += size;
   1.346 +            }
   1.347 +        }
   1.348 +    }
   1.349 +    return glyph.fImage;
   1.350 +}
   1.351 +
   1.352 +const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
   1.353 +    if (glyph.fWidth) {
   1.354 +        if (glyph.fPath == NULL) {
   1.355 +            const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
   1.356 +            fScalerContext->getPath(glyph, glyph.fPath);
   1.357 +            fMemoryUsed += sizeof(SkPath) +
   1.358 +                    glyph.fPath->countPoints() * sizeof(SkPoint);
   1.359 +        }
   1.360 +    }
   1.361 +    return glyph.fPath;
   1.362 +}
   1.363 +
   1.364 +///////////////////////////////////////////////////////////////////////////////
   1.365 +
   1.366 +bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
   1.367 +    const AuxProcRec* rec = fAuxProcList;
   1.368 +    while (rec) {
   1.369 +        if (rec->fProc == proc) {
   1.370 +            if (dataPtr) {
   1.371 +                *dataPtr = rec->fData;
   1.372 +            }
   1.373 +            return true;
   1.374 +        }
   1.375 +        rec = rec->fNext;
   1.376 +    }
   1.377 +    return false;
   1.378 +}
   1.379 +
   1.380 +void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
   1.381 +    if (proc == NULL) {
   1.382 +        return;
   1.383 +    }
   1.384 +
   1.385 +    AuxProcRec* rec = fAuxProcList;
   1.386 +    while (rec) {
   1.387 +        if (rec->fProc == proc) {
   1.388 +            rec->fData = data;
   1.389 +            return;
   1.390 +        }
   1.391 +        rec = rec->fNext;
   1.392 +    }
   1.393 +    // not found, create a new rec
   1.394 +    rec = SkNEW(AuxProcRec);
   1.395 +    rec->fProc = proc;
   1.396 +    rec->fData = data;
   1.397 +    rec->fNext = fAuxProcList;
   1.398 +    fAuxProcList = rec;
   1.399 +}
   1.400 +
   1.401 +void SkGlyphCache::invokeAndRemoveAuxProcs() {
   1.402 +    AuxProcRec* rec = fAuxProcList;
   1.403 +    while (rec) {
   1.404 +        rec->fProc(rec->fData);
   1.405 +        AuxProcRec* next = rec->fNext;
   1.406 +        SkDELETE(rec);
   1.407 +        rec = next;
   1.408 +    }
   1.409 +}
   1.410 +
   1.411 +///////////////////////////////////////////////////////////////////////////////
   1.412 +///////////////////////////////////////////////////////////////////////////////
   1.413 +
   1.414 +#include "SkThread.h"
   1.415 +
   1.416 +size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
   1.417 +    static const size_t minLimit = 256 * 1024;
   1.418 +    if (newLimit < minLimit) {
   1.419 +        newLimit = minLimit;
   1.420 +    }
   1.421 +
   1.422 +    SkAutoMutexAcquire    ac(fMutex);
   1.423 +
   1.424 +    size_t prevLimit = fCacheSizeLimit;
   1.425 +    fCacheSizeLimit = newLimit;
   1.426 +    this->internalPurge();
   1.427 +    return prevLimit;
   1.428 +}
   1.429 +
   1.430 +int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
   1.431 +    if (newCount < 0) {
   1.432 +        newCount = 0;
   1.433 +    }
   1.434 +
   1.435 +    SkAutoMutexAcquire    ac(fMutex);
   1.436 +
   1.437 +    int prevCount = fCacheCountLimit;
   1.438 +    fCacheCountLimit = newCount;
   1.439 +    this->internalPurge();
   1.440 +    return prevCount;
   1.441 +}
   1.442 +
   1.443 +void SkGlyphCache_Globals::purgeAll() {
   1.444 +    SkAutoMutexAcquire    ac(fMutex);
   1.445 +    this->internalPurge(fTotalMemoryUsed);
   1.446 +}
   1.447 +
   1.448 +void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
   1.449 +                                  void* context) {
   1.450 +    SkGlyphCache_Globals& globals = getGlobals();
   1.451 +    SkAutoMutexAcquire    ac(globals.fMutex);
   1.452 +    SkGlyphCache*         cache;
   1.453 +
   1.454 +    globals.validate();
   1.455 +
   1.456 +    for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
   1.457 +        if (proc(cache, context)) {
   1.458 +            break;
   1.459 +        }
   1.460 +    }
   1.461 +
   1.462 +    globals.validate();
   1.463 +}
   1.464 +
   1.465 +/*  This guy calls the visitor from within the mutext lock, so the visitor
   1.466 +    cannot:
   1.467 +    - take too much time
   1.468 +    - try to acquire the mutext again
   1.469 +    - call a fontscaler (which might call into the cache)
   1.470 +*/
   1.471 +SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
   1.472 +                              const SkDescriptor* desc,
   1.473 +                              bool (*proc)(const SkGlyphCache*, void*),
   1.474 +                              void* context) {
   1.475 +    if (!typeface) {
   1.476 +        typeface = SkTypeface::GetDefaultTypeface();
   1.477 +    }
   1.478 +    SkASSERT(desc);
   1.479 +
   1.480 +    SkGlyphCache_Globals& globals = getGlobals();
   1.481 +    SkAutoMutexAcquire    ac(globals.fMutex);
   1.482 +    SkGlyphCache*         cache;
   1.483 +    bool                  insideMutex = true;
   1.484 +
   1.485 +    globals.validate();
   1.486 +
   1.487 +    for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
   1.488 +        if (cache->fDesc->equals(*desc)) {
   1.489 +            globals.internalDetachCache(cache);
   1.490 +            goto FOUND_IT;
   1.491 +        }
   1.492 +    }
   1.493 +
   1.494 +    /* Release the mutex now, before we create a new entry (which might have
   1.495 +        side-effects like trying to access the cache/mutex (yikes!)
   1.496 +    */
   1.497 +    ac.release();           // release the mutex now
   1.498 +    insideMutex = false;    // can't use globals anymore
   1.499 +
   1.500 +    // Check if we can create a scaler-context before creating the glyphcache.
   1.501 +    // If not, we may have exhausted OS/font resources, so try purging the
   1.502 +    // cache once and try again.
   1.503 +    {
   1.504 +        // pass true the first time, to notice if the scalercontext failed,
   1.505 +        // so we can try the purge.
   1.506 +        SkScalerContext* ctx = typeface->createScalerContext(desc, true);
   1.507 +        if (!ctx) {
   1.508 +            getSharedGlobals().purgeAll();
   1.509 +            ctx = typeface->createScalerContext(desc, false);
   1.510 +            SkASSERT(ctx);
   1.511 +        }
   1.512 +        cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx));
   1.513 +    }
   1.514 +
   1.515 +FOUND_IT:
   1.516 +
   1.517 +    AutoValidate av(cache);
   1.518 +
   1.519 +    if (!proc(cache, context)) {   // need to reattach
   1.520 +        if (insideMutex) {
   1.521 +            globals.internalAttachCacheToHead(cache);
   1.522 +        } else {
   1.523 +            globals.attachCacheToHead(cache);
   1.524 +        }
   1.525 +        cache = NULL;
   1.526 +    }
   1.527 +    return cache;
   1.528 +}
   1.529 +
   1.530 +void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
   1.531 +    SkASSERT(cache);
   1.532 +    SkASSERT(cache->fNext == NULL);
   1.533 +
   1.534 +    getGlobals().attachCacheToHead(cache);
   1.535 +}
   1.536 +
   1.537 +///////////////////////////////////////////////////////////////////////////////
   1.538 +
   1.539 +void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
   1.540 +    SkAutoMutexAcquire    ac(fMutex);
   1.541 +
   1.542 +    this->validate();
   1.543 +    cache->validate();
   1.544 +
   1.545 +    this->internalAttachCacheToHead(cache);
   1.546 +    this->internalPurge();
   1.547 +}
   1.548 +
   1.549 +SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
   1.550 +    SkGlyphCache* cache = fHead;
   1.551 +    if (cache) {
   1.552 +        while (cache->fNext) {
   1.553 +            cache = cache->fNext;
   1.554 +        }
   1.555 +    }
   1.556 +    return cache;
   1.557 +}
   1.558 +
   1.559 +size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
   1.560 +    this->validate();
   1.561 +
   1.562 +    size_t bytesNeeded = 0;
   1.563 +    if (fTotalMemoryUsed > fCacheSizeLimit) {
   1.564 +        bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
   1.565 +    }
   1.566 +    bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
   1.567 +    if (bytesNeeded) {
   1.568 +        // no small purges!
   1.569 +        bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
   1.570 +    }
   1.571 +
   1.572 +    int countNeeded = 0;
   1.573 +    if (fCacheCount > fCacheCountLimit) {
   1.574 +        countNeeded = fCacheCount - fCacheCountLimit;
   1.575 +        // no small purges!
   1.576 +        countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
   1.577 +    }
   1.578 +
   1.579 +    // early exit
   1.580 +    if (!countNeeded && !bytesNeeded) {
   1.581 +        return 0;
   1.582 +    }
   1.583 +
   1.584 +    size_t  bytesFreed = 0;
   1.585 +    int     countFreed = 0;
   1.586 +
   1.587 +    // we start at the tail and proceed backwards, as the linklist is in LRU
   1.588 +    // order, with unimportant entries at the tail.
   1.589 +    SkGlyphCache* cache = this->internalGetTail();
   1.590 +    while (cache != NULL &&
   1.591 +           (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
   1.592 +        SkGlyphCache* prev = cache->fPrev;
   1.593 +        bytesFreed += cache->fMemoryUsed;
   1.594 +        countFreed += 1;
   1.595 +
   1.596 +        this->internalDetachCache(cache);
   1.597 +        SkDELETE(cache);
   1.598 +        cache = prev;
   1.599 +    }
   1.600 +
   1.601 +    this->validate();
   1.602 +
   1.603 +#ifdef SPEW_PURGE_STATUS
   1.604 +    if (countFreed && !gSkSuppressFontCachePurgeSpew) {
   1.605 +        SkDebugf("purging %dK from font cache [%d entries]\n",
   1.606 +                 (int)(bytesFreed >> 10), countFreed);
   1.607 +    }
   1.608 +#endif
   1.609 +
   1.610 +    return bytesFreed;
   1.611 +}
   1.612 +
   1.613 +void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
   1.614 +    SkASSERT(NULL == cache->fPrev && NULL == cache->fNext);
   1.615 +    if (fHead) {
   1.616 +        fHead->fPrev = cache;
   1.617 +        cache->fNext = fHead;
   1.618 +    }
   1.619 +    fHead = cache;
   1.620 +
   1.621 +    fCacheCount += 1;
   1.622 +    fTotalMemoryUsed += cache->fMemoryUsed;
   1.623 +}
   1.624 +
   1.625 +void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
   1.626 +    SkASSERT(fCacheCount > 0);
   1.627 +    fCacheCount -= 1;
   1.628 +    fTotalMemoryUsed -= cache->fMemoryUsed;
   1.629 +
   1.630 +    if (cache->fPrev) {
   1.631 +        cache->fPrev->fNext = cache->fNext;
   1.632 +    } else {
   1.633 +        fHead = cache->fNext;
   1.634 +    }
   1.635 +    if (cache->fNext) {
   1.636 +        cache->fNext->fPrev = cache->fPrev;
   1.637 +    }
   1.638 +    cache->fPrev = cache->fNext = NULL;
   1.639 +}
   1.640 +
   1.641 +///////////////////////////////////////////////////////////////////////////////
   1.642 +
   1.643 +#ifdef SK_DEBUG
   1.644 +
   1.645 +void SkGlyphCache::validate() const {
   1.646 +#ifdef SK_DEBUG_GLYPH_CACHE
   1.647 +    int count = fGlyphArray.count();
   1.648 +    for (int i = 0; i < count; i++) {
   1.649 +        const SkGlyph* glyph = fGlyphArray[i];
   1.650 +        SkASSERT(glyph);
   1.651 +        SkASSERT(fGlyphAlloc.contains(glyph));
   1.652 +        if (glyph->fImage) {
   1.653 +            SkASSERT(fGlyphAlloc.contains(glyph->fImage));
   1.654 +        }
   1.655 +    }
   1.656 +#endif
   1.657 +}
   1.658 +
   1.659 +void SkGlyphCache_Globals::validate() const {
   1.660 +    size_t computedBytes = 0;
   1.661 +    int computedCount = 0;
   1.662 +
   1.663 +    const SkGlyphCache* head = fHead;
   1.664 +    while (head != NULL) {
   1.665 +        computedBytes += head->fMemoryUsed;
   1.666 +        computedCount += 1;
   1.667 +        head = head->fNext;
   1.668 +    }
   1.669 +
   1.670 +    SkASSERT(fTotalMemoryUsed == computedBytes);
   1.671 +    SkASSERT(fCacheCount == computedCount);
   1.672 +}
   1.673 +
   1.674 +#endif
   1.675 +
   1.676 +///////////////////////////////////////////////////////////////////////////////
   1.677 +///////////////////////////////////////////////////////////////////////////////
   1.678 +
   1.679 +#include "SkTypefaceCache.h"
   1.680 +
   1.681 +size_t SkGraphics::GetFontCacheLimit() {
   1.682 +    return getSharedGlobals().getCacheSizeLimit();
   1.683 +}
   1.684 +
   1.685 +size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
   1.686 +    return getSharedGlobals().setCacheSizeLimit(bytes);
   1.687 +}
   1.688 +
   1.689 +size_t SkGraphics::GetFontCacheUsed() {
   1.690 +    return getSharedGlobals().getTotalMemoryUsed();
   1.691 +}
   1.692 +
   1.693 +int SkGraphics::GetFontCacheCountLimit() {
   1.694 +    return getSharedGlobals().getCacheCountLimit();
   1.695 +}
   1.696 +
   1.697 +int SkGraphics::SetFontCacheCountLimit(int count) {
   1.698 +    return getSharedGlobals().setCacheCountLimit(count);
   1.699 +}
   1.700 +
   1.701 +int SkGraphics::GetFontCacheCountUsed() {
   1.702 +    return getSharedGlobals().getCacheCountUsed();
   1.703 +}
   1.704 +
   1.705 +void SkGraphics::PurgeFontCache() {
   1.706 +    getSharedGlobals().purgeAll();
   1.707 +    SkTypefaceCache::PurgeAll();
   1.708 +}
   1.709 +
   1.710 +size_t SkGraphics::GetTLSFontCacheLimit() {
   1.711 +    const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
   1.712 +    return tls ? tls->getCacheSizeLimit() : 0;
   1.713 +}
   1.714 +
   1.715 +void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
   1.716 +    if (0 == bytes) {
   1.717 +        SkGlyphCache_Globals::DeleteTLS();
   1.718 +    } else {
   1.719 +        SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
   1.720 +    }
   1.721 +}

mercurial