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