Split GrTextContext into baseclass and subclass

This is a step towards enabling alternate text rendering code paths (GLyphy in particular)

Committed on behalf of baranowski@chromium.org

Review URL: http://codereview.appspot.com/5796071/



git-svn-id: http://skia.googlecode.com/svn/trunk@3412 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index 410a5b7..7080ebb 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -189,6 +189,8 @@
         '../src/gpu/GrAllocPool.cpp',
         '../src/gpu/GrAtlas.cpp',
         '../src/gpu/GrAtlas.h',
+        '../src/gpu/GrBatchedTextContext.cpp',
+        '../src/gpu/GrBatchedTextContext.h',
         '../src/gpu/GrBinHashKey.h',
         '../src/gpu/GrBufferAllocPool.cpp',
         '../src/gpu/GrBufferAllocPool.h',
@@ -196,6 +198,8 @@
         '../src/gpu/GrContext.cpp',
         '../src/gpu/GrDefaultPathRenderer.cpp',
         '../src/gpu/GrDefaultPathRenderer.h',
+        '../src/gpu/GrDefaultTextContext.cpp',
+        '../src/gpu/GrDefaultTextContext.h',
         '../src/gpu/GrDrawState.h',
         '../src/gpu/GrDrawTarget.cpp',
         '../src/gpu/GrDrawTarget.h',
@@ -233,7 +237,6 @@
         '../src/gpu/GrTDArray.h',
         '../src/gpu/GrTesselatedPathRenderer.cpp',
         '../src/gpu/GrTesselatedPathRenderer.h',
-        '../src/gpu/GrTextContext.cpp',
         '../src/gpu/GrTextStrike.cpp',
         '../src/gpu/GrTextStrike.h',
         '../src/gpu/GrTextStrike_impl.h',
diff --git a/include/gpu/GrTextContext.h b/include/gpu/GrTextContext.h
index 5983e35..588ae6e 100644
--- a/include/gpu/GrTextContext.h
+++ b/include/gpu/GrTextContext.h
@@ -12,57 +12,99 @@
 #define GrTextContext_DEFINED
 
 #include "GrGlyph.h"
-#include "GrPaint.h"
 #include "GrMatrix.h"
+#include "GrRefCnt.h"
 
-struct GrGpuTextVertex;
 class GrContext;
-class GrTextStrike;
 class GrFontScaler;
-class GrDrawTarget;
+class GrPaint;
 
-class GrTextContext {
-public:
-    GrTextContext(GrContext*,
-                  const GrPaint& paint,
-                  const GrMatrix* extMatrix = NULL);
-    ~GrTextContext();
+class SkGpuDevice;
+class SkPaint;
 
-    void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top,
-                         GrFontScaler*);
-
-    void flush();   // optional; automatically called by destructor
-
-private:
-    GrPaint         fPaint;
-    GrVertexLayout  fVertexLayout;
+class GrTextContext: public GrRefCnt {
+protected:
     GrContext*      fContext;
-    GrDrawTarget*   fDrawTarget;
 
-    GrMatrix        fExtMatrix;
-    GrFontScaler*   fScaler;
-    GrTextStrike*   fStrike;
+public:
+    /**
+     * To use a text context it must be wrapped in an AutoFinish. AutoFinish's
+     * destructor ensures all drawing is flushed to the GrContext.
+     */
+    class AutoFinish {
+    public:
+        AutoFinish(GrTextContext* textContext, GrContext* context,
+                   const GrPaint&, const GrMatrix* extMatrix);
+        ~AutoFinish();
+        GrTextContext* getTextContext() const;
 
-    inline void flushGlyphs();
-    void setupDrawTarget();
-
-    enum {
-        kMinRequestedGlyphs      = 1,
-        kDefaultRequestedGlyphs  = 64,
-        kMinRequestedVerts       = kMinRequestedGlyphs * 4,
-        kDefaultRequestedVerts   = kDefaultRequestedGlyphs * 4,
+    private:
+        GrTextContext* fTextContext;
     };
 
-    GrGpuTextVertex* fVertices;
+    virtual void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top,
+                                 GrFontScaler*) = 0;
 
-    int32_t     fMaxVertices;
-    GrTexture*  fCurrTexture;
-    int         fCurrVertex;
+    virtual ~GrTextContext() {}
 
-    GrIRect     fClipRect;
-    GrMatrix    fOrigViewMatrix;    // restore previous viewmatrix
+protected:
+    GrTextContext() {
+        fContext = NULL;
+    }
+
+    bool isValid() const {
+        return (NULL != fContext);
+    }
+
+    /**
+     * Initialize the object.
+     *
+     * Before call to this method, the instance is considered to be in
+     * invalid state. I.e. call to any method other than isValid will result in
+     * undefined behaviour.
+     *
+     * @see finish
+     */
+    virtual void init(GrContext* context, const GrPaint&,
+                      const GrMatrix* extMatrix) {
+        fContext = context;
+    }
+
+    /**
+     * Reset the object to invalid state.
+     *
+     * After call to this method, the instance is considered to be in
+     * invalid state.
+     *
+     * It might be brought back to a valid state by calling init.
+     *
+     * @see init
+     */
+    virtual void finish() {
+        fContext = NULL;
+    }
+
+private:
+    typedef GrRefCnt INHERITED;
 };
 
+inline GrTextContext::AutoFinish::AutoFinish(GrTextContext* textContext,
+                                             GrContext* context,
+                                             const GrPaint& grPaint,
+                                             const GrMatrix* extMatrix) {
+    GrAssert(NULL != textContext);
+    fTextContext = textContext;
+    fTextContext->ref();
+    fTextContext->init(context, grPaint, extMatrix);
+}
+
+inline GrTextContext::AutoFinish::~AutoFinish() {
+    fTextContext->finish();
+    fTextContext->unref();
+}
+
+inline GrTextContext* GrTextContext::AutoFinish::getTextContext() const {
+    return fTextContext;
+}
+
 #endif
-
-
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 46c15d6..24e6e5a 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -147,6 +147,8 @@
     bool                fNeedClear;
     bool                fNeedPrepareRenderTarget;
 
+    GrTextContext*      fTextContext;
+
     // called from rt and tex cons
     void initFromRenderTarget(GrContext*, GrRenderTarget*);
 
@@ -167,6 +169,11 @@
     void internalDrawBitmap(const SkDraw&, const SkBitmap&,
                             const SkIRect&, const SkMatrix&, GrPaint* grPaint);
 
+    /**
+     * Returns non-initialized instance.
+     */
+    GrTextContext* getTextContext();
+
     typedef SkDevice INHERITED;
 };
 
diff --git a/src/gpu/GrBatchedTextContext.cpp b/src/gpu/GrBatchedTextContext.cpp
new file mode 100644
index 0000000..3b3a4ba
--- /dev/null
+++ b/src/gpu/GrBatchedTextContext.cpp
@@ -0,0 +1,97 @@
+
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#include "GrBatchedTextContext.h"
+#include "GrContext.h"
+#include "GrDrawTarget.h"
+#include "GrIndexBuffer.h"
+#include "GrTextContext.h"
+
+
+GrBatchedTextContext::GrBatchedTextContext() {
+}
+
+GrBatchedTextContext::~GrBatchedTextContext() {
+}
+
+void GrBatchedTextContext::init(GrContext* context,
+                                const GrPaint& grPaint,
+                                const GrMatrix* extMatrix) {
+    this->INHERITED::init(context, grPaint, extMatrix);
+    fGrPaint = grPaint;
+    fDrawTarget = fContext->getTextTarget(fGrPaint);
+
+    fMaxVertices = 0;
+    fCurrTexture = NULL;
+    fCurrVertex = 0;
+}
+
+void GrBatchedTextContext::finish() {
+    fDrawTarget = NULL;
+
+    this->INHERITED::finish();
+}
+
+void GrBatchedTextContext::reset() {
+    GrAssert(this->isValid());
+    fDrawTarget->resetVertexSource();
+    fMaxVertices = 0;
+    fCurrVertex = 0;
+    fCurrTexture->unref();
+    fCurrTexture = NULL;
+}
+
+void GrBatchedTextContext::prepareForGlyph(GrTexture* texture) {
+    GrAssert(this->isValid());
+    GrAssert(texture);
+    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
+        this->flush();
+        fCurrTexture = texture;
+        fCurrTexture->ref();
+    }
+}
+
+void GrBatchedTextContext::setupVertexBuff(void** vertexBuff,
+                                           GrVertexLayout vertexLayout) {
+    GrAssert(this->isValid());
+    if (NULL == *vertexBuff) {
+        // If we need to reserve vertices allow the draw target to suggest
+        // a number of verts to reserve and whether to perform a flush.
+        fMaxVertices = kMinRequestedVerts;
+        bool flush = fDrawTarget->geometryHints(vertexLayout,
+                                                &fMaxVertices,
+                                                NULL);
+        if (flush) {
+            this->flush();
+            fContext->flushText();
+            fDrawTarget = fContext->getTextTarget(fGrPaint);
+            fMaxVertices = kDefaultRequestedVerts;
+            // ignore return, no point in flushing again.
+            fDrawTarget->geometryHints(vertexLayout,
+                                       &fMaxVertices,
+                                       NULL);
+        }
+
+        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
+        if (fMaxVertices < kMinRequestedVerts) {
+            fMaxVertices = kDefaultRequestedVerts;
+        } else if (fMaxVertices > maxQuadVertices) {
+            // don't exceed the limit of the index buffer
+            fMaxVertices = maxQuadVertices;
+        }
+        bool success = fDrawTarget->reserveVertexAndIndexSpace(
+                                                   vertexLayout,
+                                                   fMaxVertices,
+                                                   0,
+                                                   vertexBuff,
+                                                   NULL);
+        GrAlwaysAssert(success);
+    }
+}
diff --git a/src/gpu/GrBatchedTextContext.h b/src/gpu/GrBatchedTextContext.h
new file mode 100644
index 0000000..c0a136b
--- /dev/null
+++ b/src/gpu/GrBatchedTextContext.h
@@ -0,0 +1,82 @@
+
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrBatchedTextContext_DEFINED
+#define GrBatchedTextContext_DEFINED
+
+#include "GrPaint.h"
+#include "GrTextContext.h"
+
+class GrDrawTarget;
+class GrTexture;
+
+/**
+ * Base class for TextContexts that can batch multiple glyphs into single draw.
+ *
+ * Every glyph is encoded on a single texture.
+ * Every glyph is enclosed within a quad (formed by triangle fan) represented
+ * by 4 vertices.
+ */
+class GrBatchedTextContext: public GrTextContext {
+public:
+    virtual ~GrBatchedTextContext();
+
+protected:
+    enum {
+        kMinRequestedGlyphs      = 1,
+        kDefaultRequestedGlyphs  = 64,
+        kMinRequestedVerts       = kMinRequestedGlyphs * 4,
+        kDefaultRequestedVerts   = kDefaultRequestedGlyphs * 4,
+        kGlyphMaskStage          = GrPaint::kTotalStages,
+    };
+
+    GrPaint         fGrPaint;
+    GrDrawTarget*   fDrawTarget;
+
+    int32_t     fMaxVertices;
+    GrTexture*  fCurrTexture;
+    int         fCurrVertex;
+
+    GrBatchedTextContext();
+    virtual void init(GrContext* context, const GrPaint&,
+                      const GrMatrix* extMatrix) SK_OVERRIDE;
+    virtual void finish() SK_OVERRIDE;
+
+    /**
+     * Prepare to add another glyph to buffer. The glyph is encoded on the
+     * texture provided. Make sure we are using the right texture (or switch
+     * to a new texture) and that our buffer is big enough.
+     */
+    void prepareForGlyph(GrTexture*);
+
+    /**
+     * Flush the buffer. Called when switching textures.
+     * Must be called in finish() method of all derived classes.
+     */
+    virtual void flush() = 0;
+
+    /**
+     * Set up a buffer to hold vertices of given layout.
+     * If NULL != *vertexBuff, don't do anything.
+     * Might cause flushing, if draw target suggests it.
+     */
+    void setupVertexBuff(void** vertexBuff, GrVertexLayout vertexLayout);
+
+    /**
+     * Reset after flushing.
+     */
+    void reset();
+
+private:
+
+    typedef GrTextContext INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrDefaultTextContext.cpp
similarity index 70%
rename from src/gpu/GrTextContext.cpp
rename to src/gpu/GrDefaultTextContext.cpp
index 62c894e..bde06c6 100644
--- a/src/gpu/GrTextContext.cpp
+++ b/src/gpu/GrDefaultTextContext.cpp
@@ -8,21 +8,18 @@
 
 
 
-#include "GrTextContext.h"
 #include "GrAtlas.h"
+#include "GrDefaultTextContext.h"
 #include "GrContext.h"
+#include "GrDrawTarget.h"
+#include "GrFontScaler.h"
+#include "GrGpuVertex.h"
+#include "GrTemplates.h"
 #include "GrTextStrike.h"
 #include "GrTextStrike_impl.h"
-#include "GrFontScaler.h"
-#include "GrIndexBuffer.h"
-#include "GrGpuVertex.h"
-#include "GrDrawTarget.h"
 
-enum {
-    kGlyphMaskStage = GrPaint::kTotalStages,
-};
-
-void GrTextContext::flushGlyphs() {
+void GrDefaultTextContext::flushGlyphs() {
+    GrAssert(this->isValid());
     if (fCurrVertex > 0) {
         GrDrawTarget::AutoStateRestore asr(fDrawTarget);
         GrDrawState* drawState = fDrawTarget->drawState();
@@ -42,44 +39,44 @@
         drawState->setTexture(kGlyphMaskStage, fCurrTexture);
 
         if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
-            if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
-                kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
-                fPaint.hasTexture()) {
+            if (kOne_BlendCoeff != fGrPaint.fSrcBlendCoeff ||
+                kISA_BlendCoeff != fGrPaint.fDstBlendCoeff ||
+                fGrPaint.hasTexture()) {
                 GrPrintf("LCD Text will not draw correctly.\n");
             }
             // setup blend so that we get mask * paintColor + (1-mask)*dstColor
-            drawState->setBlendConstant(fPaint.fColor);
+            drawState->setBlendConstant(fGrPaint.fColor);
             drawState->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
             // don't modulate by the paint's color in the frag since we're
             // already doing it via the blend const.
             drawState->setColor(0xffffffff);
         } else {
             // set back to normal in case we took LCD path previously.
-            drawState->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
-            drawState->setColor(fPaint.fColor);
+            drawState->setBlendFunc(fGrPaint.fSrcBlendCoeff, fGrPaint.fDstBlendCoeff);
+            drawState->setColor(fGrPaint.fColor);
         }
 
         fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
 
         fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
                                  0, 0, fCurrVertex, nIndices);
-        fDrawTarget->resetVertexSource();
         fVertices = NULL;
-        fMaxVertices = 0;
-        fCurrVertex = 0;
-        fCurrTexture->unref();
-        fCurrTexture = NULL;
+        this->INHERITED::reset();
     }
 }
 
-GrTextContext::GrTextContext(GrContext* context,
-                             const GrPaint& paint,
-                             const GrMatrix* extMatrix) : fPaint(paint) {
-    fContext = context;
-    fStrike = NULL;
+GrDefaultTextContext::GrDefaultTextContext() {
+}
 
-    fCurrTexture = NULL;
-    fCurrVertex = 0;
+GrDefaultTextContext::~GrDefaultTextContext() {
+}
+
+void GrDefaultTextContext::init(GrContext* context,
+                                const GrPaint& paint,
+                                const GrMatrix* extMatrix) {
+    this->INHERITED::init(context, paint, extMatrix);
+
+    fStrike = NULL;
 
     if (NULL != extMatrix) {
         fExtMatrix = *extMatrix;
@@ -102,7 +99,7 @@
     }
 
     // save the context's original matrix off and restore in destructor
-    // this must be done before getTextTarget.
+    // getTextTarget should be called after that
     fOrigViewMatrix = fContext->getMatrix();
     fContext->setMatrix(fExtMatrix);
 
@@ -118,26 +115,27 @@
     bool invVMComputed = false;
     GrMatrix invVM;
     for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
-        if (NULL != fPaint.getTexture(t)) {
+        if (NULL != fGrPaint.getTexture(t)) {
             if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
                 invVMComputed = true;
-                fPaint.textureSampler(t)->preConcatMatrix(invVM);
+                fGrPaint.textureSampler(t)->preConcatMatrix(invVM);
             }
         }
     }
     for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
-        if (NULL != fPaint.getMask(m)) {
+        if (NULL != fGrPaint.getMask(m)) {
             if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
                 invVMComputed = true;
-                fPaint.maskSampler(m)->preConcatMatrix(invVM);
+                fGrPaint.maskSampler(m)->preConcatMatrix(invVM);
             }
         }
     }
 
-    fDrawTarget = fContext->getTextTarget(fPaint);
+    // this has been already done in the baseclass, but we need to repeat
+    // due to new matrix
+    fDrawTarget = fContext->getTextTarget(fGrPaint);
 
     fVertices = NULL;
-    fMaxVertices = 0;
 
     fVertexLayout = 
         GrDrawTarget::kTextFormat_VertexLayoutBit |
@@ -155,12 +153,17 @@
     }
 }
 
-GrTextContext::~GrTextContext() {
-    this->flushGlyphs();
+void GrDefaultTextContext::finish() {
+    this->flush();
+
+    fStrike = NULL;
     fContext->setMatrix(fOrigViewMatrix);
+
+    this->INHERITED::finish();
 }
 
-void GrTextContext::flush() {
+void GrDefaultTextContext::flush() {
+    GrAssert(this->isValid());
     this->flushGlyphs();
 }
 
@@ -172,9 +175,10 @@
     v[3 * stride].setI(r, t);
 }
 
-void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
+void GrDefaultTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
                                     GrFixed vx, GrFixed vy,
                                     GrFontScaler* scaler) {
+    GrAssert(this->isValid());
     if (NULL == fStrike) {
         fStrike = fContext->getFontCache()->getStrike(scaler);
     }
@@ -229,7 +233,7 @@
         GrPoint translate;
         translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
                       GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
-        fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
+        fContext->drawPath(fGrPaint, *glyph->fPath, kWinding_PathFill,
                            &translate);
         return;
     }
@@ -242,47 +246,10 @@
     height = GrIntToFixed(height);
 
     GrTexture* texture = glyph->fAtlas->texture();
-    GrAssert(texture);
+    this->prepareForGlyph(texture);
 
-    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
-        this->flushGlyphs();
-        fCurrTexture = texture;
-        fCurrTexture->ref();
-    }
-
-    if (NULL == fVertices) {
-        // If we need to reserve vertices allow the draw target to suggest
-        // a number of verts to reserve and whether to perform a flush.
-        fMaxVertices = kMinRequestedVerts;
-        bool flush = fDrawTarget->geometryHints(fVertexLayout,
-                                                &fMaxVertices,
-                                                NULL);
-        if (flush) {
-            this->flushGlyphs();
-            fContext->flushText();
-            fDrawTarget = fContext->getTextTarget(fPaint);
-            fMaxVertices = kDefaultRequestedVerts;
-            // ignore return, no point in flushing again.
-            fDrawTarget->geometryHints(fVertexLayout,
-                                       &fMaxVertices,
-                                       NULL);
-        }
-
-        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
-        if (fMaxVertices < kMinRequestedVerts) {
-            fMaxVertices = kDefaultRequestedVerts;
-        } else if (fMaxVertices > maxQuadVertices) {
-            // don't exceed the limit of the index buffer
-            fMaxVertices = maxQuadVertices;
-        }
-        bool success = fDrawTarget->reserveVertexAndIndexSpace(
-                                                   fVertexLayout, 
-                                                   fMaxVertices,
-                                                   0,
-                                                   GrTCast<void**>(&fVertices),
-                                                   NULL);
-        GrAlwaysAssert(success);
-    }
+    this->setupVertexBuff(GrTCast<void**>(&fVertices),
+                          fVertexLayout);
 
     GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
     GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
@@ -311,5 +278,3 @@
 #endif
     fCurrVertex += 4;
 }
-
-
diff --git a/src/gpu/GrDefaultTextContext.h b/src/gpu/GrDefaultTextContext.h
new file mode 100644
index 0000000..71a7f51
--- /dev/null
+++ b/src/gpu/GrDefaultTextContext.h
@@ -0,0 +1,49 @@
+
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrDefaultTextContext_DEFINED
+#define GrDefaultTextContext_DEFINED
+
+#include "GrBatchedTextContext.h"
+
+struct GrGpuTextVertex;
+class GrTextStrike;
+
+class GrDefaultTextContext: public GrBatchedTextContext {
+public:
+    GrDefaultTextContext();
+    ~GrDefaultTextContext();
+
+    virtual void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top,
+                                 GrFontScaler*) SK_OVERRIDE;
+
+protected:
+    virtual void flush() SK_OVERRIDE;
+    virtual void init(GrContext* context, const GrPaint&,
+                      const GrMatrix* extMatrix) SK_OVERRIDE;
+    virtual void finish() SK_OVERRIDE;
+
+private:
+    GrVertexLayout   fVertexLayout;
+    GrGpuTextVertex* fVertices;
+    GrIRect          fClipRect;
+
+    GrFontScaler*   fScaler;
+    GrTextStrike*   fStrike;
+
+    GrMatrix    fExtMatrix;
+    GrMatrix    fOrigViewMatrix;    // restore previous viewmatrix
+
+    inline void flushGlyphs();
+
+    typedef GrBatchedTextContext INHERITED;
+};
+
+#endif
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 9769cf4..2e82ad9 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -9,6 +9,7 @@
 
 
 #include "GrContext.h"
+#include "GrDefaultTextContext.h"
 #include "GrTextContext.h"
 
 #include "SkGpuDevice.h"
@@ -184,6 +185,8 @@
         pr = new SkGrRenderTargetPixelRef(fRenderTarget);
     }
     this->setPixelRef(pr, 0)->unref();
+
+    fTextContext = NULL;
 }
 
 SkGpuDevice::SkGpuDevice(GrContext* context, SkBitmap::Config config, int width,
@@ -245,6 +248,8 @@
                  width, height);
         GrAssert(false);
     }
+
+    fTextContext = NULL;
 }
 
 SkGpuDevice::~SkGpuDevice() {
@@ -260,6 +265,10 @@
         fContext->unlockTexture(fCache);
     }
     fContext->unref();
+
+    if (NULL != fTextContext) {
+        fTextContext->unref();
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1595,8 +1604,9 @@
                                    &grPaint)) {
             return;
         }
-        GrTextContext context(fContext, grPaint, draw.fExtMatrix);
-        myDraw.fProcs = this->initDrawForText(&context);
+        GrTextContext::AutoFinish txtCtxAF(this->getTextContext(), fContext,
+                                           grPaint, draw.fExtMatrix);
+        myDraw.fProcs = this->initDrawForText(txtCtxAF.getTextContext());
         this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
     }
 }
@@ -1624,9 +1634,9 @@
                                    &grPaint)) {
             return;
         }
-
-        GrTextContext context(fContext, grPaint, draw.fExtMatrix);
-        myDraw.fProcs = this->initDrawForText(&context);
+        GrTextContext::AutoFinish txtCtxAF(this->getTextContext(), fContext,
+                                           grPaint, draw.fExtMatrix);
+        myDraw.fProcs = this->initDrawForText(txtCtxAF.getTextContext());
         this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
                                      scalarsPerPos, paint);
     }
@@ -1742,3 +1752,9 @@
                                    width, height, usage));
 }
 
+GrTextContext* SkGpuDevice::getTextContext() {
+    if (NULL == fTextContext) {
+        fTextContext = new GrDefaultTextContext();
+    }
+    return fTextContext;
+}