blob: c2d81d544affdb26f3c10dff1568631cc7bf3c60 [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001/*
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
26GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
27 gpu->ref();
28 fAtlasMgr = NULL;
29
30 fHead = fTail = NULL;
31}
32
33GrFontCache::~GrFontCache() {
34 fCache.deleteAll();
35 delete fAtlasMgr;
36 fGpu->unref();
37}
38
39GrTextStrike* 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(), fAtlasMgr);
45 fCache.insert(key, strike);
46
47 if (fHead) {
48 fHead->fPrev = strike;
49 } else {
50 GrAssert(NULL == fTail);
51 fTail = strike;
52 }
53 strike->fPrev = NULL;
54 strike->fNext = fHead;
55 fHead = strike;
56
57 return strike;
58}
59
60void GrFontCache::abandonAll() {
61 fCache.deleteAll();
62 if (fAtlasMgr) {
63 fAtlasMgr->abandonAll();
64 delete fAtlasMgr;
65 fAtlasMgr = NULL;
66 }
67}
68
69void GrFontCache::freeAll() {
70 fCache.deleteAll();
71 delete fAtlasMgr;
72 fAtlasMgr = NULL;
73}
74
75void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
76 GrTextStrike* strike = fTail;
77 if (strike == preserveStrike) {
78 strike = strike->fPrev;
79 }
80 if (strike) {
81 int index = fCache.slowFindIndex(strike);
82 GrAssert(index >= 0);
83 fCache.removeAt(index, strike->fFontScalerKey->getHash());
84 this->detachStrikeFromList(strike);
85 delete strike;
86 }
87}
88
89#if GR_DEBUG
90void GrFontCache::validate() const {
91 int count = fCache.count();
92 if (0 == count) {
93 GrAssert(!fHead);
94 GrAssert(!fTail);
95 } else if (1 == count) {
96 GrAssert(fHead == fTail);
97 } else {
98 GrAssert(fHead != fTail);
99 }
100
101 int count2 = 0;
102 const GrTextStrike* strike = fHead;
103 while (strike) {
104 count2 += 1;
105 strike = strike->fNext;
106 }
107 GrAssert(count == count2);
108
109 count2 = 0;
110 strike = fTail;
111 while (strike) {
112 count2 += 1;
113 strike = strike->fPrev;
114 }
115 GrAssert(count == count2);
116}
117#endif
118
119///////////////////////////////////////////////////////////////////////////////
120
121#if GR_DEBUG
122 static int gCounter;
123#endif
124
125/*
126 The text strike is specific to a given font/style/matrix setup, which is
127 represented by the GrHostFontScaler object we are given in getGlyph().
128
129 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
130 atlas and a position within that texture.
131 */
132
133GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
134 GrAtlasMgr* atlasMgr) : fPool(64) {
135 fFontScalerKey = key;
136 fFontScalerKey->ref();
137
138 fFontCache = cache; // no need to ref, it won't go away before we do
139 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
140 fAtlas = NULL;
141
142#if GR_DEBUG
143 GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
144 gCounter += 1;
145#endif
146}
147
148static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); }
149
150GrTextStrike::~GrTextStrike() {
151 GrAtlas::FreeLList(fAtlas);
152 fFontScalerKey->unref();
153 fCache.getArray().visit(FreeGlyph);
154
155#if GR_DEBUG
156 gCounter -= 1;
157 GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
158#endif
159}
160
161GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
162 GrFontScaler* scaler) {
163 GrIRect bounds;
164 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
165 return NULL;
166 }
167
168 GrGlyph* glyph = fPool.alloc();
169 glyph->init(packed, bounds);
170 fCache.insert(packed, glyph);
171 return glyph;
172}
173
174bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
175 GrAssert(glyph);
176 GrAssert(scaler);
177 GrAssert(fCache.contains(glyph));
178 if (glyph->fAtlas) {
179 return true;
180 }
181
182 GrAutoRef ar(scaler);
183
184 size_t size = glyph->fBounds.area();
185 GrAutoSMalloc<1024> storage(size);
186 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
187 glyph->height(), glyph->width(),
188 storage.get())) {
189 return false;
190 }
191
192 GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(),
193 glyph->height(), storage.get(),
194 &glyph->fAtlasLocation);
195 if (NULL == atlas) {
196 return false;
197 }
198
199 // update fAtlas as well, since they may be chained in a linklist
200 glyph->fAtlas = fAtlas = atlas;
201 return true;
202}
203
204