blob: 28b2e0f0c0302eab1357bf625ab06f675d6fba79 [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)
Greg Danielf226e662017-12-18 15:50:34 +000074 intptr_t colorOffset = sizeof(SkPoint);
Brian Salomon18923f92017-11-06 16:26:02 -050075 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,
Brian Salomondeb53cc2017-11-08 13:50:53 -0500156 SkAutoGlyphCache* lazyCache)
Brian Salomon18923f92017-11-06 16:26:02 -0500157 : 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])
Brian Salomon18923f92017-11-06 16:26:02 -0500164 , fColor(color) {
165 // Compute translation if any
166 fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
167
168 // Because the GrAtlasGlyphCache may evict the strike a blob depends on using for
169 // generating its texture coords, we have to track whether or not the strike has
170 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
171 // otherwise we have to get the new strike, and use that to get the correct glyphs.
172 // Because we do not have the packed ids, and thus can't look up our glyphs in the
173 // new strike, we instead keep our ref to the old strike and use the packed ids from
174 // it. These ids will still be valid as long as we hold the ref. When we are done
175 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
176 if (fSubRun->strike()->isAbandoned()) {
177 fRegenFlags |= kRegenGlyph;
178 fRegenFlags |= kRegenTex;
179 }
180 if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
181 fRegenFlags |= kRegenCol;
182 }
183 if (0.f != fTransX || 0.f != fTransY) {
184 fRegenFlags |= kRegenPos;
185 }
186}
187
188template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
189Regenerator::Result Regenerator::doRegen() {
190 static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
191 GrAtlasTextStrike* strike = nullptr;
192 if (regenTexCoords) {
193 fSubRun->resetBulkUseToken();
194
195 const SkDescriptor* desc = (fRun->fOverrideDescriptor && !fSubRun->drawAsDistanceFields())
196 ? fRun->fOverrideDescriptor->getDesc()
197 : fRun->fDescriptor.getDesc();
198
199 if (!*fLazyCache || (*fLazyCache)->getDescriptor() != *desc) {
200 SkScalerContextEffects effects;
201 effects.fPathEffect = fRun->fPathEffect.get();
202 effects.fRasterizer = fRun->fRasterizer.get();
203 effects.fMaskFilter = fRun->fMaskFilter.get();
204 fLazyCache->reset(SkGlyphCache::DetachCache(fRun->fTypeface.get(), effects, desc));
205 }
206
207 if (regenGlyphs) {
208 strike = fGlyphCache->getStrike(fLazyCache->get());
209 } else {
210 strike = fSubRun->strike();
211 }
212 }
213
214 Result result;
Greg Danielf226e662017-12-18 15:50:34 +0000215 auto vertexStride = GetVertexStride(fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500216 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500217 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Brian Salomon18923f92017-11-06 16:26:02 -0500218 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
Brian Salomondeb53cc2017-11-08 13:50:53 -0500247 regen_vertices<regenPos, regenCol, regenTexCoords>(currVertex, glyph, vertexStride,
Brian Salomon18923f92017-11-06 16:26:02 -0500248 fSubRun->drawAsDistanceFields(), fTransX,
249 fTransY, fColor);
Brian Salomondeb53cc2017-11-08 13:50:53 -0500250 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
Brian Salomon18923f92017-11-06 16:26:02 -0500251 ++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;
Greg Danielf226e662017-12-18 15:50:34 +0000303 auto vertexStride = GetVertexStride(fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500304 result.fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
305 result.fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500306 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Brian Salomon18923f92017-11-06 16:26:02 -0500307 fCurrGlyph = fSubRun->glyphCount();
308
309 // set use tokens for all of the glyphs in our subrun. This is only valid if we
310 // have a valid atlas generation
311 fGlyphCache->setUseTokenBulk(*fSubRun->bulkUseToken(), fUploadTarget->nextDrawToken(),
312 fSubRun->maskFormat());
313 return result;
314 }
315 }
316 SK_ABORT("Should not get here");
317 return Result();
318}