blob: 96bf4ce1f1ca9fb9d7cb13ca7610a93d280f7a1e [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#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"
Brian Salomon18923f92017-11-06 16:26:02 -050013
Brian Salomon18923f92017-11-06 16:26:02 -050014enum RegenMask {
15 kNoRegen = 0x0,
16 kRegenPos = 0x1,
17 kRegenCol = 0x2,
18 kRegenTex = 0x4,
Brian Osman5d6be8f2019-01-08 12:02:51 -050019 kRegenGlyph = 0x8,
Brian Salomon18923f92017-11-06 16:26:02 -050020};
21
22////////////////////////////////////////////////////////////////////////////////////////////////////
Brian Osman5d6be8f2019-01-08 12:02:51 -050023
24static 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
33static 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
44static 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
Brian Salomon18923f92017-11-06 16:26:02 -050050 uint16_t u0, v0, u1, v1;
Brian Osman5d6be8f2019-01-08 12:02:51 -050051 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 < 4);
69 uint16_t uBit = (pageIndex >> 1) & 0x1;
70 uint16_t vBit = pageIndex & 0x1;
71 u0 <<= 1;
72 u0 |= uBit;
73 v0 <<= 1;
74 v0 |= vBit;
75 u1 <<= 1;
76 u1 |= uBit;
77 v1 <<= 1;
78 v1 |= vBit;
79
80 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
81 textureCoords[0] = u0;
82 textureCoords[1] = v0;
83 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
84 textureCoords[0] = u0;
85 textureCoords[1] = v1;
86 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
87 textureCoords[0] = u1;
88 textureCoords[1] = v0;
89 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
90 textureCoords[0] = u1;
91 textureCoords[1] = v1;
92
Jim Van Verthfc4f7682018-01-25 16:26:25 -050093#ifdef DISPLAY_PAGE_INDEX
94 // Enable this to visualize the page from which each glyph is being drawn.
95 // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
Brian Osman5d6be8f2019-01-08 12:02:51 -050096 GrColor hackColor;
97 switch (pageIndex) {
98 case 0:
99 hackColor = GrColorPackRGBA(0, 255, 0, 255);
100 break;
101 case 1:
102 hackColor = GrColorPackRGBA(255, 0, 0, 255);;
103 break;
104 case 2:
105 hackColor = GrColorPackRGBA(255, 0, 255, 255);
106 break;
107 case 3:
108 hackColor = GrColorPackRGBA(0, 255, 255, 255);
109 break;
110 default:
111 hackColor = GrColorPackRGBA(0, 0, 0, 255);
112 break;
113 }
114 regen_colors(vertex, vertexStride, hackColor);
Jim Van Verthfc4f7682018-01-25 16:26:25 -0500115#endif
Brian Salomon18923f92017-11-06 16:26:02 -0500116}
117
Herb Derby6dff60e2018-11-12 15:45:49 -0500118GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
119 GrTextBlob* blob,
120 int runIdx, int subRunIdx,
121 const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
122 GrColor color,
123 GrDeferredUploadTarget* uploadTarget,
Herb Derby081e6f32019-01-16 13:46:02 -0500124 GrStrikeCache* glyphCache,
Herb Derby6dff60e2018-11-12 15:45:49 -0500125 GrAtlasManager* fullAtlasManager,
Herb Derbye7efd082019-05-28 11:30:33 -0400126 SkExclusiveStrikePtr* lazyStrike)
Robert Phillips4bc70112018-03-01 10:24:02 -0500127 : fResourceProvider(resourceProvider)
128 , fViewMatrix(viewMatrix)
Brian Salomon18923f92017-11-06 16:26:02 -0500129 , fBlob(blob)
130 , fUploadTarget(uploadTarget)
131 , fGlyphCache(glyphCache)
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500132 , fFullAtlasManager(fullAtlasManager)
Herb Derbye7efd082019-05-28 11:30:33 -0400133 , fLazyStrike(lazyStrike)
Brian Salomon18923f92017-11-06 16:26:02 -0500134 , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
Brian Salomon18923f92017-11-06 16:26:02 -0500135 , fColor(color) {
136 // Compute translation if any
137 fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
138
Herb Derby081e6f32019-01-16 13:46:02 -0500139 // Because the GrStrikeCache may evict the strike a blob depends on using for
Brian Salomon18923f92017-11-06 16:26:02 -0500140 // generating its texture coords, we have to track whether or not the strike has
141 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
142 // otherwise we have to get the new strike, and use that to get the correct glyphs.
143 // Because we do not have the packed ids, and thus can't look up our glyphs in the
144 // new strike, we instead keep our ref to the old strike and use the packed ids from
145 // it. These ids will still be valid as long as we hold the ref. When we are done
146 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
147 if (fSubRun->strike()->isAbandoned()) {
148 fRegenFlags |= kRegenGlyph;
149 fRegenFlags |= kRegenTex;
150 }
151 if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
152 fRegenFlags |= kRegenCol;
153 }
154 if (0.f != fTransX || 0.f != fTransY) {
155 fRegenFlags |= kRegenPos;
156 }
157}
158
Brian Osman5d6be8f2019-01-08 12:02:51 -0500159bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result,
160 bool regenPos, bool regenCol, bool regenTexCoords,
161 bool regenGlyphs) {
162 SkASSERT(!regenGlyphs || regenTexCoords);
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500163 sk_sp<GrTextStrike> strike;
Brian Salomon18923f92017-11-06 16:26:02 -0500164 if (regenTexCoords) {
165 fSubRun->resetBulkUseToken();
166
Herb Derbye7efd082019-05-28 11:30:33 -0400167 const SkStrikeSpecStorage& strikeSpec = fSubRun->strikeSpec();
Brian Salomon18923f92017-11-06 16:26:02 -0500168
Herb Derbye7efd082019-05-28 11:30:33 -0400169 if (!*fLazyStrike || (*fLazyStrike)->getDescriptor() != strikeSpec.descriptor()) {
170 *fLazyStrike =
171 strikeSpec.findOrCreateExclusiveStrike(SkStrikeCache::GlobalStrikeCache());
Brian Salomon18923f92017-11-06 16:26:02 -0500172 }
173
174 if (regenGlyphs) {
Herb Derbye7efd082019-05-28 11:30:33 -0400175 strike = strikeSpec.findOrCreateGrStrike(fGlyphCache);
Brian Salomon18923f92017-11-06 16:26:02 -0500176 } else {
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500177 strike = fSubRun->refStrike();
Brian Salomon18923f92017-11-06 16:26:02 -0500178 }
179 }
180
Brian Salomon5c6ac642017-12-19 11:09:32 -0500181 bool hasW = fSubRun->hasWCoord();
Brian Salomon5c6ac642017-12-19 11:09:32 -0500182 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Brian Salomon18923f92017-11-06 16:26:02 -0500183 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
Brian Salomondeb53cc2017-11-08 13:50:53 -0500184 fCurrGlyph * kVerticesPerGlyph * vertexStride;
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500185 result->fFirstVertex = currVertex;
Brian Salomon18923f92017-11-06 16:26:02 -0500186
187 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
188 GrGlyph* glyph = nullptr;
189 if (regenTexCoords) {
190 size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
191
192 if (regenGlyphs) {
193 // Get the id from the old glyph, and use the new strike to lookup
194 // the glyph.
Herb Derby5a3fdee2018-12-20 14:47:03 -0500195 SkPackedGlyphID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
Herb Derbye7efd082019-05-28 11:30:33 -0400196 fBlob->fGlyphs[glyphOffset] = strike->getGlyph(id, fLazyStrike->get());
Brian Salomon18923f92017-11-06 16:26:02 -0500197 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
198 }
199 glyph = fBlob->fGlyphs[glyphOffset];
200 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
201
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500202 if (!fFullAtlasManager->hasGlyph(glyph)) {
203 GrDrawOpAtlas::ErrorCode code;
204 code = strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache,
205 fFullAtlasManager, glyph,
Herb Derbye7efd082019-05-28 11:30:33 -0400206 fLazyStrike->get(), fSubRun->maskFormat(),
Jim Van Verthb515ae72018-05-23 16:44:55 -0400207 fSubRun->needsTransform());
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500208 if (GrDrawOpAtlas::ErrorCode::kError == code) {
209 // Something horrible has happened - drop the op
210 return false;
211 }
212 else if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
213 fBrokenRun = glyphIdx > 0;
214 result->fFinished = false;
215 return true;
216 }
Brian Salomon18923f92017-11-06 16:26:02 -0500217 }
Robert Phillips40a29d72018-01-18 12:59:22 -0500218 auto tokenTracker = fUploadTarget->tokenTracker();
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500219 fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
220 tokenTracker->nextDrawToken());
Brian Salomon18923f92017-11-06 16:26:02 -0500221 }
222
Brian Osman5d6be8f2019-01-08 12:02:51 -0500223 if (regenPos) {
224 regen_positions(currVertex, vertexStride, fTransX, fTransY);
225 }
226 if (regenCol) {
227 regen_colors(currVertex, vertexStride, fColor);
228 }
229 if (regenTexCoords) {
230 regen_texcoords(currVertex, vertexStride, glyph, fSubRun->drawAsDistanceFields());
231 }
232
Brian Salomondeb53cc2017-11-08 13:50:53 -0500233 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500234 ++result->fGlyphsRegenerated;
Brian Salomon18923f92017-11-06 16:26:02 -0500235 ++fCurrGlyph;
236 }
237
238 // We may have changed the color so update it here
239 fSubRun->setColor(fColor);
240 if (regenTexCoords) {
241 if (regenGlyphs) {
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500242 fSubRun->setStrike(std::move(strike));
Brian Salomon18923f92017-11-06 16:26:02 -0500243 }
244 fSubRun->setAtlasGeneration(fBrokenRun
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500245 ? GrDrawOpAtlas::kInvalidAtlasGeneration
246 : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
Jim Van Verthba98b7d2018-12-05 12:33:43 -0500247 } else {
248 // For the non-texCoords case we need to ensure that we update the associated use tokens
249 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
250 fUploadTarget->tokenTracker()->nextDrawToken(),
251 fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500252 }
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500253 return true;
Brian Salomon18923f92017-11-06 16:26:02 -0500254}
255
Herb Derby6dff60e2018-11-12 15:45:49 -0500256bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500257 uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
Brian Salomon18923f92017-11-06 16:26:02 -0500258 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
259 // this each time.
260 if (fSubRun->atlasGeneration() != currentAtlasGen) {
261 fRegenFlags |= kRegenTex;
262 }
263
Brian Osman5d6be8f2019-01-08 12:02:51 -0500264 if (fRegenFlags) {
265 return this->doRegen(result,
266 fRegenFlags & kRegenPos,
267 fRegenFlags & kRegenCol,
268 fRegenFlags & kRegenTex,
269 fRegenFlags & kRegenGlyph);
270 } else {
271 bool hasW = fSubRun->hasWCoord();
272 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
273 result->fFinished = true;
274 result->fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
275 result->fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
276 fCurrGlyph * kVerticesPerGlyph * vertexStride;
277 fCurrGlyph = fSubRun->glyphCount();
Brian Salomon18923f92017-11-06 16:26:02 -0500278
Brian Osman5d6be8f2019-01-08 12:02:51 -0500279 // set use tokens for all of the glyphs in our subrun. This is only valid if we
280 // have a valid atlas generation
281 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
282 fUploadTarget->tokenTracker()->nextDrawToken(),
283 fSubRun->maskFormat());
284 return true;
Brian Salomon18923f92017-11-06 16:26:02 -0500285 }
286 SK_ABORT("Should not get here");
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500287 return false;
Brian Salomon18923f92017-11-06 16:26:02 -0500288}