blob: 6b6beb357af3685cd8a72f15c920dac66f8564c1 [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
Robert Phillipsc4039ea2018-03-01 11:36:45 -05008#include "GrAtlasManager.h"
Herb Derby86240592018-05-24 16:12:31 -04009#include "GrTextBlob.h"
Herb Derbyc1b482c2018-08-09 15:02:27 -040010#include "GrTextTarget.h"
Brian Salomon18923f92017-11-06 16:26:02 -050011#include "SkDistanceFieldGen.h"
12#include "SkGlyphCache.h"
13#include "ops/GrAtlasTextOp.h"
14
Brian Salomon18923f92017-11-06 16:26:02 -050015enum RegenMask {
16 kNoRegen = 0x0,
17 kRegenPos = 0x1,
18 kRegenCol = 0x2,
19 kRegenTex = 0x4,
20 kRegenGlyph = 0x8 | kRegenTex, // we have to regenerate the texture coords when we regen glyphs
21
22 // combinations
23 kRegenPosCol = kRegenPos | kRegenCol,
24 kRegenPosTex = kRegenPos | kRegenTex,
25 kRegenPosTexGlyph = kRegenPos | kRegenGlyph,
26 kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex,
27 kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph,
28 kRegenColTex = kRegenCol | kRegenTex,
29 kRegenColTexGlyph = kRegenCol | kRegenGlyph,
30};
31
32////////////////////////////////////////////////////////////////////////////////////////////////////
33// A large template to handle regenerating the vertices of a textblob with as few branches as
34// possible
35template <bool regenPos, bool regenCol, bool regenTexCoords>
36inline void regen_vertices(char* vertex, const GrGlyph* glyph, size_t vertexStride,
37 bool useDistanceFields, SkScalar transX, SkScalar transY,
38 GrColor color) {
39 uint16_t u0, v0, u1, v1;
Jim Van Verthfc4f7682018-01-25 16:26:25 -050040#ifdef DISPLAY_PAGE_INDEX
41 // Enable this to visualize the page from which each glyph is being drawn.
42 // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
43 SkColor hackColor;
44#endif
Brian Salomon18923f92017-11-06 16:26:02 -050045 if (regenTexCoords) {
46 SkASSERT(glyph);
47 int width = glyph->fBounds.width();
48 int height = glyph->fBounds.height();
49
50 if (useDistanceFields) {
51 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
52 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
53 u1 = u0 + width - 2 * SK_DistanceFieldInset;
54 v1 = v0 + height - 2 * SK_DistanceFieldInset;
55 } else {
56 u0 = glyph->fAtlasLocation.fX;
57 v0 = glyph->fAtlasLocation.fY;
58 u1 = u0 + width;
59 v1 = v0 + height;
60 }
61 // We pack the 2bit page index in the low bit of the u and v texture coords
62 uint32_t pageIndex = glyph->pageIndex();
63 SkASSERT(pageIndex < 4);
64 uint16_t uBit = (pageIndex >> 1) & 0x1;
65 uint16_t vBit = pageIndex & 0x1;
66 u0 <<= 1;
67 u0 |= uBit;
68 v0 <<= 1;
69 v0 |= vBit;
70 u1 <<= 1;
71 u1 |= uBit;
72 v1 <<= 1;
73 v1 |= vBit;
Jim Van Verthfc4f7682018-01-25 16:26:25 -050074#ifdef DISPLAY_PAGE_INDEX
75 switch (pageIndex) {
76 case 0:
77 hackColor = SK_ColorGREEN;
78 break;
79 case 1:
80 hackColor = SK_ColorRED;
81 break;
82 case 2:
83 hackColor = SK_ColorMAGENTA;
84 break;
85 case 3:
86 hackColor = SK_ColorCYAN;
87 break;
88 default:
89 hackColor = SK_ColorBLACK;
90 break;
91 }
92#endif
Brian Salomon18923f92017-11-06 16:26:02 -050093 }
94
95 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
96 // vertices, hence vertexStride - sizeof(SkIPoint16)
Brian Salomon18923f92017-11-06 16:26:02 -050097 intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
Brian Salomon5c6ac642017-12-19 11:09:32 -050098 intptr_t colorOffset = texCoordOffset - sizeof(GrColor);
Brian Salomon18923f92017-11-06 16:26:02 -050099
100 // V0
101 if (regenPos) {
102 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
103 point->fX += transX;
104 point->fY += transY;
105 }
106
107 if (regenCol) {
108 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
109 *vcolor = color;
110 }
111
112 if (regenTexCoords) {
113 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
114 textureCoords[0] = u0;
115 textureCoords[1] = v0;
Jim Van Verthfc4f7682018-01-25 16:26:25 -0500116#ifdef DISPLAY_PAGE_INDEX
117 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
118 *vcolor = hackColor;
119#endif
Brian Salomon18923f92017-11-06 16:26:02 -0500120 }
121 vertex += vertexStride;
122
123 // V1
124 if (regenPos) {
125 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
126 point->fX += transX;
127 point->fY += transY;
128 }
129
130 if (regenCol) {
131 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
132 *vcolor = color;
133 }
134
135 if (regenTexCoords) {
136 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
137 textureCoords[0] = u0;
138 textureCoords[1] = v1;
Jim Van Verthfc4f7682018-01-25 16:26:25 -0500139#ifdef DISPLAY_PAGE_INDEX
140 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
141 *vcolor = hackColor;
142#endif
Brian Salomon18923f92017-11-06 16:26:02 -0500143 }
144 vertex += vertexStride;
145
146 // V2
147 if (regenPos) {
148 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
149 point->fX += transX;
150 point->fY += transY;
151 }
152
153 if (regenCol) {
154 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
155 *vcolor = color;
156 }
157
158 if (regenTexCoords) {
159 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
160 textureCoords[0] = u1;
161 textureCoords[1] = v0;
Jim Van Verthfc4f7682018-01-25 16:26:25 -0500162#ifdef DISPLAY_PAGE_INDEX
163 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
164 *vcolor = hackColor;
165#endif
Brian Salomon18923f92017-11-06 16:26:02 -0500166 }
167 vertex += vertexStride;
168
169 // V3
170 if (regenPos) {
171 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
172 point->fX += transX;
173 point->fY += transY;
174 }
175
176 if (regenCol) {
177 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
178 *vcolor = color;
179 }
180
181 if (regenTexCoords) {
182 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
183 textureCoords[0] = u1;
184 textureCoords[1] = v1;
Jim Van Verthfc4f7682018-01-25 16:26:25 -0500185#ifdef DISPLAY_PAGE_INDEX
186 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
187 *vcolor = hackColor;
188#endif
Brian Salomon18923f92017-11-06 16:26:02 -0500189 }
190}
191
Herb Derby6dff60e2018-11-12 15:45:49 -0500192GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
193 GrTextBlob* blob,
194 int runIdx, int subRunIdx,
195 const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
196 GrColor color,
197 GrDeferredUploadTarget* uploadTarget,
198 GrGlyphCache* glyphCache,
199 GrAtlasManager* fullAtlasManager,
200 SkExclusiveStrikePtr* lazyCache)
Robert Phillips4bc70112018-03-01 10:24:02 -0500201 : fResourceProvider(resourceProvider)
202 , fViewMatrix(viewMatrix)
Brian Salomon18923f92017-11-06 16:26:02 -0500203 , fBlob(blob)
204 , fUploadTarget(uploadTarget)
205 , fGlyphCache(glyphCache)
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500206 , fFullAtlasManager(fullAtlasManager)
Brian Salomon18923f92017-11-06 16:26:02 -0500207 , fLazyCache(lazyCache)
208 , fRun(&blob->fRuns[runIdx])
209 , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
Brian Salomon18923f92017-11-06 16:26:02 -0500210 , fColor(color) {
211 // Compute translation if any
212 fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
213
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500214 // Because the GrGlyphCache may evict the strike a blob depends on using for
Brian Salomon18923f92017-11-06 16:26:02 -0500215 // generating its texture coords, we have to track whether or not the strike has
216 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
217 // otherwise we have to get the new strike, and use that to get the correct glyphs.
218 // Because we do not have the packed ids, and thus can't look up our glyphs in the
219 // new strike, we instead keep our ref to the old strike and use the packed ids from
220 // it. These ids will still be valid as long as we hold the ref. When we are done
221 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
222 if (fSubRun->strike()->isAbandoned()) {
223 fRegenFlags |= kRegenGlyph;
224 fRegenFlags |= kRegenTex;
225 }
226 if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
227 fRegenFlags |= kRegenCol;
228 }
229 if (0.f != fTransX || 0.f != fTransY) {
230 fRegenFlags |= kRegenPos;
231 }
232}
233
234template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
Herb Derby6dff60e2018-11-12 15:45:49 -0500235bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result) {
Brian Salomon18923f92017-11-06 16:26:02 -0500236 static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500237 sk_sp<GrTextStrike> strike;
Brian Salomon18923f92017-11-06 16:26:02 -0500238 if (regenTexCoords) {
239 fSubRun->resetBulkUseToken();
240
Herb Derby317adf72018-11-16 17:29:29 -0500241 const SkDescriptor* desc = fSubRun->desc();
Brian Salomon18923f92017-11-06 16:26:02 -0500242
243 if (!*fLazyCache || (*fLazyCache)->getDescriptor() != *desc) {
244 SkScalerContextEffects effects;
245 effects.fPathEffect = fRun->fPathEffect.get();
Brian Salomon18923f92017-11-06 16:26:02 -0500246 effects.fMaskFilter = fRun->fMaskFilter.get();
Herb Derby8c4cbf42018-03-09 15:28:04 -0500247 *fLazyCache =
Herb Derbyfa996902018-04-18 11:36:12 -0400248 SkStrikeCache::FindOrCreateStrikeExclusive(*desc, effects, *fRun->fTypeface);
Brian Salomon18923f92017-11-06 16:26:02 -0500249 }
250
251 if (regenGlyphs) {
252 strike = fGlyphCache->getStrike(fLazyCache->get());
253 } else {
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500254 strike = fSubRun->refStrike();
Brian Salomon18923f92017-11-06 16:26:02 -0500255 }
256 }
257
Brian Salomon5c6ac642017-12-19 11:09:32 -0500258 bool hasW = fSubRun->hasWCoord();
Brian Salomon5c6ac642017-12-19 11:09:32 -0500259 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Brian Salomon18923f92017-11-06 16:26:02 -0500260 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500261 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500262 result->fFirstVertex = currVertex;
Brian Salomon18923f92017-11-06 16:26:02 -0500263
264 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
265 GrGlyph* glyph = nullptr;
266 if (regenTexCoords) {
267 size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
268
269 if (regenGlyphs) {
270 // Get the id from the old glyph, and use the new strike to lookup
271 // the glyph.
272 GrGlyph::PackedID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
Herb Derby4c35be02018-12-20 14:03:47 -0500273 fBlob->fGlyphs[glyphOffset] = strike->getGlyph(id, fLazyCache->get());
Brian Salomon18923f92017-11-06 16:26:02 -0500274 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
275 }
276 glyph = fBlob->fGlyphs[glyphOffset];
277 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
278
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500279 if (!fFullAtlasManager->hasGlyph(glyph)) {
280 GrDrawOpAtlas::ErrorCode code;
281 code = strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache,
282 fFullAtlasManager, glyph,
283 fLazyCache->get(), fSubRun->maskFormat(),
Jim Van Verthb515ae72018-05-23 16:44:55 -0400284 fSubRun->needsTransform());
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500285 if (GrDrawOpAtlas::ErrorCode::kError == code) {
286 // Something horrible has happened - drop the op
287 return false;
288 }
289 else if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
290 fBrokenRun = glyphIdx > 0;
291 result->fFinished = false;
292 return true;
293 }
Brian Salomon18923f92017-11-06 16:26:02 -0500294 }
Robert Phillips40a29d72018-01-18 12:59:22 -0500295 auto tokenTracker = fUploadTarget->tokenTracker();
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500296 fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
297 tokenTracker->nextDrawToken());
Brian Salomon18923f92017-11-06 16:26:02 -0500298 }
299
Brian Salomondeb53cc2017-11-08 13:50:53 -0500300 regen_vertices<regenPos, regenCol, regenTexCoords>(currVertex, glyph, vertexStride,
Brian Salomon18923f92017-11-06 16:26:02 -0500301 fSubRun->drawAsDistanceFields(), fTransX,
302 fTransY, fColor);
Brian Salomondeb53cc2017-11-08 13:50:53 -0500303 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500304 ++result->fGlyphsRegenerated;
Brian Salomon18923f92017-11-06 16:26:02 -0500305 ++fCurrGlyph;
306 }
307
308 // We may have changed the color so update it here
309 fSubRun->setColor(fColor);
310 if (regenTexCoords) {
311 if (regenGlyphs) {
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500312 fSubRun->setStrike(std::move(strike));
Brian Salomon18923f92017-11-06 16:26:02 -0500313 }
314 fSubRun->setAtlasGeneration(fBrokenRun
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500315 ? GrDrawOpAtlas::kInvalidAtlasGeneration
316 : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
Jim Van Verthba98b7d2018-12-05 12:33:43 -0500317 } else {
318 // For the non-texCoords case we need to ensure that we update the associated use tokens
319 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
320 fUploadTarget->tokenTracker()->nextDrawToken(),
321 fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500322 }
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500323 return true;
Brian Salomon18923f92017-11-06 16:26:02 -0500324}
325
Herb Derby6dff60e2018-11-12 15:45:49 -0500326bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500327 uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500328 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
329 // this each time.
330 if (fSubRun->atlasGeneration() != currentAtlasGen) {
331 fRegenFlags |= kRegenTex;
332 }
333
334 switch (static_cast<RegenMask>(fRegenFlags)) {
335 case kRegenPos:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500336 return this->doRegen<true, false, false, false>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500337 case kRegenCol:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500338 return this->doRegen<false, true, false, false>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500339 case kRegenTex:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500340 return this->doRegen<false, false, true, false>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500341 case kRegenGlyph:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500342 return this->doRegen<false, false, true, true>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500343
344 // combinations
345 case kRegenPosCol:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500346 return this->doRegen<true, true, false, false>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500347 case kRegenPosTex:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500348 return this->doRegen<true, false, true, false>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500349 case kRegenPosTexGlyph:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500350 return this->doRegen<true, false, true, true>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500351 case kRegenPosColTex:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500352 return this->doRegen<true, true, true, false>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500353 case kRegenPosColTexGlyph:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500354 return this->doRegen<true, true, true, true>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500355 case kRegenColTex:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500356 return this->doRegen<false, true, true, false>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500357 case kRegenColTexGlyph:
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500358 return this->doRegen<false, true, true, true>(result);
Brian Salomon18923f92017-11-06 16:26:02 -0500359 case kNoRegen: {
Brian Salomon5c6ac642017-12-19 11:09:32 -0500360 bool hasW = fSubRun->hasWCoord();
361 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500362 result->fFinished = true;
363 result->fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
364 result->fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
365 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Brian Salomon18923f92017-11-06 16:26:02 -0500366 fCurrGlyph = fSubRun->glyphCount();
367
368 // set use tokens for all of the glyphs in our subrun. This is only valid if we
369 // have a valid atlas generation
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500370 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
371 fUploadTarget->tokenTracker()->nextDrawToken(),
372 fSubRun->maskFormat());
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500373 return true;
Brian Salomon18923f92017-11-06 16:26:02 -0500374 }
375 }
376 SK_ABORT("Should not get here");
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500377 return false;
Brian Salomon18923f92017-11-06 16:26:02 -0500378}