Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / gpu / GrBitmapTextContext.cpp
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 namespace {
34 // position + texture coord
35 extern const GrVertexAttrib gTextVertexAttribs[] = {
36     {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
37     {kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding}
38 };
39
40 static const size_t kTextVASize = 2 * sizeof(SkPoint); 
41
42 // position + color + texture coord
43 extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
44     {kVec2f_GrVertexAttribType,  0,                                 kPosition_GrVertexAttribBinding},
45     {kVec4ub_GrVertexAttribType, sizeof(SkPoint),                   kColor_GrVertexAttribBinding},
46     {kVec2f_GrVertexAttribType,  sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
47 };
48
49 static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor); 
50
51 };
52
53 GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
54                                          const SkDeviceProperties& properties)
55                                        : GrTextContext(context, properties) {
56     fStrike = NULL;
57
58     fCurrTexture = NULL;
59     fCurrVertex = 0;
60     fEffectTextureUniqueID = SK_InvalidUniqueID;
61
62     fVertices = NULL;
63     fMaxVertices = 0;
64
65     fVertexBounds.setLargestInverted();
66 }
67
68 GrBitmapTextContext::~GrBitmapTextContext() {
69     this->flushGlyphs();
70 }
71
72 bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
73     return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
74 }
75
76 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
77     unsigned r = SkColorGetR(c);
78     unsigned g = SkColorGetG(c);
79     unsigned b = SkColorGetB(c);
80     return GrColorPackRGBA(r, g, b, 0xff);
81 }
82
83 void GrBitmapTextContext::flushGlyphs() {
84     if (NULL == fDrawTarget) {
85         return;
86     }
87
88     GrDrawState* drawState = fDrawTarget->drawState();
89     GrDrawState::AutoRestoreEffects are(drawState);
90     drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
91
92     if (fCurrVertex > 0) {
93         // setup our sampler state for our text texture/atlas
94         SkASSERT(SkIsAlign4(fCurrVertex));
95         SkASSERT(fCurrTexture);
96         GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
97
98         uint32_t textureUniqueID = fCurrTexture->getUniqueID();
99         
100         if (textureUniqueID != fEffectTextureUniqueID) {
101             fCachedGeometryProcessor.reset(GrCustomCoordsTextureEffect::Create(fCurrTexture,
102                                                                                params));
103             fEffectTextureUniqueID = textureUniqueID;
104         }
105
106         // This effect could be stored with one of the cache objects (atlas?)
107         drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
108         SkASSERT(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                 // We're using per-vertex color.
142                 SkASSERT(drawState->hasColorVertexAttribute());
143                 break;
144             default:
145                 SkFAIL("Unexepected mask format.");
146         }
147         int nGlyphs = fCurrVertex / 4;
148         fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
149         fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
150                                           nGlyphs,
151                                           4, 6, &fVertexBounds);
152
153         fDrawTarget->resetVertexSource();
154         fVertices = NULL;
155         fMaxVertices = 0;
156         fCurrVertex = 0;
157         fVertexBounds.setLargestInverted();
158         SkSafeSetNull(fCurrTexture);
159     }
160 }
161
162 inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
163     GrTextContext::init(paint, skPaint);
164
165     fStrike = NULL;
166
167     fCurrTexture = NULL;
168     fCurrVertex = 0;
169
170     fVertices = NULL;
171     fMaxVertices = 0;
172 }
173
174 inline void GrBitmapTextContext::finish() {
175     this->flushGlyphs();
176
177     GrTextContext::finish();
178 }
179
180 void GrBitmapTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
181                                    const char text[], size_t byteLength,
182                                    SkScalar x, SkScalar y) {
183     SkASSERT(byteLength == 0 || text != NULL);
184
185     // nothing to draw
186     if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
187         return;
188     }
189
190     this->init(paint, skPaint);
191
192     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
193
194     SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
195     SkGlyphCache*       cache = autoCache.getCache();
196     GrFontScaler*       fontScaler = GetGrFontScaler(cache);
197
198     // transform our starting point
199     {
200         SkPoint loc;
201         fContext->getMatrix().mapXY(x, y, &loc);
202         x = loc.fX;
203         y = loc.fY;
204     }
205
206     // need to measure first
207     if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
208         SkVector    stop;
209
210         MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
211
212         SkScalar    stopX = stop.fX;
213         SkScalar    stopY = stop.fY;
214
215         if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
216             stopX = SkScalarHalf(stopX);
217             stopY = SkScalarHalf(stopY);
218         }
219         x -= stopX;
220         y -= stopY;
221     }
222
223     const char* stop = text + byteLength;
224
225     SkAutoKern autokern;
226
227     SkFixed fxMask = ~0;
228     SkFixed fyMask = ~0;
229     SkFixed halfSampleX, halfSampleY;
230     if (cache->isSubpixel()) {
231         halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
232         SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
233         if (kX_SkAxisAlignment == baseline) {
234             fyMask = 0;
235             halfSampleY = SK_FixedHalf;
236         } else if (kY_SkAxisAlignment == baseline) {
237             fxMask = 0;
238             halfSampleX = SK_FixedHalf;
239         }
240     } else {
241         halfSampleX = halfSampleY = SK_FixedHalf;
242     }
243
244     SkFixed fx = SkScalarToFixed(x) + halfSampleX;
245     SkFixed fy = SkScalarToFixed(y) + halfSampleY;
246
247     GrContext::AutoMatrix  autoMatrix;
248     autoMatrix.setIdentity(fContext, &fPaint);
249
250     while (text < stop) {
251         const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
252
253         fx += autokern.adjust(glyph);
254
255         if (glyph.fWidth) {
256             this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
257                                           glyph.getSubXFixed(),
258                                           glyph.getSubYFixed()),
259                                   SkFixedFloorToFixed(fx),
260                                   SkFixedFloorToFixed(fy),
261                                   fontScaler);
262         }
263
264         fx += glyph.fAdvanceX;
265         fy += glyph.fAdvanceY;
266     }
267
268     this->finish();
269 }
270
271 void GrBitmapTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
272                                       const char text[], size_t byteLength,
273                                       const SkScalar pos[], SkScalar constY,
274                                       int scalarsPerPosition) {
275     SkASSERT(byteLength == 0 || text != NULL);
276     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
277
278     // nothing to draw
279     if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
280         return;
281     }
282
283     this->init(paint, skPaint);
284
285     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
286
287     SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
288     SkGlyphCache*       cache = autoCache.getCache();
289     GrFontScaler*       fontScaler = GetGrFontScaler(cache);
290
291     // store original matrix before we reset, so we can use it to transform positions
292     SkMatrix ctm = fContext->getMatrix();
293     GrContext::AutoMatrix  autoMatrix;
294     autoMatrix.setIdentity(fContext, &fPaint);
295
296     const char*        stop = text + byteLength;
297     SkTextAlignProc    alignProc(fSkPaint.getTextAlign());
298     SkTextMapStateProc tmsProc(ctm, constY, scalarsPerPosition);
299     SkFixed halfSampleX = 0, halfSampleY = 0;
300
301     if (cache->isSubpixel()) {
302         // maybe we should skip the rounding if linearText is set
303         SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
304
305         SkFixed fxMask = ~0;
306         SkFixed fyMask = ~0;
307         if (kX_SkAxisAlignment == baseline) {
308             fyMask = 0;
309 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
310             halfSampleY = SK_FixedHalf;
311 #endif
312         } else if (kY_SkAxisAlignment == baseline) {
313             fxMask = 0;
314 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
315             halfSampleX = SK_FixedHalf;
316 #endif
317         }
318
319         if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
320             while (text < stop) {
321                 SkPoint tmsLoc;
322                 tmsProc(pos, &tmsLoc);
323                 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
324                 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
325
326                 const SkGlyph& glyph = glyphCacheProc(cache, &text,
327                                                       fx & fxMask, fy & fyMask);
328
329                 if (glyph.fWidth) {
330                     this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
331                                                         glyph.getSubXFixed(),
332                                                         glyph.getSubYFixed()),
333                                           SkFixedFloorToFixed(fx),
334                                           SkFixedFloorToFixed(fy),
335                                           fontScaler);
336                 }
337                 pos += scalarsPerPosition;
338             }
339         } else {
340             while (text < stop) {
341                 const char* currentText = text;
342                 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
343
344                 if (metricGlyph.fWidth) {
345                     SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
346                     SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
347                     SkPoint tmsLoc;
348                     tmsProc(pos, &tmsLoc);
349                     SkIPoint fixedLoc;
350                     alignProc(tmsLoc, metricGlyph, &fixedLoc);
351
352                     SkFixed fx = fixedLoc.fX + halfSampleX;
353                     SkFixed fy = fixedLoc.fY + halfSampleY;
354
355                     // have to call again, now that we've been "aligned"
356                     const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
357                                                           fx & fxMask, fy & fyMask);
358                     // the assumption is that the metrics haven't changed
359                     SkASSERT(prevAdvX == glyph.fAdvanceX);
360                     SkASSERT(prevAdvY == glyph.fAdvanceY);
361                     SkASSERT(glyph.fWidth);
362
363                     this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
364                                                         glyph.getSubXFixed(),
365                                                         glyph.getSubYFixed()),
366                                           SkFixedFloorToFixed(fx),
367                                           SkFixedFloorToFixed(fy),
368                                           fontScaler);
369                 }
370                 pos += scalarsPerPosition;
371             }
372         }
373     } else {    // not subpixel
374
375         if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
376             while (text < stop) {
377                 // the last 2 parameters are ignored
378                 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
379
380                 if (glyph.fWidth) {
381                     SkPoint tmsLoc;
382                     tmsProc(pos, &tmsLoc);
383
384                     SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
385                     SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
386                     this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
387                                                         glyph.getSubXFixed(),
388                                                         glyph.getSubYFixed()),
389                                           SkFixedFloorToFixed(fx),
390                                           SkFixedFloorToFixed(fy),
391                                           fontScaler);
392                 }
393                 pos += scalarsPerPosition;
394             }
395         } else {
396             while (text < stop) {
397                 // the last 2 parameters are ignored
398                 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
399
400                 if (glyph.fWidth) {
401                     SkPoint tmsLoc;
402                     tmsProc(pos, &tmsLoc);
403
404                     SkIPoint fixedLoc;
405                     alignProc(tmsLoc, glyph, &fixedLoc);
406
407                     SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
408                     SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
409                     this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
410                                                         glyph.getSubXFixed(),
411                                                         glyph.getSubYFixed()),
412                                           SkFixedFloorToFixed(fx),
413                                           SkFixedFloorToFixed(fy),
414                                           fontScaler);
415                 }
416                 pos += scalarsPerPosition;
417             }
418         }
419     }
420
421     this->finish();
422 }
423
424 void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
425                                           SkFixed vx, SkFixed vy,
426                                           GrFontScaler* scaler) {
427     if (NULL == fDrawTarget) {
428         return;
429     }
430
431     if (NULL == fStrike) {
432         fStrike = fContext->getFontCache()->getStrike(scaler, false);
433     }
434
435     GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
436     if (NULL == glyph || glyph->fBounds.isEmpty()) {
437         return;
438     }
439
440     vx += SkIntToFixed(glyph->fBounds.fLeft);
441     vy += SkIntToFixed(glyph->fBounds.fTop);
442
443     // keep them as ints until we've done the clip-test
444     SkFixed width = glyph->fBounds.width();
445     SkFixed height = glyph->fBounds.height();
446
447     // check if we clipped out
448     if (true || NULL == glyph->fPlot) {
449         int x = vx >> 16;
450         int y = vy >> 16;
451         if (fClipRect.quickReject(x, y, x + width, y + height)) {
452 //            SkCLZ(3);    // so we can set a break-point in the debugger
453             return;
454         }
455     }
456
457     if (NULL == glyph->fPlot) {
458         if (!fStrike->glyphTooLargeForAtlas(glyph)) {
459             if (fStrike->addGlyphToAtlas(glyph, scaler)) {
460                 goto HAS_ATLAS;
461             }
462
463             // try to clear out an unused plot before we flush
464             if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
465                 fStrike->addGlyphToAtlas(glyph, scaler)) {
466                 goto HAS_ATLAS;
467             }
468
469             if (c_DumpFontCache) {
470 #ifdef SK_DEVELOPER
471                 fContext->getFontCache()->dump();
472 #endif
473             }
474
475             // flush any accumulated draws to allow us to free up a plot
476             this->flushGlyphs();
477             fContext->flush();
478
479             // we should have an unused plot now
480             if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
481                 fStrike->addGlyphToAtlas(glyph, scaler)) {
482                 goto HAS_ATLAS;
483             }
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), kTextVAColorSize);
534         } else {
535             fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
536                 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
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), kTextVAColorSize);
545             } else {
546                 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
547                     SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
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().getVertexStride());
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         if (0xFF == GrColorUnpackA(fPaint.getColor())) {
598             fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
599         }
600         // color comes after position.
601         GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
602         for (int i = 0; i < 4; ++i) {
603            *colors = fPaint.getColor();
604            colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
605         }
606     }
607     fCurrVertex += 4;
608 }