• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 #include "GrBitmapTextContext.h"
9 #include "GrAtlas.h"
10 #include "GrDrawTarget.h"
11 #include "GrFontScaler.h"
12 #include "GrIndexBuffer.h"
13 #include "GrStrokeInfo.h"
14 #include "GrTextStrike.h"
15 #include "GrTextStrike_impl.h"
16 #include "SkColorPriv.h"
17 #include "SkPath.h"
18 #include "SkRTConf.h"
19 #include "SkStrokeRec.h"
20 #include "effects/GrCustomCoordsTextureEffect.h"
21 
22 #include "SkAutoKern.h"
23 #include "SkDraw.h"
24 #include "SkDrawProcs.h"
25 #include "SkGlyphCache.h"
26 #include "SkGpuDevice.h"
27 #include "SkGr.h"
28 #include "SkTextMapStateProc.h"
29 
30 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
31                 "Dump the contents of the font cache before every purge.");
32 
33 static const int kGlyphCoordsNoColorAttributeIndex = 1;
34 static const int kGlyphCoordsWithColorAttributeIndex = 2;
35 
36 namespace {
37 // position + texture coord
38 extern const GrVertexAttrib gTextVertexAttribs[] = {
39     {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
40     {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kEffect_GrVertexAttribBinding}
41 };
42 
43 // position + color + texture coord
44 extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
45     {kVec2f_GrVertexAttribType,  0,                                 kPosition_GrVertexAttribBinding},
46     {kVec4ub_GrVertexAttribType, sizeof(SkPoint),                   kColor_GrVertexAttribBinding},
47     {kVec2f_GrVertexAttribType,  sizeof(SkPoint) + sizeof(GrColor), kEffect_GrVertexAttribBinding}
48 };
49 
50 };
51 
GrBitmapTextContext(GrContext * context,const SkDeviceProperties & properties)52 GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
53                                          const SkDeviceProperties& properties)
54                                        : GrTextContext(context, properties) {
55     fStrike = NULL;
56 
57     fCurrTexture = NULL;
58     fCurrVertex = 0;
59     fEffectTextureGenID = 0;
60 
61     fVertices = NULL;
62     fMaxVertices = 0;
63 
64     fVertexBounds.setLargestInverted();
65 }
66 
~GrBitmapTextContext()67 GrBitmapTextContext::~GrBitmapTextContext() {
68     this->flushGlyphs();
69 }
70 
canDraw(const SkPaint & paint)71 bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
72     return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
73 }
74 
skcolor_to_grcolor_nopremultiply(SkColor c)75 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
76     unsigned r = SkColorGetR(c);
77     unsigned g = SkColorGetG(c);
78     unsigned b = SkColorGetB(c);
79     return GrColorPackRGBA(r, g, b, 0xff);
80 }
81 
flushGlyphs()82 void GrBitmapTextContext::flushGlyphs() {
83     if (NULL == fDrawTarget) {
84         return;
85     }
86 
87     GrDrawState* drawState = fDrawTarget->drawState();
88     GrDrawState::AutoRestoreEffects are(drawState);
89     drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
90 
91     if (fCurrVertex > 0) {
92         // setup our sampler state for our text texture/atlas
93         SkASSERT(SkIsAlign4(fCurrVertex));
94         SkASSERT(fCurrTexture);
95         GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
96 
97         uint32_t textureGenID = fCurrTexture->getGenerationID();
98 
99         if (textureGenID != fEffectTextureGenID) {
100             fCachedEffect.reset(GrCustomCoordsTextureEffect::Create(fCurrTexture, params));
101             fEffectTextureGenID = textureGenID;
102         }
103 
104         // This effect could be stored with one of the cache objects (atlas?)
105         int coordsIdx = drawState->hasColorVertexAttribute() ? kGlyphCoordsWithColorAttributeIndex :
106                                                                kGlyphCoordsNoColorAttributeIndex;
107         drawState->addCoverageEffect(fCachedEffect.get(), coordsIdx);
108         SkASSERT(NULL != fStrike);
109         switch (fStrike->getMaskFormat()) {
110             // Color bitmap text
111             case kARGB_GrMaskFormat:
112                 SkASSERT(!drawState->hasColorVertexAttribute());
113                 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
114                 drawState->setColor(0xffffffff);
115                 break;
116             // LCD text
117             case kA888_GrMaskFormat:
118             case kA565_GrMaskFormat: {
119                 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
120                     kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
121                     fPaint.numColorStages()) {
122                     GrPrintf("LCD Text will not draw correctly.\n");
123                 }
124                 SkASSERT(!drawState->hasColorVertexAttribute());
125                 // We don't use the GrPaint's color in this case because it's been premultiplied by
126                 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
127                 // the mask texture color. The end result is that we get
128                 //            mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
129                 int a = SkColorGetA(fSkPaint.getColor());
130                 // paintAlpha
131                 drawState->setColor(SkColorSetARGB(a, a, a, a));
132                 // paintColor
133                 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
134                 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
135                 break;
136             }
137             // Grayscale/BW text
138             case kA8_GrMaskFormat:
139                 // set back to normal in case we took LCD path previously.
140                 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
141                 //drawState->setColor(fPaint.getColor());
142                 // We're using per-vertex color.
143                 SkASSERT(drawState->hasColorVertexAttribute());
144                 drawState->setColor(0xFFFFFFFF);
145                 break;
146             default:
147                 SkFAIL("Unexepected mask format.");
148         }
149         int nGlyphs = fCurrVertex / 4;
150         fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
151         fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
152                                           nGlyphs,
153                                           4, 6, &fVertexBounds);
154 
155         fDrawTarget->resetVertexSource();
156         fVertices = NULL;
157         fMaxVertices = 0;
158         fCurrVertex = 0;
159         fVertexBounds.setLargestInverted();
160         SkSafeSetNull(fCurrTexture);
161     }
162 }
163 
init(const GrPaint & paint,const SkPaint & skPaint)164 inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
165     GrTextContext::init(paint, skPaint);
166 
167     fStrike = NULL;
168 
169     fCurrTexture = NULL;
170     fCurrVertex = 0;
171 
172     fVertices = NULL;
173     fMaxVertices = 0;
174 }
175 
finish()176 inline void GrBitmapTextContext::finish() {
177     this->flushGlyphs();
178 
179     GrTextContext::finish();
180 }
181 
drawText(const GrPaint & paint,const SkPaint & skPaint,const char text[],size_t byteLength,SkScalar x,SkScalar y)182 void GrBitmapTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
183                                    const char text[], size_t byteLength,
184                                    SkScalar x, SkScalar y) {
185     SkASSERT(byteLength == 0 || text != NULL);
186 
187     // nothing to draw
188     if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
189         return;
190     }
191 
192     this->init(paint, skPaint);
193 
194     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
195 
196     SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
197     SkGlyphCache*       cache = autoCache.getCache();
198     GrFontScaler*       fontScaler = GetGrFontScaler(cache);
199 
200     // transform our starting point
201     {
202         SkPoint loc;
203         fContext->getMatrix().mapXY(x, y, &loc);
204         x = loc.fX;
205         y = loc.fY;
206     }
207 
208     // need to measure first
209     if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
210         SkVector    stop;
211 
212         MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
213 
214         SkScalar    stopX = stop.fX;
215         SkScalar    stopY = stop.fY;
216 
217         if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
218             stopX = SkScalarHalf(stopX);
219             stopY = SkScalarHalf(stopY);
220         }
221         x -= stopX;
222         y -= stopY;
223     }
224 
225     const char* stop = text + byteLength;
226 
227     SkAutoKern autokern;
228 
229     SkFixed fxMask = ~0;
230     SkFixed fyMask = ~0;
231     SkFixed halfSampleX, halfSampleY;
232     if (cache->isSubpixel()) {
233         halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
234         SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
235         if (kX_SkAxisAlignment == baseline) {
236             fyMask = 0;
237             halfSampleY = SK_FixedHalf;
238         } else if (kY_SkAxisAlignment == baseline) {
239             fxMask = 0;
240             halfSampleX = SK_FixedHalf;
241         }
242     } else {
243         halfSampleX = halfSampleY = SK_FixedHalf;
244     }
245 
246     SkFixed fx = SkScalarToFixed(x) + halfSampleX;
247     SkFixed fy = SkScalarToFixed(y) + halfSampleY;
248 
249     GrContext::AutoMatrix  autoMatrix;
250     autoMatrix.setIdentity(fContext, &fPaint);
251 
252     while (text < stop) {
253         const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
254 
255         fx += autokern.adjust(glyph);
256 
257         if (glyph.fWidth) {
258             this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
259                                           glyph.getSubXFixed(),
260                                           glyph.getSubYFixed()),
261                                   SkFixedFloorToFixed(fx),
262                                   SkFixedFloorToFixed(fy),
263                                   fontScaler);
264         }
265 
266         fx += glyph.fAdvanceX;
267         fy += glyph.fAdvanceY;
268     }
269 
270     this->finish();
271 }
272 
drawPosText(const GrPaint & paint,const SkPaint & skPaint,const char text[],size_t byteLength,const SkScalar pos[],SkScalar constY,int scalarsPerPosition)273 void GrBitmapTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
274                                       const char text[], size_t byteLength,
275                                       const SkScalar pos[], SkScalar constY,
276                                       int scalarsPerPosition) {
277     SkASSERT(byteLength == 0 || text != NULL);
278     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
279 
280     // nothing to draw
281     if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
282         return;
283     }
284 
285     this->init(paint, skPaint);
286 
287     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
288 
289     SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
290     SkGlyphCache*       cache = autoCache.getCache();
291     GrFontScaler*       fontScaler = GetGrFontScaler(cache);
292 
293     // store original matrix before we reset, so we can use it to transform positions
294     SkMatrix ctm = fContext->getMatrix();
295     GrContext::AutoMatrix  autoMatrix;
296     autoMatrix.setIdentity(fContext, &fPaint);
297 
298     const char*        stop = text + byteLength;
299     SkTextAlignProc    alignProc(fSkPaint.getTextAlign());
300     SkTextMapStateProc tmsProc(ctm, constY, scalarsPerPosition);
301     SkFixed halfSampleX = 0, halfSampleY = 0;
302 
303     if (cache->isSubpixel()) {
304         // maybe we should skip the rounding if linearText is set
305         SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
306 
307         SkFixed fxMask = ~0;
308         SkFixed fyMask = ~0;
309         if (kX_SkAxisAlignment == baseline) {
310             fyMask = 0;
311 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
312             halfSampleY = SK_FixedHalf;
313 #endif
314         } else if (kY_SkAxisAlignment == baseline) {
315             fxMask = 0;
316 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
317             halfSampleX = SK_FixedHalf;
318 #endif
319         }
320 
321         if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
322             while (text < stop) {
323                 SkPoint tmsLoc;
324                 tmsProc(pos, &tmsLoc);
325                 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
326                 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
327 
328                 const SkGlyph& glyph = glyphCacheProc(cache, &text,
329                                                       fx & fxMask, fy & fyMask);
330 
331                 if (glyph.fWidth) {
332                     this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
333                                                         glyph.getSubXFixed(),
334                                                         glyph.getSubYFixed()),
335                                           SkFixedFloorToFixed(fx),
336                                           SkFixedFloorToFixed(fy),
337                                           fontScaler);
338                 }
339                 pos += scalarsPerPosition;
340             }
341         } else {
342             while (text < stop) {
343                 const char* currentText = text;
344                 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
345 
346                 if (metricGlyph.fWidth) {
347                     SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
348                     SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
349                     SkPoint tmsLoc;
350                     tmsProc(pos, &tmsLoc);
351                     SkIPoint fixedLoc;
352                     alignProc(tmsLoc, metricGlyph, &fixedLoc);
353 
354                     SkFixed fx = fixedLoc.fX + halfSampleX;
355                     SkFixed fy = fixedLoc.fY + halfSampleY;
356 
357                     // have to call again, now that we've been "aligned"
358                     const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
359                                                           fx & fxMask, fy & fyMask);
360                     // the assumption is that the metrics haven't changed
361                     SkASSERT(prevAdvX == glyph.fAdvanceX);
362                     SkASSERT(prevAdvY == glyph.fAdvanceY);
363                     SkASSERT(glyph.fWidth);
364 
365                     this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
366                                                         glyph.getSubXFixed(),
367                                                         glyph.getSubYFixed()),
368                                           SkFixedFloorToFixed(fx),
369                                           SkFixedFloorToFixed(fy),
370                                           fontScaler);
371                 }
372                 pos += scalarsPerPosition;
373             }
374         }
375     } else {    // not subpixel
376 
377         if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
378             while (text < stop) {
379                 // the last 2 parameters are ignored
380                 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
381 
382                 if (glyph.fWidth) {
383                     SkPoint tmsLoc;
384                     tmsProc(pos, &tmsLoc);
385 
386                     SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
387                     SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
388                     this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
389                                                         glyph.getSubXFixed(),
390                                                         glyph.getSubYFixed()),
391                                           SkFixedFloorToFixed(fx),
392                                           SkFixedFloorToFixed(fy),
393                                           fontScaler);
394                 }
395                 pos += scalarsPerPosition;
396             }
397         } else {
398             while (text < stop) {
399                 // the last 2 parameters are ignored
400                 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
401 
402                 if (glyph.fWidth) {
403                     SkPoint tmsLoc;
404                     tmsProc(pos, &tmsLoc);
405 
406                     SkIPoint fixedLoc;
407                     alignProc(tmsLoc, glyph, &fixedLoc);
408 
409                     SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
410                     SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
411                     this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
412                                                         glyph.getSubXFixed(),
413                                                         glyph.getSubYFixed()),
414                                           SkFixedFloorToFixed(fx),
415                                           SkFixedFloorToFixed(fy),
416                                           fontScaler);
417                 }
418                 pos += scalarsPerPosition;
419             }
420         }
421     }
422 
423     this->finish();
424 }
425 
drawPackedGlyph(GrGlyph::PackedID packed,SkFixed vx,SkFixed vy,GrFontScaler * scaler)426 void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
427                                           SkFixed vx, SkFixed vy,
428                                           GrFontScaler* scaler) {
429     if (NULL == fDrawTarget) {
430         return;
431     }
432 
433     if (NULL == fStrike) {
434         fStrike = fContext->getFontCache()->getStrike(scaler, false);
435     }
436 
437     GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
438     if (NULL == glyph || glyph->fBounds.isEmpty()) {
439         return;
440     }
441 
442     vx += SkIntToFixed(glyph->fBounds.fLeft);
443     vy += SkIntToFixed(glyph->fBounds.fTop);
444 
445     // keep them as ints until we've done the clip-test
446     SkFixed width = glyph->fBounds.width();
447     SkFixed height = glyph->fBounds.height();
448 
449     // check if we clipped out
450     if (true || NULL == glyph->fPlot) {
451         int x = vx >> 16;
452         int y = vy >> 16;
453         if (fClipRect.quickReject(x, y, x + width, y + height)) {
454 //            SkCLZ(3);    // so we can set a break-point in the debugger
455             return;
456         }
457     }
458 
459     if (NULL == glyph->fPlot) {
460         if (fStrike->addGlyphToAtlas(glyph, scaler)) {
461             goto HAS_ATLAS;
462         }
463 
464         // try to clear out an unused plot before we flush
465         if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
466             fStrike->addGlyphToAtlas(glyph, scaler)) {
467             goto HAS_ATLAS;
468         }
469 
470         if (c_DumpFontCache) {
471 #ifdef SK_DEVELOPER
472             fContext->getFontCache()->dump();
473 #endif
474         }
475 
476         // flush any accumulated draws to allow us to free up a plot
477         this->flushGlyphs();
478         fContext->flush();
479 
480         // we should have an unused plot now
481         if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
482             fStrike->addGlyphToAtlas(glyph, scaler)) {
483             goto HAS_ATLAS;
484         }
485 
486         if (NULL == glyph->fPath) {
487             SkPath* path = SkNEW(SkPath);
488             if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
489                 // flag the glyph as being dead?
490                 delete path;
491                 return;
492             }
493             glyph->fPath = path;
494         }
495 
496         GrContext::AutoMatrix am;
497         SkMatrix translate;
498         translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
499                                SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
500         GrPaint tmpPaint(fPaint);
501         am.setPreConcat(fContext, translate, &tmpPaint);
502         GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
503         fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
504         return;
505     }
506 
507 HAS_ATLAS:
508     SkASSERT(glyph->fPlot);
509     GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
510     glyph->fPlot->setDrawToken(drawToken);
511 
512     // now promote them to fixed (TODO: Rethink using fixed pt).
513     width = SkIntToFixed(width);
514     height = SkIntToFixed(height);
515 
516     GrTexture* texture = glyph->fPlot->texture();
517     SkASSERT(texture);
518 
519     if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
520         this->flushGlyphs();
521         fCurrTexture = texture;
522         fCurrTexture->ref();
523     }
524 
525     bool useColorVerts = kA8_GrMaskFormat == fStrike->getMaskFormat();
526 
527     if (NULL == fVertices) {
528        // If we need to reserve vertices allow the draw target to suggest
529         // a number of verts to reserve and whether to perform a flush.
530         fMaxVertices = kMinRequestedVerts;
531         if (useColorVerts) {
532             fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
533                 SK_ARRAY_COUNT(gTextVertexWithColorAttribs));
534         } else {
535             fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
536                 SK_ARRAY_COUNT(gTextVertexAttribs));
537         }
538         bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
539         if (flush) {
540             this->flushGlyphs();
541             fContext->flush();
542             if (useColorVerts) {
543                 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
544                   SK_ARRAY_COUNT(gTextVertexWithColorAttribs));
545             } else {
546                 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
547                   SK_ARRAY_COUNT(gTextVertexAttribs));
548             }
549         }
550         fMaxVertices = kDefaultRequestedVerts;
551         // ignore return, no point in flushing again.
552         fDrawTarget->geometryHints(&fMaxVertices, NULL);
553 
554         int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
555         if (fMaxVertices < kMinRequestedVerts) {
556             fMaxVertices = kDefaultRequestedVerts;
557         } else if (fMaxVertices > maxQuadVertices) {
558             // don't exceed the limit of the index buffer
559             fMaxVertices = maxQuadVertices;
560         }
561         bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
562                                                                0,
563                                                                &fVertices,
564                                                                NULL);
565         GrAlwaysAssert(success);
566     }
567 
568     SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
569     SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
570 
571     SkRect r;
572     r.fLeft = SkFixedToFloat(vx);
573     r.fTop = SkFixedToFloat(vy);
574     r.fRight = SkFixedToFloat(vx + width);
575     r.fBottom = SkFixedToFloat(vy + height);
576 
577     fVertexBounds.growToInclude(r);
578 
579     size_t vertSize = useColorVerts ? (2 * sizeof(SkPoint) + sizeof(GrColor)) :
580                                       (2 * sizeof(SkPoint));
581 
582     SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexSize());
583 
584     SkPoint* positions = reinterpret_cast<SkPoint*>(
585         reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
586     positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
587 
588     // The texture coords are last in both the with and without color vertex layouts.
589     SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
590             reinterpret_cast<intptr_t>(positions) + vertSize  - sizeof(SkPoint));
591     textureCoords->setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
592                               SkFixedToFloat(texture->normalizeFixedY(ty)),
593                               SkFixedToFloat(texture->normalizeFixedX(tx + width)),
594                               SkFixedToFloat(texture->normalizeFixedY(ty + height)),
595                               vertSize);
596     if (useColorVerts) {
597         // color comes after position.
598         GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
599         for (int i = 0; i < 4; ++i) {
600            *colors = fPaint.getColor();
601            colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
602         }
603     }
604     fCurrVertex += 4;
605 }
606