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