blob: e61c46e3b8bbb26887084b7a25120b763e279e6d [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@google.comac10a2d2010-12-22 21:39:39 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * 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.
reed@google.comac10a2d2010-12-22 21:39:39 +00007 */
8
9
epoger@google.comec3ed6a2011-07-28 14:26:00 +000010
reed@google.comac10a2d2010-12-22 21:39:39 +000011#include "GrAtlas.h"
12#include "GrGpu.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000013#include "GrRectanizer.h"
14#include "GrTextStrike.h"
15#include "GrTextStrike_impl.h"
16#include "GrRect.h"
17
reed@google.comfa35e3d2012-06-26 20:16:17 +000018SK_DEFINE_INST_COUNT(GrFontScaler)
19SK_DEFINE_INST_COUNT(GrKey)
20
21///////////////////////////////////////////////////////////////////////////////
22
reed@google.comac10a2d2010-12-22 21:39:39 +000023GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
24 gpu->ref();
25 fAtlasMgr = NULL;
26
27 fHead = fTail = NULL;
28}
29
30GrFontCache::~GrFontCache() {
31 fCache.deleteAll();
32 delete fAtlasMgr;
33 fGpu->unref();
34}
35
36GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
37 const Key& key) {
38 if (NULL == fAtlasMgr) {
39 fAtlasMgr = new GrAtlasMgr(fGpu);
40 }
reed@google.com98539c62011-03-15 15:40:16 +000041 GrTextStrike* strike = new GrTextStrike(this, scaler->getKey(),
42 scaler->getMaskFormat(), fAtlasMgr);
reed@google.comac10a2d2010-12-22 21:39:39 +000043 fCache.insert(key, strike);
44
45 if (fHead) {
46 fHead->fPrev = strike;
47 } else {
48 GrAssert(NULL == fTail);
49 fTail = strike;
50 }
51 strike->fPrev = NULL;
52 strike->fNext = fHead;
53 fHead = strike;
54
55 return strike;
56}
57
reed@google.comac10a2d2010-12-22 21:39:39 +000058void GrFontCache::freeAll() {
59 fCache.deleteAll();
60 delete fAtlasMgr;
61 fAtlasMgr = NULL;
bsalomon@google.com8fe72472011-03-30 21:26:44 +000062 fHead = NULL;
63 fTail = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000064}
65
66void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
67 GrTextStrike* strike = fTail;
bsalomon@google.com7359eae2011-06-21 21:18:25 +000068 while (strike) {
69 if (strike == preserveStrike) {
70 strike = strike->fPrev;
71 continue;
72 }
73 GrTextStrike* strikeToPurge = strike;
74 // keep going if we won't free up any atlases with this strike.
75 strike = (NULL == strikeToPurge->fAtlas) ? strikeToPurge->fPrev : NULL;
76 int index = fCache.slowFindIndex(strikeToPurge);
reed@google.comac10a2d2010-12-22 21:39:39 +000077 GrAssert(index >= 0);
bsalomon@google.com7359eae2011-06-21 21:18:25 +000078 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
79 this->detachStrikeFromList(strikeToPurge);
80 delete strikeToPurge;
reed@google.comac10a2d2010-12-22 21:39:39 +000081 }
82}
83
84#if GR_DEBUG
85void GrFontCache::validate() const {
86 int count = fCache.count();
87 if (0 == count) {
88 GrAssert(!fHead);
89 GrAssert(!fTail);
90 } else if (1 == count) {
91 GrAssert(fHead == fTail);
92 } else {
93 GrAssert(fHead != fTail);
94 }
95
96 int count2 = 0;
97 const GrTextStrike* strike = fHead;
98 while (strike) {
99 count2 += 1;
100 strike = strike->fNext;
101 }
102 GrAssert(count == count2);
103
104 count2 = 0;
105 strike = fTail;
106 while (strike) {
107 count2 += 1;
108 strike = strike->fPrev;
109 }
110 GrAssert(count == count2);
111}
112#endif
113
114///////////////////////////////////////////////////////////////////////////////
115
116#if GR_DEBUG
117 static int gCounter;
118#endif
119
120/*
121 The text strike is specific to a given font/style/matrix setup, which is
122 represented by the GrHostFontScaler object we are given in getGlyph().
123
124 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
125 atlas and a position within that texture.
126 */
127
128GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
reed@google.com98539c62011-03-15 15:40:16 +0000129 GrMaskFormat format,
reed@google.comac10a2d2010-12-22 21:39:39 +0000130 GrAtlasMgr* atlasMgr) : fPool(64) {
131 fFontScalerKey = key;
132 fFontScalerKey->ref();
133
134 fFontCache = cache; // no need to ref, it won't go away before we do
135 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
136 fAtlas = NULL;
137
reed@google.com98539c62011-03-15 15:40:16 +0000138 fMaskFormat = format;
139
reed@google.comac10a2d2010-12-22 21:39:39 +0000140#if GR_DEBUG
reed@google.com3ef80cf2011-07-05 19:09:47 +0000141// GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000142 gCounter += 1;
143#endif
144}
145
146static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); }
147
148GrTextStrike::~GrTextStrike() {
149 GrAtlas::FreeLList(fAtlas);
150 fFontScalerKey->unref();
151 fCache.getArray().visit(FreeGlyph);
152
153#if GR_DEBUG
154 gCounter -= 1;
reed@google.com3ef80cf2011-07-05 19:09:47 +0000155// GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000156#endif
157}
158
159GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
160 GrFontScaler* scaler) {
161 GrIRect bounds;
162 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
163 return NULL;
164 }
165
166 GrGlyph* glyph = fPool.alloc();
167 glyph->init(packed, bounds);
168 fCache.insert(packed, glyph);
169 return glyph;
170}
171
172bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
reed@google.com0ebe81a2011-04-04 20:06:59 +0000173#if 0 // testing hack to force us to flush our cache often
174 static int gCounter;
175 if ((++gCounter % 10) == 0) return false;
176#endif
177
reed@google.comac10a2d2010-12-22 21:39:39 +0000178 GrAssert(glyph);
179 GrAssert(scaler);
180 GrAssert(fCache.contains(glyph));
181 if (glyph->fAtlas) {
182 return true;
183 }
184
185 GrAutoRef ar(scaler);
reed@google.com98539c62011-03-15 15:40:16 +0000186
187 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
188 size_t size = glyph->fBounds.area() * bytesPerPixel;
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000189 SkAutoSMalloc<1024> storage(size);
reed@google.comac10a2d2010-12-22 21:39:39 +0000190 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
reed@google.com98539c62011-03-15 15:40:16 +0000191 glyph->height(),
192 glyph->width() * bytesPerPixel,
reed@google.comac10a2d2010-12-22 21:39:39 +0000193 storage.get())) {
194 return false;
195 }
196
197 GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(),
198 glyph->height(), storage.get(),
reed@google.com98539c62011-03-15 15:40:16 +0000199 fMaskFormat,
reed@google.comac10a2d2010-12-22 21:39:39 +0000200 &glyph->fAtlasLocation);
201 if (NULL == atlas) {
202 return false;
203 }
204
205 // update fAtlas as well, since they may be chained in a linklist
206 glyph->fAtlas = fAtlas = atlas;
207 return true;
208}
209
210