blob: c44ad3882d679197c82c6edd44cfb3ee75eb439e [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"
reed@google.comac10a2d2010-12-22 21:39:39 +000020#include "GrRectanizer.h"
21#include "GrTextStrike.h"
22#include "GrTextStrike_impl.h"
23#include "GrRect.h"
24
25GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
26 gpu->ref();
27 fAtlasMgr = NULL;
28
29 fHead = fTail = NULL;
30}
31
32GrFontCache::~GrFontCache() {
33 fCache.deleteAll();
34 delete fAtlasMgr;
35 fGpu->unref();
36}
37
38GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
39 const Key& key) {
40 if (NULL == fAtlasMgr) {
41 fAtlasMgr = new GrAtlasMgr(fGpu);
42 }
reed@google.com98539c62011-03-15 15:40:16 +000043 GrTextStrike* strike = new GrTextStrike(this, scaler->getKey(),
44 scaler->getMaskFormat(), fAtlasMgr);
reed@google.comac10a2d2010-12-22 21:39:39 +000045 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
reed@google.comac10a2d2010-12-22 21:39:39 +000060void GrFontCache::freeAll() {
61 fCache.deleteAll();
62 delete fAtlasMgr;
63 fAtlasMgr = NULL;
bsalomon@google.com8fe72472011-03-30 21:26:44 +000064 fHead = NULL;
65 fTail = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000066}
67
68void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
69 GrTextStrike* strike = fTail;
bsalomon@google.com7359eae2011-06-21 21:18:25 +000070 while (strike) {
71 if (strike == preserveStrike) {
72 strike = strike->fPrev;
73 continue;
74 }
75 GrTextStrike* strikeToPurge = strike;
76 // keep going if we won't free up any atlases with this strike.
77 strike = (NULL == strikeToPurge->fAtlas) ? strikeToPurge->fPrev : NULL;
78 int index = fCache.slowFindIndex(strikeToPurge);
reed@google.comac10a2d2010-12-22 21:39:39 +000079 GrAssert(index >= 0);
bsalomon@google.com7359eae2011-06-21 21:18:25 +000080 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
81 this->detachStrikeFromList(strikeToPurge);
82 delete strikeToPurge;
reed@google.comac10a2d2010-12-22 21:39:39 +000083 }
84}
85
86#if GR_DEBUG
87void GrFontCache::validate() const {
88 int count = fCache.count();
89 if (0 == count) {
90 GrAssert(!fHead);
91 GrAssert(!fTail);
92 } else if (1 == count) {
93 GrAssert(fHead == fTail);
94 } else {
95 GrAssert(fHead != fTail);
96 }
97
98 int count2 = 0;
99 const GrTextStrike* strike = fHead;
100 while (strike) {
101 count2 += 1;
102 strike = strike->fNext;
103 }
104 GrAssert(count == count2);
105
106 count2 = 0;
107 strike = fTail;
108 while (strike) {
109 count2 += 1;
110 strike = strike->fPrev;
111 }
112 GrAssert(count == count2);
113}
114#endif
115
116///////////////////////////////////////////////////////////////////////////////
117
118#if GR_DEBUG
119 static int gCounter;
120#endif
121
122/*
123 The text strike is specific to a given font/style/matrix setup, which is
124 represented by the GrHostFontScaler object we are given in getGlyph().
125
126 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
127 atlas and a position within that texture.
128 */
129
130GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
reed@google.com98539c62011-03-15 15:40:16 +0000131 GrMaskFormat format,
reed@google.comac10a2d2010-12-22 21:39:39 +0000132 GrAtlasMgr* atlasMgr) : fPool(64) {
133 fFontScalerKey = key;
134 fFontScalerKey->ref();
135
136 fFontCache = cache; // no need to ref, it won't go away before we do
137 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
138 fAtlas = NULL;
139
reed@google.com98539c62011-03-15 15:40:16 +0000140 fMaskFormat = format;
141
reed@google.comac10a2d2010-12-22 21:39:39 +0000142#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) {
reed@google.com0ebe81a2011-04-04 20:06:59 +0000175#if 0 // testing hack to force us to flush our cache often
176 static int gCounter;
177 if ((++gCounter % 10) == 0) return false;
178#endif
179
reed@google.comac10a2d2010-12-22 21:39:39 +0000180 GrAssert(glyph);
181 GrAssert(scaler);
182 GrAssert(fCache.contains(glyph));
183 if (glyph->fAtlas) {
184 return true;
185 }
186
187 GrAutoRef ar(scaler);
reed@google.com98539c62011-03-15 15:40:16 +0000188
189 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
190 size_t size = glyph->fBounds.area() * bytesPerPixel;
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000191 SkAutoSMalloc<1024> storage(size);
reed@google.comac10a2d2010-12-22 21:39:39 +0000192 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
reed@google.com98539c62011-03-15 15:40:16 +0000193 glyph->height(),
194 glyph->width() * bytesPerPixel,
reed@google.comac10a2d2010-12-22 21:39:39 +0000195 storage.get())) {
196 return false;
197 }
198
199 GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(),
200 glyph->height(), storage.get(),
reed@google.com98539c62011-03-15 15:40:16 +0000201 fMaskFormat,
reed@google.comac10a2d2010-12-22 21:39:39 +0000202 &glyph->fAtlasLocation);
203 if (NULL == atlas) {
204 return false;
205 }
206
207 // update fAtlas as well, since they may be chained in a linklist
208 glyph->fAtlas = fAtlas = atlas;
209 return true;
210}
211
212