blob: 1a9c3d73e48bf8f98deee888fe7609140a300d2d [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;
Jim Van Verthfc4f7682018-01-25 16:26:25 -050041#ifdef DISPLAY_PAGE_INDEX
42 // Enable this to visualize the page from which each glyph is being drawn.
43 // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
44 SkColor hackColor;
45#endif
Brian Salomon18923f92017-11-06 16:26:02 -050046 if (regenTexCoords) {
47 SkASSERT(glyph);
48 int width = glyph->fBounds.width();
49 int height = glyph->fBounds.height();
50
51 if (useDistanceFields) {
52 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
53 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
54 u1 = u0 + width - 2 * SK_DistanceFieldInset;
55 v1 = v0 + height - 2 * SK_DistanceFieldInset;
56 } else {
57 u0 = glyph->fAtlasLocation.fX;
58 v0 = glyph->fAtlasLocation.fY;
59 u1 = u0 + width;
60 v1 = v0 + height;
61 }
62 // We pack the 2bit page index in the low bit of the u and v texture coords
63 uint32_t pageIndex = glyph->pageIndex();
64 SkASSERT(pageIndex < 4);
65 uint16_t uBit = (pageIndex >> 1) & 0x1;
66 uint16_t vBit = pageIndex & 0x1;
67 u0 <<= 1;
68 u0 |= uBit;
69 v0 <<= 1;
70 v0 |= vBit;
71 u1 <<= 1;
72 u1 |= uBit;
73 v1 <<= 1;
74 v1 |= vBit;
Jim Van Verthfc4f7682018-01-25 16:26:25 -050075#ifdef DISPLAY_PAGE_INDEX
76 switch (pageIndex) {
77 case 0:
78 hackColor = SK_ColorGREEN;
79 break;
80 case 1:
81 hackColor = SK_ColorRED;
82 break;
83 case 2:
84 hackColor = SK_ColorMAGENTA;
85 break;
86 case 3:
87 hackColor = SK_ColorCYAN;
88 break;
89 default:
90 hackColor = SK_ColorBLACK;
91 break;
92 }
93#endif
Brian Salomon18923f92017-11-06 16:26:02 -050094 }
95
96 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
97 // vertices, hence vertexStride - sizeof(SkIPoint16)
Brian Salomon18923f92017-11-06 16:26:02 -050098 intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
Brian Salomon5c6ac642017-12-19 11:09:32 -050099 intptr_t colorOffset = texCoordOffset - sizeof(GrColor);
Brian Salomon18923f92017-11-06 16:26:02 -0500100
101 // V0
102 if (regenPos) {
103 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
104 point->fX += transX;
105 point->fY += transY;
106 }
107
108 if (regenCol) {
109 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
110 *vcolor = color;
111 }
112
113 if (regenTexCoords) {
114 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
115 textureCoords[0] = u0;
116 textureCoords[1] = v0;
Jim Van Verthfc4f7682018-01-25 16:26:25 -0500117#ifdef DISPLAY_PAGE_INDEX
118 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
119 *vcolor = hackColor;
120#endif
Brian Salomon18923f92017-11-06 16:26:02 -0500121 }
122 vertex += vertexStride;
123
124 // V1
125 if (regenPos) {
126 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
127 point->fX += transX;
128 point->fY += transY;
129 }
130
131 if (regenCol) {
132 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
133 *vcolor = color;
134 }
135
136 if (regenTexCoords) {
137 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
138 textureCoords[0] = u0;
139 textureCoords[1] = v1;
Jim Van Verthfc4f7682018-01-25 16:26:25 -0500140#ifdef DISPLAY_PAGE_INDEX
141 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
142 *vcolor = hackColor;
143#endif
Brian Salomon18923f92017-11-06 16:26:02 -0500144 }
145 vertex += vertexStride;
146
147 // V2
148 if (regenPos) {
149 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
150 point->fX += transX;
151 point->fY += transY;
152 }
153
154 if (regenCol) {
155 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
156 *vcolor = color;
157 }
158
159 if (regenTexCoords) {
160 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
161 textureCoords[0] = u1;
162 textureCoords[1] = v0;
Jim Van Verthfc4f7682018-01-25 16:26:25 -0500163#ifdef DISPLAY_PAGE_INDEX
164 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
165 *vcolor = hackColor;
166#endif
Brian Salomon18923f92017-11-06 16:26:02 -0500167 }
168 vertex += vertexStride;
169
170 // V3
171 if (regenPos) {
172 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
173 point->fX += transX;
174 point->fY += transY;
175 }
176
177 if (regenCol) {
178 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
179 *vcolor = color;
180 }
181
182 if (regenTexCoords) {
183 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
184 textureCoords[0] = u1;
185 textureCoords[1] = v1;
Jim Van Verthfc4f7682018-01-25 16:26:25 -0500186#ifdef DISPLAY_PAGE_INDEX
187 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
188 *vcolor = hackColor;
189#endif
Brian Salomon18923f92017-11-06 16:26:02 -0500190 }
191}
192
Robert Phillipsba7a1652018-02-28 17:17:11 +0000193Regenerator::VertexRegenerator(GrAtlasTextBlob* blob, int runIdx, int subRunIdx,
Brian Salomon18923f92017-11-06 16:26:02 -0500194 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
Robert Phillips5c56af12018-02-28 16:37:34 +0000195 GrDeferredUploadTarget* uploadTarget, GrAtlasGlyphCache* glyphCache,
196 SkAutoGlyphCache* lazyCache)
Robert Phillipsba7a1652018-02-28 17:17:11 +0000197 : fViewMatrix(viewMatrix)
Brian Salomon18923f92017-11-06 16:26:02 -0500198 , fBlob(blob)
199 , fUploadTarget(uploadTarget)
200 , fGlyphCache(glyphCache)
201 , fLazyCache(lazyCache)
202 , fRun(&blob->fRuns[runIdx])
203 , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
Brian Salomon18923f92017-11-06 16:26:02 -0500204 , fColor(color) {
205 // Compute translation if any
206 fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
207
Robert Phillips5c56af12018-02-28 16:37:34 +0000208 // Because the GrAtlasGlyphCache may evict the strike a blob depends on using for
Brian Salomon18923f92017-11-06 16:26:02 -0500209 // generating its texture coords, we have to track whether or not the strike has
210 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
211 // otherwise we have to get the new strike, and use that to get the correct glyphs.
212 // Because we do not have the packed ids, and thus can't look up our glyphs in the
213 // new strike, we instead keep our ref to the old strike and use the packed ids from
214 // it. These ids will still be valid as long as we hold the ref. When we are done
215 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
216 if (fSubRun->strike()->isAbandoned()) {
217 fRegenFlags |= kRegenGlyph;
218 fRegenFlags |= kRegenTex;
219 }
220 if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
221 fRegenFlags |= kRegenCol;
222 }
223 if (0.f != fTransX || 0.f != fTransY) {
224 fRegenFlags |= kRegenPos;
225 }
226}
227
228template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
229Regenerator::Result Regenerator::doRegen() {
230 static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
231 GrAtlasTextStrike* strike = nullptr;
232 if (regenTexCoords) {
233 fSubRun->resetBulkUseToken();
234
235 const SkDescriptor* desc = (fRun->fOverrideDescriptor && !fSubRun->drawAsDistanceFields())
236 ? fRun->fOverrideDescriptor->getDesc()
237 : fRun->fDescriptor.getDesc();
238
239 if (!*fLazyCache || (*fLazyCache)->getDescriptor() != *desc) {
240 SkScalerContextEffects effects;
241 effects.fPathEffect = fRun->fPathEffect.get();
Brian Salomon18923f92017-11-06 16:26:02 -0500242 effects.fMaskFilter = fRun->fMaskFilter.get();
243 fLazyCache->reset(SkGlyphCache::DetachCache(fRun->fTypeface.get(), effects, desc));
244 }
245
246 if (regenGlyphs) {
247 strike = fGlyphCache->getStrike(fLazyCache->get());
248 } else {
249 strike = fSubRun->strike();
250 }
251 }
252
Brian Salomon5c6ac642017-12-19 11:09:32 -0500253 bool hasW = fSubRun->hasWCoord();
Brian Salomon18923f92017-11-06 16:26:02 -0500254 Result result;
Brian Salomon5c6ac642017-12-19 11:09:32 -0500255 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Brian Salomon18923f92017-11-06 16:26:02 -0500256 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500257 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Brian Salomon18923f92017-11-06 16:26:02 -0500258 result.fFirstVertex = currVertex;
259
260 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
261 GrGlyph* glyph = nullptr;
262 if (regenTexCoords) {
263 size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
264
265 if (regenGlyphs) {
266 // Get the id from the old glyph, and use the new strike to lookup
267 // the glyph.
268 GrGlyph::PackedID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
269 fBlob->fGlyphs[glyphOffset] =
270 strike->getGlyph(id, fSubRun->maskFormat(), fLazyCache->get());
271 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
272 }
273 glyph = fBlob->fGlyphs[glyphOffset];
274 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
275
Robert Phillips5c56af12018-02-28 16:37:34 +0000276 if (!fGlyphCache->hasGlyph(glyph) &&
Robert Phillipsba7a1652018-02-28 17:17:11 +0000277 !strike->addGlyphToAtlas(fUploadTarget, fGlyphCache, glyph, fLazyCache->get(),
278 fSubRun->maskFormat())) {
Brian Salomon18923f92017-11-06 16:26:02 -0500279 fBrokenRun = glyphIdx > 0;
280 result.fFinished = false;
281 return result;
282 }
Robert Phillips40a29d72018-01-18 12:59:22 -0500283 auto tokenTracker = fUploadTarget->tokenTracker();
Robert Phillips5c56af12018-02-28 16:37:34 +0000284 fGlyphCache->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
285 tokenTracker->nextDrawToken());
Brian Salomon18923f92017-11-06 16:26:02 -0500286 }
287
Brian Salomondeb53cc2017-11-08 13:50:53 -0500288 regen_vertices<regenPos, regenCol, regenTexCoords>(currVertex, glyph, vertexStride,
Brian Salomon18923f92017-11-06 16:26:02 -0500289 fSubRun->drawAsDistanceFields(), fTransX,
290 fTransY, fColor);
Brian Salomondeb53cc2017-11-08 13:50:53 -0500291 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
Brian Salomon18923f92017-11-06 16:26:02 -0500292 ++result.fGlyphsRegenerated;
293 ++fCurrGlyph;
294 }
295
296 // We may have changed the color so update it here
297 fSubRun->setColor(fColor);
298 if (regenTexCoords) {
299 if (regenGlyphs) {
300 fSubRun->setStrike(strike);
301 }
302 fSubRun->setAtlasGeneration(fBrokenRun
Robert Phillips5c56af12018-02-28 16:37:34 +0000303 ? GrDrawOpAtlas::kInvalidAtlasGeneration
304 : fGlyphCache->atlasGeneration(fSubRun->maskFormat()));
Brian Salomon18923f92017-11-06 16:26:02 -0500305 }
306 return result;
307}
308
309Regenerator::Result Regenerator::regenerate() {
Robert Phillips5c56af12018-02-28 16:37:34 +0000310 uint64_t currentAtlasGen = fGlyphCache->atlasGeneration(fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500311 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
312 // this each time.
313 if (fSubRun->atlasGeneration() != currentAtlasGen) {
314 fRegenFlags |= kRegenTex;
315 }
316
317 switch (static_cast<RegenMask>(fRegenFlags)) {
318 case kRegenPos:
319 return this->doRegen<true, false, false, false>();
320 case kRegenCol:
321 return this->doRegen<false, true, false, false>();
322 case kRegenTex:
323 return this->doRegen<false, false, true, false>();
324 case kRegenGlyph:
325 return this->doRegen<false, false, true, true>();
326
327 // combinations
328 case kRegenPosCol:
329 return this->doRegen<true, true, false, false>();
330 case kRegenPosTex:
331 return this->doRegen<true, false, true, false>();
332 case kRegenPosTexGlyph:
333 return this->doRegen<true, false, true, true>();
334 case kRegenPosColTex:
335 return this->doRegen<true, true, true, false>();
336 case kRegenPosColTexGlyph:
337 return this->doRegen<true, true, true, true>();
338 case kRegenColTex:
339 return this->doRegen<false, true, true, false>();
340 case kRegenColTexGlyph:
341 return this->doRegen<false, true, true, true>();
342 case kNoRegen: {
343 Result result;
Brian Salomon5c6ac642017-12-19 11:09:32 -0500344 bool hasW = fSubRun->hasWCoord();
345 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Brian Salomon18923f92017-11-06 16:26:02 -0500346 result.fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
347 result.fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500348 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Brian Salomon18923f92017-11-06 16:26:02 -0500349 fCurrGlyph = fSubRun->glyphCount();
350
351 // set use tokens for all of the glyphs in our subrun. This is only valid if we
352 // have a valid atlas generation
Robert Phillips5c56af12018-02-28 16:37:34 +0000353 fGlyphCache->setUseTokenBulk(*fSubRun->bulkUseToken(),
354 fUploadTarget->tokenTracker()->nextDrawToken(),
355 fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500356 return result;
357 }
358 }
359 SK_ABORT("Should not get here");
360 return Result();
361}