Home | History | Annotate | Download | only in text
      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 
     28 namespace {
     29 static const int kMinDFFontSize = 18;
     30 static const int kSmallDFFontSize = 32;
     31 static const int kSmallDFFontLimit = 32;
     32 static const int kMediumDFFontSize = 72;
     33 static const int kMediumDFFontLimit = 72;
     34 static const int kLargeDFFontSize = 162;
     35 #ifdef SK_BUILD_FOR_ANDROID
     36 static const int kLargeDFFontLimit = 384;
     37 #else
     38 static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
     39 #endif
     40 };
     41 
     42 bool 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 
     53 bool 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 
     76 void 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 
    109 void 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 
    145 void 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 
    179 bool 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 
    218 void 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 
    271 void 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 
    347 void 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 
    443 bool 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 
    485 void 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 
    509 void 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 
    562 bool 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 
    570 uint32_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