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 "GrDistanceFieldTextContext.h"
      9 #include "GrAtlas.h"
     10 #include "SkColorFilter.h"
     11 #include "GrDrawTarget.h"
     12 #include "GrDrawTargetCaps.h"
     13 #include "GrFontScaler.h"
     14 #include "SkGlyphCache.h"
     15 #include "GrGpu.h"
     16 #include "GrIndexBuffer.h"
     17 #include "GrStrokeInfo.h"
     18 #include "GrTextStrike.h"
     19 #include "GrTextStrike_impl.h"
     20 #include "SkDistanceFieldGen.h"
     21 #include "SkDraw.h"
     22 #include "SkGpuDevice.h"
     23 #include "SkPath.h"
     24 #include "SkRTConf.h"
     25 #include "SkStrokeRec.h"
     26 #include "effects/GrDistanceFieldTextureEffect.h"
     27 
     28 static const int kGlyphCoordsAttributeIndex = 1;
     29 
     30 static const int kSmallDFFontSize = 32;
     31 static const int kSmallDFFontLimit = 32;
     32 static const int kMediumDFFontSize = 64;
     33 static const int kMediumDFFontLimit = 64;
     34 static const int kLargeDFFontSize = 128;
     35 
     36 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
     37                 "Dump the contents of the font cache before every purge.");
     38 
     39 GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
     40                                                        const SkDeviceProperties& properties,
     41                                                        bool enable)
     42                                                     : GrTextContext(context, properties) {
     43 #if SK_FORCE_DISTANCEFIELD_FONTS
     44     fEnableDFRendering = true;
     45 #else
     46     fEnableDFRendering = enable;
     47 #endif
     48     fStrike = NULL;
     49     fGammaTexture = NULL;
     50 
     51     fCurrTexture = NULL;
     52     fCurrVertex = 0;
     53 
     54     fVertices = NULL;
     55     fMaxVertices = 0;
     56 }
     57 
     58 GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
     59     this->flushGlyphs();
     60     SkSafeSetNull(fGammaTexture);
     61 }
     62 
     63 bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
     64     if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
     65         return false;
     66     }
     67 
     68     // rasterizers and mask filters modify alpha, which doesn't
     69     // translate well to distance
     70     if (paint.getRasterizer() || paint.getMaskFilter() ||
     71         !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
     72         return false;
     73     }
     74 
     75     // TODO: add some stroking support
     76     if (paint.getStyle() != SkPaint::kFill_Style) {
     77         return false;
     78     }
     79 
     80     // TODO: choose an appropriate maximum scale for distance fields and
     81     //       enable perspective
     82     if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
     83         return false;
     84     }
     85 
     86     // distance fields cannot represent color fonts
     87     SkScalerContext::Rec    rec;
     88     SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
     89     return rec.getFormat() != SkMask::kARGB32_Format;
     90 }
     91 
     92 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
     93     unsigned r = SkColorGetR(c);
     94     unsigned g = SkColorGetG(c);
     95     unsigned b = SkColorGetB(c);
     96     return GrColorPackRGBA(r, g, b, 0xff);
     97 }
     98 
     99 void GrDistanceFieldTextContext::flushGlyphs() {
    100     if (NULL == fDrawTarget) {
    101         return;
    102     }
    103 
    104     GrDrawState* drawState = fDrawTarget->drawState();
    105     GrDrawState::AutoRestoreEffects are(drawState);
    106     drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
    107 
    108     if (fCurrVertex > 0) {
    109         // setup our sampler state for our text texture/atlas
    110         SkASSERT(SkIsAlign4(fCurrVertex));
    111         SkASSERT(fCurrTexture);
    112         GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
    113         GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
    114 
    115         // Effects could be stored with one of the cache objects (atlas?)
    116         SkColor filteredColor;
    117         SkColorFilter* colorFilter = fSkPaint.getColorFilter();
    118         if (NULL != colorFilter) {
    119             filteredColor = colorFilter->filterColor(fSkPaint.getColor());
    120         } else {
    121             filteredColor = fSkPaint.getColor();
    122         }
    123         if (fUseLCDText) {
    124             GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
    125             bool useBGR = SkDeviceProperties::Geometry::kBGR_Layout ==
    126                                                             fDeviceProperties.fGeometry.getLayout();
    127             drawState->addCoverageEffect(GrDistanceFieldLCDTextureEffect::Create(
    128                                                             fCurrTexture,
    129                                                             params,
    130                                                             fGammaTexture,
    131                                                             gammaParams,
    132                                                             colorNoPreMul,
    133                                                             fContext->getMatrix().rectStaysRect() &&
    134                                                             fContext->getMatrix().isSimilarity(),
    135                                                             useBGR),
    136                                          kGlyphCoordsAttributeIndex)->unref();
    137 
    138             if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
    139                 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
    140                 fPaint.numColorStages()) {
    141                 GrPrintf("LCD Text will not draw correctly.\n");
    142             }
    143             // We don't use the GrPaint's color in this case because it's been premultiplied by
    144             // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
    145             // the mask texture color. The end result is that we get
    146             //            mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
    147             int a = SkColorGetA(fSkPaint.getColor());
    148             // paintAlpha
    149             drawState->setColor(SkColorSetARGB(a, a, a, a));
    150             // paintColor
    151             drawState->setBlendConstant(colorNoPreMul);
    152             drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
    153         } else {
    154 #ifdef SK_GAMMA_APPLY_TO_A8
    155             U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.fGamma,
    156                                                                 filteredColor);
    157             drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(
    158                                                               fCurrTexture, params,
    159                                                               fGammaTexture, gammaParams,
    160                                                               lum/255.f,
    161                                                               fContext->getMatrix().isSimilarity()),
    162                                          kGlyphCoordsAttributeIndex)->unref();
    163 #else
    164             drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(
    165                                                               fCurrTexture, params,
    166                                                               fContext->getMatrix().isSimilarity()),
    167                                          kGlyphCoordsAttributeIndex)->unref();
    168 #endif
    169             // set back to normal in case we took LCD path previously.
    170             drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
    171             drawState->setColor(fPaint.getColor());
    172         }
    173 
    174         int nGlyphs = fCurrVertex / 4;
    175         fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
    176         fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
    177                                           nGlyphs,
    178                                           4, 6);
    179         fDrawTarget->resetVertexSource();
    180         fVertices = NULL;
    181         fMaxVertices = 0;
    182         fCurrVertex = 0;
    183         SkSafeSetNull(fCurrTexture);
    184     }
    185 }
    186 
    187 namespace {
    188 
    189 // position + texture coord
    190 extern const GrVertexAttrib gTextVertexAttribs[] = {
    191     {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
    192     {kVec2f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding}
    193 };
    194 
    195 };
    196 
    197 void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
    198                                                  SkFixed vx, SkFixed vy,
    199                                                  GrFontScaler* scaler) {
    200     if (NULL == fDrawTarget) {
    201         return;
    202     }
    203     if (NULL == fStrike) {
    204         fStrike = fContext->getFontCache()->getStrike(scaler, true);
    205     }
    206 
    207     GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
    208     if (NULL == glyph || glyph->fBounds.isEmpty()) {
    209         return;
    210     }
    211 
    212     SkScalar sx = SkFixedToScalar(vx);
    213     SkScalar sy = SkFixedToScalar(vy);
    214 /*
    215     // not valid, need to find a different solution for this
    216     vx += SkIntToFixed(glyph->fBounds.fLeft);
    217     vy += SkIntToFixed(glyph->fBounds.fTop);
    218 
    219     // keep them as ints until we've done the clip-test
    220     GrFixed width = glyph->fBounds.width();
    221     GrFixed height = glyph->fBounds.height();
    222 
    223     // check if we clipped out
    224     if (true || NULL == glyph->fPlot) {
    225         int x = vx >> 16;
    226         int y = vy >> 16;
    227         if (fClipRect.quickReject(x, y, x + width, y + height)) {
    228 //            SkCLZ(3);    // so we can set a break-point in the debugger
    229             return;
    230         }
    231     }
    232 */
    233     if (NULL == glyph->fPlot) {
    234         if (fStrike->addGlyphToAtlas(glyph, scaler)) {
    235             goto HAS_ATLAS;
    236         }
    237 
    238         // try to clear out an unused plot before we flush
    239         if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
    240             fStrike->addGlyphToAtlas(glyph, scaler)) {
    241             goto HAS_ATLAS;
    242         }
    243 
    244         if (c_DumpFontCache) {
    245 #ifdef SK_DEVELOPER
    246             fContext->getFontCache()->dump();
    247 #endif
    248         }
    249 
    250         // before we purge the cache, we must flush any accumulated draws
    251         this->flushGlyphs();
    252         fContext->flush();
    253 
    254         // we should have an unused plot now
    255         if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
    256             fStrike->addGlyphToAtlas(glyph, scaler)) {
    257             goto HAS_ATLAS;
    258         }
    259 
    260         if (NULL == glyph->fPath) {
    261             SkPath* path = SkNEW(SkPath);
    262             if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
    263                 // flag the glyph as being dead?
    264                 delete path;
    265                 return;
    266             }
    267             glyph->fPath = path;
    268         }
    269 
    270         GrContext::AutoMatrix am;
    271         SkMatrix ctm;
    272         ctm.setScale(fTextRatio, fTextRatio);
    273         ctm.postTranslate(sx, sy);
    274         GrPaint tmpPaint(fPaint);
    275         am.setPreConcat(fContext, ctm, &tmpPaint);
    276         GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
    277         fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
    278         return;
    279     }
    280 
    281 HAS_ATLAS:
    282     SkASSERT(glyph->fPlot);
    283     GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
    284     glyph->fPlot->setDrawToken(drawToken);
    285 
    286     GrTexture* texture = glyph->fPlot->texture();
    287     SkASSERT(texture);
    288 
    289     if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
    290         this->flushGlyphs();
    291         fCurrTexture = texture;
    292         fCurrTexture->ref();
    293     }
    294 
    295     if (NULL == fVertices) {
    296        // If we need to reserve vertices allow the draw target to suggest
    297         // a number of verts to reserve and whether to perform a flush.
    298         fMaxVertices = kMinRequestedVerts;
    299         fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
    300             SK_ARRAY_COUNT(gTextVertexAttribs));
    301         bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
    302         if (flush) {
    303             this->flushGlyphs();
    304             fContext->flush();
    305             fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
    306                 SK_ARRAY_COUNT(gTextVertexAttribs));
    307         }
    308         fMaxVertices = kDefaultRequestedVerts;
    309         // ignore return, no point in flushing again.
    310         fDrawTarget->geometryHints(&fMaxVertices, NULL);
    311 
    312         int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
    313         if (fMaxVertices < kMinRequestedVerts) {
    314             fMaxVertices = kDefaultRequestedVerts;
    315         } else if (fMaxVertices > maxQuadVertices) {
    316             // don't exceed the limit of the index buffer
    317             fMaxVertices = maxQuadVertices;
    318         }
    319         bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
    320                                                                0,
    321                                                                GrTCast<void**>(&fVertices),
    322                                                                NULL);
    323         GrAlwaysAssert(success);
    324         SkASSERT(2*sizeof(SkPoint) == fDrawTarget->getDrawState().getVertexSize());
    325     }
    326 
    327     SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
    328     SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
    329     SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
    330     SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
    331 
    332     SkScalar scale = fTextRatio;
    333     dx *= scale;
    334     dy *= scale;
    335     sx += dx;
    336     sy += dy;
    337     width *= scale;
    338     height *= scale;
    339 
    340     SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
    341     SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
    342     SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
    343     SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
    344 
    345     static const size_t kVertexSize = 2 * sizeof(SkPoint);
    346     fVertices[2*fCurrVertex].setRectFan(sx,
    347                                         sy,
    348                                         sx + width,
    349                                         sy + height,
    350                                         kVertexSize);
    351     fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
    352                                           SkFixedToFloat(texture->normalizeFixedY(ty)),
    353                                           SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
    354                                           SkFixedToFloat(texture->normalizeFixedY(ty + th)),
    355                                           kVertexSize);
    356     fCurrVertex += 4;
    357 }
    358 
    359 inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
    360     GrTextContext::init(paint, skPaint);
    361 
    362     fStrike = NULL;
    363 
    364     fCurrTexture = NULL;
    365     fCurrVertex = 0;
    366 
    367     fVertices = NULL;
    368     fMaxVertices = 0;
    369 
    370     if (fSkPaint.getTextSize() <= kSmallDFFontLimit) {
    371         fTextRatio = fSkPaint.getTextSize()/kSmallDFFontSize;
    372         fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
    373     } else if (fSkPaint.getTextSize() <= kMediumDFFontLimit) {
    374         fTextRatio = fSkPaint.getTextSize()/kMediumDFFontSize;
    375         fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
    376     } else {
    377         fTextRatio = fSkPaint.getTextSize()/kLargeDFFontSize;
    378         fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
    379     }
    380 
    381     fUseLCDText = fSkPaint.isLCDRenderText();
    382 
    383     fSkPaint.setLCDRenderText(false);
    384     fSkPaint.setAutohinted(false);
    385     fSkPaint.setHinting(SkPaint::kNormal_Hinting);
    386     fSkPaint.setSubpixelText(true);
    387 
    388 }
    389 
    390 inline void GrDistanceFieldTextContext::finish() {
    391     flushGlyphs();
    392 
    393     GrTextContext::finish();
    394 }
    395 
    396 static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
    397                                 const SkDeviceProperties& deviceProperties,
    398                                 GrTexture** gammaTexture) {
    399     if (NULL == *gammaTexture) {
    400         int width, height;
    401         size_t size;
    402 
    403 #ifdef SK_GAMMA_CONTRAST
    404         SkScalar contrast = SK_GAMMA_CONTRAST;
    405 #else
    406         SkScalar contrast = 0.5f;
    407 #endif
    408         SkScalar paintGamma = deviceProperties.fGamma;
    409         SkScalar deviceGamma = deviceProperties.fGamma;
    410 
    411         size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
    412                                                 &width, &height);
    413 
    414         SkAutoTArray<uint8_t> data((int)size);
    415         SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
    416 
    417         // TODO: Update this to use the cache rather than directly creating a texture.
    418         GrTextureDesc desc;
    419         desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
    420         desc.fWidth = width;
    421         desc.fHeight = height;
    422         desc.fConfig = kAlpha_8_GrPixelConfig;
    423 
    424         *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
    425         if (NULL == *gammaTexture) {
    426             return;
    427         }
    428 
    429         context->writeTexturePixels(*gammaTexture,
    430                                     0, 0, width, height,
    431                                     (*gammaTexture)->config(), data.get(), 0,
    432                                     GrContext::kDontFlush_PixelOpsFlag);
    433     }
    434 }
    435 
    436 void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
    437                                           const char text[], size_t byteLength,
    438                                           SkScalar x, SkScalar y) {
    439     SkASSERT(byteLength == 0 || text != NULL);
    440 
    441     // nothing to draw or can't draw
    442     if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/
    443         || fSkPaint.getRasterizer()) {
    444         return;
    445     }
    446 
    447     this->init(paint, skPaint);
    448 
    449     SkScalar sizeRatio = fTextRatio;
    450 
    451     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
    452 
    453     SkAutoGlyphCacheNoGamma    autoCache(fSkPaint, &fDeviceProperties, NULL);
    454     SkGlyphCache*              cache = autoCache.getCache();
    455     GrFontScaler*              fontScaler = GetGrFontScaler(cache);
    456 
    457     setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
    458 
    459     // need to measure first
    460     // TODO - generate positions and pre-load cache as well?
    461     const char* stop = text + byteLength;
    462     if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
    463         SkFixed    stopX = 0;
    464         SkFixed    stopY = 0;
    465 
    466         const char* textPtr = text;
    467         while (textPtr < stop) {
    468             // don't need x, y here, since all subpixel variants will have the
    469             // same advance
    470             const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
    471 
    472             stopX += glyph.fAdvanceX;
    473             stopY += glyph.fAdvanceY;
    474         }
    475         SkASSERT(textPtr == stop);
    476 
    477         SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio;
    478         SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio;
    479 
    480         if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
    481             alignX = SkScalarHalf(alignX);
    482             alignY = SkScalarHalf(alignY);
    483         }
    484 
    485         x -= alignX;
    486         y -= alignY;
    487     }
    488 
    489     SkFixed fx = SkScalarToFixed(x);
    490     SkFixed fy = SkScalarToFixed(y);
    491     SkFixed fixedScale = SkScalarToFixed(sizeRatio);
    492     while (text < stop) {
    493         const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
    494 
    495         if (glyph.fWidth) {
    496             this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
    497                                                 glyph.getSubXFixed(),
    498                                                 glyph.getSubYFixed()),
    499                                   fx,
    500                                   fy,
    501                                   fontScaler);
    502         }
    503 
    504         fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
    505         fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
    506     }
    507 
    508     this->finish();
    509 }
    510 
    511 void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
    512                                              const char text[], size_t byteLength,
    513                                              const SkScalar pos[], SkScalar constY,
    514                                              int scalarsPerPosition) {
    515 
    516     SkASSERT(byteLength == 0 || text != NULL);
    517     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
    518 
    519     // nothing to draw
    520     if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
    521         return;
    522     }
    523 
    524     this->init(paint, skPaint);
    525 
    526     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
    527 
    528     SkAutoGlyphCacheNoGamma    autoCache(fSkPaint, &fDeviceProperties, NULL);
    529     SkGlyphCache*              cache = autoCache.getCache();
    530     GrFontScaler*              fontScaler = GetGrFontScaler(cache);
    531 
    532     setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
    533 
    534     const char*        stop = text + byteLength;
    535 
    536     if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
    537         while (text < stop) {
    538             // the last 2 parameters are ignored
    539             const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
    540 
    541             if (glyph.fWidth) {
    542                 SkScalar x = pos[0];
    543                 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
    544 
    545                 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
    546                                                     glyph.getSubXFixed(),
    547                                                     glyph.getSubYFixed()),
    548                                       SkScalarToFixed(x),
    549                                       SkScalarToFixed(y),
    550                                       fontScaler);
    551             }
    552             pos += scalarsPerPosition;
    553         }
    554     } else {
    555         int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0;
    556         while (text < stop) {
    557             // the last 2 parameters are ignored
    558             const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
    559 
    560             if (glyph.fWidth) {
    561                 SkScalar x = pos[0];
    562                 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
    563 
    564                 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
    565                                                     glyph.getSubXFixed(),
    566                                                     glyph.getSubYFixed()),
    567                                       SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift),
    568                                       SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift),
    569                                       fontScaler);
    570             }
    571             pos += scalarsPerPosition;
    572         }
    573     }
    574 
    575     this->finish();
    576 }
    577