Add TextRun object to nvpr text
authorcdalton <cdalton@nvidia.com>
Mon, 5 Oct 2015 21:57:20 +0000 (14:57 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 5 Oct 2015 21:57:20 +0000 (14:57 -0700)
Adds a TextRun object that defines a resolution-independent, paint-
indepent draw for a string of nvpr text.

BUG=skia:

Review URL: https://codereview.chromium.org/1375353004

src/gpu/GrStencilAndCoverTextContext.cpp
src/gpu/GrStencilAndCoverTextContext.h

index 9c0fc2a..9f394f9 100644 (file)
@@ -25,9 +25,7 @@
 
 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
                                                            const SkSurfaceProps& surfaceProps)
-    : INHERITED(context, surfaceProps)
-    , fDraw(nullptr)
-    , fStroke(SkStrokeRec::kFill_InitStyle) {
+    : INHERITED(context, surfaceProps) {
 }
 
 GrStencilAndCoverTextContext*
@@ -71,20 +69,103 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
                                               size_t byteLength,
                                               SkScalar x, SkScalar y,
                                               const SkIRect& regionClipBounds) {
-    SkASSERT(byteLength == 0 || text != nullptr);
+    TextRun run(skPaint);
+    run.setText(text, byteLength, x, y, fContext, &fSurfaceProps);
+    run.draw(dc, rt, clip, paint, viewMatrix, regionClipBounds, fFallbackTextContext, skPaint);
+}
+
+void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
+                                                 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& regionClipBounds) {
+    TextRun run(skPaint);
+    run.setPosText(text, byteLength, pos, scalarsPerPosition, offset, fContext, &fSurfaceProps);
+    run.draw(dc, rt, clip, paint, viewMatrix, regionClipBounds, fFallbackTextContext, skPaint);
+}
 
-    if (text == nullptr || byteLength == 0 /*|| fRC->isEmpty()*/) {
-        return;
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
+    : fStroke(fontAndStroke),
+      fFont(fontAndStroke) {
+    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);
     }
 
-    this->init(rt, clip, paint, skPaint, byteLength, viewMatrix, regionClipBounds);
+    if (!fFont.getPathEffect() && !fStroke.isDashed()) {
+        // We can draw the glyphs from canonically sized paths.
+        fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
+        fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
 
-    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
+        // 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;
+    }
+
+    // When drawing from canonically sized paths, the actual local coords are fTextRatio * coords.
+    fLocalMatrix.setScale(fTextRatio, fTextRatio);
+}
+
+GrStencilAndCoverTextContext::TextRun::~TextRun() {
+}
+
+void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
+                                                    SkScalar x, SkScalar y, GrContext* ctx,
+                                                    const SkSurfaceProps* surfaceProps) {
+    SkASSERT(byteLength == 0 || text != nullptr);
+
+    SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr);
+    SkGlyphCache* glyphCache = autoGlyphCache.getCache();
+
+    fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache),
+                                        GrPathRendering::kTranslate_PathTransformType,
+                                        fFont.countText(text, byteLength)));
+
+    SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
 
     const char* stop = text + byteLength;
 
     // Measure first if needed.
-    if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
+    if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
         SkFixed    stopX = 0;
         SkFixed    stopY = 0;
 
@@ -92,7 +173,7 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
         while (textPtr < stop) {
             // We don't need x, y here, since all subpixel variants will have the
             // same advance.
-            const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
+            const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0);
 
             stopX += glyph.fAdvanceX;
             stopY += glyph.fAdvanceY;
@@ -102,7 +183,7 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
         SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
         SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
 
-        if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
+        if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
             alignX = SkScalarHalf(alignX);
             alignY = SkScalarHalf(alignY);
         }
@@ -118,7 +199,7 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
     SkFixed fx = SkScalarToFixed(x);
     SkFixed fy = SkScalarToFixed(y);
     while (text < stop) {
-        const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
+        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)));
@@ -127,39 +208,30 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
         fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
         fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
     }
-
-    this->finish(dc);
 }
 
-void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
-                                                 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& regionClipBounds) {
+void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
+                                                       const SkScalar pos[], int scalarsPerPosition,
+                                                       const SkPoint& offset, GrContext* ctx,
+                                                       const SkSurfaceProps* surfaceProps) {
     SkASSERT(byteLength == 0 || text != nullptr);
     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
 
-    // nothing to draw
-    if (text == nullptr || byteLength == 0/* || fRC->isEmpty()*/) {
-        return;
-    }
+    SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr);
+    SkGlyphCache* glyphCache = autoGlyphCache.getCache();
 
-    this->init(rt, clip, paint, skPaint, byteLength, viewMatrix, regionClipBounds);
+    fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache),
+                                        GrPathRendering::kTranslate_PathTransformType,
+                                        fFont.countText(text, byteLength)));
 
-    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
+    SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
 
     const char* stop = text + byteLength;
 
     SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
-    SkTextAlignProc alignProc(fSkPaint.getTextAlign());
+    SkTextAlignProc alignProc(fFont.getTextAlign());
     while (text < stop) {
-        const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
+        const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
         if (glyph.fWidth) {
             SkPoint tmsLoc;
             tmsProc(pos, &tmsLoc);
@@ -170,23 +242,22 @@ void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarg
         }
         pos += scalarsPerPosition;
     }
-
-    this->finish(dc);
 }
 
-static GrPathRange* get_gr_glyphs(GrContext* ctx,
-                                  const SkTypeface* typeface,
-                                  const SkDescriptor* desc,
-                                  const GrStrokeInfo& stroke) {
+GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx,
+                                                                 SkGlyphCache* glyphCache) {
+    SkTypeface* typeface = fUsingRawGlyphPaths ? fFont.getTypeface()
+                                               : glyphCache->getScalerContext()->getTypeface();
+    const SkDescriptor* desc = fUsingRawGlyphPaths ? nullptr : &glyphCache->getDescriptor();
 
     static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
-    int strokeDataCount = stroke.computeUniqueKeyFragmentData32Cnt();
+    int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
     GrUniqueKey glyphKey;
     GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
     reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
     reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
     if (strokeDataCount > 0) {
-        stroke.asUniqueKeyFragment(&builder[2]);
+        fStroke.asUniqueKeyFragment(&builder[2]);
     }
     builder.finish();
 
@@ -194,7 +265,7 @@ static GrPathRange* get_gr_glyphs(GrContext* ctx,
         static_cast<GrPathRange*>(
             ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
     if (nullptr == glyphs) {
-        glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, stroke));
+        glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, fStroke));
         ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs);
     } else {
         SkASSERT(nullptr == desc || glyphs->isEqualTo(*desc));
@@ -203,107 +274,32 @@ static GrPathRange* get_gr_glyphs(GrContext* ctx,
     return glyphs.detach();
 }
 
-void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
-                                        const GrClip& clip,
-                                        const GrPaint& paint,
-                                        const SkPaint& skPaint,
-                                        size_t textByteLength,
-                                        const SkMatrix& viewMatrix,
-                                        const SkIRect& regionClipBounds) {
-    fClip = clip;
-
-    fRenderTarget.reset(SkRef(rt));
-
-    fRegionClipBounds = regionClipBounds;
-    fClip.getConservativeBounds(fRenderTarget->width(), fRenderTarget->height(), &fClipRect);
-
-    fPaint = paint;
-    fSkPaint = skPaint;
-
-    // Don't bake strokes into the glyph outlines. We will stroke the glyphs using the GPU instead.
-    fStroke = GrStrokeInfo(fSkPaint);
-    fSkPaint.setStyle(SkPaint::kFill_Style);
-
-    SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
-
-    if (fSkPaint.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
-        // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
-        SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
-                                                    kStdFakeBoldInterpKeys,
-                                                    kStdFakeBoldInterpValues,
-                                                    kStdFakeBoldInterpLength);
-        SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
-        fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
-                               true /*strokeAndFill*/);
-
-        fSkPaint.setFakeBoldText(false);
-    }
-
-    bool canUseRawPaths;
-    if (!fStroke.isDashed()) {
-        // We can draw the glyphs from canonically sized paths.
-        fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
-        fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
-
-        // Compensate for the glyphs being scaled by fTextRatio.
-        if (!fStroke.isFillStyle()) {
-            fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
-                                   SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
-        }
-
-        fSkPaint.setLinearText(true);
-        fSkPaint.setLCDRenderText(false);
-        fSkPaint.setAutohinted(false);
-        fSkPaint.setHinting(SkPaint::kNo_Hinting);
-        fSkPaint.setSubpixelText(true);
-        fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
-
-        canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
-                         0 == fSkPaint.getTextSkewX() &&
-                         !fSkPaint.isFakeBoldText() &&
-                         !fSkPaint.isVerticalText();
-    } else {
-        fTextRatio = fTextInverseRatio = 1.0f;
-        canUseRawPaths = false;
-    }
-
-    fViewMatrix = viewMatrix;
-    fViewMatrix.preScale(fTextRatio, fTextRatio);
-    fLocalMatrix.setScale(fTextRatio, fTextRatio);
-
-    fGlyphCache = fSkPaint.detachCache(&fSurfaceProps, nullptr, true /*ignoreGamma*/);
-    fGlyphs = canUseRawPaths ?
-                  get_gr_glyphs(fContext, fSkPaint.getTypeface(), nullptr, fStroke) :
-                  get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
-                                &fGlyphCache->getDescriptor(), fStroke);
-}
-
-inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) {
+inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
+                                                               const SkPoint& pos) {
     // Stick the glyphs we can't draw into the fallback arrays.
     if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
         fFallbackIndices.push_back(glyph.getGlyphID());
         fFallbackPositions.push_back(pos);
     } else {
-        // TODO: infer the reserve count from the text length.
-        if (!fDraw) {
-            fDraw = GrPathRangeDraw::Create(fGlyphs,
-                                            GrPathRendering::kTranslate_PathTransformType,
-                                            64);
-        }
         float translate[] = { fTextInverseRatio * pos.x(), fTextInverseRatio * pos.y() };
         fDraw->append(glyph.getGlyphID(), translate);
     }
 }
 
-void GrStencilAndCoverTextContext::flush(GrDrawContext* dc) {
-    if (fDraw) {
-        SkASSERT(fDraw->count());
+void GrStencilAndCoverTextContext::TextRun::draw(GrDrawContext* dc,
+                                                 GrRenderTarget* rt,
+                                                 const GrClip& clip,
+                                                 const GrPaint& paint,
+                                                 const SkMatrix& viewMatrix,
+                                                 const SkIRect& regionClipBounds,
+                                                 GrTextContext* fallbackTextContext,
+                                                 const SkPaint& originalSkPaint) const {
+    SkASSERT(fDraw);
 
-        // We should only be flushing about once every run.  However, if this impacts performance
-        // we could move the creation of the GrPipelineBuilder earlier.
-        GrPipelineBuilder pipelineBuilder(fPaint, fRenderTarget, fClip);
-        SkASSERT(fRenderTarget->isStencilBufferMultisampled() || !fPaint.isAntiAlias());
-        pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, fPaint.isAntiAlias());
+    if (fDraw->count()) {
+        GrPipelineBuilder pipelineBuilder(paint, rt, clip);
+        SkASSERT(rt->isStencilBufferMultisampled() || !paint.isAntiAlias());
+        pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, paint.isAntiAlias());
 
         GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
                                      kZero_StencilOp,
@@ -315,50 +311,38 @@ void GrStencilAndCoverTextContext::flush(GrDrawContext* dc) {
 
         *pipelineBuilder.stencil() = kStencilPass;
 
-        dc->drawPathsFromRange(&pipelineBuilder, fViewMatrix, fLocalMatrix, fPaint.getColor(),
-                               fDraw, GrPathRendering::kWinding_FillType);
-        fDraw->unref();
-        fDraw = nullptr;
+        SkMatrix drawMatrix(viewMatrix);
+        drawMatrix.preScale(fTextRatio, fTextRatio);
+
+        dc->drawPathsFromRange(&pipelineBuilder, drawMatrix, fLocalMatrix, paint.getColor(), fDraw,
+                               GrPathRendering::kWinding_FillType);
     }
 
     if (fFallbackIndices.count()) {
         SkASSERT(fFallbackPositions.count() == fFallbackIndices.count());
 
-        SkPaint fallbackSkPaint(fSkPaint);
+        enum { kPreservedFlags = SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
+                                 SkPaint::kLCDRenderText_Flag | SkPaint::kAutoHinting_Flag };
+
+        SkPaint fallbackSkPaint(originalSkPaint);
         fStroke.applyToPaint(&fallbackSkPaint);
         if (!fStroke.isFillStyle()) {
             fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
         }
         fallbackSkPaint.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
         fallbackSkPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        fallbackSkPaint.setHinting(fFont.getHinting());
+        fallbackSkPaint.setFlags((fFont.getFlags() & kPreservedFlags) |
+                                 (originalSkPaint.getFlags() & ~kPreservedFlags));
         // 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.
         fallbackSkPaint.setSubpixelText(false);
-        fallbackSkPaint.setTextSize(fSkPaint.getTextSize() * fTextRatio);
-
-        SkMatrix fallbackMatrix(fViewMatrix);
-        fallbackMatrix.preScale(fTextInverseRatio, fTextInverseRatio);
-
-        fFallbackTextContext->drawPosText(dc, fRenderTarget, fClip, fPaint, fallbackSkPaint,
-                                          fallbackMatrix, (char*)fFallbackIndices.begin(),
-                                          sizeof(uint16_t) * fFallbackIndices.count(),
-                                          fFallbackPositions[0].asScalars(), 2, SkPoint::Make(0, 0),
-                                          fRegionClipBounds);
-        fFallbackIndices.reset();
-        fFallbackPositions.reset();
-    }
-}
-
-void GrStencilAndCoverTextContext::finish(GrDrawContext* dc) {
-    this->flush(dc);
+        fallbackSkPaint.setTextSize(fFont.getTextSize() * fTextRatio);
 
-    SkASSERT(!fDraw);
-    SkASSERT(!fFallbackIndices.count());
-    SkASSERT(!fFallbackPositions.count());
-
-    fGlyphs->unref();
-    fGlyphs = nullptr;
-
-    SkGlyphCache::AttachCache(fGlyphCache);
-    fGlyphCache = nullptr;
+        fallbackTextContext->drawPosText(dc, rt, clip, paint, fallbackSkPaint, viewMatrix,
+                                         (char*)fFallbackIndices.begin(),
+                                         sizeof(uint16_t) * fFallbackIndices.count(),
+                                         fFallbackPositions[0].asScalars(), 2, SkPoint::Make(0, 0),
+                                         regionClipBounds);
+    }
 }
index 44b33a5..c5c29ad 100644 (file)
@@ -15,7 +15,6 @@
 class GrTextStrike;
 class GrPath;
 class GrPathRange;
-class GrPathRangeDraw;
 class SkSurfaceProps;
 
 /*
@@ -30,25 +29,6 @@ public:
     virtual ~GrStencilAndCoverTextContext();
 
 private:
-    SkScalar                                            fTextRatio;
-    float                                               fTextInverseRatio;
-    SkGlyphCache*                                       fGlyphCache;
-
-    GrPathRange*                                        fGlyphs;
-    GrPathRangeDraw*                                    fDraw;
-    GrStrokeInfo                                        fStroke;
-    SkSTArray<32, uint16_t, true>                       fFallbackIndices;
-    SkSTArray<32, SkPoint, true>                        fFallbackPositions;
-
-    SkMatrix                                            fViewMatrix;
-    SkMatrix                                            fLocalMatrix;
-    SkAutoTUnref<GrRenderTarget>                        fRenderTarget;
-    GrClip                                              fClip;
-    SkIRect                                             fClipRect;
-    SkIRect                                             fRegionClipBounds;
-    GrPaint                                             fPaint;
-    SkPaint                                             fSkPaint;
-
     GrStencilAndCoverTextContext(GrContext*, const SkSurfaceProps&);
 
     bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&,
@@ -65,11 +45,37 @@ private:
                        const SkScalar pos[], int scalarsPerPosition,
                        const SkPoint& offset, const SkIRect& regionClipBounds) override;
 
-    void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&, size_t textByteLength,
-              const SkMatrix& viewMatrix, const SkIRect& regionClipBounds);
-    void appendGlyph(const SkGlyph&, const SkPoint&);
-    void flush(GrDrawContext* dc);
-    void finish(GrDrawContext* dc);
+    class TextRun {
+    public:
+        TextRun(const SkPaint& fontAndStroke);
+        ~TextRun();
+
+        void setText(const char text[], size_t byteLength, SkScalar x, SkScalar y,
+                     GrContext*, const SkSurfaceProps*);
+
+        void setPosText(const char text[], size_t byteLength,
+                        const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset,
+                        GrContext*, const SkSurfaceProps*);
+
+        void draw(GrDrawContext*, GrRenderTarget*, const GrClip&, const GrPaint&, const SkMatrix&,
+                  const SkIRect& regionClipBounds, GrTextContext* fallbackTextContext,
+                  const SkPaint& originalSkPaint) const;
+
+    private:
+        GrPathRange* createGlyphs(GrContext*, SkGlyphCache*);
+
+        void appendGlyph(const SkGlyph&, const SkPoint&);
+
+        GrStrokeInfo                     fStroke;
+        SkPaint                          fFont;
+        SkScalar                         fTextRatio;
+        float                            fTextInverseRatio;
+        SkMatrix                         fLocalMatrix;
+        bool                             fUsingRawGlyphPaths;
+        SkAutoTUnref<GrPathRangeDraw>    fDraw;
+        SkSTArray<32, uint16_t, true>    fFallbackIndices;
+        SkSTArray<32, SkPoint, true>     fFallbackPositions;
+    };
 
     typedef GrTextContext INHERITED;
 };