blob: 2dcda1e3552ad79df34beb6655ef83ae380be8ab [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,
Brian Osman5d6be8f2019-01-08 12:02:51 -050020 kRegenGlyph = 0x8,
Brian Salomon18923f92017-11-06 16:26:02 -050021};
22
23////////////////////////////////////////////////////////////////////////////////////////////////////
Brian Osman5d6be8f2019-01-08 12:02:51 -050024
25static void regen_positions(char* vertex, size_t vertexStride, SkScalar transX, SkScalar transY) {
26 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
27 for (int i = 0; i < 4; ++i) {
28 point->fX += transX;
29 point->fY += transY;
30 point = SkTAddOffset<SkPoint>(point, vertexStride);
31 }
32}
33
34static void regen_colors(char* vertex, size_t vertexStride, GrColor color) {
35 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
36 // vertices, hence vertexStride - sizeof(SkIPoint16)
37 size_t colorOffset = vertexStride - sizeof(SkIPoint16) - sizeof(GrColor);
38 GrColor* vcolor = reinterpret_cast<GrColor*>(vertex + colorOffset);
39 for (int i = 0; i < 4; ++i) {
40 *vcolor = color;
41 vcolor = SkTAddOffset<GrColor>(vcolor, vertexStride);
42 }
43}
44
45static void regen_texcoords(char* vertex, size_t vertexStride, const GrGlyph* glyph,
46 bool useDistanceFields) {
47 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
48 // vertices, hence vertexStride - sizeof(SkIPoint16)
49 size_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
50
Brian Salomon18923f92017-11-06 16:26:02 -050051 uint16_t u0, v0, u1, v1;
Brian Osman5d6be8f2019-01-08 12:02:51 -050052 SkASSERT(glyph);
53 int width = glyph->fBounds.width();
54 int height = glyph->fBounds.height();
55
56 if (useDistanceFields) {
57 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
58 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
59 u1 = u0 + width - 2 * SK_DistanceFieldInset;
60 v1 = v0 + height - 2 * SK_DistanceFieldInset;
61 } else {
62 u0 = glyph->fAtlasLocation.fX;
63 v0 = glyph->fAtlasLocation.fY;
64 u1 = u0 + width;
65 v1 = v0 + height;
66 }
67 // We pack the 2bit page index in the low bit of the u and v texture coords
68 uint32_t pageIndex = glyph->pageIndex();
69 SkASSERT(pageIndex < 4);
70 uint16_t uBit = (pageIndex >> 1) & 0x1;
71 uint16_t vBit = pageIndex & 0x1;
72 u0 <<= 1;
73 u0 |= uBit;
74 v0 <<= 1;
75 v0 |= vBit;
76 u1 <<= 1;
77 u1 |= uBit;
78 v1 <<= 1;
79 v1 |= vBit;
80
81 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
82 textureCoords[0] = u0;
83 textureCoords[1] = v0;
84 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
85 textureCoords[0] = u0;
86 textureCoords[1] = v1;
87 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
88 textureCoords[0] = u1;
89 textureCoords[1] = v0;
90 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
91 textureCoords[0] = u1;
92 textureCoords[1] = v1;
93
Jim Van Verthfc4f7682018-01-25 16:26:25 -050094#ifdef DISPLAY_PAGE_INDEX
95 // Enable this to visualize the page from which each glyph is being drawn.
96 // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
Brian Osman5d6be8f2019-01-08 12:02:51 -050097 GrColor hackColor;
98 switch (pageIndex) {
99 case 0:
100 hackColor = GrColorPackRGBA(0, 255, 0, 255);
101 break;
102 case 1:
103 hackColor = GrColorPackRGBA(255, 0, 0, 255);;
104 break;
105 case 2:
106 hackColor = GrColorPackRGBA(255, 0, 255, 255);
107 break;
108 case 3:
109 hackColor = GrColorPackRGBA(0, 255, 255, 255);
110 break;
111 default:
112 hackColor = GrColorPackRGBA(0, 0, 0, 255);
113 break;
114 }
115 regen_colors(vertex, vertexStride, hackColor);
Jim Van Verthfc4f7682018-01-25 16:26:25 -0500116#endif
Brian Salomon18923f92017-11-06 16:26:02 -0500117}
118
Herb Derby6dff60e2018-11-12 15:45:49 -0500119GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
120 GrTextBlob* blob,
121 int runIdx, int subRunIdx,
122 const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
123 GrColor color,
124 GrDeferredUploadTarget* uploadTarget,
125 GrGlyphCache* glyphCache,
126 GrAtlasManager* fullAtlasManager,
127 SkExclusiveStrikePtr* lazyCache)
Robert Phillips4bc70112018-03-01 10:24:02 -0500128 : fResourceProvider(resourceProvider)
129 , fViewMatrix(viewMatrix)
Brian Salomon18923f92017-11-06 16:26:02 -0500130 , fBlob(blob)
131 , fUploadTarget(uploadTarget)
132 , fGlyphCache(glyphCache)
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500133 , fFullAtlasManager(fullAtlasManager)
Brian Salomon18923f92017-11-06 16:26:02 -0500134 , fLazyCache(lazyCache)
135 , fRun(&blob->fRuns[runIdx])
136 , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
Brian Salomon18923f92017-11-06 16:26:02 -0500137 , fColor(color) {
138 // Compute translation if any
139 fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
140
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500141 // Because the GrGlyphCache may evict the strike a blob depends on using for
Brian Salomon18923f92017-11-06 16:26:02 -0500142 // generating its texture coords, we have to track whether or not the strike has
143 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
144 // otherwise we have to get the new strike, and use that to get the correct glyphs.
145 // Because we do not have the packed ids, and thus can't look up our glyphs in the
146 // new strike, we instead keep our ref to the old strike and use the packed ids from
147 // it. These ids will still be valid as long as we hold the ref. When we are done
148 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
149 if (fSubRun->strike()->isAbandoned()) {
150 fRegenFlags |= kRegenGlyph;
151 fRegenFlags |= kRegenTex;
152 }
153 if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
154 fRegenFlags |= kRegenCol;
155 }
156 if (0.f != fTransX || 0.f != fTransY) {
157 fRegenFlags |= kRegenPos;
158 }
159}
160
Brian Osman5d6be8f2019-01-08 12:02:51 -0500161bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result,
162 bool regenPos, bool regenCol, bool regenTexCoords,
163 bool regenGlyphs) {
164 SkASSERT(!regenGlyphs || regenTexCoords);
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500165 sk_sp<GrTextStrike> strike;
Brian Salomon18923f92017-11-06 16:26:02 -0500166 if (regenTexCoords) {
167 fSubRun->resetBulkUseToken();
168
Herb Derby317adf72018-11-16 17:29:29 -0500169 const SkDescriptor* desc = fSubRun->desc();
Brian Salomon18923f92017-11-06 16:26:02 -0500170
171 if (!*fLazyCache || (*fLazyCache)->getDescriptor() != *desc) {
172 SkScalerContextEffects effects;
173 effects.fPathEffect = fRun->fPathEffect.get();
Brian Salomon18923f92017-11-06 16:26:02 -0500174 effects.fMaskFilter = fRun->fMaskFilter.get();
Herb Derby8c4cbf42018-03-09 15:28:04 -0500175 *fLazyCache =
Herb Derbyfa996902018-04-18 11:36:12 -0400176 SkStrikeCache::FindOrCreateStrikeExclusive(*desc, effects, *fRun->fTypeface);
Brian Salomon18923f92017-11-06 16:26:02 -0500177 }
178
179 if (regenGlyphs) {
180 strike = fGlyphCache->getStrike(fLazyCache->get());
181 } else {
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500182 strike = fSubRun->refStrike();
Brian Salomon18923f92017-11-06 16:26:02 -0500183 }
184 }
185
Brian Salomon5c6ac642017-12-19 11:09:32 -0500186 bool hasW = fSubRun->hasWCoord();
Brian Salomon5c6ac642017-12-19 11:09:32 -0500187 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Brian Salomon18923f92017-11-06 16:26:02 -0500188 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500189 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500190 result->fFirstVertex = currVertex;
Brian Salomon18923f92017-11-06 16:26:02 -0500191
192 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
193 GrGlyph* glyph = nullptr;
194 if (regenTexCoords) {
195 size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
196
197 if (regenGlyphs) {
198 // Get the id from the old glyph, and use the new strike to lookup
199 // the glyph.
Herb Derby5a3fdee2018-12-20 14:47:03 -0500200 SkPackedGlyphID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
Herb Derby4c35be02018-12-20 14:03:47 -0500201 fBlob->fGlyphs[glyphOffset] = strike->getGlyph(id, fLazyCache->get());
Brian Salomon18923f92017-11-06 16:26:02 -0500202 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
203 }
204 glyph = fBlob->fGlyphs[glyphOffset];
205 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
206
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500207 if (!fFullAtlasManager->hasGlyph(glyph)) {
208 GrDrawOpAtlas::ErrorCode code;
209 code = strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache,
210 fFullAtlasManager, glyph,
211 fLazyCache->get(), fSubRun->maskFormat(),
Jim Van Verthb515ae72018-05-23 16:44:55 -0400212 fSubRun->needsTransform());
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500213 if (GrDrawOpAtlas::ErrorCode::kError == code) {
214 // Something horrible has happened - drop the op
215 return false;
216 }
217 else if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
218 fBrokenRun = glyphIdx > 0;
219 result->fFinished = false;
220 return true;
221 }
Brian Salomon18923f92017-11-06 16:26:02 -0500222 }
Robert Phillips40a29d72018-01-18 12:59:22 -0500223 auto tokenTracker = fUploadTarget->tokenTracker();
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500224 fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
225 tokenTracker->nextDrawToken());
Brian Salomon18923f92017-11-06 16:26:02 -0500226 }
227
Brian Osman5d6be8f2019-01-08 12:02:51 -0500228 if (regenPos) {
229 regen_positions(currVertex, vertexStride, fTransX, fTransY);
230 }
231 if (regenCol) {
232 regen_colors(currVertex, vertexStride, fColor);
233 }
234 if (regenTexCoords) {
235 regen_texcoords(currVertex, vertexStride, glyph, fSubRun->drawAsDistanceFields());
236 }
237
Brian Salomondeb53cc2017-11-08 13:50:53 -0500238 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500239 ++result->fGlyphsRegenerated;
Brian Salomon18923f92017-11-06 16:26:02 -0500240 ++fCurrGlyph;
241 }
242
243 // We may have changed the color so update it here
244 fSubRun->setColor(fColor);
245 if (regenTexCoords) {
246 if (regenGlyphs) {
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500247 fSubRun->setStrike(std::move(strike));
Brian Salomon18923f92017-11-06 16:26:02 -0500248 }
249 fSubRun->setAtlasGeneration(fBrokenRun
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500250 ? GrDrawOpAtlas::kInvalidAtlasGeneration
251 : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
Jim Van Verthba98b7d2018-12-05 12:33:43 -0500252 } else {
253 // For the non-texCoords case we need to ensure that we update the associated use tokens
254 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
255 fUploadTarget->tokenTracker()->nextDrawToken(),
256 fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500257 }
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500258 return true;
Brian Salomon18923f92017-11-06 16:26:02 -0500259}
260
Herb Derby6dff60e2018-11-12 15:45:49 -0500261bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500262 uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500263 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
264 // this each time.
265 if (fSubRun->atlasGeneration() != currentAtlasGen) {
266 fRegenFlags |= kRegenTex;
267 }
268
Brian Osman5d6be8f2019-01-08 12:02:51 -0500269 if (fRegenFlags) {
270 return this->doRegen(result,
271 fRegenFlags & kRegenPos,
272 fRegenFlags & kRegenCol,
273 fRegenFlags & kRegenTex,
274 fRegenFlags & kRegenGlyph);
275 } else {
276 bool hasW = fSubRun->hasWCoord();
277 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
278 result->fFinished = true;
279 result->fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
280 result->fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
281 fCurrGlyph * kVerticesPerGlyph * vertexStride;
282 fCurrGlyph = fSubRun->glyphCount();
Brian Salomon18923f92017-11-06 16:26:02 -0500283
Brian Osman5d6be8f2019-01-08 12:02:51 -0500284 // set use tokens for all of the glyphs in our subrun. This is only valid if we
285 // have a valid atlas generation
286 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
287 fUploadTarget->tokenTracker()->nextDrawToken(),
288 fSubRun->maskFormat());
289 return true;
Brian Salomon18923f92017-11-06 16:26:02 -0500290 }
291 SK_ABORT("Should not get here");
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500292 return false;
Brian Salomon18923f92017-11-06 16:26:02 -0500293}