• 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 "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