blob: aebaaf81b7161ba9cb1e80bcdd22f7d1f97b059d [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) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +000039 fAtlasMgr = SkNEW_ARGS(GrAtlasMgr, (fGpu));
reed@google.comac10a2d2010-12-22 21:39:39 +000040 }
tomhudson@google.comc377baf2012-07-09 20:17:56 +000041 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
42 (this, scaler->getKey(),
43 scaler->getMaskFormat(), fAtlasMgr));
reed@google.comac10a2d2010-12-22 21:39:39 +000044 fCache.insert(key, strike);
45
46 if (fHead) {
47 fHead->fPrev = strike;
48 } else {
49 GrAssert(NULL == fTail);
50 fTail = strike;
51 }
52 strike->fPrev = NULL;
53 strike->fNext = fHead;
54 fHead = strike;
55
56 return strike;
57}
58
reed@google.comac10a2d2010-12-22 21:39:39 +000059void GrFontCache::freeAll() {
60 fCache.deleteAll();
61 delete fAtlasMgr;
62 fAtlasMgr = NULL;
bsalomon@google.com8fe72472011-03-30 21:26:44 +000063 fHead = NULL;
64 fTail = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000065}
66
67void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
68 GrTextStrike* strike = fTail;
bsalomon@google.com7359eae2011-06-21 21:18:25 +000069 while (strike) {
70 if (strike == preserveStrike) {
71 strike = strike->fPrev;
72 continue;
73 }
74 GrTextStrike* strikeToPurge = strike;
75 // keep going if we won't free up any atlases with this strike.
76 strike = (NULL == strikeToPurge->fAtlas) ? strikeToPurge->fPrev : NULL;
77 int index = fCache.slowFindIndex(strikeToPurge);
reed@google.comac10a2d2010-12-22 21:39:39 +000078 GrAssert(index >= 0);
bsalomon@google.com7359eae2011-06-21 21:18:25 +000079 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
80 this->detachStrikeFromList(strikeToPurge);
81 delete strikeToPurge;
reed@google.comac10a2d2010-12-22 21:39:39 +000082 }
83}
84
85#if GR_DEBUG
86void GrFontCache::validate() const {
87 int count = fCache.count();
88 if (0 == count) {
89 GrAssert(!fHead);
90 GrAssert(!fTail);
91 } else if (1 == count) {
92 GrAssert(fHead == fTail);
93 } else {
94 GrAssert(fHead != fTail);
95 }
96
97 int count2 = 0;
98 const GrTextStrike* strike = fHead;
99 while (strike) {
100 count2 += 1;
101 strike = strike->fNext;
102 }
103 GrAssert(count == count2);
104
105 count2 = 0;
106 strike = fTail;
107 while (strike) {
108 count2 += 1;
109 strike = strike->fPrev;
110 }
111 GrAssert(count == count2);
112}
113#endif
114
115///////////////////////////////////////////////////////////////////////////////
116
117#if GR_DEBUG
118 static int gCounter;
119#endif
120
121/*
122 The text strike is specific to a given font/style/matrix setup, which is
123 represented by the GrHostFontScaler object we are given in getGlyph().
124
125 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
126 atlas and a position within that texture.
127 */
128
129GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
reed@google.com98539c62011-03-15 15:40:16 +0000130 GrMaskFormat format,
reed@google.comac10a2d2010-12-22 21:39:39 +0000131 GrAtlasMgr* atlasMgr) : fPool(64) {
132 fFontScalerKey = key;
133 fFontScalerKey->ref();
134
135 fFontCache = cache; // no need to ref, it won't go away before we do
136 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
137 fAtlas = NULL;
138
reed@google.com98539c62011-03-15 15:40:16 +0000139 fMaskFormat = format;
140
reed@google.comac10a2d2010-12-22 21:39:39 +0000141#if GR_DEBUG
reed@google.com3ef80cf2011-07-05 19:09:47 +0000142// GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000143 gCounter += 1;
144#endif
145}
146
147static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); }
148
149GrTextStrike::~GrTextStrike() {
150 GrAtlas::FreeLList(fAtlas);
151 fFontScalerKey->unref();
152 fCache.getArray().visit(FreeGlyph);
153
154#if GR_DEBUG
155 gCounter -= 1;
reed@google.com3ef80cf2011-07-05 19:09:47 +0000156// GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000157#endif
158}
159
160GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
161 GrFontScaler* scaler) {
162 GrIRect bounds;
163 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
164 return NULL;
165 }
166
167 GrGlyph* glyph = fPool.alloc();
168 glyph->init(packed, bounds);
169 fCache.insert(packed, glyph);
170 return glyph;
171}
172
173bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
reed@google.com0ebe81a2011-04-04 20:06:59 +0000174#if 0 // testing hack to force us to flush our cache often
175 static int gCounter;
176 if ((++gCounter % 10) == 0) return false;
177#endif
178
reed@google.comac10a2d2010-12-22 21:39:39 +0000179 GrAssert(glyph);
180 GrAssert(scaler);
181 GrAssert(fCache.contains(glyph));
182 if (glyph->fAtlas) {
183 return true;
184 }
185
186 GrAutoRef ar(scaler);
reed@google.com98539c62011-03-15 15:40:16 +0000187
188 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
189 size_t size = glyph->fBounds.area() * bytesPerPixel;
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000190 SkAutoSMalloc<1024> storage(size);
reed@google.comac10a2d2010-12-22 21:39:39 +0000191 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
reed@google.com98539c62011-03-15 15:40:16 +0000192 glyph->height(),
193 glyph->width() * bytesPerPixel,
reed@google.comac10a2d2010-12-22 21:39:39 +0000194 storage.get())) {
195 return false;
196 }
197
198 GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(),
199 glyph->height(), storage.get(),
reed@google.com98539c62011-03-15 15:40:16 +0000200 fMaskFormat,
reed@google.comac10a2d2010-12-22 21:39:39 +0000201 &glyph->fAtlasLocation);
202 if (NULL == atlas) {
203 return false;
204 }
205
206 // update fAtlas as well, since they may be chained in a linklist
207 glyph->fAtlas = fAtlas = atlas;
208 return true;
209}
210
211