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