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
28SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
29                "Dump the contents of the font cache before every purge.");
30
31static const int kSmallDFFontSize = 32;
32static const int kSmallDFFontLimit = 32;
33static const int kMediumDFFontSize = 64;
34static const int kMediumDFFontLimit = 64;
35static const int kLargeDFFontSize = 128;
36
37namespace {
38// position + texture coord
39extern const GrVertexAttrib gTextVertexAttribs[] = {
40    {kVec2f_GrVertexAttribType, 0,                kPosition_GrVertexAttribBinding},
41    {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kGeometryProcessor_GrVertexAttribBinding}
42};
43
44static const size_t kTextVASize = 2 * sizeof(SkPoint);
45
46// position + color + texture coord
47extern 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
53static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
54
55};
56
57GrDistanceFieldTextContext::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
81GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
82    this->flushGlyphs();
83    SkSafeSetNull(fGammaTexture);
84}
85
86bool 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
115static 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
122void 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
174void 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
240void 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
327HAS_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
446inline 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
489inline void GrDistanceFieldTextContext::finish() {
490    this->flushGlyphs();
491
492    GrTextContext::finish();
493}
494
495static 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
535void 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
610void 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