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