Line data Source code
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 "GrAtlasTextBlob.h"
9 :
10 : #include "GrOpFlushState.h"
11 : #include "GrTextUtils.h"
12 :
13 : #include "SkDistanceFieldGen.h"
14 : #include "SkGlyphCache.h"
15 :
16 : #include "ops/GrAtlasTextOp.h"
17 :
18 : ////////////////////////////////////////////////////////////////////////////////////////////////////
19 : // A large template to handle regenerating the vertices of a textblob with as few branches as
20 : // possible
21 : template <bool regenPos, bool regenCol, bool regenTexCoords>
22 0 : inline void regen_vertices(intptr_t vertex, const GrGlyph* glyph, size_t vertexStride,
23 : bool useDistanceFields, SkScalar transX, SkScalar transY,
24 : int32_t log2Width, int32_t log2Height,
25 : GrColor color) {
26 : int u0, v0, u1, v1;
27 : if (regenTexCoords) {
28 0 : SkASSERT(glyph);
29 0 : int width = glyph->fBounds.width();
30 0 : int height = glyph->fBounds.height();
31 :
32 0 : if (useDistanceFields) {
33 0 : u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
34 0 : v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
35 0 : u1 = u0 + width - 2 * SK_DistanceFieldInset;
36 0 : v1 = v0 + height - 2 * SK_DistanceFieldInset;
37 : } else {
38 0 : u0 = glyph->fAtlasLocation.fX;
39 0 : v0 = glyph->fAtlasLocation.fY;
40 0 : u1 = u0 + width;
41 0 : v1 = v0 + height;
42 : }
43 :
44 : // normalize
45 0 : u0 *= 65535;
46 0 : u0 >>= log2Width;
47 0 : u1 *= 65535;
48 0 : u1 >>= log2Width;
49 0 : v0 *= 65535;
50 0 : v0 >>= log2Height;
51 0 : v1 *= 65535;
52 0 : v1 >>= log2Height;
53 0 : SkASSERT(u0 >= 0 && u0 <= 65535);
54 0 : SkASSERT(u1 >= 0 && u1 <= 65535);
55 0 : SkASSERT(v0 >= 0 && v0 <= 65535);
56 0 : SkASSERT(v1 >= 0 && v1 <= 65535);
57 : }
58 :
59 : // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
60 : // vertices, hence vertexStride - sizeof(SkIPoint16)
61 0 : intptr_t colorOffset = sizeof(SkPoint);
62 0 : intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
63 :
64 : // V0
65 : if (regenPos) {
66 0 : SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
67 0 : point->fX += transX;
68 0 : point->fY += transY;
69 : }
70 :
71 : if (regenCol) {
72 0 : SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
73 0 : *vcolor = color;
74 : }
75 :
76 : if (regenTexCoords) {
77 0 : uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
78 0 : textureCoords[0] = (uint16_t) u0;
79 0 : textureCoords[1] = (uint16_t) v0;
80 : }
81 0 : vertex += vertexStride;
82 :
83 : // V1
84 : if (regenPos) {
85 0 : SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
86 0 : point->fX += transX;
87 0 : point->fY += transY;
88 : }
89 :
90 : if (regenCol) {
91 0 : SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
92 0 : *vcolor = color;
93 : }
94 :
95 : if (regenTexCoords) {
96 0 : uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
97 0 : textureCoords[0] = (uint16_t)u0;
98 0 : textureCoords[1] = (uint16_t)v1;
99 : }
100 0 : vertex += vertexStride;
101 :
102 : // V2
103 : if (regenPos) {
104 0 : SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
105 0 : point->fX += transX;
106 0 : point->fY += transY;
107 : }
108 :
109 : if (regenCol) {
110 0 : SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
111 0 : *vcolor = color;
112 : }
113 :
114 : if (regenTexCoords) {
115 0 : uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
116 0 : textureCoords[0] = (uint16_t)u1;
117 0 : textureCoords[1] = (uint16_t)v1;
118 : }
119 0 : vertex += vertexStride;
120 :
121 : // V3
122 : if (regenPos) {
123 0 : SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
124 0 : point->fX += transX;
125 0 : point->fY += transY;
126 : }
127 :
128 : if (regenCol) {
129 0 : SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
130 0 : *vcolor = color;
131 : }
132 :
133 : if (regenTexCoords) {
134 0 : uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
135 0 : textureCoords[0] = (uint16_t)u1;
136 0 : textureCoords[1] = (uint16_t)v0;
137 : }
138 0 : }
139 :
140 : template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
141 0 : void GrAtlasTextBlob::regenInOp(GrDrawOp::Target* target, GrAtlasGlyphCache* fontCache,
142 : GrBlobRegenHelper* helper, Run* run, Run::SubRunInfo* info,
143 : SkAutoGlyphCache* lazyCache, int glyphCount, size_t vertexStride,
144 : GrColor color, SkScalar transX, SkScalar transY) const {
145 0 : SkASSERT(lazyCache);
146 : static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
147 0 : GrAtlasTextStrike* strike = nullptr;
148 : if (regenTexCoords) {
149 0 : info->resetBulkUseToken();
150 :
151 0 : const SkDescriptor* desc = (run->fOverrideDescriptor && !info->drawAsDistanceFields())
152 0 : ? run->fOverrideDescriptor->getDesc()
153 0 : : run->fDescriptor.getDesc();
154 :
155 0 : if (!*lazyCache || (*lazyCache)->getDescriptor() != *desc) {
156 0 : SkScalerContextEffects effects;
157 0 : effects.fPathEffect = run->fPathEffect.get();
158 0 : effects.fRasterizer = run->fRasterizer.get();
159 0 : effects.fMaskFilter = run->fMaskFilter.get();
160 0 : lazyCache->reset(SkGlyphCache::DetachCache(run->fTypeface.get(), effects, desc));
161 : }
162 :
163 : if (regenGlyphs) {
164 0 : strike = fontCache->getStrike(lazyCache->get());
165 : } else {
166 0 : strike = info->strike();
167 : }
168 : }
169 :
170 0 : bool brokenRun = false;
171 0 : for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
172 0 : GrGlyph* glyph = nullptr;
173 0 : int log2Width = 0, log2Height = 0;
174 : if (regenTexCoords) {
175 0 : size_t glyphOffset = glyphIdx + info->glyphStartIndex();
176 :
177 : if (regenGlyphs) {
178 : // Get the id from the old glyph, and use the new strike to lookup
179 : // the glyph.
180 0 : GrGlyph::PackedID id = fGlyphs[glyphOffset]->fPackedID;
181 0 : fGlyphs[glyphOffset] = strike->getGlyph(id, info->maskFormat(), lazyCache->get());
182 0 : SkASSERT(id == fGlyphs[glyphOffset]->fPackedID);
183 : }
184 0 : glyph = fGlyphs[glyphOffset];
185 0 : SkASSERT(glyph && glyph->fMaskFormat == info->maskFormat());
186 :
187 0 : if (!fontCache->hasGlyph(glyph) &&
188 0 : !strike->addGlyphToAtlas(target, glyph, lazyCache->get(), info->maskFormat())) {
189 0 : helper->flush();
190 0 : brokenRun = glyphIdx > 0;
191 :
192 0 : SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
193 : glyph,
194 : lazyCache->get(),
195 0 : info->maskFormat());
196 0 : SkASSERT(success);
197 : }
198 0 : fontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph,
199 : target->nextDrawToken());
200 0 : log2Width = fontCache->log2Width(info->maskFormat());
201 0 : log2Height = fontCache->log2Height(info->maskFormat());
202 : }
203 :
204 0 : intptr_t vertex = reinterpret_cast<intptr_t>(fVertices);
205 0 : vertex += info->vertexStartIndex();
206 0 : vertex += vertexStride * glyphIdx * GrAtlasTextOp::kVerticesPerGlyph;
207 0 : regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
208 0 : info->drawAsDistanceFields(), transX,
209 : transY, log2Width, log2Height, color);
210 0 : helper->incGlyphCount();
211 : }
212 :
213 : // We may have changed the color so update it here
214 0 : info->setColor(color);
215 : if (regenTexCoords) {
216 : if (regenGlyphs) {
217 0 : info->setStrike(strike);
218 : }
219 0 : info->setAtlasGeneration(brokenRun ? GrDrawOpAtlas::kInvalidAtlasGeneration
220 0 : : fontCache->atlasGeneration(info->maskFormat()));
221 : }
222 0 : }
223 :
224 : enum RegenMask {
225 : kNoRegen = 0x0,
226 : kRegenPos = 0x1,
227 : kRegenCol = 0x2,
228 : kRegenTex = 0x4,
229 : kRegenGlyph = 0x8 | kRegenTex, // we have to regenerate the texture coords when we regen glyphs
230 :
231 : // combinations
232 : kRegenPosCol = kRegenPos | kRegenCol,
233 : kRegenPosTex = kRegenPos | kRegenTex,
234 : kRegenPosTexGlyph = kRegenPos | kRegenGlyph,
235 : kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex,
236 : kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph,
237 : kRegenColTex = kRegenCol | kRegenTex,
238 : kRegenColTexGlyph = kRegenCol | kRegenGlyph,
239 : };
240 :
241 : #define REGEN_ARGS target, fontCache, helper, &run, &info, lazyCache, \
242 : *glyphCount, vertexStride, color, transX, transY
243 :
244 0 : void GrAtlasTextBlob::regenInOp(GrDrawOp::Target* target,
245 : GrAtlasGlyphCache* fontCache,
246 : GrBlobRegenHelper* helper,
247 : int runIndex, int subRunIndex, SkAutoGlyphCache* lazyCache,
248 : size_t vertexStride, const SkMatrix& viewMatrix,
249 : SkScalar x, SkScalar y, GrColor color,
250 : void** vertices, size_t* byteCount, int* glyphCount) {
251 0 : Run& run = fRuns[runIndex];
252 0 : Run::SubRunInfo& info = run.fSubRunInfo[subRunIndex];
253 :
254 0 : uint64_t currentAtlasGen = fontCache->atlasGeneration(info.maskFormat());
255 :
256 : // Compute translation if any
257 : SkScalar transX, transY;
258 0 : info.computeTranslation(viewMatrix, x, y, &transX, &transY);
259 :
260 : // Because the GrAtlasGlyphCache may evict the strike a blob depends on using for
261 : // generating its texture coords, we have to track whether or not the strike has
262 : // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
263 : // otherwise we have to get the new strike, and use that to get the correct glyphs.
264 : // Because we do not have the packed ids, and thus can't look up our glyphs in the
265 : // new strike, we instead keep our ref to the old strike and use the packed ids from
266 : // it. These ids will still be valid as long as we hold the ref. When we are done
267 : // updating our cache of the GrGlyph*s, we drop our ref on the old strike
268 0 : bool regenerateGlyphs = info.strike()->isAbandoned();
269 0 : bool regenerateTextureCoords = info.atlasGeneration() != currentAtlasGen ||
270 0 : regenerateGlyphs;
271 0 : bool regenerateColors = kARGB_GrMaskFormat != info.maskFormat() &&
272 0 : info.color() != color;
273 0 : bool regeneratePositions = transX != 0.f || transY != 0.f;
274 0 : *glyphCount = info.glyphCount();
275 :
276 0 : uint32_t regenMaskBits = kNoRegen;
277 0 : regenMaskBits |= regeneratePositions ? kRegenPos : 0;
278 0 : regenMaskBits |= regenerateColors ? kRegenCol : 0;
279 0 : regenMaskBits |= regenerateTextureCoords ? kRegenTex : 0;
280 0 : regenMaskBits |= regenerateGlyphs ? kRegenGlyph : 0;
281 0 : RegenMask regenMask = (RegenMask)regenMaskBits;
282 :
283 0 : switch (regenMask) {
284 : case kRegenPos:
285 0 : this->regenInOp<true, false, false, false>(REGEN_ARGS);
286 0 : break;
287 : case kRegenCol:
288 0 : this->regenInOp<false, true, false, false>(REGEN_ARGS);
289 0 : break;
290 : case kRegenTex:
291 0 : this->regenInOp<false, false, true, false>(REGEN_ARGS);
292 0 : break;
293 : case kRegenGlyph:
294 0 : this->regenInOp<false, false, true, true>(REGEN_ARGS);
295 0 : break;
296 :
297 : // combinations
298 : case kRegenPosCol:
299 0 : this->regenInOp<true, true, false, false>(REGEN_ARGS);
300 0 : break;
301 : case kRegenPosTex:
302 0 : this->regenInOp<true, false, true, false>(REGEN_ARGS);
303 0 : break;
304 : case kRegenPosTexGlyph:
305 0 : this->regenInOp<true, false, true, true>(REGEN_ARGS);
306 0 : break;
307 : case kRegenPosColTex:
308 0 : this->regenInOp<true, true, true, false>(REGEN_ARGS);
309 0 : break;
310 : case kRegenPosColTexGlyph:
311 0 : this->regenInOp<true, true, true, true>(REGEN_ARGS);
312 0 : break;
313 : case kRegenColTex:
314 0 : this->regenInOp<false, true, true, false>(REGEN_ARGS);
315 0 : break;
316 : case kRegenColTexGlyph:
317 0 : this->regenInOp<false, true, true, true>(REGEN_ARGS);
318 0 : break;
319 : case kNoRegen:
320 0 : helper->incGlyphCount(*glyphCount);
321 :
322 : // set use tokens for all of the glyphs in our subrun. This is only valid if we
323 : // have a valid atlas generation
324 0 : fontCache->setUseTokenBulk(*info.bulkUseToken(), target->nextDrawToken(),
325 0 : info.maskFormat());
326 0 : break;
327 : }
328 :
329 0 : *byteCount = info.byteCount();
330 0 : *vertices = fVertices + info.vertexStartIndex();
331 0 : }
|