blob: 14bf4a5d61ef8a6bb40929a916b9ede20c64409c [file] [log] [blame]
joshualittddd22d82016-02-16 06:47:52 -08001/*
2 * Copyright 2016 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.
6 */
7
8#include "GrAtlasTextBlob.h"
9
10#include "GrBatchFlushState.h"
joshualitt8e84a1e2016-02-16 11:09:25 -080011#include "GrTextUtils.h"
joshualittddd22d82016-02-16 06:47:52 -080012
13#include "SkDistanceFieldGen.h"
14#include "SkGlyphCache.h"
15
16#include "batches/GrAtlasTextBatch.h"
17
18////////////////////////////////////////////////////////////////////////////////////////////////////
19// A large template to handle regenerating the vertices of a textblob with as few branches as
20// possible
21template <bool regenPos, bool regenCol, bool regenTexCoords>
22inline void regen_vertices(intptr_t vertex, const GrGlyph* glyph, size_t vertexStride,
23 bool useDistanceFields, SkScalar transX, SkScalar transY,
24 GrColor color) {
25 int u0, v0, u1, v1;
26 if (regenTexCoords) {
27 SkASSERT(glyph);
28 int width = glyph->fBounds.width();
29 int height = glyph->fBounds.height();
30
31 if (useDistanceFields) {
32 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
33 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
34 u1 = u0 + width - 2 * SK_DistanceFieldInset;
35 v1 = v0 + height - 2 * SK_DistanceFieldInset;
36 } else {
37 u0 = glyph->fAtlasLocation.fX;
38 v0 = glyph->fAtlasLocation.fY;
39 u1 = u0 + width;
40 v1 = v0 + height;
41 }
42 }
43
44 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
45 // vertices, hence vertexStride - sizeof(SkIPoint16)
46 intptr_t colorOffset = sizeof(SkPoint);
47 intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
48
49 // V0
50 if (regenPos) {
51 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
52 point->fX += transX;
53 point->fY += transY;
54 }
55
56 if (regenCol) {
57 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
58 *vcolor = color;
59 }
60
61 if (regenTexCoords) {
62 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCoordOffset);
63 textureCoords->set(u0, v0);
64 }
65 vertex += vertexStride;
66
67 // V1
68 if (regenPos) {
69 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
70 point->fX += transX;
71 point->fY += transY;
72 }
73
74 if (regenCol) {
75 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
76 *vcolor = color;
77 }
78
79 if (regenTexCoords) {
80 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCoordOffset);
81 textureCoords->set(u0, v1);
82 }
83 vertex += vertexStride;
84
85 // V2
86 if (regenPos) {
87 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
88 point->fX += transX;
89 point->fY += transY;
90 }
91
92 if (regenCol) {
93 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
94 *vcolor = color;
95 }
96
97 if (regenTexCoords) {
98 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCoordOffset);
99 textureCoords->set(u1, v1);
100 }
101 vertex += vertexStride;
102
103 // V3
104 if (regenPos) {
105 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
106 point->fX += transX;
107 point->fY += transY;
108 }
109
110 if (regenCol) {
111 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
112 *vcolor = color;
113 }
114
115 if (regenTexCoords) {
116 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCoordOffset);
117 textureCoords->set(u1, v0);
118 }
119}
120
121template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
122void GrAtlasTextBlob::regenInBatch(GrDrawBatch::Target* target,
123 GrBatchFontCache* fontCache,
124 GrBlobRegenHelper *helper,
125 Run* run,
126 Run::SubRunInfo* info, SkGlyphCache** cache,
127 SkTypeface** typeface, GrFontScaler** scaler,
128 const SkDescriptor** desc,
129 int glyphCount, size_t vertexStride,
130 GrColor color, SkScalar transX,
131 SkScalar transY) const {
132 static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
133 GrBatchTextStrike* strike = nullptr;
134 if (regenTexCoords) {
135 info->resetBulkUseToken();
136
137 // We can reuse if we have a valid strike and our descriptors / typeface are the
138 // same. The override descriptor is only for the non distance field text within
139 // a run
140 const SkDescriptor* newDesc = (run->fOverrideDescriptor && !info->drawAsDistanceFields()) ?
141 run->fOverrideDescriptor->getDesc() :
142 run->fDescriptor.getDesc();
143 if (!*cache || !SkTypeface::Equal(*typeface, run->fTypeface) ||
144 !((*desc)->equals(*newDesc))) {
145 if (*cache) {
146 SkGlyphCache::AttachCache(*cache);
147 }
148 *desc = newDesc;
149 *cache = SkGlyphCache::DetachCache(run->fTypeface, *desc);
joshualitt8e84a1e2016-02-16 11:09:25 -0800150 *scaler = GrTextUtils::GetGrFontScaler(*cache);
joshualittddd22d82016-02-16 06:47:52 -0800151 *typeface = run->fTypeface;
152 }
153
154 if (regenGlyphs) {
155 strike = fontCache->getStrike(*scaler);
156 } else {
157 strike = info->strike();
158 }
159 }
160
161 bool brokenRun = false;
162 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
163 GrGlyph* glyph = nullptr;
164 if (regenTexCoords) {
165 size_t glyphOffset = glyphIdx + info->glyphStartIndex();
166
167 if (regenGlyphs) {
168 // Get the id from the old glyph, and use the new strike to lookup
169 // the glyph.
170 GrGlyph::PackedID id = fGlyphs[glyphOffset]->fPackedID;
171 fGlyphs[glyphOffset] = strike->getGlyph(id, info->maskFormat(), *scaler);
172 SkASSERT(id == fGlyphs[glyphOffset]->fPackedID);
173 }
174 glyph = fGlyphs[glyphOffset];
175 SkASSERT(glyph && glyph->fMaskFormat == info->maskFormat());
176
177 if (!fontCache->hasGlyph(glyph) &&
178 !strike->addGlyphToAtlas(target, glyph, *scaler, info->maskFormat())) {
179 helper->flush();
180 brokenRun = glyphIdx > 0;
181
182 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
183 glyph,
184 *scaler,
185 info->maskFormat());
186 SkASSERT(success);
187 }
188 fontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph,
189 target->currentToken());
190 }
191
192 intptr_t vertex = reinterpret_cast<intptr_t>(fVertices);
193 vertex += info->vertexStartIndex();
194 vertex += vertexStride * glyphIdx * GrAtlasTextBatch::kVerticesPerGlyph;
195 regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
196 info->drawAsDistanceFields(), transX,
197 transY, color);
198 helper->incGlyphCount();
199 }
200
201 // We may have changed the color so update it here
202 info->setColor(color);
203 if (regenTexCoords) {
204 if (regenGlyphs) {
205 info->setStrike(strike);
206 }
207 info->setAtlasGeneration(brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
208 fontCache->atlasGeneration(info->maskFormat()));
209 }
210}
211
212enum RegenMask {
213 kNoRegen = 0x0,
214 kRegenPos = 0x1,
215 kRegenCol = 0x2,
216 kRegenTex = 0x4,
217 kRegenGlyph = 0x8 | kRegenTex, // we have to regenerate the texture coords when we regen glyphs
218
219 // combinations
220 kRegenPosCol = kRegenPos | kRegenCol,
221 kRegenPosTex = kRegenPos | kRegenTex,
222 kRegenPosTexGlyph = kRegenPos | kRegenGlyph,
223 kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex,
224 kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph,
225 kRegenColTex = kRegenCol | kRegenTex,
226 kRegenColTexGlyph = kRegenCol | kRegenGlyph,
227};
228
229#define REGEN_ARGS target, fontCache, helper, &run, &info, cache, typeface, scaler, desc, \
230 *glyphCount, vertexStride, color, transX, transY
231
232void GrAtlasTextBlob::regenInBatch(GrDrawBatch::Target* target,
233 GrBatchFontCache* fontCache,
234 GrBlobRegenHelper *helper,
235 int runIndex, int subRunIndex, SkGlyphCache** cache,
236 SkTypeface** typeface, GrFontScaler** scaler,
237 const SkDescriptor** desc, size_t vertexStride,
238 GrColor color, SkScalar transX,
239 SkScalar transY,
240 void** vertices, size_t* byteCount, int* glyphCount) {
241 Run& run = fRuns[runIndex];
242 Run::SubRunInfo& info = run.fSubRunInfo[subRunIndex];
243
244 uint64_t currentAtlasGen = fontCache->atlasGeneration(info.maskFormat());
245
246 // Because the GrBatchFontCache may evict the strike a blob depends on using for
247 // generating its texture coords, we have to track whether or not the strike has
248 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
249 // otherwise we have to get the new strike, and use that to get the correct glyphs.
250 // Because we do not have the packed ids, and thus can't look up our glyphs in the
251 // new strike, we instead keep our ref to the old strike and use the packed ids from
252 // it. These ids will still be valid as long as we hold the ref. When we are done
253 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
254 bool regenerateGlyphs = info.strike()->isAbandoned();
255 bool regenerateTextureCoords = info.atlasGeneration() != currentAtlasGen ||
256 regenerateGlyphs;
257 bool regenerateColors = kARGB_GrMaskFormat != info.maskFormat() &&
258 info.color() != color;
259 bool regeneratePositions = transX != 0.f || transY != 0.f;
260 *glyphCount = info.glyphCount();
261
262 uint32_t regenMaskBits = kNoRegen;
263 regenMaskBits |= regeneratePositions ? kRegenPos : 0;
264 regenMaskBits |= regenerateColors ? kRegenCol : 0;
265 regenMaskBits |= regenerateTextureCoords ? kRegenTex : 0;
266 regenMaskBits |= regenerateGlyphs ? kRegenGlyph : 0;
267 RegenMask regenMask = (RegenMask)regenMaskBits;
268
269 switch (regenMask) {
270 case kRegenPos: this->regenInBatch<true, false, false, false>(REGEN_ARGS); break;
271 case kRegenCol: this->regenInBatch<false, true, false, false>(REGEN_ARGS); break;
272 case kRegenTex: this->regenInBatch<false, false, true, false>(REGEN_ARGS); break;
273 case kRegenGlyph: this->regenInBatch<false, false, true, true>(REGEN_ARGS); break;
274
275 // combinations
276 case kRegenPosCol: this->regenInBatch<true, true, false, false>(REGEN_ARGS); break;
277 case kRegenPosTex: this->regenInBatch<true, false, true, false>(REGEN_ARGS); break;
278 case kRegenPosTexGlyph: this->regenInBatch<true, false, true, true>(REGEN_ARGS); break;
279 case kRegenPosColTex: this->regenInBatch<true, true, true, false>(REGEN_ARGS); break;
280 case kRegenPosColTexGlyph: this->regenInBatch<true, true, true, true>(REGEN_ARGS); break;
281 case kRegenColTex: this->regenInBatch<false, true, true, false>(REGEN_ARGS); break;
282 case kRegenColTexGlyph: this->regenInBatch<false, true, true, true>(REGEN_ARGS); break;
283 case kNoRegen:
284 helper->incGlyphCount(*glyphCount);
285
286 // set use tokens for all of the glyphs in our subrun. This is only valid if we
287 // have a valid atlas generation
288 fontCache->setUseTokenBulk(*info.bulkUseToken(), target->currentToken(),
289 info.maskFormat());
290 break;
291 }
292
293 *byteCount = info.byteCount();
294 *vertices = fVertices + info.vertexStartIndex();
295}