Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / gpu / GrDistanceFieldTextContext.cpp
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 }