OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrAtlasTextBatch.h" | 8 #include "GrAtlasTextBatch.h" |
9 | 9 |
10 #include "GrBatchFlushState.h" | 10 #include "GrBatchFlushState.h" |
11 #include "GrBatchTest.h" | |
12 #include "GrResourceProvider.h" | 11 #include "GrResourceProvider.h" |
13 | 12 |
14 #include "SkDistanceFieldGen.h" | |
15 #include "SkGlyphCache.h" | 13 #include "SkGlyphCache.h" |
16 | 14 |
17 #include "effects/GrBitmapTextGeoProc.h" | 15 #include "effects/GrBitmapTextGeoProc.h" |
18 #include "effects/GrDistanceFieldGeoProc.h" | 16 #include "effects/GrDistanceFieldGeoProc.h" |
19 #include "text/GrBatchFontCache.h" | 17 #include "text/GrBatchFontCache.h" |
20 | 18 |
21 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
22 // A large template to handle regenerating the vertices of a textblob with as fe
w branches as | |
23 // possible | |
24 template <bool regenPos, bool regenCol, bool regenTexCoords> | |
25 inline void regen_vertices(intptr_t vertex, const GrGlyph* glyph, size_t vertexS
tride, | |
26 bool useDistanceFields, SkScalar transX, SkScalar tra
nsY, | |
27 GrColor color) { | |
28 int u0, v0, u1, v1; | |
29 if (regenTexCoords) { | |
30 SkASSERT(glyph); | |
31 int width = glyph->fBounds.width(); | |
32 int height = glyph->fBounds.height(); | |
33 | |
34 if (useDistanceFields) { | |
35 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset; | |
36 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset; | |
37 u1 = u0 + width - 2 * SK_DistanceFieldInset; | |
38 v1 = v0 + height - 2 * SK_DistanceFieldInset; | |
39 } else { | |
40 u0 = glyph->fAtlasLocation.fX; | |
41 v0 = glyph->fAtlasLocation.fY; | |
42 u1 = u0 + width; | |
43 v1 = v0 + height; | |
44 } | |
45 } | |
46 | |
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 intptr_t colorOffset = sizeof(SkPoint); | |
50 intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16); | |
51 | |
52 // V0 | |
53 if (regenPos) { | |
54 SkPoint* point = reinterpret_cast<SkPoint*>(vertex); | |
55 point->fX += transX; | |
56 point->fY += transY; | |
57 } | |
58 | |
59 if (regenCol) { | |
60 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset); | |
61 *vcolor = color; | |
62 } | |
63 | |
64 if (regenTexCoords) { | |
65 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCo
ordOffset); | |
66 textureCoords->set(u0, v0); | |
67 } | |
68 vertex += vertexStride; | |
69 | |
70 // V1 | |
71 if (regenPos) { | |
72 SkPoint* point = reinterpret_cast<SkPoint*>(vertex); | |
73 point->fX += transX; | |
74 point->fY += transY; | |
75 } | |
76 | |
77 if (regenCol) { | |
78 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset); | |
79 *vcolor = color; | |
80 } | |
81 | |
82 if (regenTexCoords) { | |
83 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCo
ordOffset); | |
84 textureCoords->set(u0, v1); | |
85 } | |
86 vertex += vertexStride; | |
87 | |
88 // V2 | |
89 if (regenPos) { | |
90 SkPoint* point = reinterpret_cast<SkPoint*>(vertex); | |
91 point->fX += transX; | |
92 point->fY += transY; | |
93 } | |
94 | |
95 if (regenCol) { | |
96 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset); | |
97 *vcolor = color; | |
98 } | |
99 | |
100 if (regenTexCoords) { | |
101 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCo
ordOffset); | |
102 textureCoords->set(u1, v1); | |
103 } | |
104 vertex += vertexStride; | |
105 | |
106 // V3 | |
107 if (regenPos) { | |
108 SkPoint* point = reinterpret_cast<SkPoint*>(vertex); | |
109 point->fX += transX; | |
110 point->fY += transY; | |
111 } | |
112 | |
113 if (regenCol) { | |
114 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset); | |
115 *vcolor = color; | |
116 } | |
117 | |
118 if (regenTexCoords) { | |
119 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + texCo
ordOffset); | |
120 textureCoords->set(u1, v0); | |
121 } | |
122 } | |
123 | |
124 template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs> | |
125 inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl
ob* blob, Run* run, | |
126 TextInfo* info, SkGlyphCache** cache, | |
127 SkTypeface** typeface, GrFontScaler** sc
aler, | |
128 const SkDescriptor** desc, const GrGeome
tryProcessor* gp, | |
129 int glyphCount, size_t vertexStride, | |
130 GrColor color, SkScalar transX, SkScalar
transY) const { | |
131 static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along reg
enGlyphs"); | |
132 GrBatchTextStrike* strike = nullptr; | |
133 if (regenTexCoords) { | |
134 info->resetBulkUseToken(); | |
135 | |
136 // We can reuse if we have a valid strike and our descriptors / typeface
are the | |
137 // same. The override descriptor is only for the non distance field tex
t within | |
138 // a run | |
139 const SkDescriptor* newDesc = (run->fOverrideDescriptor && !this->usesDi
stanceFields()) ? | |
140 run->fOverrideDescriptor->getDesc() : | |
141 run->fDescriptor.getDesc(); | |
142 if (!*cache || !SkTypeface::Equal(*typeface, run->fTypeface) || | |
143 !((*desc)->equals(*newDesc))) { | |
144 if (*cache) { | |
145 SkGlyphCache::AttachCache(*cache); | |
146 } | |
147 *desc = newDesc; | |
148 *cache = SkGlyphCache::DetachCache(run->fTypeface, *desc); | |
149 *scaler = GrTextContext::GetGrFontScaler(*cache); | |
150 *typeface = run->fTypeface; | |
151 } | |
152 | |
153 if (regenGlyphs) { | |
154 strike = fFontCache->getStrike(*scaler); | |
155 } else { | |
156 strike = info->strike(); | |
157 } | |
158 } | |
159 | |
160 bool brokenRun = false; | |
161 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) { | |
162 GrGlyph* glyph = nullptr; | |
163 if (regenTexCoords) { | |
164 size_t glyphOffset = glyphIdx + info->glyphStartIndex(); | |
165 | |
166 if (regenGlyphs) { | |
167 // Get the id from the old glyph, and use the new strike to look
up | |
168 // the glyph. | |
169 GrGlyph::PackedID id = blob->fGlyphs[glyphOffset]->fPackedID; | |
170 blob->fGlyphs[glyphOffset] = strike->getGlyph(id, this->maskForm
at(), *scaler); | |
171 SkASSERT(id == blob->fGlyphs[glyphOffset]->fPackedID); | |
172 } | |
173 glyph = blob->fGlyphs[glyphOffset]; | |
174 SkASSERT(glyph && glyph->fMaskFormat == this->maskFormat()); | |
175 | |
176 if (!fFontCache->hasGlyph(glyph) && | |
177 !strike->addGlyphToAtlas(target, glyph, *scaler, this->maskForma
t())) { | |
178 this->flush(target, flushInfo); | |
179 target->initDraw(gp, this->pipeline()); | |
180 brokenRun = glyphIdx > 0; | |
181 | |
182 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target, | |
183 glyph, | |
184 *scaler, | |
185 this->maskFo
rmat()); | |
186 SkASSERT(success); | |
187 } | |
188 fFontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph
, | |
189 target->currentToken()); | |
190 } | |
191 | |
192 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices); | |
193 vertex += info->vertexStartIndex(); | |
194 vertex += vertexStride * glyphIdx * GrAtlasTextBatch::kVerticesPerGlyph; | |
195 regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertex
Stride, | |
196 this->usesDistanceFie
lds(), transX, | |
197 transY, color); | |
198 flushInfo->fGlyphsToFlush++; | |
199 } | |
200 | |
201 // We may have changed the color so update it here | |
202 info->setColor(color); | |
203 if (regenTexCoords) { | |
204 if (regenGlyphs) { | |
205 info->setStrike(strike); | |
206 } | |
207 info->setAtlasGeneration(brokenRun ? GrBatchAtlas::kInvalidAtlasGenerati
on : | |
208 fFontCache->atlasGeneration(this->m
askFormat())); | |
209 } | |
210 } | |
211 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 19 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
212 | 20 |
213 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) { | 21 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) { |
214 unsigned r = SkColorGetR(c); | 22 unsigned r = SkColorGetR(c); |
215 unsigned g = SkColorGetG(c); | 23 unsigned g = SkColorGetG(c); |
216 unsigned b = SkColorGetB(c); | 24 unsigned b = SkColorGetB(c); |
217 return GrColorPackRGBA(r, g, b, 0xff); | 25 return GrColorPackRGBA(r, g, b, 0xff); |
218 } | 26 } |
219 | 27 |
220 static const int kDistanceAdjustLumShift = 5; | 28 static const int kDistanceAdjustLumShift = 5; |
221 | 29 |
222 SkString GrAtlasTextBatch::dumpInfo() const { | 30 SkString GrAtlasTextBatch::dumpInfo() const { |
223 SkString str; | 31 SkString str; |
224 | 32 |
225 for (int i = 0; i < fGeoCount; ++i) { | 33 for (int i = 0; i < fGeoCount; ++i) { |
226 str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n", | 34 str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n", |
227 i, | 35 i, |
228 fGeoData[i].fColor, | 36 fGeoData[i].fColor, |
229 fGeoData[i].fTransX, | 37 fGeoData[i].fTransX, |
230 fGeoData[i].fTransY, | 38 fGeoData[i].fTransY, |
231 fGeoData[i].fBlob->fRunCount); | 39 fGeoData[i].fBlob->runCount()); |
232 } | 40 } |
233 | 41 |
234 str.append(INHERITED::dumpInfo()); | 42 str.append(INHERITED::dumpInfo()); |
235 return str; | 43 return str; |
236 } | 44 } |
237 | 45 |
238 void GrAtlasTextBatch::computePipelineOptimizations(GrInitInvariantOutput* color
, | 46 void GrAtlasTextBatch::computePipelineOptimizations(GrInitInvariantOutput* color
, |
239 GrInitInvariantOutput* cover
age, | 47 GrInitInvariantOutput* cover
age, |
240 GrBatchToXPOverrides* overri
des) const { | 48 GrBatchToXPOverrides* overri
des) const { |
241 if (kColorBitmapMask_MaskType == fMaskType) { | 49 if (kColorBitmapMask_MaskType == fMaskType) { |
(...skipping 23 matching lines...) Expand all Loading... |
265 } | 73 } |
266 overrides.getOverrideColorIfSet(&fGeoData[0].fColor); | 74 overrides.getOverrideColorIfSet(&fGeoData[0].fColor); |
267 | 75 |
268 // setup batch properties | 76 // setup batch properties |
269 fBatch.fColorIgnored = !overrides.readsColor(); | 77 fBatch.fColorIgnored = !overrides.readsColor(); |
270 fBatch.fColor = fGeoData[0].fColor; | 78 fBatch.fColor = fGeoData[0].fColor; |
271 fBatch.fUsesLocalCoords = overrides.readsLocalCoords(); | 79 fBatch.fUsesLocalCoords = overrides.readsLocalCoords(); |
272 fBatch.fCoverageIgnored = !overrides.readsCoverage(); | 80 fBatch.fCoverageIgnored = !overrides.readsCoverage(); |
273 } | 81 } |
274 | 82 |
275 enum RegenMask { | |
276 kNoRegen = 0x0, | |
277 kRegenPos = 0x1, | |
278 kRegenCol = 0x2, | |
279 kRegenTex = 0x4, | |
280 kRegenGlyph = 0x8 | kRegenTex, // we have to regenerate the texture coords w
hen we regen glyphs | |
281 | |
282 // combinations | |
283 kRegenPosCol = kRegenPos | kRegenCol, | |
284 kRegenPosTex = kRegenPos | kRegenTex, | |
285 kRegenPosTexGlyph = kRegenPos | kRegenGlyph, | |
286 kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex, | |
287 kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph, | |
288 kRegenColTex = kRegenCol | kRegenTex, | |
289 kRegenColTexGlyph = kRegenCol | kRegenGlyph, | |
290 }; | |
291 | |
292 #define REGEN_ARGS target, &flushInfo, blob, &run, &info, &cache, &typeface, &sc
aler, &desc, gp, \ | |
293 glyphCount, vertexStride, args.fColor, args.fTransX, args.fTr
ansY | |
294 | |
295 void GrAtlasTextBatch::onPrepareDraws(Target* target) const { | 83 void GrAtlasTextBatch::onPrepareDraws(Target* target) const { |
296 // if we have RGB, then we won't have any SkShaders so no need to use a loca
lmatrix. | 84 // if we have RGB, then we won't have any SkShaders so no need to use a loca
lmatrix. |
297 // TODO actually only invert if we don't have RGBA | 85 // TODO actually only invert if we don't have RGBA |
298 SkMatrix localMatrix; | 86 SkMatrix localMatrix; |
299 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) { | 87 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) { |
300 SkDebugf("Cannot invert viewmatrix\n"); | 88 SkDebugf("Cannot invert viewmatrix\n"); |
301 return; | 89 return; |
302 } | 90 } |
303 | 91 |
304 GrTexture* texture = fFontCache->getTexture(this->maskFormat()); | 92 GrTexture* texture = fFontCache->getTexture(this->maskFormat()); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
346 | 134 |
347 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices); | 135 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices); |
348 | 136 |
349 // We cache some values to avoid going to the glyphcache for the same fontSc
aler twice | 137 // We cache some values to avoid going to the glyphcache for the same fontSc
aler twice |
350 // in a row | 138 // in a row |
351 const SkDescriptor* desc = nullptr; | 139 const SkDescriptor* desc = nullptr; |
352 SkGlyphCache* cache = nullptr; | 140 SkGlyphCache* cache = nullptr; |
353 GrFontScaler* scaler = nullptr; | 141 GrFontScaler* scaler = nullptr; |
354 SkTypeface* typeface = nullptr; | 142 SkTypeface* typeface = nullptr; |
355 | 143 |
| 144 GrBlobRegenHelper helper(this, target, &flushInfo, gp); |
| 145 |
356 for (int i = 0; i < fGeoCount; i++) { | 146 for (int i = 0; i < fGeoCount; i++) { |
357 const Geometry& args = fGeoData[i]; | 147 const Geometry& args = fGeoData[i]; |
358 Blob* blob = args.fBlob; | 148 Blob* blob = args.fBlob; |
359 Run& run = blob->fRuns[args.fRun]; | 149 size_t byteCount; |
360 TextInfo& info = run.fSubRunInfo[args.fSubRun]; | 150 void* blobVertices; |
361 | 151 int glyphCount; |
362 uint64_t currentAtlasGen = fFontCache->atlasGeneration(maskFormat); | 152 blob->regenInBatch(target, fFontCache, &helper, args.fRun, args.fSubRun,
&cache, |
363 | 153 &typeface, &scaler, &desc, vertexStride, args.fColor,
args.fTransX, |
364 // Because the GrBatchFontCache may evict the strike a blob depends on u
sing for | 154 args.fTransY, &blobVertices, &byteCount, &glyphCount)
; |
365 // generating its texture coords, we have to track whether or not the st
rike has | |
366 // been abandoned. If it hasn't been abandoned, then we can use the GrG
lyph*s as is | |
367 // otherwise we have to get the new strike, and use that to get the corr
ect glyphs. | |
368 // Because we do not have the packed ids, and thus can't look up our gly
phs in the | |
369 // new strike, we instead keep our ref to the old strike and use the pac
ked ids from | |
370 // it. These ids will still be valid as long as we hold the ref. When
we are done | |
371 // updating our cache of the GrGlyph*s, we drop our ref on the old strik
e | |
372 bool regenerateGlyphs = info.strike()->isAbandoned(); | |
373 bool regenerateTextureCoords = info.atlasGeneration() != currentAtlasGen
|| | |
374 regenerateGlyphs; | |
375 bool regenerateColors = kARGB_GrMaskFormat != maskFormat && | |
376 info.color() != args.fColor; | |
377 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f; | |
378 int glyphCount = info.glyphCount(); | |
379 | |
380 uint32_t regenMaskBits = kNoRegen; | |
381 regenMaskBits |= regeneratePositions ? kRegenPos : 0; | |
382 regenMaskBits |= regenerateColors ? kRegenCol : 0; | |
383 regenMaskBits |= regenerateTextureCoords ? kRegenTex : 0; | |
384 regenMaskBits |= regenerateGlyphs ? kRegenGlyph : 0; | |
385 RegenMask regenMask = (RegenMask)regenMaskBits; | |
386 | |
387 switch (regenMask) { | |
388 case kRegenPos: this->regenBlob<true, false, false, false>(REGEN_ARG
S); break; | |
389 case kRegenCol: this->regenBlob<false, true, false, false>(REGEN_ARG
S); break; | |
390 case kRegenTex: this->regenBlob<false, false, true, false>(REGEN_ARG
S); break; | |
391 case kRegenGlyph: this->regenBlob<false, false, true, true>(REGEN_AR
GS); break; | |
392 | |
393 // combinations | |
394 case kRegenPosCol: this->regenBlob<true, true, false, false>(REGEN_A
RGS); break; | |
395 case kRegenPosTex: this->regenBlob<true, false, true, false>(REGEN_A
RGS); break; | |
396 case kRegenPosTexGlyph: this->regenBlob<true, false, true, true>(REG
EN_ARGS); break; | |
397 case kRegenPosColTex: this->regenBlob<true, true, true, false>(REGEN
_ARGS); break; | |
398 case kRegenPosColTexGlyph: this->regenBlob<true, true, true, true>(R
EGEN_ARGS); break; | |
399 case kRegenColTex: this->regenBlob<false, true, true, false>(REGEN_A
RGS); break; | |
400 case kRegenColTexGlyph: this->regenBlob<false, true, true, true>(REG
EN_ARGS); break; | |
401 case kNoRegen: | |
402 flushInfo.fGlyphsToFlush += glyphCount; | |
403 | |
404 // set use tokens for all of the glyphs in our subrun. This is
only valid if we | |
405 // have a valid atlas generation | |
406 fFontCache->setUseTokenBulk(*info.bulkUseToken(), target->curren
tToken(), | |
407 maskFormat); | |
408 break; | |
409 } | |
410 | 155 |
411 // now copy all vertices | 156 // now copy all vertices |
412 size_t byteCount = info.byteCount(); | 157 memcpy(currVertex, blobVertices, byteCount); |
413 memcpy(currVertex, blob->fVertices + info.vertexStartIndex(), byteCount)
; | |
414 | 158 |
415 #ifdef SK_DEBUG | 159 #ifdef SK_DEBUG |
416 // bounds sanity check | 160 // bounds sanity check |
417 SkRect rect; | 161 SkRect rect; |
418 rect.setLargestInverted(); | 162 rect.setLargestInverted(); |
419 SkPoint* vertex = (SkPoint*) ((char*)blob->fVertices + info.vertexStartI
ndex()); | 163 SkPoint* vertex = (SkPoint*) ((char*)blobVertices); |
420 rect.growToInclude(vertex, vertexStride, kVerticesPerGlyph * info.glyphC
ount()); | 164 rect.growToInclude(vertex, vertexStride, kVerticesPerGlyph * glyphCount)
; |
421 | 165 |
422 if (this->usesDistanceFields()) { | 166 if (this->usesDistanceFields()) { |
423 fBatch.fViewMatrix.mapRect(&rect); | 167 fBatch.fViewMatrix.mapRect(&rect); |
424 } | 168 } |
425 SkASSERT(fBounds.contains(rect)); | 169 SkASSERT(fBounds.contains(rect)); |
426 #endif | 170 #endif |
427 | 171 |
428 currVertex += byteCount; | 172 currVertex += byteCount; |
429 } | 173 } |
430 | 174 |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
559 return GrDistanceFieldA8TextGeoProc::Create(color, | 303 return GrDistanceFieldA8TextGeoProc::Create(color, |
560 viewMatrix, | 304 viewMatrix, |
561 texture, | 305 texture, |
562 params, | 306 params, |
563 flags, | 307 flags, |
564 this->usesLocalCoords()); | 308 this->usesLocalCoords()); |
565 #endif | 309 #endif |
566 } | 310 } |
567 | 311 |
568 } | 312 } |
| 313 |
| 314 void GrBlobRegenHelper::flush() { |
| 315 fBatch->flush(fTarget, fFlushInfo); |
| 316 fTarget->initDraw(fGP, fBatch->pipeline()); |
| 317 } |
OLD | NEW |