blob: 6b242adcde79127a9be54cbafa82092d771eab16 [file] [log] [blame]
Brian Salomon18923f92017-11-06 16:26:02 -05001/*
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#include "GrTextUtils.h"
10#include "SkDistanceFieldGen.h"
11#include "SkGlyphCache.h"
12#include "ops/GrAtlasTextOp.h"
13
14using Regenerator = GrAtlasTextBlob::VertexRegenerator;
15
16enum RegenMask {
17 kNoRegen = 0x0,
18 kRegenPos = 0x1,
19 kRegenCol = 0x2,
20 kRegenTex = 0x4,
21 kRegenGlyph = 0x8 | kRegenTex, // we have to regenerate the texture coords when we regen glyphs
22
23 // combinations
24 kRegenPosCol = kRegenPos | kRegenCol,
25 kRegenPosTex = kRegenPos | kRegenTex,
26 kRegenPosTexGlyph = kRegenPos | kRegenGlyph,
27 kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex,
28 kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph,
29 kRegenColTex = kRegenCol | kRegenTex,
30 kRegenColTexGlyph = kRegenCol | kRegenGlyph,
31};
32
33////////////////////////////////////////////////////////////////////////////////////////////////////
34// A large template to handle regenerating the vertices of a textblob with as few branches as
35// possible
36template <bool regenPos, bool regenCol, bool regenTexCoords>
37inline void regen_vertices(char* vertex, const GrGlyph* glyph, size_t vertexStride,
38 bool useDistanceFields, SkScalar transX, SkScalar transY,
39 GrColor color) {
40 uint16_t u0, v0, u1, v1;
41 if (regenTexCoords) {
42 SkASSERT(glyph);
43 int width = glyph->fBounds.width();
44 int height = glyph->fBounds.height();
45
46 if (useDistanceFields) {
47 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
48 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
49 u1 = u0 + width - 2 * SK_DistanceFieldInset;
50 v1 = v0 + height - 2 * SK_DistanceFieldInset;
51 } else {
52 u0 = glyph->fAtlasLocation.fX;
53 v0 = glyph->fAtlasLocation.fY;
54 u1 = u0 + width;
55 v1 = v0 + height;
56 }
57 // We pack the 2bit page index in the low bit of the u and v texture coords
58 uint32_t pageIndex = glyph->pageIndex();
59 SkASSERT(pageIndex < 4);
60 uint16_t uBit = (pageIndex >> 1) & 0x1;
61 uint16_t vBit = pageIndex & 0x1;
62 u0 <<= 1;
63 u0 |= uBit;
64 v0 <<= 1;
65 v0 |= vBit;
66 u1 <<= 1;
67 u1 |= uBit;
68 v1 <<= 1;
69 v1 |= vBit;
70 }
71
72 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
73 // vertices, hence vertexStride - sizeof(SkIPoint16)
74 intptr_t colorOffset = sizeof(SkPoint);
75 intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
76
77 // V0
78 if (regenPos) {
79 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
80 point->fX += transX;
81 point->fY += transY;
82 }
83
84 if (regenCol) {
85 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
86 *vcolor = color;
87 }
88
89 if (regenTexCoords) {
90 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
91 textureCoords[0] = u0;
92 textureCoords[1] = v0;
93 }
94 vertex += vertexStride;
95
96 // V1
97 if (regenPos) {
98 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
99 point->fX += transX;
100 point->fY += transY;
101 }
102
103 if (regenCol) {
104 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
105 *vcolor = color;
106 }
107
108 if (regenTexCoords) {
109 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
110 textureCoords[0] = u0;
111 textureCoords[1] = v1;
112 }
113 vertex += vertexStride;
114
115 // V2
116 if (regenPos) {
117 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
118 point->fX += transX;
119 point->fY += transY;
120 }
121
122 if (regenCol) {
123 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
124 *vcolor = color;
125 }
126
127 if (regenTexCoords) {
128 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
129 textureCoords[0] = u1;
130 textureCoords[1] = v0;
131 }
132 vertex += vertexStride;
133
134 // V3
135 if (regenPos) {
136 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
137 point->fX += transX;
138 point->fY += transY;
139 }
140
141 if (regenCol) {
142 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
143 *vcolor = color;
144 }
145
146 if (regenTexCoords) {
147 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
148 textureCoords[0] = u1;
149 textureCoords[1] = v1;
150 }
151}
152
153Regenerator::VertexRegenerator(GrAtlasTextBlob* blob, int runIdx, int subRunIdx,
154 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
155 GrDeferredUploadTarget* uploadTarget, GrAtlasGlyphCache* glyphCache,
156 SkAutoGlyphCache* lazyCache, size_t vertexStride)
157 : fViewMatrix(viewMatrix)
158 , fBlob(blob)
159 , fUploadTarget(uploadTarget)
160 , fGlyphCache(glyphCache)
161 , fLazyCache(lazyCache)
162 , fRun(&blob->fRuns[runIdx])
163 , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
164 , fVertexStride(vertexStride)
165 , fColor(color) {
166 // Compute translation if any
167 fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
168
169 // Because the GrAtlasGlyphCache may evict the strike a blob depends on using for
170 // generating its texture coords, we have to track whether or not the strike has
171 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
172 // otherwise we have to get the new strike, and use that to get the correct glyphs.
173 // Because we do not have the packed ids, and thus can't look up our glyphs in the
174 // new strike, we instead keep our ref to the old strike and use the packed ids from
175 // it. These ids will still be valid as long as we hold the ref. When we are done
176 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
177 if (fSubRun->strike()->isAbandoned()) {
178 fRegenFlags |= kRegenGlyph;
179 fRegenFlags |= kRegenTex;
180 }
181 if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
182 fRegenFlags |= kRegenCol;
183 }
184 if (0.f != fTransX || 0.f != fTransY) {
185 fRegenFlags |= kRegenPos;
186 }
187}
188
189template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
190Regenerator::Result Regenerator::doRegen() {
191 static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
192 GrAtlasTextStrike* strike = nullptr;
193 if (regenTexCoords) {
194 fSubRun->resetBulkUseToken();
195
196 const SkDescriptor* desc = (fRun->fOverrideDescriptor && !fSubRun->drawAsDistanceFields())
197 ? fRun->fOverrideDescriptor->getDesc()
198 : fRun->fDescriptor.getDesc();
199
200 if (!*fLazyCache || (*fLazyCache)->getDescriptor() != *desc) {
201 SkScalerContextEffects effects;
202 effects.fPathEffect = fRun->fPathEffect.get();
203 effects.fRasterizer = fRun->fRasterizer.get();
204 effects.fMaskFilter = fRun->fMaskFilter.get();
205 fLazyCache->reset(SkGlyphCache::DetachCache(fRun->fTypeface.get(), effects, desc));
206 }
207
208 if (regenGlyphs) {
209 strike = fGlyphCache->getStrike(fLazyCache->get());
210 } else {
211 strike = fSubRun->strike();
212 }
213 }
214
215 Result result;
216 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
217 fCurrGlyph * kVerticesPerGlyph * fVertexStride;
218 result.fFirstVertex = currVertex;
219
220 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
221 GrGlyph* glyph = nullptr;
222 if (regenTexCoords) {
223 size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
224
225 if (regenGlyphs) {
226 // Get the id from the old glyph, and use the new strike to lookup
227 // the glyph.
228 GrGlyph::PackedID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
229 fBlob->fGlyphs[glyphOffset] =
230 strike->getGlyph(id, fSubRun->maskFormat(), fLazyCache->get());
231 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
232 }
233 glyph = fBlob->fGlyphs[glyphOffset];
234 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
235
236 if (!fGlyphCache->hasGlyph(glyph) &&
237 !strike->addGlyphToAtlas(fUploadTarget, glyph, fLazyCache->get(),
238 fSubRun->maskFormat())) {
239 fBrokenRun = glyphIdx > 0;
240 result.fFinished = false;
241 return result;
242 }
243 fGlyphCache->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
244 fUploadTarget->nextDrawToken());
245 }
246
247 regen_vertices<regenPos, regenCol, regenTexCoords>(currVertex, glyph, fVertexStride,
248 fSubRun->drawAsDistanceFields(), fTransX,
249 fTransY, fColor);
250 currVertex += fVertexStride * GrAtlasTextOp::kVerticesPerGlyph;
251 ++result.fGlyphsRegenerated;
252 ++fCurrGlyph;
253 }
254
255 // We may have changed the color so update it here
256 fSubRun->setColor(fColor);
257 if (regenTexCoords) {
258 if (regenGlyphs) {
259 fSubRun->setStrike(strike);
260 }
261 fSubRun->setAtlasGeneration(fBrokenRun
262 ? GrDrawOpAtlas::kInvalidAtlasGeneration
263 : fGlyphCache->atlasGeneration(fSubRun->maskFormat()));
264 }
265 return result;
266}
267
268Regenerator::Result Regenerator::regenerate() {
269 uint64_t currentAtlasGen = fGlyphCache->atlasGeneration(fSubRun->maskFormat());
270 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
271 // this each time.
272 if (fSubRun->atlasGeneration() != currentAtlasGen) {
273 fRegenFlags |= kRegenTex;
274 }
275
276 switch (static_cast<RegenMask>(fRegenFlags)) {
277 case kRegenPos:
278 return this->doRegen<true, false, false, false>();
279 case kRegenCol:
280 return this->doRegen<false, true, false, false>();
281 case kRegenTex:
282 return this->doRegen<false, false, true, false>();
283 case kRegenGlyph:
284 return this->doRegen<false, false, true, true>();
285
286 // combinations
287 case kRegenPosCol:
288 return this->doRegen<true, true, false, false>();
289 case kRegenPosTex:
290 return this->doRegen<true, false, true, false>();
291 case kRegenPosTexGlyph:
292 return this->doRegen<true, false, true, true>();
293 case kRegenPosColTex:
294 return this->doRegen<true, true, true, false>();
295 case kRegenPosColTexGlyph:
296 return this->doRegen<true, true, true, true>();
297 case kRegenColTex:
298 return this->doRegen<false, true, true, false>();
299 case kRegenColTexGlyph:
300 return this->doRegen<false, true, true, true>();
301 case kNoRegen: {
302 Result result;
303 result.fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
304 result.fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
305 fCurrGlyph * kVerticesPerGlyph * fVertexStride;
306 fCurrGlyph = fSubRun->glyphCount();
307
308 // set use tokens for all of the glyphs in our subrun. This is only valid if we
309 // have a valid atlas generation
310 fGlyphCache->setUseTokenBulk(*fSubRun->bulkUseToken(), fUploadTarget->nextDrawToken(),
311 fSubRun->maskFormat());
312 return result;
313 }
314 }
315 SK_ABORT("Should not get here");
316 return Result();
317}