blob: d3214a7012e06b96fa85481340ab7ad3728b2653 [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2010 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@google.comac10a2d2010-12-22 21:39:39 +00006 */
7
reed@google.comac10a2d2010-12-22 21:39:39 +00008#include "GrAtlas.h"
9#include "GrGpu.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000010#include "GrRectanizer.h"
11#include "GrTextStrike.h"
12#include "GrTextStrike_impl.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000013
reed@google.comfa35e3d2012-06-26 20:16:17 +000014SK_DEFINE_INST_COUNT(GrFontScaler)
15SK_DEFINE_INST_COUNT(GrKey)
16
17///////////////////////////////////////////////////////////////////////////////
18
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000019#define FONT_CACHE_STATS 0
20#if FONT_CACHE_STATS
21static int g_PurgeCount = 0;
22#endif
23
reed@google.comac10a2d2010-12-22 21:39:39 +000024GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
25 gpu->ref();
26 fAtlasMgr = NULL;
27
28 fHead = fTail = NULL;
29}
30
31GrFontCache::~GrFontCache() {
32 fCache.deleteAll();
33 delete fAtlasMgr;
34 fGpu->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000035#if FONT_CACHE_STATS
36 GrPrintf("Num purges: %d\n", g_PurgeCount);
37#endif
reed@google.comac10a2d2010-12-22 21:39:39 +000038}
39
40GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
41 const Key& key) {
42 if (NULL == fAtlasMgr) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +000043 fAtlasMgr = SkNEW_ARGS(GrAtlasMgr, (fGpu));
reed@google.comac10a2d2010-12-22 21:39:39 +000044 }
tomhudson@google.comc377baf2012-07-09 20:17:56 +000045 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
46 (this, scaler->getKey(),
47 scaler->getMaskFormat(), fAtlasMgr));
reed@google.comac10a2d2010-12-22 21:39:39 +000048 fCache.insert(key, strike);
49
50 if (fHead) {
51 fHead->fPrev = strike;
52 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000053 SkASSERT(NULL == fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +000054 fTail = strike;
55 }
56 strike->fPrev = NULL;
57 strike->fNext = fHead;
58 fHead = strike;
59
60 return strike;
61}
62
reed@google.comac10a2d2010-12-22 21:39:39 +000063void GrFontCache::freeAll() {
64 fCache.deleteAll();
65 delete fAtlasMgr;
66 fAtlasMgr = NULL;
bsalomon@google.com8fe72472011-03-30 21:26:44 +000067 fHead = NULL;
68 fTail = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000069}
70
71void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
72 GrTextStrike* strike = fTail;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000073 bool purge = true;
bsalomon@google.com7359eae2011-06-21 21:18:25 +000074 while (strike) {
75 if (strike == preserveStrike) {
76 strike = strike->fPrev;
77 continue;
78 }
79 GrTextStrike* strikeToPurge = strike;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000080 strike = strikeToPurge->fPrev;
81 if (purge) {
82 // keep purging if we won't free up any atlases with this strike.
83 purge = (NULL == strikeToPurge->fAtlas);
84 int index = fCache.slowFindIndex(strikeToPurge);
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000085 SkASSERT(index >= 0);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000086 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
87 this->detachStrikeFromList(strikeToPurge);
88 delete strikeToPurge;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000089 }
90 }
91#if FONT_CACHE_STATS
92 ++g_PurgeCount;
93#endif
94}
95
96void GrFontCache::freeAtlasExceptFor(GrTextStrike* preserveStrike) {
97 GrTextStrike* strike = fTail;
98 while (strike) {
99 if (strike == preserveStrike) {
100 strike = strike->fPrev;
101 continue;
102 }
103 GrTextStrike* strikeToPurge = strike;
104 strike = strikeToPurge->fPrev;
105 if (strikeToPurge->removeUnusedAtlases()) {
106 if (NULL == strikeToPurge->fAtlas) {
107 int index = fCache.slowFindIndex(strikeToPurge);
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000108 SkASSERT(index >= 0);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000109 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
110 this->detachStrikeFromList(strikeToPurge);
111 delete strikeToPurge;
112 }
113 break;
114 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000115 }
116}
117
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000118#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000119void GrFontCache::validate() const {
120 int count = fCache.count();
121 if (0 == count) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000122 SkASSERT(!fHead);
123 SkASSERT(!fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000124 } else if (1 == count) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000125 SkASSERT(fHead == fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000126 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000127 SkASSERT(fHead != fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000128 }
129
130 int count2 = 0;
131 const GrTextStrike* strike = fHead;
132 while (strike) {
133 count2 += 1;
134 strike = strike->fNext;
135 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000136 SkASSERT(count == count2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000137
138 count2 = 0;
139 strike = fTail;
140 while (strike) {
141 count2 += 1;
142 strike = strike->fPrev;
143 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000144 SkASSERT(count == count2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000145}
146#endif
147
148///////////////////////////////////////////////////////////////////////////////
149
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000150#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000151 static int gCounter;
152#endif
153
154/*
155 The text strike is specific to a given font/style/matrix setup, which is
156 represented by the GrHostFontScaler object we are given in getGlyph().
157
158 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
159 atlas and a position within that texture.
160 */
161
162GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
reed@google.com98539c62011-03-15 15:40:16 +0000163 GrMaskFormat format,
reed@google.comac10a2d2010-12-22 21:39:39 +0000164 GrAtlasMgr* atlasMgr) : fPool(64) {
165 fFontScalerKey = key;
166 fFontScalerKey->ref();
167
168 fFontCache = cache; // no need to ref, it won't go away before we do
169 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
170 fAtlas = NULL;
171
reed@google.com98539c62011-03-15 15:40:16 +0000172 fMaskFormat = format;
173
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000174#ifdef SK_DEBUG
reed@google.com3ef80cf2011-07-05 19:09:47 +0000175// GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000176 gCounter += 1;
177#endif
178}
179
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000180// these signatures are needed because they're used with
181// SkTDArray::visitAll() (see destructor & removeUnusedAtlases())
182static void free_glyph(GrGlyph*& glyph) { glyph->free(); }
183
184static void invalidate_glyph(GrGlyph*& glyph) {
commit-bot@chromium.orga8916ff2013-08-16 15:53:46 +0000185 if (glyph->fAtlas && glyph->fAtlas->drawToken().isIssued()) {
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000186 glyph->fAtlas = NULL;
187 }
188}
reed@google.comac10a2d2010-12-22 21:39:39 +0000189
190GrTextStrike::~GrTextStrike() {
191 GrAtlas::FreeLList(fAtlas);
192 fFontScalerKey->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000193 fCache.getArray().visitAll(free_glyph);
reed@google.comac10a2d2010-12-22 21:39:39 +0000194
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000195#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000196 gCounter -= 1;
reed@google.com3ef80cf2011-07-05 19:09:47 +0000197// GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000198#endif
199}
200
201GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
202 GrFontScaler* scaler) {
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000203 SkIRect bounds;
reed@google.comac10a2d2010-12-22 21:39:39 +0000204 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
205 return NULL;
206 }
207
208 GrGlyph* glyph = fPool.alloc();
209 glyph->init(packed, bounds);
210 fCache.insert(packed, glyph);
211 return glyph;
212}
213
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000214bool GrTextStrike::removeUnusedAtlases() {
215 fCache.getArray().visitAll(invalidate_glyph);
216 return GrAtlas::RemoveUnusedAtlases(fAtlasMgr, &fAtlas);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000217}
218
commit-bot@chromium.orga8916ff2013-08-16 15:53:46 +0000219bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler,
220 GrDrawTarget::DrawToken currentDrawToken) {
reed@google.com0ebe81a2011-04-04 20:06:59 +0000221#if 0 // testing hack to force us to flush our cache often
222 static int gCounter;
223 if ((++gCounter % 10) == 0) return false;
224#endif
225
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000226 SkASSERT(glyph);
227 SkASSERT(scaler);
228 SkASSERT(fCache.contains(glyph));
reed@google.comac10a2d2010-12-22 21:39:39 +0000229 if (glyph->fAtlas) {
commit-bot@chromium.orga8916ff2013-08-16 15:53:46 +0000230 glyph->fAtlas->setDrawToken(currentDrawToken);
reed@google.comac10a2d2010-12-22 21:39:39 +0000231 return true;
232 }
233
commit-bot@chromium.orga4de8c22013-09-09 13:38:37 +0000234 SkAutoRef ar(scaler);
reed@google.com98539c62011-03-15 15:40:16 +0000235
236 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
237 size_t size = glyph->fBounds.area() * bytesPerPixel;
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000238 SkAutoSMalloc<1024> storage(size);
reed@google.comac10a2d2010-12-22 21:39:39 +0000239 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
reed@google.com98539c62011-03-15 15:40:16 +0000240 glyph->height(),
241 glyph->width() * bytesPerPixel,
reed@google.comac10a2d2010-12-22 21:39:39 +0000242 storage.get())) {
243 return false;
244 }
245
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000246 GrAtlas* atlas = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
reed@google.comac10a2d2010-12-22 21:39:39 +0000247 glyph->height(), storage.get(),
reed@google.com98539c62011-03-15 15:40:16 +0000248 fMaskFormat,
reed@google.comac10a2d2010-12-22 21:39:39 +0000249 &glyph->fAtlasLocation);
250 if (NULL == atlas) {
251 return false;
252 }
253
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000254 glyph->fAtlas = atlas;
commit-bot@chromium.orga8916ff2013-08-16 15:53:46 +0000255 atlas->setDrawToken(currentDrawToken);
reed@google.comac10a2d2010-12-22 21:39:39 +0000256 return true;
257}