blob: 500ae3170361f9d56ad6bb9fdb91790ff2c770a8 [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 Phillips4bc70112018-03-01 10:24:02 -0500193Regenerator::VertexRegenerator(GrResourceProvider* resourceProvider, GrAtlasTextBlob* blob,
194 int runIdx, int subRunIdx,
Brian Salomon18923f92017-11-06 16:26:02 -0500195 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
Robert Phillips5c56af12018-02-28 16:37:34 +0000196 GrDeferredUploadTarget* uploadTarget, GrAtlasGlyphCache* glyphCache,
197 SkAutoGlyphCache* lazyCache)
Robert Phillips4bc70112018-03-01 10:24:02 -0500198 : fResourceProvider(resourceProvider)
199 , fViewMatrix(viewMatrix)
Brian Salomon18923f92017-11-06 16:26:02 -0500200 , fBlob(blob)
201 , fUploadTarget(uploadTarget)
202 , fGlyphCache(glyphCache)
203 , fLazyCache(lazyCache)
204 , fRun(&blob->fRuns[runIdx])
205 , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
Brian Salomon18923f92017-11-06 16:26:02 -0500206 , fColor(color) {
207 // Compute translation if any
208 fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
209
Robert Phillips5c56af12018-02-28 16:37:34 +0000210 // Because the GrAtlasGlyphCache may evict the strike a blob depends on using for
Brian Salomon18923f92017-11-06 16:26:02 -0500211 // generating its texture coords, we have to track whether or not the strike has
212 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
213 // otherwise we have to get the new strike, and use that to get the correct glyphs.
214 // Because we do not have the packed ids, and thus can't look up our glyphs in the
215 // new strike, we instead keep our ref to the old strike and use the packed ids from
216 // it. These ids will still be valid as long as we hold the ref. When we are done
217 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
218 if (fSubRun->strike()->isAbandoned()) {
219 fRegenFlags |= kRegenGlyph;
220 fRegenFlags |= kRegenTex;
221 }
222 if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
223 fRegenFlags |= kRegenCol;
224 }
225 if (0.f != fTransX || 0.f != fTransY) {
226 fRegenFlags |= kRegenPos;
227 }
228}
229
230template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
231Regenerator::Result Regenerator::doRegen() {
232 static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
233 GrAtlasTextStrike* strike = nullptr;
234 if (regenTexCoords) {
235 fSubRun->resetBulkUseToken();
236
237 const SkDescriptor* desc = (fRun->fOverrideDescriptor && !fSubRun->drawAsDistanceFields())
238 ? fRun->fOverrideDescriptor->getDesc()
239 : fRun->fDescriptor.getDesc();
240
241 if (!*fLazyCache || (*fLazyCache)->getDescriptor() != *desc) {
242 SkScalerContextEffects effects;
243 effects.fPathEffect = fRun->fPathEffect.get();
Brian Salomon18923f92017-11-06 16:26:02 -0500244 effects.fMaskFilter = fRun->fMaskFilter.get();
245 fLazyCache->reset(SkGlyphCache::DetachCache(fRun->fTypeface.get(), effects, desc));
246 }
247
248 if (regenGlyphs) {
249 strike = fGlyphCache->getStrike(fLazyCache->get());
250 } else {
251 strike = fSubRun->strike();
252 }
253 }
254
Brian Salomon5c6ac642017-12-19 11:09:32 -0500255 bool hasW = fSubRun->hasWCoord();
Brian Salomon18923f92017-11-06 16:26:02 -0500256 Result result;
Brian Salomon5c6ac642017-12-19 11:09:32 -0500257 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Brian Salomon18923f92017-11-06 16:26:02 -0500258 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500259 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Brian Salomon18923f92017-11-06 16:26:02 -0500260 result.fFirstVertex = currVertex;
261
262 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
263 GrGlyph* glyph = nullptr;
264 if (regenTexCoords) {
265 size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
266
267 if (regenGlyphs) {
268 // Get the id from the old glyph, and use the new strike to lookup
269 // the glyph.
270 GrGlyph::PackedID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
271 fBlob->fGlyphs[glyphOffset] =
272 strike->getGlyph(id, fSubRun->maskFormat(), fLazyCache->get());
273 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
274 }
275 glyph = fBlob->fGlyphs[glyphOffset];
276 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
277
Robert Phillips5c56af12018-02-28 16:37:34 +0000278 if (!fGlyphCache->hasGlyph(glyph) &&
Robert Phillips4bc70112018-03-01 10:24:02 -0500279 !strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache, glyph,
280 fLazyCache->get(), fSubRun->maskFormat())) {
Brian Salomon18923f92017-11-06 16:26:02 -0500281 fBrokenRun = glyphIdx > 0;
282 result.fFinished = false;
283 return result;
284 }
Robert Phillips40a29d72018-01-18 12:59:22 -0500285 auto tokenTracker = fUploadTarget->tokenTracker();
Robert Phillips5c56af12018-02-28 16:37:34 +0000286 fGlyphCache->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
287 tokenTracker->nextDrawToken());
Brian Salomon18923f92017-11-06 16:26:02 -0500288 }
289
Brian Salomondeb53cc2017-11-08 13:50:53 -0500290 regen_vertices<regenPos, regenCol, regenTexCoords>(currVertex, glyph, vertexStride,
Brian Salomon18923f92017-11-06 16:26:02 -0500291 fSubRun->drawAsDistanceFields(), fTransX,
292 fTransY, fColor);
Brian Salomondeb53cc2017-11-08 13:50:53 -0500293 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
Brian Salomon18923f92017-11-06 16:26:02 -0500294 ++result.fGlyphsRegenerated;
295 ++fCurrGlyph;
296 }
297
298 // We may have changed the color so update it here
299 fSubRun->setColor(fColor);
300 if (regenTexCoords) {
301 if (regenGlyphs) {
302 fSubRun->setStrike(strike);
303 }
304 fSubRun->setAtlasGeneration(fBrokenRun
Robert Phillips5c56af12018-02-28 16:37:34 +0000305 ? GrDrawOpAtlas::kInvalidAtlasGeneration
306 : fGlyphCache->atlasGeneration(fSubRun->maskFormat()));
Brian Salomon18923f92017-11-06 16:26:02 -0500307 }
308 return result;
309}
310
311Regenerator::Result Regenerator::regenerate() {
Robert Phillips5c56af12018-02-28 16:37:34 +0000312 uint64_t currentAtlasGen = fGlyphCache->atlasGeneration(fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500313 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
314 // this each time.
315 if (fSubRun->atlasGeneration() != currentAtlasGen) {
316 fRegenFlags |= kRegenTex;
317 }
318
319 switch (static_cast<RegenMask>(fRegenFlags)) {
320 case kRegenPos:
321 return this->doRegen<true, false, false, false>();
322 case kRegenCol:
323 return this->doRegen<false, true, false, false>();
324 case kRegenTex:
325 return this->doRegen<false, false, true, false>();
326 case kRegenGlyph:
327 return this->doRegen<false, false, true, true>();
328
329 // combinations
330 case kRegenPosCol:
331 return this->doRegen<true, true, false, false>();
332 case kRegenPosTex:
333 return this->doRegen<true, false, true, false>();
334 case kRegenPosTexGlyph:
335 return this->doRegen<true, false, true, true>();
336 case kRegenPosColTex:
337 return this->doRegen<true, true, true, false>();
338 case kRegenPosColTexGlyph:
339 return this->doRegen<true, true, true, true>();
340 case kRegenColTex:
341 return this->doRegen<false, true, true, false>();
342 case kRegenColTexGlyph:
343 return this->doRegen<false, true, true, true>();
344 case kNoRegen: {
345 Result result;
Brian Salomon5c6ac642017-12-19 11:09:32 -0500346 bool hasW = fSubRun->hasWCoord();
347 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Brian Salomon18923f92017-11-06 16:26:02 -0500348 result.fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
349 result.fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500350 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Brian Salomon18923f92017-11-06 16:26:02 -0500351 fCurrGlyph = fSubRun->glyphCount();
352
353 // set use tokens for all of the glyphs in our subrun. This is only valid if we
354 // have a valid atlas generation
Robert Phillips5c56af12018-02-28 16:37:34 +0000355 fGlyphCache->setUseTokenBulk(*fSubRun->bulkUseToken(),
356 fUploadTarget->tokenTracker()->nextDrawToken(),
357 fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500358 return result;
359 }
360 }
361 SK_ABORT("Should not get here");
362 return Result();
363}