Home | History | Annotate | Download | only in gpu
      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 
     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 
     67 GrBitmapTextContext::~GrBitmapTextContext() {
     68     this->flushGlyphs();
     69 }
     70 
     71 bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
     72     return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
     73 }
     74 
     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 
     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 
    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 
    176 inline void GrBitmapTextContext::finish() {
    177     this->flushGlyphs();
    178 
    179     GrTextContext::finish();
    180 }
    181 
    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 
    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 
    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