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
GrFontCache(GrGpu * gpu)18 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
19 gpu->ref();
20 fAtlasMgr = NULL;
21
22 fHead = fTail = NULL;
23 }
24
~GrFontCache()25 GrFontCache::~GrFontCache() {
26 fCache.deleteAll();
27 delete fAtlasMgr;
28 fGpu->unref();
29 }
30
generateStrike(GrFontScaler * scaler,const Key & key)31 GrTextStrike* 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
freeAll()53 void GrFontCache::freeAll() {
54 fCache.deleteAll();
55 delete fAtlasMgr;
56 fAtlasMgr = NULL;
57 fHead = NULL;
58 fTail = NULL;
59 }
60
purgeExceptFor(GrTextStrike * preserveStrike)61 void 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
validate() const80 void 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
GrTextStrike(GrFontCache * cache,const GrKey * key,GrMaskFormat format,GrAtlasMgr * atlasMgr)123 GrTextStrike::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
FreeGlyph(GrGlyph * & glyph)141 static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); }
142
~GrTextStrike()143 GrTextStrike::~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
generateGlyph(GrGlyph::PackedID packed,GrFontScaler * scaler)154 GrGlyph* 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
getGlyphAtlas(GrGlyph * glyph,GrFontScaler * scaler)167 bool 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