1 /*
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 "src/core/SkDistanceFieldGen.h"
9 #include "src/gpu/ops/GrAtlasTextOp.h"
10 #include "src/gpu/text/GrAtlasManager.h"
11 #include "src/gpu/text/GrTextBlob.h"
12 #include "src/gpu/text/GrTextTarget.h"
13
14 enum RegenMask {
15 kNoRegen = 0x0,
16 kRegenPos = 0x1,
17 kRegenCol = 0x2,
18 kRegenTex = 0x4,
19 kRegenGlyph = 0x8,
20 };
21
22 ////////////////////////////////////////////////////////////////////////////////////////////////////
23
regen_positions(char * vertex,size_t vertexStride,SkScalar transX,SkScalar transY)24 static void regen_positions(char* vertex, size_t vertexStride, SkScalar transX, SkScalar transY) {
25 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
26 for (int i = 0; i < 4; ++i) {
27 point->fX += transX;
28 point->fY += transY;
29 point = SkTAddOffset<SkPoint>(point, vertexStride);
30 }
31 }
32
regen_colors(char * vertex,size_t vertexStride,GrColor color)33 static void regen_colors(char* vertex, size_t vertexStride, GrColor color) {
34 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
35 // vertices, hence vertexStride - sizeof(SkIPoint16)
36 size_t colorOffset = vertexStride - sizeof(SkIPoint16) - sizeof(GrColor);
37 GrColor* vcolor = reinterpret_cast<GrColor*>(vertex + colorOffset);
38 for (int i = 0; i < 4; ++i) {
39 *vcolor = color;
40 vcolor = SkTAddOffset<GrColor>(vcolor, vertexStride);
41 }
42 }
43
regen_texcoords(char * vertex,size_t vertexStride,const GrGlyph * glyph,bool useDistanceFields)44 static void regen_texcoords(char* vertex, size_t vertexStride, const GrGlyph* glyph,
45 bool useDistanceFields) {
46 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
47 // vertices, hence vertexStride - sizeof(SkIPoint16)
48 size_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
49
50 uint16_t u0, v0, u1, v1;
51 SkASSERT(glyph);
52 int width = glyph->fBounds.width();
53 int height = glyph->fBounds.height();
54
55 if (useDistanceFields) {
56 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
57 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
58 u1 = u0 + width - 2 * SK_DistanceFieldInset;
59 v1 = v0 + height - 2 * SK_DistanceFieldInset;
60 } else {
61 u0 = glyph->fAtlasLocation.fX;
62 v0 = glyph->fAtlasLocation.fY;
63 u1 = u0 + width;
64 v1 = v0 + height;
65 }
66 // We pack the 2bit page index in the low bit of the u and v texture coords
67 uint32_t pageIndex = glyph->pageIndex();
68 SkASSERT(pageIndex < 16);
69 #ifdef SK_ENABLE_SMALL_PAGE
70 uint16_t uBit = (pageIndex >> 2) & 0x3;
71 uint16_t vBit = pageIndex & 0x3;
72 u0 <<= 2;
73 u0 |= uBit;
74 v0 <<= 2;
75 v0 |= vBit;
76 u1 <<= 2;
77 u1 |= uBit;
78 v1 <<= 2;
79 v1 |= vBit;
80 #else
81 uint16_t uBit = (pageIndex >> 1) & 0x1;
82 uint16_t vBit = pageIndex & 0x1;
83 u0 <<= 1;
84 u0 |= uBit;
85 v0 <<= 1;
86 v0 |= vBit;
87 u1 <<= 1;
88 u1 |= uBit;
89 v1 <<= 1;
90 v1 |= vBit;
91 #endif
92
93 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
94 textureCoords[0] = u0;
95 textureCoords[1] = v0;
96 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
97 textureCoords[0] = u0;
98 textureCoords[1] = v1;
99 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
100 textureCoords[0] = u1;
101 textureCoords[1] = v0;
102 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
103 textureCoords[0] = u1;
104 textureCoords[1] = v1;
105
106 #ifdef DISPLAY_PAGE_INDEX
107 // Enable this to visualize the page from which each glyph is being drawn.
108 // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
109 GrColor hackColor;
110 switch (pageIndex) {
111 case 0:
112 hackColor = GrColorPackRGBA(0, 255, 0, 255);
113 break;
114 case 1:
115 hackColor = GrColorPackRGBA(255, 0, 0, 255);
116 break;
117 case 2:
118 hackColor = GrColorPackRGBA(255, 0, 255, 255);
119 break;
120 case 3:
121 hackColor = GrColorPackRGBA(0, 255, 255, 255);
122 break;
123 default:
124 hackColor = GrColorPackRGBA(0, 0, 0, 255);
125 break;
126 }
127 regen_colors(vertex, vertexStride, hackColor);
128 #endif
129 }
130
VertexRegenerator(GrResourceProvider * resourceProvider,GrTextBlob * blob,int runIdx,int subRunIdx,const SkMatrix & viewMatrix,SkScalar x,SkScalar y,GrColor color,GrDeferredUploadTarget * uploadTarget,GrStrikeCache * glyphCache,GrAtlasManager * fullAtlasManager,SkExclusiveStrikePtr * lazyStrike)131 GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
132 GrTextBlob* blob,
133 int runIdx, int subRunIdx,
134 const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
135 GrColor color,
136 GrDeferredUploadTarget* uploadTarget,
137 GrStrikeCache* glyphCache,
138 GrAtlasManager* fullAtlasManager,
139 SkExclusiveStrikePtr* lazyStrike)
140 : fResourceProvider(resourceProvider)
141 , fViewMatrix(viewMatrix)
142 , fBlob(blob)
143 , fUploadTarget(uploadTarget)
144 , fGlyphCache(glyphCache)
145 , fFullAtlasManager(fullAtlasManager)
146 , fLazyStrike(lazyStrike)
147 , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
148 , fColor(color) {
149 // Compute translation if any
150 fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
151
152 // Because the GrStrikeCache may evict the strike a blob depends on using for
153 // generating its texture coords, we have to track whether or not the strike has
154 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
155 // otherwise we have to get the new strike, and use that to get the correct glyphs.
156 // Because we do not have the packed ids, and thus can't look up our glyphs in the
157 // new strike, we instead keep our ref to the old strike and use the packed ids from
158 // it. These ids will still be valid as long as we hold the ref. When we are done
159 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
160 if (fSubRun->strike()->isAbandoned()) {
161 fRegenFlags |= kRegenGlyph;
162 fRegenFlags |= kRegenTex;
163 }
164 if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
165 fRegenFlags |= kRegenCol;
166 }
167 if (0.f != fTransX || 0.f != fTransY) {
168 fRegenFlags |= kRegenPos;
169 }
170 }
171
172
173
doRegen(GrTextBlob::VertexRegenerator::Result * result,bool regenPos,bool regenCol,bool regenTexCoords,bool regenGlyphs)174 bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result,
175 bool regenPos, bool regenCol, bool regenTexCoords,
176 bool regenGlyphs) {
177 SkASSERT(!regenGlyphs || regenTexCoords);
178 sk_sp<GrTextStrike> strike;
179 if (regenTexCoords) {
180 fSubRun->resetBulkUseToken();
181
182 const SkStrikeSpec& strikeSpec = fSubRun->strikeSpec();
183
184 if (!*fLazyStrike || (*fLazyStrike)->getDescriptor() != strikeSpec.descriptor()) {
185 *fLazyStrike =
186 strikeSpec.findOrCreateExclusiveStrike(SkStrikeCache::GlobalStrikeCache());
187 }
188
189 if (regenGlyphs) {
190 strike = strikeSpec.findOrCreateGrStrike(fGlyphCache);
191 } else {
192 strike = fSubRun->refStrike();
193 }
194 }
195
196 bool hasW = fSubRun->hasWCoord();
197 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
198 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
199 fCurrGlyph * kVerticesPerGlyph * vertexStride;
200 result->fFirstVertex = currVertex;
201
202 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
203 GrGlyph* glyph = nullptr;
204 if (regenTexCoords) {
205 size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
206
207 if (regenGlyphs) {
208 // Get the id from the old glyph, and use the new strike to lookup
209 // the glyph.
210 SkPackedGlyphID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
211 fBlob->fGlyphs[glyphOffset] = strike->getGlyph(id, fLazyStrike->get());
212 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
213 }
214 glyph = fBlob->fGlyphs[glyphOffset];
215 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
216
217 if (!fFullAtlasManager->hasGlyph(glyph)) {
218 fFullAtlasManager->incAtlasMissCount();
219 GrDrawOpAtlas::ErrorCode code;
220 code = strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache,
221 fFullAtlasManager, glyph,
222 fLazyStrike->get(), fSubRun->maskFormat(),
223 fSubRun->needsTransform());
224 if (GrDrawOpAtlas::ErrorCode::kError == code) {
225 // Something horrible has happened - drop the op
226 return false;
227 }
228 else if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
229 fBrokenRun = glyphIdx > 0;
230 result->fFinished = false;
231 return true;
232 }
233 } else {
234 fFullAtlasManager->incAtlasHitCount();
235 }
236 auto tokenTracker = fUploadTarget->tokenTracker();
237 fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
238 tokenTracker->nextDrawToken());
239 }
240
241 if (regenPos) {
242 regen_positions(currVertex, vertexStride, fTransX, fTransY);
243 }
244 if (regenCol) {
245 regen_colors(currVertex, vertexStride, fColor);
246 }
247 if (regenTexCoords) {
248 regen_texcoords(currVertex, vertexStride, glyph, fSubRun->drawAsDistanceFields());
249 }
250
251 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
252 ++result->fGlyphsRegenerated;
253 ++fCurrGlyph;
254 }
255
256 // We may have changed the color so update it here
257 fSubRun->setColor(fColor);
258 if (regenTexCoords) {
259 if (regenGlyphs) {
260 fSubRun->setStrike(std::move(strike));
261 }
262 fSubRun->setAtlasGeneration(fBrokenRun
263 ? GrDrawOpAtlas::kInvalidAtlasGeneration
264 : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
265 } else {
266 // For the non-texCoords case we need to ensure that we update the associated use tokens
267 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
268 fUploadTarget->tokenTracker()->nextDrawToken(),
269 fSubRun->maskFormat());
270 }
271 return true;
272 }
273
regenerate(GrTextBlob::VertexRegenerator::Result * result)274 bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result) {
275 uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
276 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
277 // this each time.
278 if (fSubRun->atlasGeneration() != currentAtlasGen) {
279 fRegenFlags |= kRegenTex;
280 }
281
282 if (fRegenFlags) {
283 return this->doRegen(result,
284 fRegenFlags & kRegenPos,
285 fRegenFlags & kRegenCol,
286 fRegenFlags & kRegenTex,
287 fRegenFlags & kRegenGlyph);
288 } else {
289 bool hasW = fSubRun->hasWCoord();
290 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
291 result->fFinished = true;
292 result->fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
293 result->fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
294 fCurrGlyph * kVerticesPerGlyph * vertexStride;
295 fCurrGlyph = fSubRun->glyphCount();
296
297 // set use tokens for all of the glyphs in our subrun. This is only valid if we
298 // have a valid atlas generation
299 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
300 fUploadTarget->tokenTracker()->nextDrawToken(),
301 fSubRun->maskFormat());
302 return true;
303 }
304 SK_ABORT("Should not get here");
305 }
306