Let text contexts fall back directly to paths

BUG=skia:

Review URL: https://codereview.chromium.org/1015173002
diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrTextContext.cpp
index ad5e7c0..28e10e3 100644
--- a/src/gpu/GrTextContext.cpp
+++ b/src/gpu/GrTextContext.cpp
@@ -11,11 +11,19 @@
 #include "GrFontScaler.h"
 
 #include "SkAutoKern.h"
+#include "SkDrawProcs.h"
 #include "SkGlyphCache.h"
+#include "SkGpuDevice.h"
+#include "SkTextMapStateProc.h"
+#include "SkTextToPathIter.h"
 
-GrTextContext::GrTextContext(GrContext* context, const SkDeviceProperties& properties) :
-                            fFallbackTextContext(NULL),
-                            fContext(context), fDeviceProperties(properties), fDrawTarget(NULL) {
+GrTextContext::GrTextContext(GrContext* context, SkGpuDevice* gpuDevice,
+                             const SkDeviceProperties& properties)
+    : fFallbackTextContext(NULL)
+    , fContext(context)
+    , fGpuDevice(gpuDevice)
+    , fDeviceProperties(properties)
+    , fDrawTarget(NULL) {
 }
 
 GrTextContext::~GrTextContext() {
@@ -23,11 +31,12 @@
 }
 
 void GrTextContext::init(GrRenderTarget* rt, const GrClip& clip, const GrPaint& grPaint,
-                         const SkPaint& skPaint) {
+                         const SkPaint& skPaint, const SkIRect& regionClipBounds) {
     fClip = clip;
 
     fRenderTarget.reset(SkRef(rt));
 
+    fRegionClipBounds = regionClipBounds;
     fClip.getConservativeBounds(fRenderTarget->width(), fRenderTarget->height(), &fClipRect);
 
     fDrawTarget = fContext->getTextTarget();
@@ -36,48 +45,119 @@
     fSkPaint = skPaint;
 }
 
-bool GrTextContext::drawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
+void GrTextContext::drawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
                              const SkPaint& skPaint, const SkMatrix& viewMatrix,
                              const char text[], size_t byteLength,
-                             SkScalar x, SkScalar y) {
+                             SkScalar x, SkScalar y, const SkIRect& clipBounds) {
     if (!fContext->getTextTarget()) {
-        return false;
+        return;
     }
 
     GrTextContext* textContext = this;
     do {
         if (textContext->canDraw(skPaint, viewMatrix)) {
-            textContext->onDrawText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, x, y);
-            return true;
+            textContext->onDrawText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, x, y,
+                                    clipBounds);
+            return;
         }
         textContext = textContext->fFallbackTextContext;
     } while (textContext);
 
-    return false;
+    // fall back to drawing as a path
+    this->drawTextAsPath(skPaint, viewMatrix, text, byteLength, x, y, clipBounds);
 }
 
-bool GrTextContext::drawPosText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
+void GrTextContext::drawPosText(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 SkPoint& offset, const SkIRect& clipBounds) {
     if (!fContext->getTextTarget()) {
-        return false;
+        return;
     }
 
     GrTextContext* textContext = this;
     do {
         if (textContext->canDraw(skPaint, viewMatrix)) {
             textContext->onDrawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, pos,
-                                       scalarsPerPosition, offset);
-            return true;
+                                       scalarsPerPosition, offset, clipBounds);
+            return;
         }
         textContext = textContext->fFallbackTextContext;
     } while (textContext);
 
-    return false;
+    // fall back to drawing as a path
+    this->drawPosTextAsPath(skPaint, viewMatrix, text, byteLength, pos, scalarsPerPosition, offset,
+                            clipBounds);
 }
 
+void GrTextContext::drawTextAsPath(const SkPaint& skPaint, const SkMatrix& viewMatrix,
+                                   const char text[], size_t byteLength, SkScalar x, SkScalar y,
+                                   const SkIRect& clipBounds) {
+    SkTextToPathIter iter(text, byteLength, skPaint, true);
+
+    SkMatrix    matrix;
+    matrix.setScale(iter.getPathScale(), iter.getPathScale());
+    matrix.postTranslate(x, y);
+
+    const SkPath* iterPath;
+    SkScalar xpos, prevXPos = 0;
+
+    while (iter.next(&iterPath, &xpos)) {
+        matrix.postTranslate(xpos - prevXPos, 0);
+        if (iterPath) {
+            const SkPaint& pnt = iter.getPaint();
+            fGpuDevice->internalDrawPath(*iterPath, pnt, viewMatrix, &matrix, clipBounds, false);
+        }
+        prevXPos = xpos;
+    }
+}
+
+void GrTextContext::drawPosTextAsPath(const SkPaint& origPaint, const SkMatrix& viewMatrix,
+                                      const char text[], size_t byteLength,
+                                      const SkScalar pos[], int scalarsPerPosition,
+                                      const SkPoint& offset, const SkIRect& clipBounds) {
+    // setup our std paint, in hopes of getting hits in the cache
+    SkPaint paint(origPaint);
+    SkScalar matrixScale = paint.setupForAsPaths();
+
+    SkMatrix matrix;
+    matrix.setScale(matrixScale, matrixScale);
+
+    // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
+    paint.setStyle(SkPaint::kFill_Style);
+    paint.setPathEffect(NULL);
+
+    SkDrawCacheProc     glyphCacheProc = paint.getDrawCacheProc();
+    SkAutoGlyphCache    autoCache(paint, NULL, NULL);
+    SkGlyphCache*       cache = autoCache.getCache();
+
+    const char*        stop = text + byteLength;
+    SkTextAlignProc    alignProc(paint.getTextAlign());
+    SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
+
+    // Now restore the original settings, so we "draw" with whatever style/stroking.
+    paint.setStyle(origPaint.getStyle());
+    paint.setPathEffect(origPaint.getPathEffect());
+
+    while (text < stop) {
+        const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+        if (glyph.fWidth) {
+            const SkPath* path = cache->findPath(glyph);
+            if (path) {
+                SkPoint tmsLoc;
+                tmsProc(pos, &tmsLoc);
+                SkPoint loc;
+                alignProc(tmsLoc, glyph, &loc);
+
+                matrix[SkMatrix::kMTransX] = loc.fX;
+                matrix[SkMatrix::kMTransY] = loc.fY;
+                fGpuDevice->internalDrawPath(*path, paint, viewMatrix, &matrix, clipBounds, false);
+            }
+        }
+        pos += scalarsPerPosition;
+    }
+}
 
 //*** change to output positions?
 int GrTextContext::MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,