2 * Copyright 2013 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "GrDistanceFieldTextContext.h"
10 #include "GrBitmapTextContext.h"
11 #include "GrDrawTarget.h"
12 #include "GrDrawTargetCaps.h"
13 #include "GrFontScaler.h"
15 #include "GrIndexBuffer.h"
16 #include "GrStrokeInfo.h"
17 #include "GrTexturePriv.h"
18 #include "GrTextStrike.h"
19 #include "GrTextStrike_impl.h"
21 #include "SkAutoKern.h"
22 #include "SkColorFilter.h"
23 #include "SkDistanceFieldGen.h"
25 #include "SkGlyphCache.h"
26 #include "SkGpuDevice.h"
29 #include "SkStrokeRec.h"
30 #include "effects/GrDistanceFieldTextureEffect.h"
32 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
33 "Dump the contents of the font cache before every purge.");
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;
42 // position + texture coord
43 extern const GrVertexAttrib gTextVertexAttribs[] = {
44 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
45 {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kGeometryProcessor_GrVertexAttribBinding}
48 static const size_t kTextVASize = 2 * sizeof(SkPoint);
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}
57 static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
59 static const int kVerticesPerGlyph = 4;
60 static const int kIndicesPerGlyph = 6;
63 GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
64 const SkDeviceProperties& properties,
66 : GrTextContext(context, properties) {
67 #if SK_FORCE_DISTANCEFIELD_FONTS
68 fEnableDFRendering = true;
70 fEnableDFRendering = enable;
75 fEffectTextureUniqueID = SK_InvalidUniqueID;
76 fEffectColor = GrColor_ILLEGAL;
77 fEffectFlags = kInvalid_DistanceFieldEffectFlag;
81 fAllocVertexCount = 0;
82 fTotalVertexCount = 0;
85 fVertexBounds.setLargestInverted();
88 GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
89 const SkDeviceProperties& props,
91 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
92 (context, props, enable));
93 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
98 GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
100 SkSafeSetNull(fGammaTexture);
103 bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
104 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
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()) {
115 // TODO: add some stroking support
116 if (paint.getStyle() != SkPaint::kFill_Style) {
120 // TODO: choose an appropriate maximum scale for distance fields and
121 // enable perspective
122 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
129 inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
130 GrTextContext::init(paint, skPaint);
134 const SkMatrix& ctm = fContext->getMatrix();
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;
150 fAllocVertexCount = 0;
151 fTotalVertexCount = 0;
153 if (scaledTextSize <= kSmallDFFontLimit) {
154 fTextRatio = textSize / kSmallDFFontSize;
155 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
157 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF));
158 fPaint.setColor(GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF));
160 } else if (scaledTextSize <= kMediumDFFontLimit) {
161 fTextRatio = textSize / kMediumDFFontSize;
162 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
164 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00));
165 fPaint.setColor(GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF));
168 fTextRatio = textSize / kLargeDFFontSize;
169 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
171 fSkPaint.setColor(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
172 fPaint.setColor(GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF));
176 fUseLCDText = fSkPaint.isLCDRenderText();
178 fSkPaint.setLCDRenderText(false);
179 fSkPaint.setAutohinted(false);
180 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
181 fSkPaint.setSubpixelText(true);
184 static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
185 const SkDeviceProperties& deviceProperties,
186 GrTexture** gammaTexture) {
187 if (NULL == *gammaTexture) {
191 #ifdef SK_GAMMA_CONTRAST
192 SkScalar contrast = SK_GAMMA_CONTRAST;
194 SkScalar contrast = 0.5f;
196 SkScalar paintGamma = deviceProperties.gamma();
197 SkScalar deviceGamma = deviceProperties.gamma();
199 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
202 SkAutoTArray<uint8_t> data((int)size);
203 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
205 // TODO: Update this to use the cache rather than directly creating a texture.
207 desc.fFlags = kNone_GrSurfaceFlags;
209 desc.fHeight = height;
210 desc.fConfig = kAlpha_8_GrPixelConfig;
212 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
213 if (NULL == *gammaTexture) {
217 (*gammaTexture)->writePixels(0, 0, width, height,
218 (*gammaTexture)->config(), data.get(), 0,
219 GrContext::kDontFlush_PixelOpsFlag);
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);
229 if (text == NULL || byteLength == 0) {
233 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
234 SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL);
235 SkGlyphCache* cache = autoCache.getCache();
237 SkTArray<SkScalar> positions;
239 const char* textPtr = text;
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;
251 const char* stop = text + byteLength;
252 while (textPtr < stop) {
253 // don't need x, y here, since all subpixel variants will have the
255 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
257 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
258 positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
260 SkFixed height = glyph.fAdvanceY;
261 positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
266 SkASSERT(textPtr == stop);
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) {
280 SkPoint offset = SkPoint::Make(x, y);
282 this->drawPosText(paint, skPaint, text, byteLength, positions.begin(), 2, offset);
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) {
290 SkASSERT(byteLength == 0 || text != NULL);
291 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
294 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
298 this->init(paint, skPaint);
300 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
302 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
303 SkGlyphCache* cache = autoCache.getCache();
304 GrFontScaler* fontScaler = GetGrFontScaler(cache);
306 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
308 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
309 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
311 const char* stop = text + byteLength;
312 SkTArray<char> fallbackTxt;
313 SkTArray<SkScalar> fallbackPos;
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);
322 SkScalar x = offset.x() + pos[0];
323 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
325 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
326 glyph.getSubXFixed(),
327 glyph.getSubYFixed()),
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]);
337 pos += scalarsPerPosition;
340 SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
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);
348 SkScalar x = offset.x() + pos[0];
349 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
351 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
352 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
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]);
366 pos += scalarsPerPosition;
372 if (fallbackTxt.count() > 0) {
373 fFallbackTextContext->drawPosText(paint, skPaint, fallbackTxt.begin(), fallbackTxt.count(),
374 fallbackPos.begin(), scalarsPerPosition, offset);
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);
385 static void* alloc_vertices(GrDrawTarget* drawTarget, int numVertices, bool useColorVerts) {
386 if (numVertices <= 0) {
392 drawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
393 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
395 drawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
396 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
398 void* vertices = NULL;
399 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
403 GrAlwaysAssert(success);
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);
411 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
412 const SkMatrix& ctm = fContext->getMatrix();
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;
423 // see if we need to create a new effect
424 if (textureUniqueID != fEffectTextureUniqueID ||
425 filteredColor != fEffectColor ||
426 flags != fEffectFlags) {
428 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
429 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
436 #ifdef SK_GAMMA_APPLY_TO_A8
437 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
439 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
446 fCachedGeometryProcessor.reset(GrDistanceFieldNoGammaTextureEffect::Create(fCurrTexture,
450 fEffectTextureUniqueID = textureUniqueID;
451 fEffectColor = filteredColor;
452 fEffectFlags = flags;
457 // Returns true if this method handled the glyph, false if needs to be passed to fallback
459 bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
460 SkScalar sx, SkScalar sy,
461 GrFontScaler* scaler) {
462 if (NULL == fDrawTarget) {
466 if (NULL == fStrike) {
467 fStrike = fContext->getFontCache()->getStrike(scaler, true);
470 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
471 if (NULL == glyph || glyph->fBounds.isEmpty()) {
475 // fallback to color glyph support
476 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
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);
485 SkScalar scale = fTextRatio;
492 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
494 // check if we clipped out
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
506 if (NULL == glyph->fPlot) {
507 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
508 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
512 // try to clear out an unused plot before we flush
513 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
514 fStrike->addGlyphToAtlas(glyph, scaler)) {
518 if (c_DumpFontCache) {
520 fContext->getFontCache()->dump();
524 // before we purge the cache, we must flush any accumulated draws
528 // we should have an unused plot now
529 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
530 fStrike->addGlyphToAtlas(glyph, scaler)) {
535 if (NULL == glyph->fPath) {
536 SkPath* path = SkNEW(SkPath);
537 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
538 // flag the glyph as being dead?
545 // flush any accumulated draws before drawing this glyph as a path.
548 GrContext::AutoMatrix am;
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);
557 // remove this glyph from the vertices we need to allocate
558 fTotalVertexCount -= kVerticesPerGlyph;
563 SkASSERT(glyph->fPlot);
564 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
565 glyph->fPlot->setDrawToken(drawToken);
567 GrTexture* texture = glyph->fPlot->texture();
570 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) {
572 fCurrTexture = texture;
576 bool useColorVerts = !fUseLCDText;
578 if (NULL == fVertices) {
579 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
580 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
581 fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, useColorVerts);
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);
589 fVertexBounds.joinNonEmptyArg(glyphRect);
591 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint))
592 : (2 * sizeof(SkPoint) + sizeof(GrColor));
594 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
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,
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)),
610 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
611 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
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);
626 void GrDistanceFieldTextContext::flush() {
627 if (NULL == fDrawTarget) {
631 GrDrawState* drawState = fDrawTarget->drawState();
632 GrDrawState::AutoRestoreEffects are(drawState);
634 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
636 if (fCurrVertex > 0) {
637 // setup our sampler state for our text texture/atlas
638 SkASSERT(SkIsAlign4(fCurrVertex));
640 // get our current color
641 SkColor filteredColor;
642 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
644 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
646 filteredColor = fSkPaint.getColor();
648 this->setupCoverageEffect(filteredColor);
650 // Effects could be stored with one of the cache objects (atlas?)
651 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
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");
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());
668 drawState->setColor(SkColorSetARGB(a, a, a, a));
670 drawState->setBlendConstant(colorNoPreMul);
671 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
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());
678 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
679 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
680 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
682 kVerticesPerGlyph, kIndicesPerGlyph, &fVertexBounds);
683 fDrawTarget->resetVertexSource();
685 fTotalVertexCount -= fCurrVertex;
687 SkSafeSetNull(fCurrTexture);
688 fVertexBounds.setLargestInverted();
692 inline void GrDistanceFieldTextContext::finish() {
694 fTotalVertexCount = 0;
696 GrTextContext::finish();