• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "GrAtlasManager.h"
9 #include "GrAtlasTextBlob.h"
10 #include "GrTextUtils.h"
11 #include "SkDistanceFieldGen.h"
12 #include "SkGlyphCache.h"
13 #include "ops/GrAtlasTextOp.h"
14 
15 using Regenerator = GrAtlasTextBlob::VertexRegenerator;
16 
17 enum RegenMask {
18     kNoRegen    = 0x0,
19     kRegenPos   = 0x1,
20     kRegenCol   = 0x2,
21     kRegenTex   = 0x4,
22     kRegenGlyph = 0x8 | kRegenTex,  // we have to regenerate the texture coords when we regen glyphs
23 
24     // combinations
25     kRegenPosCol = kRegenPos | kRegenCol,
26     kRegenPosTex = kRegenPos | kRegenTex,
27     kRegenPosTexGlyph = kRegenPos | kRegenGlyph,
28     kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex,
29     kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph,
30     kRegenColTex = kRegenCol | kRegenTex,
31     kRegenColTexGlyph = kRegenCol | kRegenGlyph,
32 };
33 
34 ////////////////////////////////////////////////////////////////////////////////////////////////////
35 // A large template to handle regenerating the vertices of a textblob with as few branches as
36 // possible
37 template <bool regenPos, bool regenCol, bool regenTexCoords>
regen_vertices(char * vertex,const GrGlyph * glyph,size_t vertexStride,bool useDistanceFields,SkScalar transX,SkScalar transY,GrColor color)38 inline void regen_vertices(char* vertex, const GrGlyph* glyph, size_t vertexStride,
39                            bool useDistanceFields, SkScalar transX, SkScalar transY,
40                            GrColor color) {
41     uint16_t u0, v0, u1, v1;
42 #ifdef DISPLAY_PAGE_INDEX
43     // Enable this to visualize the page from which each glyph is being drawn.
44     // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
45     SkColor hackColor;
46 #endif
47     if (regenTexCoords) {
48         SkASSERT(glyph);
49         int width = glyph->fBounds.width();
50         int height = glyph->fBounds.height();
51 
52         if (useDistanceFields) {
53             u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
54             v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
55             u1 = u0 + width - 2 * SK_DistanceFieldInset;
56             v1 = v0 + height - 2 * SK_DistanceFieldInset;
57         } else {
58             u0 = glyph->fAtlasLocation.fX;
59             v0 = glyph->fAtlasLocation.fY;
60             u1 = u0 + width;
61             v1 = v0 + height;
62         }
63         // We pack the 2bit page index in the low bit of the u and v texture coords
64         uint32_t pageIndex = glyph->pageIndex();
65         SkASSERT(pageIndex < 4);
66         uint16_t uBit = (pageIndex >> 1) & 0x1;
67         uint16_t vBit = pageIndex & 0x1;
68         u0 <<= 1;
69         u0 |= uBit;
70         v0 <<= 1;
71         v0 |= vBit;
72         u1 <<= 1;
73         u1 |= uBit;
74         v1 <<= 1;
75         v1 |= vBit;
76 #ifdef DISPLAY_PAGE_INDEX
77         switch (pageIndex) {
78             case 0:
79                 hackColor = SK_ColorGREEN;
80                 break;
81             case 1:
82                 hackColor = SK_ColorRED;
83                 break;
84             case 2:
85                 hackColor = SK_ColorMAGENTA;
86                 break;
87             case 3:
88                 hackColor = SK_ColorCYAN;
89                 break;
90             default:
91                 hackColor = SK_ColorBLACK;
92                 break;
93         }
94 #endif
95     }
96 
97     // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
98     // vertices, hence vertexStride - sizeof(SkIPoint16)
99     intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
100     intptr_t colorOffset = texCoordOffset - sizeof(GrColor);
101 
102     // V0
103     if (regenPos) {
104         SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
105         point->fX += transX;
106         point->fY += transY;
107     }
108 
109     if (regenCol) {
110         SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
111         *vcolor = color;
112     }
113 
114     if (regenTexCoords) {
115         uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
116         textureCoords[0] = u0;
117         textureCoords[1] = v0;
118 #ifdef DISPLAY_PAGE_INDEX
119         SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
120         *vcolor = hackColor;
121 #endif
122     }
123     vertex += vertexStride;
124 
125     // V1
126     if (regenPos) {
127         SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
128         point->fX += transX;
129         point->fY += transY;
130     }
131 
132     if (regenCol) {
133         SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
134         *vcolor = color;
135     }
136 
137     if (regenTexCoords) {
138         uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
139         textureCoords[0] = u0;
140         textureCoords[1] = v1;
141 #ifdef DISPLAY_PAGE_INDEX
142         SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
143         *vcolor = hackColor;
144 #endif
145     }
146     vertex += vertexStride;
147 
148     // V2
149     if (regenPos) {
150         SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
151         point->fX += transX;
152         point->fY += transY;
153     }
154 
155     if (regenCol) {
156         SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
157         *vcolor = color;
158     }
159 
160     if (regenTexCoords) {
161         uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
162         textureCoords[0] = u1;
163         textureCoords[1] = v0;
164 #ifdef DISPLAY_PAGE_INDEX
165         SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
166         *vcolor = hackColor;
167 #endif
168     }
169     vertex += vertexStride;
170 
171     // V3
172     if (regenPos) {
173         SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
174         point->fX += transX;
175         point->fY += transY;
176     }
177 
178     if (regenCol) {
179         SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
180         *vcolor = color;
181     }
182 
183     if (regenTexCoords) {
184         uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
185         textureCoords[0] = u1;
186         textureCoords[1] = v1;
187 #ifdef DISPLAY_PAGE_INDEX
188         SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
189         *vcolor = hackColor;
190 #endif
191     }
192 }
193 
VertexRegenerator(GrResourceProvider * resourceProvider,GrAtlasTextBlob * blob,int runIdx,int subRunIdx,const SkMatrix & viewMatrix,SkScalar x,SkScalar y,GrColor color,GrDeferredUploadTarget * uploadTarget,GrGlyphCache * glyphCache,GrAtlasManager * fullAtlasManager,SkAutoGlyphCache * lazyCache)194 Regenerator::VertexRegenerator(GrResourceProvider* resourceProvider, GrAtlasTextBlob* blob,
195                                int runIdx, int subRunIdx,
196                                const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
197                                GrDeferredUploadTarget* uploadTarget, GrGlyphCache* glyphCache,
198                                GrAtlasManager* fullAtlasManager, SkAutoGlyphCache* lazyCache)
199         : fResourceProvider(resourceProvider)
200         , fViewMatrix(viewMatrix)
201         , fBlob(blob)
202         , fUploadTarget(uploadTarget)
203         , fGlyphCache(glyphCache)
204         , fFullAtlasManager(fullAtlasManager)
205         , fLazyCache(lazyCache)
206         , fRun(&blob->fRuns[runIdx])
207         , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
208         , fColor(color) {
209     // Compute translation if any
210     fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
211 
212     // Because the GrGlyphCache may evict the strike a blob depends on using for
213     // generating its texture coords, we have to track whether or not the strike has
214     // been abandoned.  If it hasn't been abandoned, then we can use the GrGlyph*s as is
215     // otherwise we have to get the new strike, and use that to get the correct glyphs.
216     // Because we do not have the packed ids, and thus can't look up our glyphs in the
217     // new strike, we instead keep our ref to the old strike and use the packed ids from
218     // it.  These ids will still be valid as long as we hold the ref.  When we are done
219     // updating our cache of the GrGlyph*s, we drop our ref on the old strike
220     if (fSubRun->strike()->isAbandoned()) {
221         fRegenFlags |= kRegenGlyph;
222         fRegenFlags |= kRegenTex;
223     }
224     if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
225         fRegenFlags |= kRegenCol;
226     }
227     if (0.f != fTransX || 0.f != fTransY) {
228         fRegenFlags |= kRegenPos;
229     }
230 }
231 
232 template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
doRegen()233 Regenerator::Result Regenerator::doRegen() {
234     static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
235     sk_sp<GrTextStrike> strike;
236     if (regenTexCoords) {
237         fSubRun->resetBulkUseToken();
238 
239         const SkDescriptor* desc = (fRun->fOverrideDescriptor && !fSubRun->drawAsDistanceFields())
240                                            ? fRun->fOverrideDescriptor->getDesc()
241                                            : fRun->fDescriptor.getDesc();
242 
243         if (!*fLazyCache || (*fLazyCache)->getDescriptor() != *desc) {
244             SkScalerContextEffects effects;
245             effects.fPathEffect = fRun->fPathEffect.get();
246             effects.fMaskFilter = fRun->fMaskFilter.get();
247             fLazyCache->reset(SkGlyphCache::DetachCache(fRun->fTypeface.get(), effects, desc));
248         }
249 
250         if (regenGlyphs) {
251             strike = fGlyphCache->getStrike(fLazyCache->get());
252         } else {
253             strike = fSubRun->refStrike();
254         }
255     }
256 
257     bool hasW = fSubRun->hasWCoord();
258     Result result;
259     auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
260     char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
261                        fCurrGlyph * kVerticesPerGlyph * vertexStride;
262     result.fFirstVertex = currVertex;
263 
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;
273                 fBlob->fGlyphs[glyphOffset] =
274                         strike->getGlyph(id, fSubRun->maskFormat(), fLazyCache->get());
275                 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
276             }
277             glyph = fBlob->fGlyphs[glyphOffset];
278             SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
279 
280             if (!fFullAtlasManager->hasGlyph(glyph) &&
281                 !strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache,
282                                          fFullAtlasManager, glyph,
283                                          fLazyCache->get(), fSubRun->maskFormat())) {
284                 fBrokenRun = glyphIdx > 0;
285                 result.fFinished = false;
286                 return result;
287             }
288             auto tokenTracker = fUploadTarget->tokenTracker();
289             fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
290                                                             tokenTracker->nextDrawToken());
291         }
292 
293         regen_vertices<regenPos, regenCol, regenTexCoords>(currVertex, glyph, vertexStride,
294                                                            fSubRun->drawAsDistanceFields(), fTransX,
295                                                            fTransY, fColor);
296         currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
297         ++result.fGlyphsRegenerated;
298         ++fCurrGlyph;
299     }
300 
301     // We may have changed the color so update it here
302     fSubRun->setColor(fColor);
303     if (regenTexCoords) {
304         if (regenGlyphs) {
305             fSubRun->setStrike(std::move(strike));
306         }
307         fSubRun->setAtlasGeneration(fBrokenRun
308                                     ? GrDrawOpAtlas::kInvalidAtlasGeneration
309                                     : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
310     }
311     return result;
312 }
313 
regenerate()314 Regenerator::Result Regenerator::regenerate() {
315     uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
316     // If regenerate() is called multiple times then the atlas gen may have changed. So we check
317     // this each time.
318     if (fSubRun->atlasGeneration() != currentAtlasGen) {
319         fRegenFlags |= kRegenTex;
320     }
321 
322     switch (static_cast<RegenMask>(fRegenFlags)) {
323         case kRegenPos:
324             return this->doRegen<true, false, false, false>();
325         case kRegenCol:
326             return this->doRegen<false, true, false, false>();
327         case kRegenTex:
328             return this->doRegen<false, false, true, false>();
329         case kRegenGlyph:
330             return this->doRegen<false, false, true, true>();
331 
332         // combinations
333         case kRegenPosCol:
334             return this->doRegen<true, true, false, false>();
335         case kRegenPosTex:
336             return this->doRegen<true, false, true, false>();
337         case kRegenPosTexGlyph:
338             return this->doRegen<true, false, true, true>();
339         case kRegenPosColTex:
340             return this->doRegen<true, true, true, false>();
341         case kRegenPosColTexGlyph:
342             return this->doRegen<true, true, true, true>();
343         case kRegenColTex:
344             return this->doRegen<false, true, true, false>();
345         case kRegenColTexGlyph:
346             return this->doRegen<false, true, true, true>();
347         case kNoRegen: {
348             Result result;
349             bool hasW = fSubRun->hasWCoord();
350             auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
351             result.fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
352             result.fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
353                                   fCurrGlyph * kVerticesPerGlyph * vertexStride;
354             fCurrGlyph = fSubRun->glyphCount();
355 
356             // set use tokens for all of the glyphs in our subrun.  This is only valid if we
357             // have a valid atlas generation
358             fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
359                                                fUploadTarget->tokenTracker()->nextDrawToken(),
360                                                fSubRun->maskFormat());
361             return result;
362         }
363     }
364     SK_ABORT("Should not get here");
365     return Result();
366 }
367