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