Home | History | Annotate | Download | only in text
      1 /*
      2  * Copyright 2014 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 "GrStencilAndCoverTextContext.h"
      9 #include "GrAtlasTextContext.h"
     10 #include "GrContext.h"
     11 #include "GrPath.h"
     12 #include "GrPathRange.h"
     13 #include "GrRenderTargetContext.h"
     14 #include "GrResourceProvider.h"
     15 #include "GrTextUtils.h"
     16 #include "SkAutoKern.h"
     17 #include "SkDraw.h"
     18 #include "SkDrawFilter.h"
     19 #include "SkDrawProcs.h"
     20 #include "SkGlyphCache.h"
     21 #include "SkGr.h"
     22 #include "SkPath.h"
     23 #include "SkTextBlobRunIterator.h"
     24 #include "SkTextFormatParams.h"
     25 #include "SkTextMapStateProc.h"
     26 
     27 #include "ops/GrDrawPathOp.h"
     28 
     29 template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
     30     SkASSERT(*val);
     31     delete *val;
     32 }
     33 
     34 template<typename T> static void delete_hash_table_entry(T* val) {
     35     SkASSERT(*val);
     36     delete *val;
     37 }
     38 
     39 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrAtlasTextContext* fallbackTextContext)
     40     : fFallbackTextContext(fallbackTextContext)
     41     , fCacheSize(0) {
     42 }
     43 
     44 GrStencilAndCoverTextContext*
     45 GrStencilAndCoverTextContext::Create(GrAtlasTextContext* fallbackTextContext) {
     46     return new GrStencilAndCoverTextContext(fallbackTextContext);;
     47 }
     48 
     49 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
     50     fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
     51     fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
     52 }
     53 
     54 bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
     55     if (skPaint.getRasterizer()) {
     56         return false;
     57     }
     58     if (skPaint.getMaskFilter()) {
     59         return false;
     60     }
     61     if (SkPathEffect* pe = skPaint.getPathEffect()) {
     62         if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
     63             return false;
     64         }
     65     }
     66     // No hairlines. They would require new paths with customized strokes for every new draw matrix.
     67     return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
     68 }
     69 
     70 void GrStencilAndCoverTextContext::drawText(GrContext* context, GrRenderTargetContext* rtc,
     71                                             const GrClip& clip, const SkPaint& skPaint,
     72                                             const SkMatrix& viewMatrix, const SkSurfaceProps& props,
     73                                             const char text[], size_t byteLength, SkScalar x,
     74                                             SkScalar y, const SkIRect& clipBounds) {
     75     if (context->abandoned()) {
     76         return;
     77     } else if (this->canDraw(skPaint, viewMatrix)) {
     78         if (skPaint.getTextSize() > 0) {
     79             TextRun run(skPaint);
     80             run.setText(text, byteLength, x, y);
     81             run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
     82                      skPaint);
     83         }
     84         return;
     85     } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
     86                                              *context->caps()->shaderCaps())) {
     87         fFallbackTextContext->drawText(context, rtc, clip, skPaint, viewMatrix, props, text,
     88                                        byteLength, x, y, clipBounds);
     89         return;
     90     }
     91 
     92     // fall back to drawing as a path
     93     GrTextUtils::DrawTextAsPath(context, rtc, clip, skPaint, viewMatrix, text, byteLength, x, y,
     94                                 clipBounds);
     95 }
     96 
     97 void GrStencilAndCoverTextContext::drawPosText(GrContext* context, GrRenderTargetContext* rtc,
     98                                                const GrClip& clip, const SkPaint& skPaint,
     99                                                const SkMatrix& viewMatrix,
    100                                                const SkSurfaceProps& props, const char text[],
    101                                                size_t byteLength, const SkScalar pos[],
    102                                                int scalarsPerPosition, const SkPoint& offset,
    103                                                const SkIRect& clipBounds) {
    104     if (context->abandoned()) {
    105         return;
    106     } else if (this->canDraw(skPaint, viewMatrix)) {
    107         if (skPaint.getTextSize() > 0) {
    108             TextRun run(skPaint);
    109             run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
    110             run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
    111                      skPaint);
    112         }
    113         return;
    114     } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
    115                                              *context->caps()->shaderCaps())) {
    116         fFallbackTextContext->drawPosText(context, rtc, clip, skPaint, viewMatrix, props, text,
    117                                           byteLength, pos, scalarsPerPosition, offset, clipBounds);
    118         return;
    119     }
    120 
    121     // fall back to drawing as a path
    122     GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, skPaint, viewMatrix, text,
    123                                    byteLength, pos, scalarsPerPosition, offset, clipBounds);
    124 }
    125 
    126 void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrContext* context,
    127                                                         GrRenderTargetContext* rtc,
    128                                                         const GrClip& clip,
    129                                                         const SkPaint& skPaint,
    130                                                         const SkMatrix& viewMatrix,
    131                                                         const SkSurfaceProps& props,
    132                                                         const SkTextBlob* blob,
    133                                                         SkScalar x, SkScalar y,
    134                                                         SkDrawFilter* drawFilter,
    135                                                         const SkIRect& clipBounds) {
    136     GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
    137     GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
    138     SkTextBlobRunIterator it(blob);
    139     for (;!it.done(); it.next()) {
    140         if (!runPaint.modifyForRun(it)) {
    141             continue;
    142         }
    143         size_t textLen = it.glyphCount() * sizeof(uint16_t);
    144         const SkPoint& offset = it.offset();
    145 
    146         switch (it.positioning()) {
    147             case SkTextBlob::kDefault_Positioning:
    148                 this->drawText(context, rtc, clip, runPaint, viewMatrix, props,
    149                                (const char*)it.glyphs(), textLen, x + offset.x(), y + offset.y(),
    150                                clipBounds);
    151                 break;
    152             case SkTextBlob::kHorizontal_Positioning:
    153                 this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
    154                                   (const char*)it.glyphs(), textLen, it.pos(), 1,
    155                                   SkPoint::Make(x, y + offset.y()), clipBounds);
    156                 break;
    157             case SkTextBlob::kFull_Positioning:
    158                 this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
    159                                   (const char*)it.glyphs(), textLen, it.pos(), 2,
    160                                   SkPoint::Make(x, y), clipBounds);
    161                 break;
    162         }
    163     }
    164 }
    165 
    166 void GrStencilAndCoverTextContext::drawTextBlob(GrContext* context, GrRenderTargetContext* rtc,
    167                                                 const GrClip& clip, const SkPaint& skPaint,
    168                                                 const SkMatrix& viewMatrix,
    169                                                 const SkSurfaceProps& props,
    170                                                 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
    171                                                 SkDrawFilter* drawFilter,
    172                                                 const SkIRect& clipBounds) {
    173     if (context->abandoned()) {
    174         return;
    175     }
    176 
    177     if (!this->internalCanDraw(skPaint)) {
    178         fFallbackTextContext->drawTextBlob(context, rtc, clip, skPaint, viewMatrix, props, skBlob,
    179                                            x, y, drawFilter, clipBounds);
    180         return;
    181     }
    182 
    183     if (drawFilter || skPaint.getPathEffect()) {
    184         // This draw can't be cached.
    185         this->uncachedDrawTextBlob(context, rtc, clip, skPaint, viewMatrix, props, skBlob, x, y,
    186                                    drawFilter, clipBounds);
    187         return;
    188     }
    189 
    190     const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
    191 
    192     TextBlob::Iter iter(blob);
    193     for (TextRun *run = iter.get(), *nextRun; run; run = nextRun) {
    194         nextRun = iter.next();
    195         run->draw(context, rtc, clip, viewMatrix, props, x, y, clipBounds, fFallbackTextContext,
    196                   skPaint);
    197         run->releaseGlyphCache();
    198     }
    199 }
    200 
    201 static inline int style_key_cnt(const GrStyle& style) {
    202     int cnt = GrStyle::KeySize(style, GrStyle::Apply::kPathEffectAndStrokeRec);
    203     // We should be able to make a key because we filtered out arbitrary path effects.
    204     SkASSERT(cnt > 0);
    205     return cnt;
    206 }
    207 
    208 static inline void write_style_key(uint32_t* dst, const GrStyle& style) {
    209     // Pass 1 for the scale since the GPU will apply the style not GrStyle::applyToPath().
    210     GrStyle::WriteKey(dst, style, GrStyle::Apply::kPathEffectAndStrokeRec, SK_Scalar1);
    211 }
    212 
    213 const GrStencilAndCoverTextContext::TextBlob&
    214 GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
    215                                                    const SkPaint& skPaint) {
    216     // The font-related parameters are baked into the text blob and will override this skPaint, so
    217     // the only remaining properties that can affect a TextBlob are the ones related to stroke.
    218     if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
    219         if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
    220             fLRUList.remove(*found);
    221             fLRUList.addToTail(*found);
    222             return **found;
    223         }
    224         TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
    225         this->purgeToFit(*blob);
    226         fBlobIdCache.set(skBlob->uniqueID(), blob);
    227         fLRUList.addToTail(blob);
    228         fCacheSize += blob->cpuMemorySize();
    229         return *blob;
    230     } else {
    231         GrStyle style(skPaint);
    232         SkSTArray<4, uint32_t, true> key;
    233         key.reset(1 + style_key_cnt(style));
    234         key[0] = skBlob->uniqueID();
    235         write_style_key(&key[1], style);
    236         if (TextBlob** found = fBlobKeyCache.find(key)) {
    237             fLRUList.remove(*found);
    238             fLRUList.addToTail(*found);
    239             return **found;
    240         }
    241         TextBlob* blob = new TextBlob(key, skBlob, skPaint);
    242         this->purgeToFit(*blob);
    243         fBlobKeyCache.set(blob);
    244         fLRUList.addToTail(blob);
    245         fCacheSize += blob->cpuMemorySize();
    246         return *blob;
    247     }
    248 }
    249 
    250 void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
    251     static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
    252 
    253     size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
    254     while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
    255         TextBlob* lru = fLRUList.head();
    256         if (1 == lru->key().count()) {
    257             // 1-length keys are unterstood to be the blob id.
    258             fBlobIdCache.remove(lru->key()[0]);
    259         } else {
    260             fBlobKeyCache.remove(lru->key());
    261         }
    262         fLRUList.remove(lru);
    263         fCacheSize -= lru->cpuMemorySize();
    264         delete lru;
    265     }
    266 }
    267 
    268 ////////////////////////////////////////////////////////////////////////////////////////////////////
    269 
    270 void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
    271                                                   const SkPaint& skPaint) {
    272     fCpuMemorySize = sizeof(TextBlob);
    273     SkPaint runPaint(skPaint);
    274     for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
    275         iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
    276         if (runPaint.getTextSize() <= 0) {
    277             continue;
    278         }
    279         TextRun* run = this->addToTail(runPaint);
    280 
    281         const char* text = reinterpret_cast<const char*>(iter.glyphs());
    282         size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
    283         const SkPoint& runOffset = iter.offset();
    284 
    285         switch (iter.positioning()) {
    286             case SkTextBlob::kDefault_Positioning:
    287                 run->setText(text, byteLength, runOffset.fX, runOffset.fY);
    288                 break;
    289             case SkTextBlob::kHorizontal_Positioning:
    290                 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
    291                 break;
    292             case SkTextBlob::kFull_Positioning:
    293                 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
    294                 break;
    295         }
    296 
    297         fCpuMemorySize += run->computeSizeInCache();
    298     }
    299 }
    300 
    301 ////////////////////////////////////////////////////////////////////////////////////////////////////
    302 
    303 class GrStencilAndCoverTextContext::FallbackBlobBuilder {
    304 public:
    305     FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
    306 
    307     bool isInitialized() const { return fBuilder != nullptr; }
    308 
    309     void init(const SkPaint& font, SkScalar textRatio);
    310 
    311     void appendGlyph(uint16_t glyphId, const SkPoint& pos);
    312 
    313     sk_sp<SkTextBlob> makeIfNeeded(int* count);
    314 
    315 private:
    316     enum { kWriteBufferSize = 1024 };
    317 
    318     void flush();
    319 
    320     std::unique_ptr<SkTextBlobBuilder> fBuilder;
    321     SkPaint                            fFont;
    322     int                                fBuffIdx;
    323     int                                fCount;
    324     uint16_t                           fGlyphIds[kWriteBufferSize];
    325     SkPoint                            fPositions[kWriteBufferSize];
    326 };
    327 
    328 ////////////////////////////////////////////////////////////////////////////////////////////////////
    329 
    330 GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
    331     : fStyle(fontAndStroke)
    332     , fFont(fontAndStroke)
    333     , fTotalGlyphCount(0)
    334     , fFallbackGlyphCount(0)
    335     , fDetachedGlyphCache(nullptr)
    336     , fLastDrawnGlyphsID(SK_InvalidUniqueID) {
    337     SkASSERT(fFont.getTextSize() > 0);
    338     SkASSERT(!fStyle.hasNonDashPathEffect()); // Arbitrary path effects not supported.
    339     SkASSERT(!fStyle.isSimpleHairline()); // Hairlines are not supported.
    340 
    341     // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
    342     // rendering API for stroking).
    343     fFont.setStyle(SkPaint::kFill_Style);
    344 
    345     if (fFont.isFakeBoldText() && fStyle.isSimpleFill()) {
    346         const SkStrokeRec& stroke = fStyle.strokeRec();
    347         // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
    348         SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
    349                                                     kStdFakeBoldInterpKeys,
    350                                                     kStdFakeBoldInterpValues,
    351                                                     kStdFakeBoldInterpLength);
    352         SkScalar extra = fFont.getTextSize() * fakeBoldScale;
    353 
    354         SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
    355         strokeRec.setStrokeStyle(stroke.needToApply() ? stroke.getWidth() + extra : extra,
    356                                  true /*strokeAndFill*/);
    357         fStyle = GrStyle(strokeRec, fStyle.refPathEffect());
    358         fFont.setFakeBoldText(false);
    359     }
    360 
    361     if (!fFont.getPathEffect() && !fStyle.isDashed()) {
    362         const SkStrokeRec& stroke = fStyle.strokeRec();
    363         // We can draw the glyphs from canonically sized paths.
    364         fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
    365         fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
    366 
    367         // Compensate for the glyphs being scaled by fTextRatio.
    368         if (!fStyle.isSimpleFill()) {
    369             SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
    370             strokeRec.setStrokeStyle(stroke.getWidth() / fTextRatio,
    371                                      SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle());
    372             fStyle = GrStyle(strokeRec, fStyle.refPathEffect());
    373         }
    374 
    375         fFont.setLinearText(true);
    376         fFont.setLCDRenderText(false);
    377         fFont.setAutohinted(false);
    378         fFont.setHinting(SkPaint::kNo_Hinting);
    379         fFont.setSubpixelText(true);
    380         fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
    381 
    382         fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
    383                               0 == fFont.getTextSkewX() &&
    384                               !fFont.isFakeBoldText() &&
    385                               !fFont.isVerticalText();
    386     } else {
    387         fTextRatio = fTextInverseRatio = 1.0f;
    388         fUsingRawGlyphPaths = false;
    389     }
    390 
    391     // Generate the key that will be used to cache the GPU glyph path objects.
    392     if (fUsingRawGlyphPaths && fStyle.isSimpleFill()) {
    393         static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
    394 
    395         const SkTypeface* typeface = fFont.getTypeface();
    396         GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
    397         reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
    398     } else {
    399         static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
    400 
    401         int styleDataCount = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec);
    402         // Key should be valid since we opted out of drawing arbitrary path effects.
    403         SkASSERT(styleDataCount >= 0);
    404         if (fUsingRawGlyphPaths) {
    405             const SkTypeface* typeface = fFont.getTypeface();
    406             GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + styleDataCount);
    407             reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
    408             reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount;
    409             if (styleDataCount) {
    410                 write_style_key(&builder[2], fStyle);
    411             }
    412         } else {
    413             SkGlyphCache* glyphCache = this->getGlyphCache();
    414             const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
    415             const SkDescriptor* desc = &glyphCache->getDescriptor();
    416             int descDataCount = (desc->getLength() + 3) / 4;
    417             GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
    418                                          2 + styleDataCount + descDataCount);
    419             reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
    420             reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount | (descDataCount << 16);
    421             if (styleDataCount) {
    422                 write_style_key(&builder[2], fStyle);
    423             }
    424             memcpy(&builder[2 + styleDataCount], desc, desc->getLength());
    425         }
    426     }
    427 }
    428 
    429 GrStencilAndCoverTextContext::TextRun::~TextRun() {
    430     this->releaseGlyphCache();
    431 }
    432 
    433 void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
    434                                                     SkScalar x, SkScalar y) {
    435     SkASSERT(byteLength == 0 || text != nullptr);
    436 
    437     SkGlyphCache* glyphCache = this->getGlyphCache();
    438     SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
    439                                                                         fFont.isDevKernText(),
    440                                                                         true);
    441 
    442     fTotalGlyphCount = fFont.countText(text, byteLength);
    443     fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
    444                                             fTotalGlyphCount));
    445 
    446     const char* stop = text + byteLength;
    447 
    448     // Measure first if needed.
    449     if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
    450         SkScalar   stopX = 0;
    451         SkScalar   stopY = 0;
    452 
    453         const char* textPtr = text;
    454         while (textPtr < stop) {
    455             // We don't need x, y here, since all subpixel variants will have the
    456             // same advance.
    457             const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr);
    458 
    459             stopX += SkFloatToScalar(glyph.fAdvanceX);
    460             stopY += SkFloatToScalar(glyph.fAdvanceY);
    461         }
    462         SkASSERT(textPtr == stop);
    463 
    464         SkScalar alignX = stopX * fTextRatio;
    465         SkScalar alignY = stopY * fTextRatio;
    466 
    467         if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
    468             alignX = SkScalarHalf(alignX);
    469             alignY = SkScalarHalf(alignY);
    470         }
    471 
    472         x -= alignX;
    473         y -= alignY;
    474     }
    475 
    476     SkAutoKern autokern;
    477 
    478     FallbackBlobBuilder fallback;
    479     while (text < stop) {
    480         const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
    481         x += autokern.adjust(glyph) * fTextRatio;
    482         if (glyph.fWidth) {
    483             this->appendGlyph(glyph, SkPoint::Make(x, y), &fallback);
    484         }
    485 
    486         x += SkFloatToScalar(glyph.fAdvanceX) * fTextRatio;
    487         y += SkFloatToScalar(glyph.fAdvanceY) * fTextRatio;
    488     }
    489 
    490     fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
    491 }
    492 
    493 void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
    494                                                        const SkScalar pos[], int scalarsPerPosition,
    495                                                        const SkPoint& offset) {
    496     SkASSERT(byteLength == 0 || text != nullptr);
    497     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
    498 
    499     SkGlyphCache* glyphCache = this->getGlyphCache();
    500     SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
    501                                                                         fFont.isDevKernText(),
    502                                                                         true);
    503 
    504     fTotalGlyphCount = fFont.countText(text, byteLength);
    505     fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
    506                                             fTotalGlyphCount));
    507 
    508     const char* stop = text + byteLength;
    509 
    510     SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
    511     SkTextAlignProc alignProc(fFont.getTextAlign());
    512     FallbackBlobBuilder fallback;
    513     while (text < stop) {
    514         const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
    515         if (glyph.fWidth) {
    516             SkPoint tmsLoc;
    517             tmsProc(pos, &tmsLoc);
    518             SkPoint loc;
    519             alignProc(tmsLoc, glyph, &loc);
    520 
    521             this->appendGlyph(glyph, loc, &fallback);
    522         }
    523         pos += scalarsPerPosition;
    524     }
    525 
    526     fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
    527 }
    528 
    529 sk_sp<GrPathRange> GrStencilAndCoverTextContext::TextRun::createGlyphs(
    530                                                     GrResourceProvider* resourceProvider) const {
    531     sk_sp<GrPathRange> glyphs;
    532 
    533     glyphs.reset(static_cast<GrPathRange*>(
    534             resourceProvider->findAndRefResourceByUniqueKey(fGlyphPathsKey)));
    535     if (!glyphs) {
    536         if (fUsingRawGlyphPaths) {
    537             SkScalerContextEffects noeffects;
    538             glyphs = resourceProvider->createGlyphs(fFont.getTypeface(), noeffects,
    539                                                     nullptr, fStyle);
    540         } else {
    541             SkGlyphCache* cache = this->getGlyphCache();
    542             glyphs = resourceProvider->createGlyphs(cache->getScalerContext()->getTypeface(),
    543                                                     cache->getScalerContext()->getEffects(),
    544                                                     &cache->getDescriptor(),
    545                                                     fStyle);
    546         }
    547         resourceProvider->assignUniqueKeyToResource(fGlyphPathsKey, glyphs.get());
    548     }
    549     return glyphs;
    550 }
    551 
    552 inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
    553                                                                const SkPoint& pos,
    554                                                                FallbackBlobBuilder* fallback) {
    555     // Stick the glyphs we can't draw into the fallback text blob.
    556     if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
    557         if (!fallback->isInitialized()) {
    558             fallback->init(fFont, fTextRatio);
    559         }
    560         fallback->appendGlyph(glyph.getGlyphID(), pos);
    561     } else {
    562         fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
    563                               fTextInverseRatio * pos.y());
    564     }
    565 }
    566 
    567 void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
    568                                                  GrRenderTargetContext* renderTargetContext,
    569                                                  const GrClip& clip, const SkMatrix& viewMatrix,
    570                                                  const SkSurfaceProps& props, SkScalar x,
    571                                                  SkScalar y, const SkIRect& clipBounds,
    572                                                  GrAtlasTextContext* fallbackTextContext,
    573                                                  const SkPaint& originalSkPaint) const {
    574     SkASSERT(fInstanceData);
    575 
    576     if (fInstanceData->count()) {
    577         static constexpr GrUserStencilSettings kCoverPass(
    578             GrUserStencilSettings::StaticInit<
    579                 0x0000,
    580                 GrUserStencilTest::kNotEqual, // Stencil pass accounts for clip.
    581                 0xffff,
    582                 GrUserStencilOp::kZero,
    583                 GrUserStencilOp::kKeep,
    584                 0xffff>()
    585         );
    586 
    587         sk_sp<GrPathRange> glyphs(this->createGlyphs(ctx->resourceProvider()));
    588         if (fLastDrawnGlyphsID != glyphs->uniqueID()) {
    589             // Either this is the first draw or the glyphs object was purged since last draw.
    590             glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
    591             fLastDrawnGlyphsID = glyphs->uniqueID();
    592         }
    593 
    594         GrPaint grPaint;
    595         if (!SkPaintToGrPaint(ctx, renderTargetContext, originalSkPaint, viewMatrix, &grPaint)) {
    596             return;
    597         }
    598 
    599         // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
    600         // the entire dst. Realistically this is a moot point, because any context that supports
    601         // NV_path_rendering will also support NV_blend_equation_advanced.
    602         // For clipping we'll just skip any optimizations based on the bounds. This does, however,
    603         // hurt GrOp combining.
    604         const SkRect bounds = SkRect::MakeIWH(renderTargetContext->width(),
    605                                               renderTargetContext->height());
    606 
    607         // The run's "font" overrides the anti-aliasing of the passed in SkPaint!
    608         GrAAType aaType = GrChooseAAType(this->aa(), renderTargetContext->fsaaType(),
    609                                          GrAllowMixedSamples::kYes, *renderTargetContext->caps());
    610 
    611         std::unique_ptr<GrDrawOp> op = GrDrawPathRangeOp::Make(
    612                 viewMatrix, fTextRatio, fTextInverseRatio * x, fTextInverseRatio * y,
    613                 std::move(grPaint), GrPathRendering::kWinding_FillType, aaType, glyphs.get(),
    614                 fInstanceData.get(), bounds);
    615 
    616         renderTargetContext->addDrawOp(clip, std::move(op));
    617     }
    618 
    619     if (fFallbackTextBlob) {
    620         SkPaint fallbackSkPaint(originalSkPaint);
    621         fStyle.strokeRec().applyToPaint(&fallbackSkPaint);
    622         if (!fStyle.isSimpleFill()) {
    623             fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fTextRatio);
    624         }
    625 
    626         fallbackTextContext->drawTextBlob(ctx, renderTargetContext, clip, fallbackSkPaint,
    627                                           viewMatrix, props, fFallbackTextBlob.get(), x, y, nullptr,
    628                                           clipBounds);
    629     }
    630 }
    631 
    632 SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
    633     if (!fDetachedGlyphCache) {
    634         fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::kNone_ScalerContextFlags,
    635                                                 nullptr);
    636     }
    637     return fDetachedGlyphCache;
    638 }
    639 
    640 
    641 void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
    642     if (fDetachedGlyphCache) {
    643         SkGlyphCache::AttachCache(fDetachedGlyphCache);
    644         fDetachedGlyphCache = nullptr;
    645     }
    646 }
    647 
    648 size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
    649     size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
    650     // The instance data always reserves enough space for every glyph.
    651     size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
    652     if (fInstanceData) {
    653         size += sizeof(InstanceData);
    654     }
    655     if (fFallbackTextBlob) {
    656         size += sizeof(SkTextBlob);
    657     }
    658     return size;
    659 }
    660 
    661 ////////////////////////////////////////////////////////////////////////////////////////////////////
    662 
    663 void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
    664                                                              SkScalar textRatio) {
    665     SkASSERT(!this->isInitialized());
    666     fBuilder.reset(new SkTextBlobBuilder);
    667     fFont = font;
    668     fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
    669     fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    670     // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
    671     // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
    672     fFont.setSubpixelText(false);
    673     fFont.setTextSize(fFont.getTextSize() * textRatio);
    674     fBuffIdx = 0;
    675 }
    676 
    677 void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
    678                                                                     const SkPoint& pos) {
    679     SkASSERT(this->isInitialized());
    680     if (fBuffIdx >= kWriteBufferSize) {
    681         this->flush();
    682     }
    683     fGlyphIds[fBuffIdx] = glyphId;
    684     fPositions[fBuffIdx] = pos;
    685     fBuffIdx++;
    686     fCount++;
    687 }
    688 
    689 void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
    690     SkASSERT(this->isInitialized());
    691     SkASSERT(fBuffIdx <= kWriteBufferSize);
    692     if (!fBuffIdx) {
    693         return;
    694     }
    695     // This will automatically merge with previous runs since we use the same font.
    696     const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
    697     memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
    698     memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
    699     fBuffIdx = 0;
    700 }
    701 
    702 sk_sp<SkTextBlob> GrStencilAndCoverTextContext::FallbackBlobBuilder::makeIfNeeded(int *count) {
    703     *count = fCount;
    704     if (fCount) {
    705         this->flush();
    706         return fBuilder->make();
    707     }
    708     return nullptr;
    709 }
    710