1/*
2 * Copyright 2015 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 "GrTextUtils.h"
9#include "GrAtlasGlyphCache.h"
10#include "GrAtlasTextBlob.h"
11#include "GrBlurUtils.h"
12#include "GrCaps.h"
13#include "GrContext.h"
14#include "GrRenderTargetContext.h"
15#include "GrSurfaceContextPriv.h"
16#include "SkDistanceFieldGen.h"
17#include "SkDrawFilter.h"
18#include "SkDrawProcs.h"
19#include "SkFindAndPlaceGlyph.h"
20#include "SkGlyphCache.h"
21#include "SkGr.h"
22#include "SkPaint.h"
23#include "SkRect.h"
24#include "SkTextBlobRunIterator.h"
25#include "SkTextMapStateProc.h"
26#include "SkTextToPathIter.h"
27
28namespace {
29static const int kMinDFFontSize = 18;
30static const int kSmallDFFontSize = 32;
31static const int kSmallDFFontLimit = 32;
32static const int kMediumDFFontSize = 72;
33static const int kMediumDFFontLimit = 72;
34static const int kLargeDFFontSize = 162;
35#ifdef SK_BUILD_FOR_ANDROID
36static const int kLargeDFFontLimit = 384;
37#else
38static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
39#endif
40};
41
42bool GrTextUtils::Paint::toGrPaint(GrMaskFormat maskFormat, GrRenderTargetContext* rtc,
43                                   const SkMatrix& viewMatrix, GrPaint* grPaint) const {
44    // TODO: this is the last use of GrSurfaceContextPriv
45    GrContext* context = rtc->surfPriv().getContext();
46    if (kARGB_GrMaskFormat == maskFormat) {
47        return SkPaintToGrPaintWithPrimitiveColor(context, rtc, this->skPaint(), grPaint);
48    } else {
49        return SkPaintToGrPaint(context, rtc, this->skPaint(), viewMatrix, grPaint);
50    }
51}
52
53bool GrTextUtils::RunPaint::modifyForRun(const SkTextBlobRunIterator& run) {
54    if (!fModifiedPaint.isValid()) {
55        fModifiedPaint.init(fOriginalPaint->skPaint());
56        fPaint = fModifiedPaint.get();
57    } else if (fFilter) {
58        // We have to reset before applying the run because the filter could have arbitrary
59        // changed the paint.
60        *fModifiedPaint.get() = fOriginalPaint->skPaint();
61    }
62    run.applyFontToPaint(fModifiedPaint.get());
63
64    if (fFilter) {
65        if (!fFilter->filter(fModifiedPaint.get(), SkDrawFilter::kText_Type)) {
66            // A false return from filter() means we should abort the current draw.
67            return false;
68        }
69        // The draw filter could have changed either the paint color or color filter.
70        this->initFilteredColor();
71    }
72    fModifiedPaint.get()->setFlags(FilterTextFlags(fProps, *fModifiedPaint.get()));
73    return true;
74}
75
76void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
77                              const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
78                              uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
79                              const char text[], size_t byteLength, SkScalar x, SkScalar y) {
80    SkASSERT(byteLength == 0 || text != nullptr);
81
82    // nothing to draw
83    if (text == nullptr || byteLength == 0) {
84        return;
85    }
86
87    // Ensure the blob is set for bitmaptext
88    blob->setHasBitmap();
89
90    GrAtlasTextStrike* currStrike = nullptr;
91
92    SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
93    SkFindAndPlaceGlyph::ProcessText(
94        paint.skPaint().getTextEncoding(), text, byteLength,
95        {x, y}, viewMatrix, paint.skPaint().getTextAlign(),
96        cache,
97        [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
98             position += rounding;
99             BmpAppendGlyph(
100                 blob, runIndex, fontCache, &currStrike, glyph,
101                 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
102                 paint.filteredPremulGrColor(), cache);
103        }
104    );
105
106    SkGlyphCache::AttachCache(cache);
107}
108
109void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
110                                 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
111                                 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
112                                 const char text[], size_t byteLength, const SkScalar pos[],
113                                 int scalarsPerPosition, const SkPoint& offset) {
114    SkASSERT(byteLength == 0 || text != nullptr);
115    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
116
117    // nothing to draw
118    if (text == nullptr || byteLength == 0) {
119        return;
120    }
121
122    // Ensure the blob is set for bitmaptext
123    blob->setHasBitmap();
124
125    GrAtlasTextStrike* currStrike = nullptr;
126
127    SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
128
129    SkFindAndPlaceGlyph::ProcessPosText(
130        paint.skPaint().getTextEncoding(), text, byteLength,
131        offset, viewMatrix, pos, scalarsPerPosition,
132        paint.skPaint().getTextAlign(), cache,
133        [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
134            position += rounding;
135            BmpAppendGlyph(
136                blob, runIndex, fontCache, &currStrike, glyph,
137                SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
138                paint.filteredPremulGrColor(), cache);
139        }
140    );
141
142    SkGlyphCache::AttachCache(cache);
143}
144
145void GrTextUtils::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
146                                 GrAtlasGlyphCache* fontCache,
147                                 GrAtlasTextStrike** strike, const SkGlyph& skGlyph,
148                                 int vx, int vy, GrColor color, SkGlyphCache* cache) {
149    if (!*strike) {
150        *strike = fontCache->getStrike(cache);
151    }
152
153    GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
154                                         skGlyph.getSubXFixed(),
155                                         skGlyph.getSubYFixed(),
156                                         GrGlyph::kCoverage_MaskStyle);
157    GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, cache);
158    if (!glyph) {
159        return;
160    }
161
162    int x = vx + glyph->fBounds.fLeft;
163    int y = vy + glyph->fBounds.fTop;
164
165    // keep them as ints until we've done the clip-test
166    int width = glyph->fBounds.width();
167    int height = glyph->fBounds.height();
168
169    SkRect r;
170    r.fLeft = SkIntToScalar(x);
171    r.fTop = SkIntToScalar(y);
172    r.fRight = r.fLeft + SkIntToScalar(width);
173    r.fBottom = r.fTop + SkIntToScalar(height);
174
175    blob->appendGlyph(runIndex, r, color, *strike, glyph, cache, skGlyph,
176                      SkIntToScalar(vx), SkIntToScalar(vy), 1.0f, true);
177}
178
179bool GrTextUtils::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
180                                          const SkSurfaceProps& props, const GrShaderCaps& caps) {
181    // TODO: support perspective (need getMaxScale replacement)
182    if (viewMatrix.hasPerspective()) {
183        return false;
184    }
185
186    SkScalar maxScale = viewMatrix.getMaxScale();
187    SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
188    // Hinted text looks far better at small resolutions
189    // Scaling up beyond 2x yields undesireable artifacts
190    if (scaledTextSize < kMinDFFontSize ||
191        scaledTextSize > kLargeDFFontLimit) {
192        return false;
193    }
194
195    bool useDFT = props.isUseDeviceIndependentFonts();
196#if SK_FORCE_DISTANCE_FIELD_TEXT
197    useDFT = true;
198#endif
199
200    if (!useDFT && scaledTextSize < kLargeDFFontSize) {
201        return false;
202    }
203
204    // rasterizers and mask filters modify alpha, which doesn't
205    // translate well to distance
206    if (skPaint.getRasterizer() || skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
207        return false;
208    }
209
210    // TODO: add some stroking support
211    if (skPaint.getStyle() != SkPaint::kFill_Style) {
212        return false;
213    }
214
215    return true;
216}
217
218void GrTextUtils::InitDistanceFieldPaint(GrAtlasTextBlob* blob,
219                                         SkPaint* skPaint,
220                                         SkScalar* textRatio,
221                                         const SkMatrix& viewMatrix) {
222    // getMaxScale doesn't support perspective, so neither do we at the moment
223    SkASSERT(!viewMatrix.hasPerspective());
224    SkScalar maxScale = viewMatrix.getMaxScale();
225    SkScalar textSize = skPaint->getTextSize();
226    SkScalar scaledTextSize = textSize;
227    // if we have non-unity scale, we need to choose our base text size
228    // based on the SkPaint's text size multiplied by the max scale factor
229    // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
230    if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
231        scaledTextSize *= maxScale;
232    }
233
234    // We have three sizes of distance field text, and within each size 'bucket' there is a floor
235    // and ceiling.  A scale outside of this range would require regenerating the distance fields
236    SkScalar dfMaskScaleFloor;
237    SkScalar dfMaskScaleCeil;
238    if (scaledTextSize <= kSmallDFFontLimit) {
239        dfMaskScaleFloor = kMinDFFontSize;
240        dfMaskScaleCeil = kSmallDFFontLimit;
241        *textRatio = textSize / kSmallDFFontSize;
242        skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
243    } else if (scaledTextSize <= kMediumDFFontLimit) {
244        dfMaskScaleFloor = kSmallDFFontLimit;
245        dfMaskScaleCeil = kMediumDFFontLimit;
246        *textRatio = textSize / kMediumDFFontSize;
247        skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
248    } else {
249        dfMaskScaleFloor = kMediumDFFontLimit;
250        dfMaskScaleCeil = kLargeDFFontLimit;
251        *textRatio = textSize / kLargeDFFontSize;
252        skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
253    }
254
255    // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
256    // minMaxScale to make regeneration decisions.  Specifically, we want the maximum minimum scale
257    // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
258    // tolerate before we'd have to move to a large mip size.  When we actually test these values
259    // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
260    // against these values to decide if we can reuse or not(ie, will a given scale change our mip
261    // level)
262    SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
263    blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
264
265    skPaint->setLCDRenderText(false);
266    skPaint->setAutohinted(false);
267    skPaint->setHinting(SkPaint::kNormal_Hinting);
268    skPaint->setSubpixelText(true);
269}
270
271void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex,
272                             GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
273                             const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
274                             const SkMatrix& viewMatrix,
275                             const char text[], size_t byteLength,
276                             SkScalar x, SkScalar y) {
277    SkASSERT(byteLength == 0 || text != nullptr);
278
279    // nothing to draw
280    if (text == nullptr || byteLength == 0) {
281        return;
282    }
283
284    const SkPaint& skPaint = paint.skPaint();
285    SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(),
286                                                                        skPaint.isDevKernText(),
287                                                                        true);
288    SkAutoDescriptor desc;
289    SkScalerContextEffects effects;
290    // We apply the fake-gamma by altering the distance in the shader, so we ignore the
291    // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
292    skPaint.getScalerContextDescriptor(&effects, &desc, props, SkPaint::kNone_ScalerContextFlags,
293                                       nullptr);
294    SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(), effects,
295                                                             desc.getDesc());
296
297    SkTArray<SkScalar> positions;
298
299    const char* textPtr = text;
300    SkScalar stopX = 0;
301    SkScalar stopY = 0;
302    SkScalar origin = 0;
303    switch (skPaint.getTextAlign()) {
304        case SkPaint::kRight_Align: origin = SK_Scalar1; break;
305        case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
306        case SkPaint::kLeft_Align: origin = 0; break;
307    }
308
309    SkAutoKern autokern;
310    const char* stop = text + byteLength;
311    while (textPtr < stop) {
312        // don't need x, y here, since all subpixel variants will have the
313        // same advance
314        const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
315
316        SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
317        positions.push_back(stopX + origin * width);
318
319        SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
320        positions.push_back(stopY + origin * height);
321
322        stopX += width;
323        stopY += height;
324    }
325    SkASSERT(textPtr == stop);
326
327    SkGlyphCache::AttachCache(origPaintCache);
328
329    // now adjust starting point depending on alignment
330    SkScalar alignX = stopX;
331    SkScalar alignY = stopY;
332    if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
333        alignX = SkScalarHalf(alignX);
334        alignY = SkScalarHalf(alignY);
335    } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
336        alignX = 0;
337        alignY = 0;
338    }
339    x -= alignX;
340    y -= alignY;
341    SkPoint offset = SkPoint::Make(x, y);
342
343    DrawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
344                  byteLength, positions.begin(), 2, offset);
345}
346
347void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
348                                const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
349                                uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
350                                const char text[], size_t byteLength, const SkScalar pos[],
351                                int scalarsPerPosition, const SkPoint& offset) {
352    SkASSERT(byteLength == 0 || text != nullptr);
353    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
354
355    // nothing to draw
356    if (text == nullptr || byteLength == 0) {
357        return;
358    }
359
360    SkTDArray<char> fallbackTxt;
361    SkTDArray<SkScalar> fallbackPos;
362
363    // Setup distance field paint and text ratio
364    SkScalar textRatio;
365    SkPaint dfPaint(paint);
366    GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
367    blob->setHasDistanceField();
368    blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText());
369
370    GrAtlasTextStrike* currStrike = nullptr;
371
372    // We apply the fake-gamma by altering the distance in the shader, so we ignore the
373    // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
374    SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::kNone_ScalerContextFlags,
375                                           dfPaint, nullptr);
376    SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(),
377                                                                        dfPaint.isDevKernText(),
378                                                                        true);
379
380    const char* stop = text + byteLength;
381
382    if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) {
383        while (text < stop) {
384            const char* lastText = text;
385            // the last 2 parameters are ignored
386            const SkGlyph& glyph = glyphCacheProc(cache, &text);
387
388            if (glyph.fWidth) {
389                SkScalar x = offset.x() + pos[0];
390                SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
391
392                if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y,
393                                   paint.filteredPremulGrColor(), cache, textRatio, viewMatrix)) {
394                    // couldn't append, send to fallback
395                    fallbackTxt.append(SkToInt(text-lastText), lastText);
396                    *fallbackPos.append() = pos[0];
397                    if (2 == scalarsPerPosition) {
398                        *fallbackPos.append() = pos[1];
399                    }
400                }
401            }
402            pos += scalarsPerPosition;
403        }
404    } else {
405        SkScalar alignMul = SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf
406                                                                             : SK_Scalar1;
407        while (text < stop) {
408            const char* lastText = text;
409            // the last 2 parameters are ignored
410            const SkGlyph& glyph = glyphCacheProc(cache, &text);
411
412            if (glyph.fWidth) {
413                SkScalar x = offset.x() + pos[0];
414                SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
415
416                SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
417                SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
418
419                if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
420                                   y - advanceY, paint.filteredPremulGrColor(), cache, textRatio,
421                                   viewMatrix)) {
422                    // couldn't append, send to fallback
423                    fallbackTxt.append(SkToInt(text-lastText), lastText);
424                    *fallbackPos.append() = pos[0];
425                    if (2 == scalarsPerPosition) {
426                        *fallbackPos.append() = pos[1];
427                    }
428                }
429            }
430            pos += scalarsPerPosition;
431        }
432    }
433
434    SkGlyphCache::AttachCache(cache);
435    if (fallbackTxt.count()) {
436        blob->initOverride(runIndex);
437        GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags,
438                                    viewMatrix, fallbackTxt.begin(), fallbackTxt.count(),
439                                    fallbackPos.begin(), scalarsPerPosition, offset);
440    }
441}
442
443bool GrTextUtils::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* cache,
444                                GrAtlasTextStrike** strike, const SkGlyph& skGlyph,
445                                SkScalar sx, SkScalar sy, GrColor color,
446                                SkGlyphCache* glyphCache,
447                                SkScalar textRatio, const SkMatrix& viewMatrix) {
448    if (!*strike) {
449        *strike = cache->getStrike(glyphCache);
450    }
451
452    GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
453                                         skGlyph.getSubXFixed(),
454                                         skGlyph.getSubYFixed(),
455                                         GrGlyph::kDistance_MaskStyle);
456    GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
457    if (!glyph) {
458        return true;
459    }
460
461    // fallback to color glyph support
462    if (kA8_GrMaskFormat != glyph->fMaskFormat) {
463        return false;
464    }
465
466    SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
467    SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
468    SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
469    SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
470
471    SkScalar scale = textRatio;
472    dx *= scale;
473    dy *= scale;
474    width *= scale;
475    height *= scale;
476    sx += dx;
477    sy += dy;
478    SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
479
480    blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph,
481                      sx - dx, sy - dy, scale, false);
482    return true;
483}
484
485void GrTextUtils::DrawTextAsPath(GrContext* context, GrRenderTargetContext* rtc, const GrClip& clip,
486                                 const SkPaint& paint, const SkMatrix& viewMatrix,
487                                 const char text[], size_t byteLength, SkScalar x, SkScalar y,
488                                 const SkIRect& clipBounds) {
489    SkTextToPathIter iter(text, byteLength, paint, true);
490
491    SkMatrix    matrix;
492    matrix.setScale(iter.getPathScale(), iter.getPathScale());
493    matrix.postTranslate(x, y);
494
495    const SkPath* iterPath;
496    SkScalar xpos, prevXPos = 0;
497
498    while (iter.next(&iterPath, &xpos)) {
499        matrix.postTranslate(xpos - prevXPos, 0);
500        if (iterPath) {
501            const SkPaint& pnt = iter.getPaint();
502            GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, *iterPath,
503                                                pnt, viewMatrix, &matrix, clipBounds, false);
504        }
505        prevXPos = xpos;
506    }
507}
508
509void GrTextUtils::DrawPosTextAsPath(GrContext* context,
510                                    GrRenderTargetContext* rtc,
511                                    const SkSurfaceProps& props,
512                                    const GrClip& clip,
513                                    const SkPaint& origPaint, const SkMatrix& viewMatrix,
514                                    const char text[], size_t byteLength,
515                                    const SkScalar pos[], int scalarsPerPosition,
516                                    const SkPoint& offset, const SkIRect& clipBounds) {
517    // setup our std paint, in hopes of getting hits in the cache
518    SkPaint paint(origPaint);
519    SkScalar matrixScale = paint.setupForAsPaths();
520
521    SkMatrix matrix;
522    matrix.setScale(matrixScale, matrixScale);
523
524    // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
525    paint.setStyle(SkPaint::kFill_Style);
526    paint.setPathEffect(nullptr);
527
528    SkPaint::GlyphCacheProc    glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
529                                                                           paint.isDevKernText(),
530                                                                           true);
531    SkAutoGlyphCache           autoCache(paint, &props, nullptr);
532    SkGlyphCache*              cache = autoCache.getCache();
533
534    const char*        stop = text + byteLength;
535    SkTextAlignProc    alignProc(paint.getTextAlign());
536    SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
537
538    // Now restore the original settings, so we "draw" with whatever style/stroking.
539    paint.setStyle(origPaint.getStyle());
540    paint.setPathEffect(origPaint.refPathEffect());
541
542    while (text < stop) {
543        const SkGlyph& glyph = glyphCacheProc(cache, &text);
544        if (glyph.fWidth) {
545            const SkPath* path = cache->findPath(glyph);
546            if (path) {
547                SkPoint tmsLoc;
548                tmsProc(pos, &tmsLoc);
549                SkPoint loc;
550                alignProc(tmsLoc, glyph, &loc);
551
552                matrix[SkMatrix::kMTransX] = loc.fX;
553                matrix[SkMatrix::kMTransY] = loc.fY;
554                GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, *path, paint,
555                                                    viewMatrix, &matrix, clipBounds, false);
556            }
557        }
558        pos += scalarsPerPosition;
559    }
560}
561
562bool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) {
563    return paint.getMaskFilter() ||
564           paint.getRasterizer() ||
565           paint.getPathEffect() ||
566           paint.isFakeBoldText() ||
567           paint.getStyle() != SkPaint::kFill_Style;
568}
569
570uint32_t GrTextUtils::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
571    uint32_t flags = paint.getFlags();
572
573    if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
574        return flags;
575    }
576
577    if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
578        flags &= ~SkPaint::kLCDRenderText_Flag;
579        flags |= SkPaint::kGenA8FromLCD_Flag;
580    }
581
582    return flags;
583}
584