1
2/*
3 * Copyright 2010 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10
11#include "GrAtlas.h"
12#include "GrGpu.h"
13#include "GrRectanizer.h"
14#include "GrTextStrike.h"
15#include "GrTextStrike_impl.h"
16#include "GrRect.h"
17
18GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
19    gpu->ref();
20    fAtlasMgr = NULL;
21
22    fHead = fTail = NULL;
23}
24
25GrFontCache::~GrFontCache() {
26    fCache.deleteAll();
27    delete fAtlasMgr;
28    fGpu->unref();
29}
30
31GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
32                                          const Key& key) {
33    if (NULL == fAtlasMgr) {
34        fAtlasMgr = new GrAtlasMgr(fGpu);
35    }
36    GrTextStrike* strike = new GrTextStrike(this, scaler->getKey(),
37                                            scaler->getMaskFormat(), fAtlasMgr);
38    fCache.insert(key, strike);
39
40    if (fHead) {
41        fHead->fPrev = strike;
42    } else {
43        GrAssert(NULL == fTail);
44        fTail = strike;
45    }
46    strike->fPrev = NULL;
47    strike->fNext = fHead;
48    fHead = strike;
49
50    return strike;
51}
52
53void GrFontCache::freeAll() {
54    fCache.deleteAll();
55    delete fAtlasMgr;
56    fAtlasMgr = NULL;
57    fHead = NULL;
58    fTail = NULL;
59}
60
61void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
62    GrTextStrike* strike = fTail;
63    while (strike) {
64        if (strike == preserveStrike) {
65            strike = strike->fPrev;
66            continue;
67        }
68        GrTextStrike* strikeToPurge = strike;
69        // keep going if we won't free up any atlases with this strike.
70        strike = (NULL == strikeToPurge->fAtlas) ? strikeToPurge->fPrev : NULL;
71        int index = fCache.slowFindIndex(strikeToPurge);
72        GrAssert(index >= 0);
73        fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
74        this->detachStrikeFromList(strikeToPurge);
75        delete strikeToPurge;
76    }
77}
78
79#if GR_DEBUG
80void GrFontCache::validate() const {
81    int count = fCache.count();
82    if (0 == count) {
83        GrAssert(!fHead);
84        GrAssert(!fTail);
85    } else if (1 == count) {
86        GrAssert(fHead == fTail);
87    } else {
88        GrAssert(fHead != fTail);
89    }
90
91    int count2 = 0;
92    const GrTextStrike* strike = fHead;
93    while (strike) {
94        count2 += 1;
95        strike = strike->fNext;
96    }
97    GrAssert(count == count2);
98
99    count2 = 0;
100    strike = fTail;
101    while (strike) {
102        count2 += 1;
103        strike = strike->fPrev;
104    }
105    GrAssert(count == count2);
106}
107#endif
108
109///////////////////////////////////////////////////////////////////////////////
110
111#if GR_DEBUG
112    static int gCounter;
113#endif
114
115/*
116    The text strike is specific to a given font/style/matrix setup, which is
117    represented by the GrHostFontScaler object we are given in getGlyph().
118
119    We map a 32bit glyphID to a GrGlyph record, which in turn points to a
120    atlas and a position within that texture.
121 */
122
123GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
124                           GrMaskFormat format,
125                           GrAtlasMgr* atlasMgr) : fPool(64) {
126    fFontScalerKey = key;
127    fFontScalerKey->ref();
128
129    fFontCache = cache;     // no need to ref, it won't go away before we do
130    fAtlasMgr = atlasMgr;   // no need to ref, it won't go away before we do
131    fAtlas = NULL;
132
133    fMaskFormat = format;
134
135#if GR_DEBUG
136//    GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
137    gCounter += 1;
138#endif
139}
140
141static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); }
142
143GrTextStrike::~GrTextStrike() {
144    GrAtlas::FreeLList(fAtlas);
145    fFontScalerKey->unref();
146    fCache.getArray().visit(FreeGlyph);
147
148#if GR_DEBUG
149    gCounter -= 1;
150//    GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
151#endif
152}
153
154GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
155                                     GrFontScaler* scaler) {
156    GrIRect bounds;
157    if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
158        return NULL;
159    }
160
161    GrGlyph* glyph = fPool.alloc();
162    glyph->init(packed, bounds);
163    fCache.insert(packed, glyph);
164    return glyph;
165}
166
167bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
168#if 0   // testing hack to force us to flush our cache often
169    static int gCounter;
170    if ((++gCounter % 10) == 0) return false;
171#endif
172
173    GrAssert(glyph);
174    GrAssert(scaler);
175    GrAssert(fCache.contains(glyph));
176    if (glyph->fAtlas) {
177        return true;
178    }
179
180    GrAutoRef ar(scaler);
181
182    int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
183    size_t size = glyph->fBounds.area() * bytesPerPixel;
184    SkAutoSMalloc<1024> storage(size);
185    if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
186                                     glyph->height(),
187                                     glyph->width() * bytesPerPixel,
188                                     storage.get())) {
189        return false;
190    }
191
192    GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(),
193                                           glyph->height(), storage.get(),
194                                           fMaskFormat,
195                                           &glyph->fAtlasLocation);
196    if (NULL == atlas) {
197        return false;
198    }
199
200    // update fAtlas as well, since they may be chained in a linklist
201    glyph->fAtlas = fAtlas = atlas;
202    return true;
203}
204
205
206