| Index: src/gpu/GrStencilAndCoverTextContext.cpp
 | 
| diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp
 | 
| deleted file mode 100644
 | 
| index d28f1a803a0abe7df26843e7848fd21821d8b4f5..0000000000000000000000000000000000000000
 | 
| --- a/src/gpu/GrStencilAndCoverTextContext.cpp
 | 
| +++ /dev/null
 | 
| @@ -1,616 +0,0 @@
 | 
| -/*
 | 
| - * Copyright 2014 Google Inc.
 | 
| - *
 | 
| - * Use of this source code is governed by a BSD-style license that can be
 | 
| - * found in the LICENSE file.
 | 
| - */
 | 
| -
 | 
| -#include "GrStencilAndCoverTextContext.h"
 | 
| -#include "GrAtlasTextContext.h"
 | 
| -#include "GrDrawContext.h"
 | 
| -#include "GrDrawTarget.h"
 | 
| -#include "GrPath.h"
 | 
| -#include "GrPathRange.h"
 | 
| -#include "GrResourceProvider.h"
 | 
| -#include "SkAutoKern.h"
 | 
| -#include "SkDraw.h"
 | 
| -#include "SkDrawProcs.h"
 | 
| -#include "SkGlyphCache.h"
 | 
| -#include "SkGpuDevice.h"
 | 
| -#include "SkGrPriv.h"
 | 
| -#include "SkPath.h"
 | 
| -#include "SkTextBlobRunIterator.h"
 | 
| -#include "SkTextMapStateProc.h"
 | 
| -#include "SkTextFormatParams.h"
 | 
| -
 | 
| -#include "batches/GrDrawPathBatch.h"
 | 
| -
 | 
| -template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
 | 
| -    SkASSERT(*val);
 | 
| -    delete *val;
 | 
| -}
 | 
| -
 | 
| -template<typename T> static void delete_hash_table_entry(T* val) {
 | 
| -    SkASSERT(*val);
 | 
| -    delete *val;
 | 
| -}
 | 
| -
 | 
| -GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
 | 
| -                                                           const SkSurfaceProps& surfaceProps)
 | 
| -    : INHERITED(context, surfaceProps),
 | 
| -      fCacheSize(0) {
 | 
| -}
 | 
| -
 | 
| -GrStencilAndCoverTextContext*
 | 
| -GrStencilAndCoverTextContext::Create(GrContext* context, const SkSurfaceProps& surfaceProps) {
 | 
| -    GrStencilAndCoverTextContext* textContext = 
 | 
| -        new GrStencilAndCoverTextContext(context, surfaceProps);
 | 
| -    textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, surfaceProps);
 | 
| -
 | 
| -    return textContext;
 | 
| -}
 | 
| -
 | 
| -GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
 | 
| -    fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
 | 
| -    fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
 | 
| -}
 | 
| -
 | 
| -bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
 | 
| -    if (skPaint.getRasterizer()) {
 | 
| -        return false;
 | 
| -    }
 | 
| -    if (skPaint.getMaskFilter()) {
 | 
| -        return false;
 | 
| -    }
 | 
| -    if (SkPathEffect* pe = skPaint.getPathEffect()) {
 | 
| -        if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
 | 
| -            return false;
 | 
| -        }
 | 
| -    }
 | 
| -    // No hairlines. They would require new paths with customized strokes for every new draw matrix.
 | 
| -    return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
 | 
| -}
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc,
 | 
| -                                              const GrClip& clip,
 | 
| -                                              const GrPaint& paint,
 | 
| -                                              const SkPaint& skPaint,
 | 
| -                                              const SkMatrix& viewMatrix,
 | 
| -                                              const char text[],
 | 
| -                                              size_t byteLength,
 | 
| -                                              SkScalar x, SkScalar y,
 | 
| -                                              const SkIRect& clipBounds) {
 | 
| -    TextRun run(skPaint);
 | 
| -    GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
 | 
| -    run.setText(text, byteLength, x, y);
 | 
| -    run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
 | 
| -             fFallbackTextContext, skPaint);
 | 
| -}
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc,
 | 
| -                                                 const GrClip& clip,
 | 
| -                                                 const GrPaint& paint,
 | 
| -                                                 const SkPaint& skPaint,
 | 
| -                                                 const SkMatrix& viewMatrix,
 | 
| -                                                 const char text[],
 | 
| -                                                 size_t byteLength,
 | 
| -                                                 const SkScalar pos[],
 | 
| -                                                 int scalarsPerPosition,
 | 
| -                                                 const SkPoint& offset,
 | 
| -                                                 const SkIRect& clipBounds) {
 | 
| -    TextRun run(skPaint);
 | 
| -    GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
 | 
| -    run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
 | 
| -    run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
 | 
| -             fFallbackTextContext, skPaint);
 | 
| -}
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc,
 | 
| -                                                const GrClip& clip, const SkPaint& skPaint,
 | 
| -                                                const SkMatrix& viewMatrix,
 | 
| -                                                const SkTextBlob* skBlob, SkScalar x, SkScalar y,
 | 
| -                                                SkDrawFilter* drawFilter,
 | 
| -                                                const SkIRect& clipBounds) {
 | 
| -    if (!this->internalCanDraw(skPaint)) {
 | 
| -        fFallbackTextContext->drawTextBlob(dc, clip, skPaint, viewMatrix, skBlob, x, y,
 | 
| -                                           drawFilter, clipBounds);
 | 
| -        return;
 | 
| -    }
 | 
| -
 | 
| -    if (drawFilter || skPaint.getPathEffect()) {
 | 
| -        // This draw can't be cached.
 | 
| -        INHERITED::drawTextBlob(dc, clip, skPaint, viewMatrix, skBlob, x, y, drawFilter,
 | 
| -                                clipBounds);
 | 
| -        return;
 | 
| -    }
 | 
| -
 | 
| -    if (fContext->abandoned()) {
 | 
| -        return;
 | 
| -    }
 | 
| -
 | 
| -    GrPaint paint;
 | 
| -    if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &paint)) {
 | 
| -        return;
 | 
| -    }
 | 
| -
 | 
| -    const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
 | 
| -    GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
 | 
| -
 | 
| -    TextBlob::Iter iter(blob);
 | 
| -    for (TextRun* run = iter.get(); run; run = iter.next()) {
 | 
| -        run->draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, x, y, clipBounds,
 | 
| -                  fFallbackTextContext, skPaint);
 | 
| -        run->releaseGlyphCache();
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -const GrStencilAndCoverTextContext::TextBlob&
 | 
| -GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
 | 
| -                                                   const SkPaint& skPaint) {
 | 
| -    // The font-related parameters are baked into the text blob and will override this skPaint, so
 | 
| -    // the only remaining properties that can affect a TextBlob are the ones related to stroke.
 | 
| -    if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
 | 
| -        if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
 | 
| -            fLRUList.remove(*found);
 | 
| -            fLRUList.addToTail(*found);
 | 
| -            return **found;
 | 
| -        }
 | 
| -        TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
 | 
| -        this->purgeToFit(*blob);
 | 
| -        fBlobIdCache.set(skBlob->uniqueID(), blob);
 | 
| -        fLRUList.addToTail(blob);
 | 
| -        fCacheSize += blob->cpuMemorySize();
 | 
| -        return *blob;
 | 
| -    } else {
 | 
| -        GrStrokeInfo stroke(skPaint);
 | 
| -        SkSTArray<4, uint32_t, true> key;
 | 
| -        key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt());
 | 
| -        key[0] = skBlob->uniqueID();
 | 
| -        stroke.asUniqueKeyFragment(&key[1]);
 | 
| -        if (TextBlob** found = fBlobKeyCache.find(key)) {
 | 
| -            fLRUList.remove(*found);
 | 
| -            fLRUList.addToTail(*found);
 | 
| -            return **found;
 | 
| -        }
 | 
| -        TextBlob* blob = new TextBlob(key, skBlob, skPaint);
 | 
| -        this->purgeToFit(*blob);
 | 
| -        fBlobKeyCache.set(blob);
 | 
| -        fLRUList.addToTail(blob);
 | 
| -        fCacheSize += blob->cpuMemorySize();
 | 
| -        return *blob;
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
 | 
| -    static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
 | 
| -
 | 
| -    size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
 | 
| -    while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
 | 
| -        TextBlob* lru = fLRUList.head();
 | 
| -        if (1 == lru->key().count()) {
 | 
| -            // 1-length keys are unterstood to be the blob id.
 | 
| -            fBlobIdCache.remove(lru->key()[0]);
 | 
| -        } else {
 | 
| -            fBlobKeyCache.remove(lru->key());
 | 
| -        }
 | 
| -        fLRUList.remove(lru);
 | 
| -        fCacheSize -= lru->cpuMemorySize();
 | 
| -        delete lru;
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
 | 
| -                                                  const SkPaint& skPaint) {
 | 
| -    fCpuMemorySize = sizeof(TextBlob);
 | 
| -    SkPaint runPaint(skPaint);
 | 
| -    for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
 | 
| -        iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
 | 
| -        TextRun* run = this->addToTail(runPaint);
 | 
| -
 | 
| -        const char* text = reinterpret_cast<const char*>(iter.glyphs());
 | 
| -        size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
 | 
| -        const SkPoint& runOffset = iter.offset();
 | 
| -
 | 
| -        switch (iter.positioning()) {
 | 
| -            case SkTextBlob::kDefault_Positioning:
 | 
| -                run->setText(text, byteLength, runOffset.fX, runOffset.fY);
 | 
| -                break;
 | 
| -            case SkTextBlob::kHorizontal_Positioning:
 | 
| -                run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
 | 
| -                break;
 | 
| -            case SkTextBlob::kFull_Positioning:
 | 
| -                run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
 | 
| -                break;
 | 
| -        }
 | 
| -
 | 
| -        fCpuMemorySize += run->computeSizeInCache();
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
| -
 | 
| -class GrStencilAndCoverTextContext::FallbackBlobBuilder {
 | 
| -public:
 | 
| -    FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
 | 
| -
 | 
| -    bool isInitialized() const { return SkToBool(fBuilder); }
 | 
| -
 | 
| -    void init(const SkPaint& font, SkScalar textRatio);
 | 
| -
 | 
| -    void appendGlyph(uint16_t glyphId, const SkPoint& pos);
 | 
| -
 | 
| -    const SkTextBlob* buildIfNeeded(int* count);
 | 
| -
 | 
| -private:
 | 
| -    enum { kWriteBufferSize = 1024 };
 | 
| -
 | 
| -    void flush();
 | 
| -
 | 
| -    SkAutoTDelete<SkTextBlobBuilder>   fBuilder;
 | 
| -    SkPaint                            fFont;
 | 
| -    int                                fBuffIdx;
 | 
| -    int                                fCount;
 | 
| -    uint16_t                           fGlyphIds[kWriteBufferSize];
 | 
| -    SkPoint                            fPositions[kWriteBufferSize];
 | 
| -};
 | 
| -
 | 
| -////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
| -
 | 
| -GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
 | 
| -    : fStroke(fontAndStroke),
 | 
| -      fFont(fontAndStroke),
 | 
| -      fTotalGlyphCount(0),
 | 
| -      fFallbackGlyphCount(0),
 | 
| -      fDetachedGlyphCache(nullptr),
 | 
| -      fLastDrawnGlyphsID(SK_InvalidUniqueID) {
 | 
| -    SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
 | 
| -
 | 
| -    // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
 | 
| -    // rendering API for stroking).
 | 
| -    fFont.setStyle(SkPaint::kFill_Style);
 | 
| -
 | 
| -    if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
 | 
| -        // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
 | 
| -        SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
 | 
| -                                                    kStdFakeBoldInterpKeys,
 | 
| -                                                    kStdFakeBoldInterpValues,
 | 
| -                                                    kStdFakeBoldInterpLength);
 | 
| -        SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
 | 
| -        fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
 | 
| -                               true /*strokeAndFill*/);
 | 
| -
 | 
| -        fFont.setFakeBoldText(false);
 | 
| -    }
 | 
| -
 | 
| -    if (!fFont.getPathEffect() && !fStroke.isDashed()) {
 | 
| -        // We can draw the glyphs from canonically sized paths.
 | 
| -        fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
 | 
| -        fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
 | 
| -
 | 
| -        // Compensate for the glyphs being scaled by fTextRatio.
 | 
| -        if (!fStroke.isFillStyle()) {
 | 
| -            fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
 | 
| -                                   SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
 | 
| -        }
 | 
| -
 | 
| -        fFont.setLinearText(true);
 | 
| -        fFont.setLCDRenderText(false);
 | 
| -        fFont.setAutohinted(false);
 | 
| -        fFont.setHinting(SkPaint::kNo_Hinting);
 | 
| -        fFont.setSubpixelText(true);
 | 
| -        fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
 | 
| -
 | 
| -        fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
 | 
| -                              0 == fFont.getTextSkewX() &&
 | 
| -                              !fFont.isFakeBoldText() &&
 | 
| -                              !fFont.isVerticalText();
 | 
| -    } else {
 | 
| -        fTextRatio = fTextInverseRatio = 1.0f;
 | 
| -        fUsingRawGlyphPaths = false;
 | 
| -    }
 | 
| -
 | 
| -    // Generate the key that will be used to cache the GPU glyph path objects.
 | 
| -    if (fUsingRawGlyphPaths && fStroke.isFillStyle()) {
 | 
| -        static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
 | 
| -
 | 
| -        const SkTypeface* typeface = fFont.getTypeface();
 | 
| -        GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
 | 
| -        reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
 | 
| -    } else {
 | 
| -        static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
 | 
| -
 | 
| -        int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
 | 
| -        if (fUsingRawGlyphPaths) {
 | 
| -            const SkTypeface* typeface = fFont.getTypeface();
 | 
| -            GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + strokeDataCount);
 | 
| -            reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
 | 
| -            reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount;
 | 
| -            fStroke.asUniqueKeyFragment(&builder[2]);
 | 
| -        } else {
 | 
| -            SkGlyphCache* glyphCache = this->getGlyphCache();
 | 
| -            const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
 | 
| -            const SkDescriptor* desc = &glyphCache->getDescriptor();
 | 
| -            int descDataCount = (desc->getLength() + 3) / 4;
 | 
| -            GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
 | 
| -                                         2 + strokeDataCount + descDataCount);
 | 
| -            reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
 | 
| -            reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDataCount << 16);
 | 
| -            fStroke.asUniqueKeyFragment(&builder[2]);
 | 
| -            memcpy(&builder[2 + strokeDataCount], desc, desc->getLength());
 | 
| -        }
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -GrStencilAndCoverTextContext::TextRun::~TextRun() {
 | 
| -    this->releaseGlyphCache();
 | 
| -}
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
 | 
| -                                                    SkScalar x, SkScalar y) {
 | 
| -    SkASSERT(byteLength == 0 || text != nullptr);
 | 
| -
 | 
| -    SkGlyphCache* glyphCache = this->getGlyphCache();
 | 
| -    SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
 | 
| -
 | 
| -    fTotalGlyphCount = fFont.countText(text, byteLength);
 | 
| -    fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
 | 
| -                                            fTotalGlyphCount));
 | 
| -
 | 
| -    const char* stop = text + byteLength;
 | 
| -
 | 
| -    // Measure first if needed.
 | 
| -    if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
 | 
| -        SkFixed    stopX = 0;
 | 
| -        SkFixed    stopY = 0;
 | 
| -
 | 
| -        const char* textPtr = text;
 | 
| -        while (textPtr < stop) {
 | 
| -            // We don't need x, y here, since all subpixel variants will have the
 | 
| -            // same advance.
 | 
| -            const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0);
 | 
| -
 | 
| -            stopX += glyph.fAdvanceX;
 | 
| -            stopY += glyph.fAdvanceY;
 | 
| -        }
 | 
| -        SkASSERT(textPtr == stop);
 | 
| -
 | 
| -        SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
 | 
| -        SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
 | 
| -
 | 
| -        if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
 | 
| -            alignX = SkScalarHalf(alignX);
 | 
| -            alignY = SkScalarHalf(alignY);
 | 
| -        }
 | 
| -
 | 
| -        x -= alignX;
 | 
| -        y -= alignY;
 | 
| -    }
 | 
| -
 | 
| -    SkAutoKern autokern;
 | 
| -
 | 
| -    SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
 | 
| -
 | 
| -    SkFixed fx = SkScalarToFixed(x);
 | 
| -    SkFixed fy = SkScalarToFixed(y);
 | 
| -    FallbackBlobBuilder fallback;
 | 
| -    while (text < stop) {
 | 
| -        const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
 | 
| -        fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
 | 
| -        if (glyph.fWidth) {
 | 
| -            this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)),
 | 
| -                              &fallback);
 | 
| -        }
 | 
| -
 | 
| -        fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
 | 
| -        fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
 | 
| -    }
 | 
| -
 | 
| -    fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
 | 
| -}
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
 | 
| -                                                       const SkScalar pos[], int scalarsPerPosition,
 | 
| -                                                       const SkPoint& offset) {
 | 
| -    SkASSERT(byteLength == 0 || text != nullptr);
 | 
| -    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
 | 
| -
 | 
| -    SkGlyphCache* glyphCache = this->getGlyphCache();
 | 
| -    SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
 | 
| -
 | 
| -    fTotalGlyphCount = fFont.countText(text, byteLength);
 | 
| -    fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
 | 
| -                                            fTotalGlyphCount));
 | 
| -
 | 
| -    const char* stop = text + byteLength;
 | 
| -
 | 
| -    SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
 | 
| -    SkTextAlignProc alignProc(fFont.getTextAlign());
 | 
| -    FallbackBlobBuilder fallback;
 | 
| -    while (text < stop) {
 | 
| -        const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
 | 
| -        if (glyph.fWidth) {
 | 
| -            SkPoint tmsLoc;
 | 
| -            tmsProc(pos, &tmsLoc);
 | 
| -            SkPoint loc;
 | 
| -            alignProc(tmsLoc, glyph, &loc);
 | 
| -
 | 
| -            this->appendGlyph(glyph, loc, &fallback);
 | 
| -        }
 | 
| -        pos += scalarsPerPosition;
 | 
| -    }
 | 
| -
 | 
| -    fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
 | 
| -}
 | 
| -
 | 
| -GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const {
 | 
| -    GrPathRange* glyphs = static_cast<GrPathRange*>(
 | 
| -            ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey));
 | 
| -    if (nullptr == glyphs) {
 | 
| -        if (fUsingRawGlyphPaths) {
 | 
| -            glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), nullptr, fStroke);
 | 
| -        } else {
 | 
| -            SkGlyphCache* cache = this->getGlyphCache();
 | 
| -            glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(),
 | 
| -                                                           &cache->getDescriptor(),
 | 
| -                                                           fStroke);
 | 
| -        }
 | 
| -        ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
 | 
| -    }
 | 
| -    return glyphs;
 | 
| -}
 | 
| -
 | 
| -inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
 | 
| -                                                               const SkPoint& pos,
 | 
| -                                                               FallbackBlobBuilder* fallback) {
 | 
| -    // Stick the glyphs we can't draw into the fallback text blob.
 | 
| -    if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
 | 
| -        if (!fallback->isInitialized()) {
 | 
| -            fallback->init(fFont, fTextRatio);
 | 
| -        }
 | 
| -        fallback->appendGlyph(glyph.getGlyphID(), pos);
 | 
| -    } else {
 | 
| -        fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
 | 
| -                              fTextInverseRatio * pos.y());
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
 | 
| -                                                 GrDrawContext* dc,
 | 
| -                                                 GrPipelineBuilder* pipelineBuilder,
 | 
| -                                                 GrColor color,
 | 
| -                                                 const SkMatrix& viewMatrix,
 | 
| -                                                 SkScalar x, SkScalar y,
 | 
| -                                                 const SkIRect& clipBounds,
 | 
| -                                                 GrTextContext* fallbackTextContext,
 | 
| -                                                 const SkPaint& originalSkPaint) const {
 | 
| -    SkASSERT(fInstanceData);
 | 
| -    SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.isAntiAlias());
 | 
| -
 | 
| -    if (fInstanceData->count()) {
 | 
| -        pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias());
 | 
| -
 | 
| -        GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
 | 
| -                                     kZero_StencilOp,
 | 
| -                                     kKeep_StencilOp,
 | 
| -                                     kNotEqual_StencilFunc,
 | 
| -                                     0xffff,
 | 
| -                                     0x0000,
 | 
| -                                     0xffff);
 | 
| -
 | 
| -        *pipelineBuilder->stencil() = kStencilPass;
 | 
| -
 | 
| -        SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx));
 | 
| -        if (fLastDrawnGlyphsID != glyphs->getUniqueID()) {
 | 
| -            // Either this is the first draw or the glyphs object was purged since last draw.
 | 
| -            glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
 | 
| -            fLastDrawnGlyphsID = glyphs->getUniqueID();
 | 
| -        }
 | 
| -
 | 
| -        // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
 | 
| -        // the entire dst. Realistically this is a moot point, because any context that supports
 | 
| -        // NV_path_rendering will also support NV_blend_equation_advanced.
 | 
| -        // For clipping we'll just skip any optimizations based on the bounds. This does, however,
 | 
| -        // hurt batching.
 | 
| -        SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->width(),
 | 
| -                                        pipelineBuilder->getRenderTarget()->height());
 | 
| -
 | 
| -        SkAutoTUnref<GrDrawPathBatchBase> batch(
 | 
| -            GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x,
 | 
| -                                         fTextInverseRatio * y, color,
 | 
| -                                         GrPathRendering::kWinding_FillType, glyphs, fInstanceData,
 | 
| -                                         bounds));
 | 
| -
 | 
| -        dc->drawPathBatch(*pipelineBuilder, batch);
 | 
| -    }
 | 
| -
 | 
| -    if (fFallbackTextBlob) {
 | 
| -        SkPaint fallbackSkPaint(originalSkPaint);
 | 
| -        fStroke.applyToPaint(&fallbackSkPaint);
 | 
| -        if (!fStroke.isFillStyle()) {
 | 
| -            fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
 | 
| -        }
 | 
| -
 | 
| -        fallbackTextContext->drawTextBlob(dc, pipelineBuilder->clip(), fallbackSkPaint, viewMatrix,
 | 
| -                                          fFallbackTextBlob, x, y, nullptr, clipBounds);
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
 | 
| -    if (!fDetachedGlyphCache) {
 | 
| -        fDetachedGlyphCache = fFont.detachCache(nullptr, nullptr, true /*ignoreGamma*/);
 | 
| -    }
 | 
| -    return fDetachedGlyphCache;
 | 
| -}
 | 
| -
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
 | 
| -    if (fDetachedGlyphCache) {
 | 
| -        SkGlyphCache::AttachCache(fDetachedGlyphCache);
 | 
| -        fDetachedGlyphCache = nullptr;
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
 | 
| -    size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
 | 
| -    // The instance data always reserves enough space for every glyph.
 | 
| -    size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
 | 
| -    if (fInstanceData) {
 | 
| -        size += sizeof(InstanceData);
 | 
| -    }
 | 
| -    if (fFallbackTextBlob) {
 | 
| -        size += sizeof(SkTextBlob);
 | 
| -    }
 | 
| -    return size;
 | 
| -}
 | 
| -
 | 
| -////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
 | 
| -                                                             SkScalar textRatio) {
 | 
| -    SkASSERT(!this->isInitialized());
 | 
| -    fBuilder.reset(new SkTextBlobBuilder);
 | 
| -    fFont = font;
 | 
| -    fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
 | 
| -    fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 | 
| -    // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
 | 
| -    // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
 | 
| -    fFont.setSubpixelText(false);
 | 
| -    fFont.setTextSize(fFont.getTextSize() * textRatio);
 | 
| -    fBuffIdx = 0;
 | 
| -}
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
 | 
| -                                                                    const SkPoint& pos) {
 | 
| -    SkASSERT(this->isInitialized());
 | 
| -    if (fBuffIdx >= kWriteBufferSize) {
 | 
| -        this->flush();
 | 
| -    }
 | 
| -    fGlyphIds[fBuffIdx] = glyphId;
 | 
| -    fPositions[fBuffIdx] = pos;
 | 
| -    fBuffIdx++;
 | 
| -    fCount++;
 | 
| -}
 | 
| -
 | 
| -void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
 | 
| -    SkASSERT(this->isInitialized());
 | 
| -    SkASSERT(fBuffIdx <= kWriteBufferSize);
 | 
| -    if (!fBuffIdx) {
 | 
| -        return;
 | 
| -    }
 | 
| -    // This will automatically merge with previous runs since we use the same font.
 | 
| -    const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
 | 
| -    memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
 | 
| -    memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
 | 
| -    fBuffIdx = 0;
 | 
| -}
 | 
| -
 | 
| -const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeeded(int *count) {
 | 
| -    *count = fCount;
 | 
| -    if (fCount) {
 | 
| -        this->flush();
 | 
| -        return fBuilder->build();
 | 
| -    }
 | 
| -    return nullptr;
 | 
| -}
 | 
| 
 |