OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2013 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 #include "GrBitmapTextContext.h" | |
8 | |
9 #include "GrAtlas.h" | |
10 #include "GrBatch.h" | |
11 #include "GrBatchFontCache.h" | |
12 #include "GrBatchTarget.h" | |
13 #include "GrDefaultGeoProcFactory.h" | |
14 #include "GrDrawTarget.h" | |
15 #include "GrFontCache.h" | |
16 #include "GrFontScaler.h" | |
17 #include "GrIndexBuffer.h" | |
18 #include "GrStrokeInfo.h" | |
19 #include "GrTexturePriv.h" | |
20 | |
21 #include "SkAutoKern.h" | |
22 #include "SkColorPriv.h" | |
23 #include "SkDraw.h" | |
24 #include "SkDrawFilter.h" | |
25 #include "SkDrawProcs.h" | |
26 #include "SkGlyphCache.h" | |
27 #include "SkGpuDevice.h" | |
28 #include "SkGr.h" | |
29 #include "SkPath.h" | |
30 #include "SkRTConf.h" | |
31 #include "SkStrokeRec.h" | |
32 #include "SkTextBlob.h" | |
33 #include "SkTextMapStateProc.h" | |
34 | |
35 #include "effects/GrBitmapTextGeoProc.h" | |
36 #include "effects/GrSimpleTextureEffect.h" | |
37 | |
38 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, | |
39 "Dump the contents of the font cache before every purge."); | |
40 | |
41 namespace { | |
42 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); | |
43 | |
44 // position + local coord | |
45 static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); | |
46 | |
47 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof
(SkIPoint16); | |
48 | |
49 static const int kVerticesPerGlyph = 4; | |
50 static const int kIndicesPerGlyph = 6; | |
51 | |
52 static size_t get_vertex_stride(GrMaskFormat maskFormat) { | |
53 switch (maskFormat) { | |
54 case kA8_GrMaskFormat: | |
55 return kGrayTextVASize; | |
56 case kARGB_GrMaskFormat: | |
57 return kColorTextVASize; | |
58 default: | |
59 return kLCDTextVASize; | |
60 } | |
61 } | |
62 | |
63 }; | |
64 | |
65 GrBitmapTextContext::GrBitmapTextContext(GrContext* context, | |
66 SkGpuDevice* gpuDevice, | |
67 const SkDeviceProperties& properties) | |
68 : GrTextContext(context, gpuDevice, properties) { | |
69 fStrike = NULL; | |
70 | |
71 fCurrTexture = NULL; | |
72 fEffectTextureUniqueID = SK_InvalidUniqueID; | |
73 | |
74 fVertices = NULL; | |
75 fCurrVertex = 0; | |
76 fAllocVertexCount = 0; | |
77 fTotalVertexCount = 0; | |
78 | |
79 fVertexBounds.setLargestInverted(); | |
80 } | |
81 | |
82 GrBitmapTextContext* GrBitmapTextContext::Create(GrContext* context, | |
83 SkGpuDevice* gpuDevice, | |
84 const SkDeviceProperties& props
) { | |
85 return SkNEW_ARGS(GrBitmapTextContext, (context, gpuDevice, props)); | |
86 } | |
87 | |
88 bool GrBitmapTextContext::canDraw(const GrRenderTarget* rt, | |
89 const GrClip& clip, | |
90 const GrPaint& paint, | |
91 const SkPaint& skPaint, | |
92 const SkMatrix& viewMatrix) { | |
93 return !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix); | |
94 } | |
95 | |
96 inline void GrBitmapTextContext::init(GrRenderTarget* rt, const GrClip& clip, | |
97 const GrPaint& paint, const SkPaint& skPai
nt, | |
98 const SkIRect& regionClipBounds) { | |
99 GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds); | |
100 | |
101 fStrike = NULL; | |
102 | |
103 fCurrTexture = NULL; | |
104 fCurrVertex = 0; | |
105 | |
106 fVertices = NULL; | |
107 fAllocVertexCount = 0; | |
108 fTotalVertexCount = 0; | |
109 } | |
110 | |
111 void GrBitmapTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip, | |
112 const GrPaint& paint, const SkPaint& skPain
t, | |
113 const SkMatrix& viewMatrix, | |
114 const char text[], size_t byteLength, | |
115 SkScalar x, SkScalar y, const SkIRect& regi
onClipBounds) { | |
116 SkASSERT(byteLength == 0 || text != NULL); | |
117 | |
118 // nothing to draw | |
119 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) { | |
120 return; | |
121 } | |
122 | |
123 this->init(rt, clip, paint, skPaint, regionClipBounds); | |
124 | |
125 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | |
126 | |
127 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &viewMatrix); | |
128 SkGlyphCache* cache = autoCache.getCache(); | |
129 GrFontScaler* fontScaler = GetGrFontScaler(cache); | |
130 | |
131 // transform our starting point | |
132 { | |
133 SkPoint loc; | |
134 viewMatrix.mapXY(x, y, &loc); | |
135 x = loc.fX; | |
136 y = loc.fY; | |
137 } | |
138 | |
139 // need to measure first | |
140 int numGlyphs; | |
141 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { | |
142 SkVector stopVector; | |
143 numGlyphs = MeasureText(cache, glyphCacheProc, text, byteLength, &stopVe
ctor); | |
144 | |
145 SkScalar stopX = stopVector.fX; | |
146 SkScalar stopY = stopVector.fY; | |
147 | |
148 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { | |
149 stopX = SkScalarHalf(stopX); | |
150 stopY = SkScalarHalf(stopY); | |
151 } | |
152 x -= stopX; | |
153 y -= stopY; | |
154 } else { | |
155 numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL); | |
156 } | |
157 fTotalVertexCount = kVerticesPerGlyph*numGlyphs; | |
158 | |
159 const char* stop = text + byteLength; | |
160 | |
161 SkAutoKern autokern; | |
162 | |
163 SkFixed fxMask = ~0; | |
164 SkFixed fyMask = ~0; | |
165 SkScalar halfSampleX, halfSampleY; | |
166 if (cache->isSubpixel()) { | |
167 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound); | |
168 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix); | |
169 if (kX_SkAxisAlignment == baseline) { | |
170 fyMask = 0; | |
171 halfSampleY = SK_ScalarHalf; | |
172 } else if (kY_SkAxisAlignment == baseline) { | |
173 fxMask = 0; | |
174 halfSampleX = SK_ScalarHalf; | |
175 } | |
176 } else { | |
177 halfSampleX = halfSampleY = SK_ScalarHalf; | |
178 } | |
179 | |
180 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX); | |
181 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY); | |
182 | |
183 // if we have RGB, then we won't have any SkShaders so no need to use a loca
lmatrix, but for | |
184 // performance reasons we just invert here instead | |
185 if (!viewMatrix.invert(&fLocalMatrix)) { | |
186 SkDebugf("Cannot invert viewmatrix\n"); | |
187 return; | |
188 } | |
189 | |
190 while (text < stop) { | |
191 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fy
Mask); | |
192 | |
193 fx += autokern.adjust(glyph); | |
194 | |
195 if (glyph.fWidth) { | |
196 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
197 glyph.getSubXFixed(), | |
198 glyph.getSubYFixed(), | |
199 GrGlyph::kCoverage_MaskStyle), | |
200 Sk48Dot16FloorToInt(fx), | |
201 Sk48Dot16FloorToInt(fy), | |
202 fontScaler); | |
203 } | |
204 | |
205 fx += glyph.fAdvanceX; | |
206 fy += glyph.fAdvanceY; | |
207 } | |
208 | |
209 this->finish(); | |
210 } | |
211 | |
212 void GrBitmapTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip, | |
213 const GrPaint& paint, const SkPaint& skP
aint, | |
214 const SkMatrix& viewMatrix, | |
215 const char text[], size_t byteLength, | |
216 const SkScalar pos[], int scalarsPerPosi
tion, | |
217 const SkPoint& offset, const SkIRect& re
gionClipBounds) { | |
218 SkASSERT(byteLength == 0 || text != NULL); | |
219 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); | |
220 | |
221 // nothing to draw | |
222 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) { | |
223 return; | |
224 } | |
225 | |
226 this->init(rt, clip, paint, skPaint, regionClipBounds); | |
227 | |
228 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | |
229 | |
230 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &viewMatrix); | |
231 SkGlyphCache* cache = autoCache.getCache(); | |
232 GrFontScaler* fontScaler = GetGrFontScaler(cache); | |
233 | |
234 // if we have RGB, then we won't have any SkShaders so no need to use a loca
lmatrix, but for | |
235 // performance reasons we just invert here instead | |
236 if (!viewMatrix.invert(&fLocalMatrix)) { | |
237 SkDebugf("Cannot invert viewmatrix\n"); | |
238 return; | |
239 } | |
240 | |
241 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL); | |
242 fTotalVertexCount = kVerticesPerGlyph*numGlyphs; | |
243 | |
244 const char* stop = text + byteLength; | |
245 SkTextAlignProc alignProc(fSkPaint.getTextAlign()); | |
246 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition); | |
247 | |
248 if (cache->isSubpixel()) { | |
249 // maybe we should skip the rounding if linearText is set | |
250 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix); | |
251 | |
252 SkFixed fxMask = ~0; | |
253 SkFixed fyMask = ~0; | |
254 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound); | |
255 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound); | |
256 if (kX_SkAxisAlignment == baseline) { | |
257 fyMask = 0; | |
258 halfSampleY = SK_ScalarHalf; | |
259 } else if (kY_SkAxisAlignment == baseline) { | |
260 fxMask = 0; | |
261 halfSampleX = SK_ScalarHalf; | |
262 } | |
263 | |
264 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | |
265 while (text < stop) { | |
266 SkPoint tmsLoc; | |
267 tmsProc(pos, &tmsLoc); | |
268 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX); | |
269 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY); | |
270 | |
271 const SkGlyph& glyph = glyphCacheProc(cache, &text, | |
272 fx & fxMask, fy & fyMask); | |
273 | |
274 if (glyph.fWidth) { | |
275 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
276 glyph.getSubXFixed(), | |
277 glyph.getSubYFixed(), | |
278 GrGlyph::kCoverage_MaskStyle
), | |
279 Sk48Dot16FloorToInt(fx), | |
280 Sk48Dot16FloorToInt(fy), | |
281 fontScaler); | |
282 } | |
283 pos += scalarsPerPosition; | |
284 } | |
285 } else { | |
286 while (text < stop) { | |
287 const char* currentText = text; | |
288 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0); | |
289 | |
290 if (metricGlyph.fWidth) { | |
291 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;) | |
292 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;) | |
293 SkPoint tmsLoc; | |
294 tmsProc(pos, &tmsLoc); | |
295 SkPoint alignLoc; | |
296 alignProc(tmsLoc, metricGlyph, &alignLoc); | |
297 | |
298 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX); | |
299 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY); | |
300 | |
301 // have to call again, now that we've been "aligned" | |
302 const SkGlyph& glyph = glyphCacheProc(cache, ¤tText, | |
303 fx & fxMask, fy & fyMa
sk); | |
304 // the assumption is that the metrics haven't changed | |
305 SkASSERT(prevAdvX == glyph.fAdvanceX); | |
306 SkASSERT(prevAdvY == glyph.fAdvanceY); | |
307 SkASSERT(glyph.fWidth); | |
308 | |
309 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
310 glyph.getSubXFixed(), | |
311 glyph.getSubYFixed(), | |
312 GrGlyph::kCoverage_MaskStyle
), | |
313 Sk48Dot16FloorToInt(fx), | |
314 Sk48Dot16FloorToInt(fy), | |
315 fontScaler); | |
316 } | |
317 pos += scalarsPerPosition; | |
318 } | |
319 } | |
320 } else { // not subpixel | |
321 | |
322 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | |
323 while (text < stop) { | |
324 // the last 2 parameters are ignored | |
325 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
326 | |
327 if (glyph.fWidth) { | |
328 SkPoint tmsLoc; | |
329 tmsProc(pos, &tmsLoc); | |
330 | |
331 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); | |
332 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); | |
333 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
334 glyph.getSubXFixed(), | |
335 glyph.getSubYFixed(), | |
336 GrGlyph::kCoverage_MaskStyle
), | |
337 Sk48Dot16FloorToInt(fx), | |
338 Sk48Dot16FloorToInt(fy), | |
339 fontScaler); | |
340 } | |
341 pos += scalarsPerPosition; | |
342 } | |
343 } else { | |
344 while (text < stop) { | |
345 // the last 2 parameters are ignored | |
346 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
347 | |
348 if (glyph.fWidth) { | |
349 SkPoint tmsLoc; | |
350 tmsProc(pos, &tmsLoc); | |
351 | |
352 SkPoint alignLoc; | |
353 alignProc(tmsLoc, glyph, &alignLoc); | |
354 | |
355 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf
); | |
356 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf
); | |
357 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
358 glyph.getSubXFixed(), | |
359 glyph.getSubYFixed(), | |
360 GrGlyph::kCoverage_MaskStyle
), | |
361 Sk48Dot16FloorToInt(fx), | |
362 Sk48Dot16FloorToInt(fy), | |
363 fontScaler); | |
364 } | |
365 pos += scalarsPerPosition; | |
366 } | |
367 } | |
368 } | |
369 | |
370 this->finish(); | |
371 } | |
372 | |
373 static void* alloc_vertices(GrDrawTarget* drawTarget, | |
374 int numVertices, | |
375 GrMaskFormat maskFormat) { | |
376 if (numVertices <= 0) { | |
377 return NULL; | |
378 } | |
379 | |
380 // set up attributes | |
381 void* vertices = NULL; | |
382 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices, | |
383 get_vertex_stride(mask
Format), | |
384 0, | |
385 &vertices, | |
386 NULL); | |
387 GrAlwaysAssert(success); | |
388 return vertices; | |
389 } | |
390 | |
391 inline bool GrBitmapTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scale
r) { | |
392 if (!fStrike->glyphTooLargeForAtlas(glyph)) { | |
393 if (fStrike->addGlyphToAtlas(glyph, scaler)) { | |
394 return true; | |
395 } | |
396 | |
397 // try to clear out an unused plot before we flush | |
398 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && | |
399 fStrike->addGlyphToAtlas(glyph, scaler)) { | |
400 return true; | |
401 } | |
402 | |
403 if (c_DumpFontCache) { | |
404 #ifdef SK_DEVELOPER | |
405 fContext->getFontCache()->dump(); | |
406 #endif | |
407 } | |
408 | |
409 // before we purge the cache, we must flush any accumulated draws | |
410 this->flush(); | |
411 fContext->flush(); | |
412 | |
413 // we should have an unused plot now | |
414 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && | |
415 fStrike->addGlyphToAtlas(glyph, scaler)) { | |
416 return true; | |
417 } | |
418 | |
419 // we should never get here | |
420 SkASSERT(false); | |
421 } | |
422 | |
423 return false; | |
424 } | |
425 | |
426 void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed, | |
427 int vx, int vy, | |
428 GrFontScaler* scaler) { | |
429 if (NULL == fDrawTarget) { | |
430 return; | |
431 } | |
432 | |
433 if (NULL == fStrike) { | |
434 fStrike = fContext->getFontCache()->getStrike(scaler); | |
435 } | |
436 | |
437 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); | |
438 if (NULL == glyph || glyph->fBounds.isEmpty()) { | |
439 return; | |
440 } | |
441 | |
442 int x = vx + glyph->fBounds.fLeft; | |
443 int y = vy + glyph->fBounds.fTop; | |
444 | |
445 // keep them as ints until we've done the clip-test | |
446 int width = glyph->fBounds.width(); | |
447 int height = glyph->fBounds.height(); | |
448 | |
449 // check if we clipped out | |
450 if (fClipRect.quickReject(x, y, x + width, y + height)) { | |
451 return; | |
452 } | |
453 | |
454 // If the glyph is too large we fall back to paths | |
455 if (NULL == glyph->fPlot && !uploadGlyph(glyph, scaler)) { | |
456 if (NULL == glyph->fPath) { | |
457 SkPath* path = SkNEW(SkPath); | |
458 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { | |
459 // flag the glyph as being dead? | |
460 delete path; | |
461 return; | |
462 } | |
463 glyph->fPath = path; | |
464 } | |
465 | |
466 // flush any accumulated draws before drawing this glyph as a path. | |
467 this->flush(); | |
468 | |
469 SkMatrix translate; | |
470 translate.setTranslate(SkIntToScalar(vx), SkIntToScalar(vy)); | |
471 SkPath tmpPath(*glyph->fPath); | |
472 tmpPath.transform(translate); | |
473 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); | |
474 fContext->drawPath(fRenderTarget, fClip, fPaint, SkMatrix::I(), tmpPath,
strokeInfo); | |
475 | |
476 // remove this glyph from the vertices we need to allocate | |
477 fTotalVertexCount -= kVerticesPerGlyph; | |
478 return; | |
479 } | |
480 | |
481 SkASSERT(glyph->fPlot); | |
482 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); | |
483 glyph->fPlot->setDrawToken(drawToken); | |
484 | |
485 // the current texture/maskformat must match what the glyph needs | |
486 GrTexture* texture = glyph->fPlot->texture(); | |
487 SkASSERT(texture); | |
488 | |
489 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fAllocVerte
xCount) { | |
490 this->flush(); | |
491 fCurrTexture = texture; | |
492 fCurrTexture->ref(); | |
493 fCurrMaskFormat = glyph->fMaskFormat; | |
494 } | |
495 | |
496 if (NULL == fVertices) { | |
497 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()
->maxQuads(); | |
498 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices); | |
499 fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, fCurrMaskForm
at); | |
500 } | |
501 | |
502 SkRect r; | |
503 r.fLeft = SkIntToScalar(x); | |
504 r.fTop = SkIntToScalar(y); | |
505 r.fRight = r.fLeft + SkIntToScalar(width); | |
506 r.fBottom = r.fTop + SkIntToScalar(height); | |
507 | |
508 fVertexBounds.joinNonEmptyArg(r); | |
509 | |
510 int u0 = glyph->fAtlasLocation.fX; | |
511 int v0 = glyph->fAtlasLocation.fY; | |
512 int u1 = u0 + width; | |
513 int v1 = v0 + height; | |
514 | |
515 size_t vertSize = get_vertex_stride(fCurrMaskFormat); | |
516 intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVe
rtex; | |
517 | |
518 // V0 | |
519 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); | |
520 position->set(r.fLeft, r.fTop); | |
521 if (kA8_GrMaskFormat == fCurrMaskFormat) { | |
522 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); | |
523 *color = fPaint.getColor(); | |
524 } | |
525 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize
- | |
526 sizeof(SkIPoint16)
); | |
527 textureCoords->set(u0, v0); | |
528 vertex += vertSize; | |
529 | |
530 // V1 | |
531 position = reinterpret_cast<SkPoint*>(vertex); | |
532 position->set(r.fLeft, r.fBottom); | |
533 if (kA8_GrMaskFormat == fCurrMaskFormat) { | |
534 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); | |
535 *color = fPaint.getColor(); | |
536 } | |
537 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(Sk
IPoint16)); | |
538 textureCoords->set(u0, v1); | |
539 vertex += vertSize; | |
540 | |
541 // V2 | |
542 position = reinterpret_cast<SkPoint*>(vertex); | |
543 position->set(r.fRight, r.fBottom); | |
544 if (kA8_GrMaskFormat == fCurrMaskFormat) { | |
545 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); | |
546 *color = fPaint.getColor(); | |
547 } | |
548 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(Sk
IPoint16)); | |
549 textureCoords->set(u1, v1); | |
550 vertex += vertSize; | |
551 | |
552 // V3 | |
553 position = reinterpret_cast<SkPoint*>(vertex); | |
554 position->set(r.fRight, r.fTop); | |
555 if (kA8_GrMaskFormat == fCurrMaskFormat) { | |
556 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); | |
557 *color = fPaint.getColor(); | |
558 } | |
559 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(Sk
IPoint16)); | |
560 textureCoords->set(u1, v0); | |
561 | |
562 fCurrVertex += 4; | |
563 } | |
564 | |
565 void GrBitmapTextContext::flush() { | |
566 if (NULL == fDrawTarget) { | |
567 return; | |
568 } | |
569 | |
570 if (fCurrVertex > 0) { | |
571 GrPipelineBuilder pipelineBuilder; | |
572 pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip); | |
573 | |
574 // setup our sampler state for our text texture/atlas | |
575 SkASSERT(SkIsAlign4(fCurrVertex)); | |
576 SkASSERT(fCurrTexture); | |
577 | |
578 SkASSERT(fStrike); | |
579 GrColor color = fPaint.getColor(); | |
580 switch (fCurrMaskFormat) { | |
581 // Color bitmap text | |
582 case kARGB_GrMaskFormat: { | |
583 int a = fSkPaint.getAlpha(); | |
584 color = SkColorSetARGB(a, a, a, a); | |
585 break; | |
586 } | |
587 // LCD text | |
588 case kA565_GrMaskFormat: { | |
589 // TODO: move supportsRGBCoverage check to setupCoverageEffect a
nd only add LCD | |
590 // processor if the xp can support it. For now we will simply as
sume that if | |
591 // fUseLCDText is true, then we have a known color output. | |
592 const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory(); | |
593 if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFla
gs)) { | |
594 SkDebugf("LCD Text will not draw correctly.\n"); | |
595 } | |
596 break; | |
597 } | |
598 // Grayscale/BW text | |
599 case kA8_GrMaskFormat: | |
600 break; | |
601 default: | |
602 SkFAIL("Unexpected mask format."); | |
603 } | |
604 | |
605 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone
_FilterMode); | |
606 uint32_t textureUniqueID = fCurrTexture->getUniqueID(); | |
607 if (textureUniqueID != fEffectTextureUniqueID || | |
608 fCachedGeometryProcessor->color() != color || | |
609 !fCachedGeometryProcessor->localMatrix().cheapEqualTo(fLocalMatrix))
{ | |
610 // This will be ignored in the non A8 case | |
611 bool opaqueVertexColors = GrColorIsOpaque(fPaint.getColor()); | |
612 fCachedGeometryProcessor.reset(GrBitmapTextGeoProc::Create(color, | |
613 fCurrText
ure, | |
614 params, | |
615 fCurrMask
Format, | |
616 opaqueVer
texColors, | |
617 fLocalMat
rix)); | |
618 fEffectTextureUniqueID = textureUniqueID; | |
619 } | |
620 | |
621 int nGlyphs = fCurrVertex / kVerticesPerGlyph; | |
622 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); | |
623 fDrawTarget->drawIndexedInstances(&pipelineBuilder, | |
624 fCachedGeometryProcessor.get(), | |
625 kTriangles_GrPrimitiveType, | |
626 nGlyphs, | |
627 kVerticesPerGlyph, | |
628 kIndicesPerGlyph, | |
629 &fVertexBounds); | |
630 | |
631 fDrawTarget->resetVertexSource(); | |
632 fVertices = NULL; | |
633 fAllocVertexCount = 0; | |
634 // reset to be those that are left | |
635 fTotalVertexCount -= fCurrVertex; | |
636 fCurrVertex = 0; | |
637 fVertexBounds.setLargestInverted(); | |
638 SkSafeSetNull(fCurrTexture); | |
639 } | |
640 } | |
641 | |
642 inline void GrBitmapTextContext::finish() { | |
643 this->flush(); | |
644 fTotalVertexCount = 0; | |
645 | |
646 GrTextContext::finish(); | |
647 } | |
OLD | NEW |