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