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 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 }
    609