blob: 455f88a7bb63b9d2a6e893dbc3bcd689247b3b20 [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 }
reed@google.com98539c62011-03-15 15:40:16 +000044 GrTextStrike* strike = new GrTextStrike(this, scaler->getKey(),
45 scaler->getMaskFormat(), fAtlasMgr);
reed@google.comac10a2d2010-12-22 21:39:39 +000046 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
reed@google.comac10a2d2010-12-22 21:39:39 +000061void GrFontCache::freeAll() {
62 fCache.deleteAll();
63 delete fAtlasMgr;
64 fAtlasMgr = NULL;
bsalomon@google.com8fe72472011-03-30 21:26:44 +000065 fHead = NULL;
66 fTail = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000067}
68
69void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
70 GrTextStrike* strike = fTail;
bsalomon@google.com7359eae2011-06-21 21:18:25 +000071 while (strike) {
72 if (strike == preserveStrike) {
73 strike = strike->fPrev;
74 continue;
75 }
76 GrTextStrike* strikeToPurge = strike;
77 // keep going if we won't free up any atlases with this strike.
78 strike = (NULL == strikeToPurge->fAtlas) ? strikeToPurge->fPrev : NULL;
79 int index = fCache.slowFindIndex(strikeToPurge);
reed@google.comac10a2d2010-12-22 21:39:39 +000080 GrAssert(index >= 0);
bsalomon@google.com7359eae2011-06-21 21:18:25 +000081 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
82 this->detachStrikeFromList(strikeToPurge);
83 delete strikeToPurge;
reed@google.comac10a2d2010-12-22 21:39:39 +000084 }
85}
86
87#if GR_DEBUG
88void GrFontCache::validate() const {
89 int count = fCache.count();
90 if (0 == count) {
91 GrAssert(!fHead);
92 GrAssert(!fTail);
93 } else if (1 == count) {
94 GrAssert(fHead == fTail);
95 } else {
96 GrAssert(fHead != fTail);
97 }
98
99 int count2 = 0;
100 const GrTextStrike* strike = fHead;
101 while (strike) {
102 count2 += 1;
103 strike = strike->fNext;
104 }
105 GrAssert(count == count2);
106
107 count2 = 0;
108 strike = fTail;
109 while (strike) {
110 count2 += 1;
111 strike = strike->fPrev;
112 }
113 GrAssert(count == count2);
114}
115#endif
116
117///////////////////////////////////////////////////////////////////////////////
118
119#if GR_DEBUG
120 static int gCounter;
121#endif
122
123/*
124 The text strike is specific to a given font/style/matrix setup, which is
125 represented by the GrHostFontScaler object we are given in getGlyph().
126
127 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
128 atlas and a position within that texture.
129 */
130
131GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
reed@google.com98539c62011-03-15 15:40:16 +0000132 GrMaskFormat format,
reed@google.comac10a2d2010-12-22 21:39:39 +0000133 GrAtlasMgr* atlasMgr) : fPool(64) {
134 fFontScalerKey = key;
135 fFontScalerKey->ref();
136
137 fFontCache = cache; // no need to ref, it won't go away before we do
138 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
139 fAtlas = NULL;
140
reed@google.com98539c62011-03-15 15:40:16 +0000141 fMaskFormat = format;
142
reed@google.comac10a2d2010-12-22 21:39:39 +0000143#if GR_DEBUG
144 GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
145 gCounter += 1;
146#endif
147}
148
149static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); }
150
151GrTextStrike::~GrTextStrike() {
152 GrAtlas::FreeLList(fAtlas);
153 fFontScalerKey->unref();
154 fCache.getArray().visit(FreeGlyph);
155
156#if GR_DEBUG
157 gCounter -= 1;
158 GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
159#endif
160}
161
162GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
163 GrFontScaler* scaler) {
164 GrIRect bounds;
165 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
166 return NULL;
167 }
168
169 GrGlyph* glyph = fPool.alloc();
170 glyph->init(packed, bounds);
171 fCache.insert(packed, glyph);
172 return glyph;
173}
174
175bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
reed@google.com0ebe81a2011-04-04 20:06:59 +0000176#if 0 // testing hack to force us to flush our cache often
177 static int gCounter;
178 if ((++gCounter % 10) == 0) return false;
179#endif
180
reed@google.comac10a2d2010-12-22 21:39:39 +0000181 GrAssert(glyph);
182 GrAssert(scaler);
183 GrAssert(fCache.contains(glyph));
184 if (glyph->fAtlas) {
185 return true;
186 }
187
188 GrAutoRef ar(scaler);
reed@google.com98539c62011-03-15 15:40:16 +0000189
190 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
191 size_t size = glyph->fBounds.area() * bytesPerPixel;
reed@google.comac10a2d2010-12-22 21:39:39 +0000192 GrAutoSMalloc<1024> storage(size);
193 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
reed@google.com98539c62011-03-15 15:40:16 +0000194 glyph->height(),
195 glyph->width() * bytesPerPixel,
reed@google.comac10a2d2010-12-22 21:39:39 +0000196 storage.get())) {
197 return false;
198 }
199
200 GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(),
201 glyph->height(), storage.get(),
reed@google.com98539c62011-03-15 15:40:16 +0000202 fMaskFormat,
reed@google.comac10a2d2010-12-22 21:39:39 +0000203 &glyph->fAtlasLocation);
204 if (NULL == atlas) {
205 return false;
206 }
207
208 // update fAtlas as well, since they may be chained in a linklist
209 glyph->fAtlas = fAtlas = atlas;
210 return true;
211}
212
213