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 #include "GrAtlasTextContext.h"
      8 #include "GrContext.h"
      9 #include "GrContextPriv.h"
     10 #include "GrTextBlobCache.h"
     11 #include "SkDistanceFieldGen.h"
     12 #include "SkDraw.h"
     13 #include "SkDrawFilter.h"
     14 #include "SkFindAndPlaceGlyph.h"
     15 #include "SkGr.h"
     16 #include "SkGraphics.h"
     17 #include "SkMakeUnique.h"
     18 #include "SkMaskFilterBase.h"
     19 #include "ops/GrMeshDrawOp.h"
     20 
     21 // DF sizes and thresholds for usage of the small and medium sizes. For example, above
     22 // kSmallDFFontLimit we will use the medium size. The large size is used up until the size at
     23 // which we switch over to drawing as paths as controlled by Options.
     24 static const int kSmallDFFontSize = 32;
     25 static const int kSmallDFFontLimit = 32;
     26 static const int kMediumDFFontSize = 72;
     27 static const int kMediumDFFontLimit = 72;
     28 static const int kLargeDFFontSize = 162;
     29 
     30 static const int kDefaultMinDistanceFieldFontSize = 18;
     31 #ifdef SK_BUILD_FOR_ANDROID
     32 static const int kDefaultMaxDistanceFieldFontSize = 384;
     33 #else
     34 static const int kDefaultMaxDistanceFieldFontSize = 2 * kLargeDFFontSize;
     35 #endif
     36 
     37 GrAtlasTextContext::GrAtlasTextContext(const Options& options)
     38         : fDistanceAdjustTable(new GrDistanceFieldAdjustTable) {
     39     fMaxDistanceFieldFontSize = options.fMaxDistanceFieldFontSize < 0.f
     40                                         ? kDefaultMaxDistanceFieldFontSize
     41                                         : options.fMaxDistanceFieldFontSize;
     42     fMinDistanceFieldFontSize = options.fMinDistanceFieldFontSize < 0.f
     43                                         ? kDefaultMinDistanceFieldFontSize
     44                                         : options.fMinDistanceFieldFontSize;
     45     fDistanceFieldVerticesAlwaysHaveW = options.fDistanceFieldVerticesAlwaysHaveW;
     46 }
     47 
     48 std::unique_ptr<GrAtlasTextContext> GrAtlasTextContext::Make(const Options& options) {
     49     return std::unique_ptr<GrAtlasTextContext>(new GrAtlasTextContext(options));
     50 }
     51 
     52 bool GrAtlasTextContext::canDraw(const GrAtlasGlyphCache* fontCache,
     53                                  const SkPaint& skPaint,
     54                                  const SkMatrix& viewMatrix,
     55                                  const SkSurfaceProps& props,
     56                                  const GrShaderCaps& shaderCaps) {
     57     return this->canDrawAsDistanceFields(skPaint, viewMatrix, props, shaderCaps) ||
     58            !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix, fontCache->getGlyphSizeLimit());
     59 }
     60 
     61 SkColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
     62     SkColor canonicalColor = paint.computeLuminanceColor();
     63     if (lcd) {
     64         // This is the correct computation, but there are tons of cases where LCD can be overridden.
     65         // For now we just regenerate if any run in a textblob has LCD.
     66         // TODO figure out where all of these overrides are and see if we can incorporate that logic
     67         // at a higher level *OR* use sRGB
     68         SkASSERT(false);
     69         //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
     70     } else {
     71         // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
     72         // gamma corrected masks anyways, nor color
     73         U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
     74                                        SkColorGetG(canonicalColor),
     75                                        SkColorGetB(canonicalColor));
     76         // reduce to our finite number of bits
     77         canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
     78     }
     79     return canonicalColor;
     80 }
     81 
     82 SkScalerContextFlags GrAtlasTextContext::ComputeScalerContextFlags(
     83         const GrColorSpaceInfo& colorSpaceInfo) {
     84     // If we're doing gamma-correct rendering, then we can disable the gamma hacks.
     85     // Otherwise, leave them on. In either case, we still want the contrast boost:
     86     if (colorSpaceInfo.isGammaCorrect()) {
     87         return SkScalerContextFlags::kBoostContrast;
     88     } else {
     89         return SkScalerContextFlags::kFakeGammaAndBoostContrast;
     90     }
     91 }
     92 
     93 // TODO if this function ever shows up in profiling, then we can compute this value when the
     94 // textblob is being built and cache it.  However, for the time being textblobs mostly only have 1
     95 // run so this is not a big deal to compute here.
     96 bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
     97     SkTextBlobRunIterator it(blob);
     98     for (; !it.done(); it.next()) {
     99         if (it.isLCD()) {
    100             return true;
    101         }
    102     }
    103     return false;
    104 }
    105 
    106 void GrAtlasTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* target,
    107                                       const GrClip& clip, const SkPaint& skPaint,
    108                                       const SkMatrix& viewMatrix, const SkSurfaceProps& props,
    109                                       const SkTextBlob* blob, SkScalar x, SkScalar y,
    110                                       SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
    111     // If we have been abandoned, then don't draw
    112     if (context->abandoned()) {
    113         return;
    114     }
    115 
    116     sk_sp<GrAtlasTextBlob> cacheBlob;
    117     SkMaskFilterBase::BlurRec blurRec;
    118     GrAtlasTextBlob::Key key;
    119     // It might be worth caching these things, but its not clear at this time
    120     // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
    121     const SkMaskFilter* mf = skPaint.getMaskFilter();
    122     bool canCache = !(skPaint.getPathEffect() ||
    123                       (mf && !as_MFB(mf)->asABlur(&blurRec)) ||
    124                       drawFilter);
    125     SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo());
    126 
    127     auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache();
    128     GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache();
    129 
    130     if (canCache) {
    131         bool hasLCD = HasLCD(blob);
    132 
    133         // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
    134         SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
    135                                                  kUnknown_SkPixelGeometry;
    136 
    137         // TODO we want to figure out a way to be able to use the canonical color on LCD text,
    138         // see the note on ComputeCanonicalColor above.  We pick a dummy value for LCD text to
    139         // ensure we always match the same key
    140         GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
    141                                           ComputeCanonicalColor(skPaint, hasLCD);
    142 
    143         key.fPixelGeometry = pixelGeometry;
    144         key.fUniqueID = blob->uniqueID();
    145         key.fStyle = skPaint.getStyle();
    146         key.fHasBlur = SkToBool(mf);
    147         key.fCanonicalColor = canonicalColor;
    148         key.fScalerContextFlags = scalerContextFlags;
    149         cacheBlob = textBlobCache->find(key);
    150     }
    151 
    152     GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
    153     if (cacheBlob) {
    154         if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, x, y)) {
    155             // We have to remake the blob because changes may invalidate our masks.
    156             // TODO we could probably get away reuse most of the time if the pointer is unique,
    157             // but we'd have to clear the subrun information
    158             textBlobCache->remove(cacheBlob.get());
    159             cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
    160             this->regenerateTextBlob(cacheBlob.get(), atlasGlyphCache,
    161                                      *context->caps()->shaderCaps(), paint, scalerContextFlags,
    162                                      viewMatrix, props, blob, x, y, drawFilter);
    163         } else {
    164             textBlobCache->makeMRU(cacheBlob.get());
    165 
    166             if (CACHE_SANITY_CHECK) {
    167                 int glyphCount = 0;
    168                 int runCount = 0;
    169                 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
    170                 sk_sp<GrAtlasTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount));
    171                 sanityBlob->setupKey(key, blurRec, skPaint);
    172                 this->regenerateTextBlob(sanityBlob.get(), atlasGlyphCache,
    173                                          *context->caps()->shaderCaps(), paint, scalerContextFlags,
    174                                          viewMatrix, props, blob, x, y, drawFilter);
    175                 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
    176             }
    177         }
    178     } else {
    179         if (canCache) {
    180             cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
    181         } else {
    182             cacheBlob = textBlobCache->makeBlob(blob);
    183         }
    184         this->regenerateTextBlob(cacheBlob.get(), atlasGlyphCache,
    185                                  *context->caps()->shaderCaps(), paint, scalerContextFlags,
    186                                  viewMatrix, props, blob, x, y, drawFilter);
    187     }
    188 
    189     cacheBlob->flushCached(atlasGlyphCache, target, blob, props, fDistanceAdjustTable.get(), paint,
    190                            drawFilter, clip, viewMatrix, clipBounds, x, y);
    191 }
    192 
    193 void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
    194                                             GrAtlasGlyphCache* fontCache,
    195                                             const GrShaderCaps& shaderCaps,
    196                                             const GrTextUtils::Paint& paint,
    197                                             SkScalerContextFlags scalerContextFlags,
    198                                             const SkMatrix& viewMatrix,
    199                                             const SkSurfaceProps& props, const SkTextBlob* blob,
    200                                             SkScalar x, SkScalar y,
    201                                             SkDrawFilter* drawFilter) const {
    202     cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, x, y);
    203 
    204     // Regenerate textblob
    205     SkTextBlobRunIterator it(blob);
    206     GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
    207     for (int run = 0; !it.done(); it.next(), run++) {
    208         int glyphCount = it.glyphCount();
    209         size_t textLen = glyphCount * sizeof(uint16_t);
    210         const SkPoint& offset = it.offset();
    211         cacheBlob->push_back_run(run);
    212         if (!runPaint.modifyForRun(it)) {
    213             continue;
    214         }
    215         if (this->canDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) {
    216             switch (it.positioning()) {
    217                 case SkTextBlob::kDefault_Positioning: {
    218                     this->drawDFText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
    219                                      viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(),
    220                                      y + offset.y());
    221                     break;
    222                 }
    223                 case SkTextBlob::kHorizontal_Positioning: {
    224                     SkPoint dfOffset = SkPoint::Make(x, y + offset.y());
    225                     this->drawDFPosText(cacheBlob, run, fontCache, props, runPaint,
    226                                         scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
    227                                         textLen, it.pos(), 1, dfOffset);
    228                     break;
    229                 }
    230                 case SkTextBlob::kFull_Positioning: {
    231                     SkPoint dfOffset = SkPoint::Make(x, y);
    232                     this->drawDFPosText(cacheBlob, run, fontCache, props, runPaint,
    233                                         scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
    234                                         textLen, it.pos(), 2, dfOffset);
    235                     break;
    236                 }
    237             }
    238         } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
    239             cacheBlob->setRunTooBigForAtlas(run);
    240         } else {
    241             switch (it.positioning()) {
    242                 case SkTextBlob::kDefault_Positioning:
    243                     DrawBmpText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
    244                                 viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(),
    245                                 y + offset.y());
    246                     break;
    247                 case SkTextBlob::kHorizontal_Positioning:
    248                     DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
    249                                    viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1,
    250                                    SkPoint::Make(x, y + offset.y()), SK_Scalar1);
    251                     break;
    252                 case SkTextBlob::kFull_Positioning:
    253                     DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
    254                                    viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2,
    255                                    SkPoint::Make(x, y), SK_Scalar1);
    256                     break;
    257             }
    258         }
    259     }
    260 }
    261 
    262 inline sk_sp<GrAtlasTextBlob>
    263 GrAtlasTextContext::makeDrawTextBlob(GrTextBlobCache* blobCache,
    264                                      GrAtlasGlyphCache* fontCache,
    265                                      const GrShaderCaps& shaderCaps,
    266                                      const GrTextUtils::Paint& paint,
    267                                      SkScalerContextFlags scalerContextFlags,
    268                                      const SkMatrix& viewMatrix,
    269                                      const SkSurfaceProps& props,
    270                                      const char text[], size_t byteLength,
    271                                      SkScalar x, SkScalar y) const {
    272     int glyphCount = paint.skPaint().countText(text, byteLength);
    273     if (!glyphCount) {
    274         return nullptr;
    275     }
    276     sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
    277     blob->initThrowawayBlob(viewMatrix, x, y);
    278 
    279     if (this->canDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
    280         this->drawDFText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix,
    281                          text, byteLength, x, y);
    282     } else {
    283         DrawBmpText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
    284                     byteLength, x, y);
    285     }
    286     return blob;
    287 }
    288 
    289 inline sk_sp<GrAtlasTextBlob>
    290 GrAtlasTextContext::makeDrawPosTextBlob(GrTextBlobCache* blobCache,
    291                                         GrAtlasGlyphCache* fontCache,
    292                                         const GrShaderCaps& shaderCaps,
    293                                         const GrTextUtils::Paint& paint,
    294                                         SkScalerContextFlags scalerContextFlags,
    295                                         const SkMatrix& viewMatrix,
    296                                         const SkSurfaceProps& props,
    297                                         const char text[], size_t byteLength,
    298                                         const SkScalar pos[], int scalarsPerPosition, const
    299                                         SkPoint& offset) const {
    300     int glyphCount = paint.skPaint().countText(text, byteLength);
    301     if (!glyphCount) {
    302         return nullptr;
    303     }
    304 
    305     sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
    306     blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y());
    307 
    308     if (this->canDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
    309         this->drawDFPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix,
    310                             text, byteLength, pos, scalarsPerPosition, offset);
    311     } else {
    312         DrawBmpPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
    313                        byteLength, pos, scalarsPerPosition, offset, SK_Scalar1);
    314     }
    315     return blob;
    316 }
    317 
    318 void GrAtlasTextContext::drawText(GrContext* context, GrTextUtils::Target* target,
    319                                   const GrClip& clip, const SkPaint& skPaint,
    320                                   const SkMatrix& viewMatrix, const SkSurfaceProps& props,
    321                                   const char text[], size_t byteLength, SkScalar x, SkScalar y,
    322                                   const SkIRect& regionClipBounds) {
    323     if (context->abandoned()) {
    324         return;
    325     }
    326 
    327     auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache();
    328     auto textBlobCache = context->contextPriv().getTextBlobCache();
    329 
    330     GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
    331     if (this->canDraw(atlasGlyphCache, skPaint, viewMatrix, props,
    332                       *context->caps()->shaderCaps())) {
    333         sk_sp<GrAtlasTextBlob> blob(
    334                 this->makeDrawTextBlob(textBlobCache, atlasGlyphCache,
    335                                        *context->caps()->shaderCaps(), paint,
    336                                        ComputeScalerContextFlags(target->colorSpaceInfo()),
    337                                        viewMatrix, props, text, byteLength, x, y));
    338         if (blob) {
    339             blob->flushThrowaway(atlasGlyphCache, target, props, fDistanceAdjustTable.get(), paint,
    340                                  clip, viewMatrix, regionClipBounds, x, y);
    341         }
    342         return;
    343     }
    344 
    345     // fall back to drawing as a path or scaled glyph
    346     GrTextUtils::DrawBigText(target, clip, paint, viewMatrix, text, byteLength, x, y,
    347                              regionClipBounds);
    348 }
    349 
    350 void GrAtlasTextContext::drawPosText(GrContext* context, GrTextUtils::Target* target,
    351                                      const GrClip& clip, const SkPaint& skPaint,
    352                                      const SkMatrix& viewMatrix, const SkSurfaceProps& props,
    353                                      const char text[], size_t byteLength, const SkScalar pos[],
    354                                      int scalarsPerPosition, const SkPoint& offset,
    355                                      const SkIRect& regionClipBounds) {
    356     GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
    357     if (context->abandoned()) {
    358         return;
    359     }
    360 
    361     auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache();
    362     auto textBlobCache = context->contextPriv().getTextBlobCache();
    363 
    364     if (this->canDraw(atlasGlyphCache, skPaint, viewMatrix, props,
    365                       *context->caps()->shaderCaps())) {
    366         sk_sp<GrAtlasTextBlob> blob(this->makeDrawPosTextBlob(
    367                 textBlobCache, atlasGlyphCache,
    368                 *context->caps()->shaderCaps(), paint,
    369                 ComputeScalerContextFlags(target->colorSpaceInfo()), viewMatrix, props, text,
    370                 byteLength, pos, scalarsPerPosition, offset));
    371         if (blob) {
    372             blob->flushThrowaway(atlasGlyphCache, target, props, fDistanceAdjustTable.get(), paint,
    373                                  clip, viewMatrix, regionClipBounds, offset.fX, offset.fY);
    374         }
    375         return;
    376     }
    377 
    378     // fall back to drawing as a path or scaled glyph
    379     GrTextUtils::DrawBigPosText(target, props, clip, paint, viewMatrix, text,
    380                                 byteLength, pos, scalarsPerPosition, offset, regionClipBounds);
    381 }
    382 
    383 void GrAtlasTextContext::DrawBmpText(GrAtlasTextBlob* blob, int runIndex,
    384                                      GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
    385                                      const GrTextUtils::Paint& paint,
    386                                      SkScalerContextFlags scalerContextFlags,
    387                                      const SkMatrix& viewMatrix, const char text[],
    388                                      size_t byteLength, SkScalar x, SkScalar y) {
    389     SkASSERT(byteLength == 0 || text != nullptr);
    390 
    391     // nothing to draw
    392     if (text == nullptr || byteLength == 0) {
    393         return;
    394     }
    395 
    396     // Ensure the blob is set for bitmaptext
    397     blob->setHasBitmap();
    398 
    399     GrAtlasTextStrike* currStrike = nullptr;
    400 
    401     SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
    402     SkFindAndPlaceGlyph::ProcessText(paint.skPaint().getTextEncoding(), text, byteLength, {x, y},
    403                                      viewMatrix, paint.skPaint().getTextAlign(), cache,
    404                                      [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
    405                                          position += rounding;
    406                                          BmpAppendGlyph(blob, runIndex, fontCache, &currStrike,
    407                                                         glyph, SkScalarFloorToScalar(position.fX),
    408                                                         SkScalarFloorToScalar(position.fY),
    409                                                         paint.filteredPremulColor(), cache,
    410                                                         SK_Scalar1);
    411                                      });
    412 
    413     SkGlyphCache::AttachCache(cache);
    414 }
    415 
    416 void GrAtlasTextContext::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex,
    417                                         GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
    418                                         const GrTextUtils::Paint& paint,
    419                                         SkScalerContextFlags scalerContextFlags,
    420                                         const SkMatrix& viewMatrix,
    421                                         const char text[], size_t byteLength, const SkScalar pos[],
    422                                         int scalarsPerPosition, const SkPoint& offset,
    423                                         SkScalar textRatio) {
    424     SkASSERT(byteLength == 0 || text != nullptr);
    425     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
    426 
    427     // nothing to draw
    428     if (text == nullptr || byteLength == 0) {
    429         return;
    430     }
    431 
    432     // Ensure the blob is set for bitmaptext
    433     blob->setHasBitmap();
    434 
    435     GrAtlasTextStrike* currStrike = nullptr;
    436 
    437     SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
    438 
    439     SkFindAndPlaceGlyph::ProcessPosText(
    440             paint.skPaint().getTextEncoding(), text, byteLength, offset, viewMatrix, pos,
    441             scalarsPerPosition, paint.skPaint().getTextAlign(), cache,
    442             [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
    443                 position += rounding;
    444                 BmpAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph,
    445                                SkScalarFloorToScalar(position.fX),
    446                                SkScalarFloorToScalar(position.fY),
    447                                paint.filteredPremulColor(), cache, textRatio);
    448             });
    449 
    450     SkGlyphCache::AttachCache(cache);
    451 }
    452 
    453 void GrAtlasTextContext::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
    454                                         GrAtlasGlyphCache* fontCache, GrAtlasTextStrike** strike,
    455                                         const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
    456                                         GrColor color, SkGlyphCache* glyphCache,
    457                                         SkScalar textRatio) {
    458     if (!*strike) {
    459         *strike = fontCache->getStrike(glyphCache);
    460     }
    461 
    462     GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
    463                                          skGlyph.getSubXFixed(),
    464                                          skGlyph.getSubYFixed(),
    465                                          GrGlyph::kCoverage_MaskStyle);
    466     GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
    467     if (!glyph) {
    468         return;
    469     }
    470 
    471     SkASSERT(skGlyph.fWidth == glyph->width());
    472     SkASSERT(skGlyph.fHeight == glyph->height());
    473 
    474     SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
    475     SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
    476     SkScalar width = SkIntToScalar(glyph->fBounds.width());
    477     SkScalar height = SkIntToScalar(glyph->fBounds.height());
    478 
    479     dx *= textRatio;
    480     dy *= textRatio;
    481     width *= textRatio;
    482     height *= textRatio;
    483 
    484     SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
    485 
    486     blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph, sx, sy,
    487                       textRatio, true);
    488 }
    489 
    490 bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
    491                                                  const SkSurfaceProps& props,
    492                                                  const GrShaderCaps& caps) const {
    493     if (!viewMatrix.hasPerspective()) {
    494         SkScalar maxScale = viewMatrix.getMaxScale();
    495         SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
    496         // Hinted text looks far better at small resolutions
    497         // Scaling up beyond 2x yields undesireable artifacts
    498         if (scaledTextSize < fMinDistanceFieldFontSize ||
    499             scaledTextSize > fMaxDistanceFieldFontSize) {
    500             return false;
    501         }
    502 
    503         bool useDFT = props.isUseDeviceIndependentFonts();
    504 #if SK_FORCE_DISTANCE_FIELD_TEXT
    505         useDFT = true;
    506 #endif
    507 
    508         if (!useDFT && scaledTextSize < kLargeDFFontSize) {
    509             return false;
    510         }
    511     }
    512 
    513     // mask filters modify alpha, which doesn't translate well to distance
    514     if (skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
    515         return false;
    516     }
    517 
    518     // TODO: add some stroking support
    519     if (skPaint.getStyle() != SkPaint::kFill_Style) {
    520         return false;
    521     }
    522 
    523     return true;
    524 }
    525 
    526 void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
    527                                                 SkPaint* skPaint,
    528                                                 SkScalar* textRatio,
    529                                                 const SkMatrix& viewMatrix) const {
    530     SkScalar textSize = skPaint->getTextSize();
    531     SkScalar scaledTextSize = textSize;
    532 
    533     if (viewMatrix.hasPerspective()) {
    534         // for perspective, we simply force to the medium size
    535         // TODO: compute a size based on approximate screen area
    536         scaledTextSize = kMediumDFFontLimit;
    537     } else {
    538         SkScalar maxScale = viewMatrix.getMaxScale();
    539         // if we have non-unity scale, we need to choose our base text size
    540         // based on the SkPaint's text size multiplied by the max scale factor
    541         // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
    542         if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
    543             scaledTextSize *= maxScale;
    544         }
    545     }
    546 
    547     // We have three sizes of distance field text, and within each size 'bucket' there is a floor
    548     // and ceiling.  A scale outside of this range would require regenerating the distance fields
    549     SkScalar dfMaskScaleFloor;
    550     SkScalar dfMaskScaleCeil;
    551     if (scaledTextSize <= kSmallDFFontLimit) {
    552         dfMaskScaleFloor = fMinDistanceFieldFontSize;
    553         dfMaskScaleCeil = kSmallDFFontLimit;
    554         *textRatio = textSize / kSmallDFFontSize;
    555         skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
    556     } else if (scaledTextSize <= kMediumDFFontLimit) {
    557         dfMaskScaleFloor = kSmallDFFontLimit;
    558         dfMaskScaleCeil = kMediumDFFontLimit;
    559         *textRatio = textSize / kMediumDFFontSize;
    560         skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
    561     } else {
    562         dfMaskScaleFloor = kMediumDFFontLimit;
    563         dfMaskScaleCeil = fMaxDistanceFieldFontSize;
    564         *textRatio = textSize / kLargeDFFontSize;
    565         skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
    566     }
    567 
    568     // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
    569     // minMaxScale to make regeneration decisions.  Specifically, we want the maximum minimum scale
    570     // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
    571     // tolerate before we'd have to move to a large mip size.  When we actually test these values
    572     // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
    573     // against these values to decide if we can reuse or not(ie, will a given scale change our mip
    574     // level)
    575     SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
    576     blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
    577 
    578     skPaint->setAntiAlias(true);
    579     skPaint->setLCDRenderText(false);
    580     skPaint->setAutohinted(false);
    581     skPaint->setHinting(SkPaint::kNormal_Hinting);
    582     skPaint->setSubpixelText(true);
    583 }
    584 
    585 void GrAtlasTextContext::drawDFText(GrAtlasTextBlob* blob, int runIndex,
    586                                     GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
    587                                     const GrTextUtils::Paint& paint,
    588                                     SkScalerContextFlags scalerContextFlags,
    589                                     const SkMatrix& viewMatrix, const char text[],
    590                                     size_t byteLength, SkScalar x, SkScalar y) const {
    591     SkASSERT(byteLength == 0 || text != nullptr);
    592 
    593     // nothing to draw
    594     if (text == nullptr || byteLength == 0) {
    595         return;
    596     }
    597 
    598     const SkPaint& skPaint = paint.skPaint();
    599     SkPaint::GlyphCacheProc glyphCacheProc =
    600             SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(), skPaint.isDevKernText(), true);
    601     SkAutoDescriptor desc;
    602     SkScalerContextEffects effects;
    603     // We apply the fake-gamma by altering the distance in the shader, so we ignore the
    604     // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
    605     SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
    606         skPaint, &props, SkScalerContextFlags::kNone, nullptr, &desc, &effects);
    607     SkGlyphCache* origPaintCache =
    608             SkGlyphCache::DetachCache(skPaint.getTypeface(), effects, desc.getDesc());
    609 
    610     SkTArray<SkScalar> positions;
    611 
    612     const char* textPtr = text;
    613     SkScalar stopX = 0;
    614     SkScalar stopY = 0;
    615     SkScalar origin = 0;
    616     switch (skPaint.getTextAlign()) {
    617         case SkPaint::kRight_Align: origin = SK_Scalar1; break;
    618         case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
    619         case SkPaint::kLeft_Align: origin = 0; break;
    620     }
    621 
    622     SkAutoKern autokern;
    623     const char* stop = text + byteLength;
    624     while (textPtr < stop) {
    625         // don't need x, y here, since all subpixel variants will have the
    626         // same advance
    627         const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
    628 
    629         SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
    630         positions.push_back(stopX + origin * width);
    631 
    632         SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
    633         positions.push_back(stopY + origin * height);
    634 
    635         stopX += width;
    636         stopY += height;
    637     }
    638     SkASSERT(textPtr == stop);
    639 
    640     SkGlyphCache::AttachCache(origPaintCache);
    641 
    642     // now adjust starting point depending on alignment
    643     SkScalar alignX = stopX;
    644     SkScalar alignY = stopY;
    645     if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
    646         alignX = SkScalarHalf(alignX);
    647         alignY = SkScalarHalf(alignY);
    648     } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
    649         alignX = 0;
    650         alignY = 0;
    651     }
    652     x -= alignX;
    653     y -= alignY;
    654     SkPoint offset = SkPoint::Make(x, y);
    655 
    656     this->drawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix,
    657                         text, byteLength, positions.begin(), 2, offset);
    658 }
    659 
    660 void GrAtlasTextContext::drawDFPosText(GrAtlasTextBlob* blob, int runIndex,
    661                                        GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
    662                                        const GrTextUtils::Paint& paint,
    663                                        SkScalerContextFlags scalerContextFlags,
    664                                        const SkMatrix& viewMatrix, const char text[],
    665                                        size_t byteLength, const SkScalar pos[],
    666                                        int scalarsPerPosition, const SkPoint& offset) const {
    667     SkASSERT(byteLength == 0 || text != nullptr);
    668     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
    669 
    670     // nothing to draw
    671     if (text == nullptr || byteLength == 0) {
    672         return;
    673     }
    674 
    675     SkTDArray<char> fallbackTxt;
    676     SkTDArray<SkScalar> fallbackPos;
    677 
    678     SkTDArray<char> bigFallbackTxt;
    679     SkTDArray<SkScalar> bigFallbackPos;
    680     SkScalar textSize = paint.skPaint().getTextSize();
    681     SkScalar maxTextSize = fontCache->getGlyphSizeLimit();
    682     SkScalar bigFallbackTextSize = maxTextSize;
    683     SkScalar maxScale = viewMatrix.getMaxScale();
    684 
    685     bool hasWCoord = viewMatrix.hasPerspective() || fDistanceFieldVerticesAlwaysHaveW;
    686 
    687     // Setup distance field paint and text ratio
    688     SkScalar textRatio;
    689     SkPaint dfPaint(paint);
    690     this->initDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
    691     blob->setHasDistanceField();
    692     blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
    693                                      paint.skPaint().isAntiAlias(), hasWCoord);
    694 
    695     GrAtlasTextStrike* currStrike = nullptr;
    696 
    697     // We apply the fake-gamma by altering the distance in the shader, so we ignore the
    698     // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
    699     SkGlyphCache* cache =
    700             blob->setupCache(runIndex, props, SkScalerContextFlags::kNone, dfPaint, nullptr);
    701     SkPaint::GlyphCacheProc glyphCacheProc =
    702             SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(), dfPaint.isDevKernText(), true);
    703 
    704     const char* stop = text + byteLength;
    705 
    706     SkPaint::Align align = dfPaint.getTextAlign();
    707     SkScalar alignMul = SkPaint::kCenter_Align == align ? SK_ScalarHalf :
    708                         (SkPaint::kRight_Align == align ? SK_Scalar1 : 0);
    709     while (text < stop) {
    710         const char* lastText = text;
    711         // the last 2 parameters are ignored
    712         const SkGlyph& glyph = glyphCacheProc(cache, &text);
    713 
    714         if (glyph.fWidth) {
    715             SkScalar x = offset.x() + pos[0];
    716             SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
    717 
    718             SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
    719             SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
    720 
    721             if (glyph.fMaskFormat != SkMask::kARGB32_Format) {
    722                 DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
    723                               y - advanceY, paint.filteredPremulColor(), cache, textRatio);
    724             } else {
    725                 // can't append color glyph to SDF batch, send to fallback
    726                 SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*textRatio;
    727                 SkScalar scaledGlyphSize = maxDim*maxScale;
    728 
    729                 if (!viewMatrix.hasPerspective() && scaledGlyphSize > maxTextSize) {
    730                     bigFallbackTxt.append(SkToInt(text - lastText), lastText);
    731                     *bigFallbackPos.append() = maxScale*pos[0];
    732                     if (2 == scalarsPerPosition) {
    733                         *bigFallbackPos.append() = maxScale*pos[1];
    734                     }
    735                     SkScalar glyphTextSize = SkScalarFloorToScalar(maxTextSize*textSize/maxDim);
    736                     bigFallbackTextSize = SkTMin(glyphTextSize, bigFallbackTextSize);
    737                 } else {
    738                     fallbackTxt.append(SkToInt(text - lastText), lastText);
    739                     *fallbackPos.append() = pos[0];
    740                     if (2 == scalarsPerPosition) {
    741                         *fallbackPos.append() = pos[1];
    742                     }
    743                 }
    744             }
    745         }
    746         pos += scalarsPerPosition;
    747     }
    748 
    749     SkGlyphCache::AttachCache(cache);
    750     if (fallbackTxt.count()) {
    751         blob->initOverride(runIndex);
    752         GrAtlasTextContext::DrawBmpPosText(blob, runIndex, fontCache, props, paint,
    753                                            scalerContextFlags, viewMatrix, fallbackTxt.begin(),
    754                                            fallbackTxt.count(), fallbackPos.begin(),
    755                                            scalarsPerPosition, offset, SK_Scalar1);
    756     }
    757 
    758     if (bigFallbackTxt.count()) {
    759         // Set up paint and matrix to scale glyphs
    760         blob->initOverride(runIndex);
    761         SkPaint largePaint(paint);
    762         largePaint.setTextSize(bigFallbackTextSize);
    763         // remove maxScale from viewMatrix and move it into textRatio
    764         // this keeps the base glyph size consistent regardless of matrix scale
    765         SkMatrix modMatrix(viewMatrix);
    766         SkScalar invScale = SkScalarInvert(maxScale);
    767         modMatrix.preScale(invScale, invScale);
    768         SkScalar bigFallbackTextRatio = textSize*maxScale/bigFallbackTextSize;
    769         SkPoint modOffset(offset);
    770         modOffset *= maxScale;
    771         GrTextUtils::Paint textPaint(&largePaint, paint.dstColorSpaceInfo());
    772         GrAtlasTextContext::DrawBmpPosText(blob, runIndex, fontCache, props, textPaint,
    773                                            scalerContextFlags, modMatrix, bigFallbackTxt.begin(),
    774                                            bigFallbackTxt.count(), bigFallbackPos.begin(),
    775                                            scalarsPerPosition, modOffset, bigFallbackTextRatio);
    776     }
    777 }
    778 
    779 // TODO: merge with BmpAppendGlyph
    780 void GrAtlasTextContext::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
    781                                        GrAtlasGlyphCache* cache, GrAtlasTextStrike** strike,
    782                                        const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
    783                                        GrColor color, SkGlyphCache* glyphCache,
    784                                        SkScalar textRatio) {
    785     if (!*strike) {
    786         *strike = cache->getStrike(glyphCache);
    787     }
    788 
    789     GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
    790                                          skGlyph.getSubXFixed(),
    791                                          skGlyph.getSubYFixed(),
    792                                          GrGlyph::kDistance_MaskStyle);
    793     GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
    794     if (!glyph) {
    795         return;
    796     }
    797 
    798     SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
    799     SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
    800     SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
    801     SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
    802 
    803     dx *= textRatio;
    804     dy *= textRatio;
    805     width *= textRatio;
    806     height *= textRatio;
    807     SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
    808 
    809     blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph, sx, sy,
    810                       textRatio, false);
    811 }
    812 
    813 ///////////////////////////////////////////////////////////////////////////////////////////////////
    814 
    815 #if GR_TEST_UTILS
    816 
    817 #include "GrRenderTargetContext.h"
    818 
    819 GR_DRAW_OP_TEST_DEFINE(GrAtlasTextOp) {
    820     static uint32_t gContextID = SK_InvalidGenID;
    821     static std::unique_ptr<GrAtlasTextContext> gTextContext;
    822     static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
    823 
    824     if (context->uniqueID() != gContextID) {
    825         gContextID = context->uniqueID();
    826         gTextContext = GrAtlasTextContext::Make(GrAtlasTextContext::Options());
    827     }
    828 
    829     // Setup dummy SkPaint / GrPaint / GrRenderTargetContext
    830     sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
    831         SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
    832 
    833     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
    834 
    835     // Because we the GrTextUtils::Paint requires an SkPaint for font info, we ignore the GrPaint
    836     // param.
    837     SkPaint skPaint;
    838     skPaint.setColor(random->nextU());
    839     skPaint.setLCDRenderText(random->nextBool());
    840     skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
    841     skPaint.setSubpixelText(random->nextBool());
    842     GrTextUtils::Paint utilsPaint(&skPaint, &rtc->colorSpaceInfo());
    843 
    844     const char* text = "The quick brown fox jumps over the lazy dog.";
    845     int textLen = (int)strlen(text);
    846 
    847     // create some random x/y offsets, including negative offsets
    848     static const int kMaxTrans = 1024;
    849     int xPos = (random->nextU() % 2) * 2 - 1;
    850     int yPos = (random->nextU() % 2) * 2 - 1;
    851     int xInt = (random->nextU() % kMaxTrans) * xPos;
    852     int yInt = (random->nextU() % kMaxTrans) * yPos;
    853     SkScalar x = SkIntToScalar(xInt);
    854     SkScalar y = SkIntToScalar(yInt);
    855 
    856     auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache();
    857 
    858     // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to
    859     // test the text op with this unit test, that is okay.
    860     sk_sp<GrAtlasTextBlob> blob(gTextContext->makeDrawTextBlob(
    861             context->contextPriv().getTextBlobCache(), atlasGlyphCache,
    862             *context->caps()->shaderCaps(), utilsPaint,
    863             GrAtlasTextContext::kTextBlobOpScalerContextFlags, viewMatrix, gSurfaceProps, text,
    864             static_cast<size_t>(textLen), x, y));
    865 
    866     return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, utilsPaint, gSurfaceProps,
    867                              gTextContext->dfAdjustTable(), atlasGlyphCache,
    868                              rtc->textTarget());
    869 }
    870 
    871 #endif
    872