blob: a49a3ff998b6f2b7530b30714dad6f48e1e53108 [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)
Brian Salomon18923f92017-11-06 16:26:02 -050074 intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
Brian Salomon5c6ac642017-12-19 11:09:32 -050075 intptr_t colorOffset = texCoordOffset - sizeof(GrColor);
Brian Salomon18923f92017-11-06 16:26:02 -050076
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
Brian Salomon5c6ac642017-12-19 11:09:32 -0500214 bool hasW = fSubRun->hasWCoord();
Brian Salomon18923f92017-11-06 16:26:02 -0500215 Result result;
Brian Salomon5c6ac642017-12-19 11:09:32 -0500216 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Brian Salomon18923f92017-11-06 16:26:02 -0500217 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500218 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Brian Salomon18923f92017-11-06 16:26:02 -0500219 result.fFirstVertex = currVertex;
220
221 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
222 GrGlyph* glyph = nullptr;
223 if (regenTexCoords) {
224 size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
225
226 if (regenGlyphs) {
227 // Get the id from the old glyph, and use the new strike to lookup
228 // the glyph.
229 GrGlyph::PackedID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
230 fBlob->fGlyphs[glyphOffset] =
231 strike->getGlyph(id, fSubRun->maskFormat(), fLazyCache->get());
232 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
233 }
234 glyph = fBlob->fGlyphs[glyphOffset];
235 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
236
237 if (!fGlyphCache->hasGlyph(glyph) &&
238 !strike->addGlyphToAtlas(fUploadTarget, glyph, fLazyCache->get(),
239 fSubRun->maskFormat())) {
240 fBrokenRun = glyphIdx > 0;
241 result.fFinished = false;
242 return result;
243 }
Robert Phillips40a29d72018-01-18 12:59:22 -0500244 auto tokenTracker = fUploadTarget->tokenTracker();
Brian Salomon18923f92017-11-06 16:26:02 -0500245 fGlyphCache->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
Robert Phillips40a29d72018-01-18 12:59:22 -0500246 tokenTracker->nextDrawToken());
Brian Salomon18923f92017-11-06 16:26:02 -0500247 }
248
Brian Salomondeb53cc2017-11-08 13:50:53 -0500249 regen_vertices<regenPos, regenCol, regenTexCoords>(currVertex, glyph, vertexStride,
Brian Salomon18923f92017-11-06 16:26:02 -0500250 fSubRun->drawAsDistanceFields(), fTransX,
251 fTransY, fColor);
Brian Salomondeb53cc2017-11-08 13:50:53 -0500252 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
Brian Salomon18923f92017-11-06 16:26:02 -0500253 ++result.fGlyphsRegenerated;
254 ++fCurrGlyph;
255 }
256
257 // We may have changed the color so update it here
258 fSubRun->setColor(fColor);
259 if (regenTexCoords) {
260 if (regenGlyphs) {
261 fSubRun->setStrike(strike);
262 }
263 fSubRun->setAtlasGeneration(fBrokenRun
264 ? GrDrawOpAtlas::kInvalidAtlasGeneration
265 : fGlyphCache->atlasGeneration(fSubRun->maskFormat()));
266 }
267 return result;
268}
269
270Regenerator::Result Regenerator::regenerate() {
271 uint64_t currentAtlasGen = fGlyphCache->atlasGeneration(fSubRun->maskFormat());
272 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
273 // this each time.
274 if (fSubRun->atlasGeneration() != currentAtlasGen) {
275 fRegenFlags |= kRegenTex;
276 }
277
278 switch (static_cast<RegenMask>(fRegenFlags)) {
279 case kRegenPos:
280 return this->doRegen<true, false, false, false>();
281 case kRegenCol:
282 return this->doRegen<false, true, false, false>();
283 case kRegenTex:
284 return this->doRegen<false, false, true, false>();
285 case kRegenGlyph:
286 return this->doRegen<false, false, true, true>();
287
288 // combinations
289 case kRegenPosCol:
290 return this->doRegen<true, true, false, false>();
291 case kRegenPosTex:
292 return this->doRegen<true, false, true, false>();
293 case kRegenPosTexGlyph:
294 return this->doRegen<true, false, true, true>();
295 case kRegenPosColTex:
296 return this->doRegen<true, true, true, false>();
297 case kRegenPosColTexGlyph:
298 return this->doRegen<true, true, true, true>();
299 case kRegenColTex:
300 return this->doRegen<false, true, true, false>();
301 case kRegenColTexGlyph:
302 return this->doRegen<false, true, true, true>();
303 case kNoRegen: {
304 Result result;
Brian Salomon5c6ac642017-12-19 11:09:32 -0500305 bool hasW = fSubRun->hasWCoord();
306 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Brian Salomon18923f92017-11-06 16:26:02 -0500307 result.fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
308 result.fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500309 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Brian Salomon18923f92017-11-06 16:26:02 -0500310 fCurrGlyph = fSubRun->glyphCount();
311
312 // set use tokens for all of the glyphs in our subrun. This is only valid if we
313 // have a valid atlas generation
Robert Phillips40a29d72018-01-18 12:59:22 -0500314 fGlyphCache->setUseTokenBulk(*fSubRun->bulkUseToken(),
315 fUploadTarget->tokenTracker()->nextDrawToken(),
Brian Salomon18923f92017-11-06 16:26:02 -0500316 fSubRun->maskFormat());
317 return result;
318 }
319 }
320 SK_ABORT("Should not get here");
321 return Result();
322}