Handle large paths in textblobs

BUG=skia:

Review URL: https://codereview.chromium.org/1057613002
diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp
index b2de1cf..08e6a9a 100644
--- a/src/gpu/GrAtlasTextContext.cpp
+++ b/src/gpu/GrAtlasTextContext.cpp
@@ -93,14 +93,6 @@
     return !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
 }
 
-inline void GrAtlasTextContext::init(GrRenderTarget* rt, const GrClip& clip,
-                                     const GrPaint& paint, const SkPaint& skPaint,
-                                     const SkIRect& regionClipBounds) {
-    INHERITED::init(rt, clip, paint, skPaint, regionClipBounds);
-
-    fCurrStrike = NULL;
-}
-
 bool GrAtlasTextContext::MustRegenerateBlob(const BitmapTextBlob& blob, const SkPaint& paint,
                                             const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
     // We always regenerate blobs with patheffects or mask filters we could cache these
@@ -192,8 +184,8 @@
     GrPaint grPaint;
     SkPaint2GrPaintShader(fContext, rt, skPaint, viewMatrix, true, &grPaint);
 
-    this->flush(fContext->getTextTarget(), cacheBlob, rt, grPaint, clip, viewMatrix,
-                fSkPaint.getAlpha());
+    this->flush(fContext->getTextTarget(), blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
+                clip, viewMatrix, clipBounds, x, y);
 }
 
 void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
@@ -238,6 +230,12 @@
             newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
         }
 
+        if (SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix)) {
+            cacheBlob->fRuns[run].fDrawAsPaths = true;
+            continue;
+        }
+        cacheBlob->fRuns[run].fDrawAsPaths = false;
+
         switch (it.positioning()) {
             case SkTextBlob::kDefault_Positioning:
                 this->internalDrawText(cacheBlob, run, cache, runPaint, viewMatrix,
@@ -285,7 +283,7 @@
     this->internalDrawText(blob, 0, cache, skPaint, viewMatrix, text, byteLength, x, y, clipRect);
     SkGlyphCache::AttachCache(cache);
 
-    this->flush(fContext->getTextTarget(), blob, rt, paint, clip, viewMatrix, skPaint.getAlpha());
+    this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, viewMatrix);
 }
 
 void GrAtlasTextContext::internalDrawText(BitmapTextBlob* blob, int runIndex,
@@ -398,7 +396,7 @@
                               scalarsPerPosition, offset, clipRect);
     SkGlyphCache::AttachCache(cache);
 
-    this->flush(fContext->getTextTarget(), blob, rt, paint, clip, viewMatrix, fSkPaint.getAlpha());
+    this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, viewMatrix);
 }
 
 void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
@@ -694,9 +692,9 @@
         GrColor fColor;
     };
 
-    static GrBatch* Create(const Geometry& geometry, GrColor color, GrMaskFormat maskFormat,
+    static GrBatch* Create(const Geometry& geometry, GrMaskFormat maskFormat,
                            int glyphCount, GrBatchFontCache* fontCache) {
-        return SkNEW_ARGS(BitmapTextBatch, (geometry, color, maskFormat, glyphCount, fontCache));
+        return SkNEW_ARGS(BitmapTextBatch, (geometry, maskFormat, glyphCount, fontCache));
     }
 
     const char* name() const override { return "BitmapTextBatch"; }
@@ -893,14 +891,14 @@
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
 
 private:
-    BitmapTextBatch(const Geometry& geometry, GrColor color, GrMaskFormat maskFormat,
+    BitmapTextBatch(const Geometry& geometry, GrMaskFormat maskFormat,
                     int glyphCount, GrBatchFontCache* fontCache)
             : fMaskFormat(maskFormat)
             , fPixelConfig(fontCache->getPixelConfig(maskFormat))
             , fFontCache(fontCache) {
         this->initClassID<BitmapTextBatch>();
         fGeoData.push_back(geometry);
-        fBatch.fColor = color;
+        fBatch.fColor = geometry.fColor;
         fBatch.fViewMatrix = geometry.fBlob->fViewMatrix;
         fBatch.fNumGlyphs = glyphCount;
     }
@@ -1015,47 +1013,128 @@
     GrBatchFontCache* fFontCache;
 };
 
-void GrAtlasTextContext::flush(GrDrawTarget* target, BitmapTextBlob* blob, GrRenderTarget* rt,
-                               const GrPaint& paint, const GrClip& clip,
-                               const SkMatrix& viewMatrix, int paintAlpha) {
-    GrPipelineBuilder pipelineBuilder;
-    pipelineBuilder.setFromPaint(paint, rt, clip);
+void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint,
+                                         SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
+                                         const SkIRect& clipBounds, SkScalar x, SkScalar y) {
+    SkPaint runPaint = skPaint;
 
-    GrColor color = paint.getColor();
-    for (uint32_t run = 0; run < blob->fRunCount; run++) {
-        for (int subRun = 0; subRun < blob->fRuns[run].fSubRunInfo.count(); subRun++) {
-            PerSubRunInfo& info = blob->fRuns[run].fSubRunInfo[subRun];
-            int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
-            if (0 == glyphCount) {
-                continue;
-            }
+    size_t textLen = it.glyphCount() * sizeof(uint16_t);
+    const SkPoint& offset = it.offset();
 
-            GrMaskFormat format = info.fMaskFormat;
-            GrColor subRunColor = kARGB_GrMaskFormat == format ?
-                                  SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha) :
-                                  color;
+    it.applyFontToPaint(&runPaint);
 
-            BitmapTextBatch::Geometry geometry;
-            geometry.fBlob.reset(SkRef(blob));
-            geometry.fRun = run;
-            geometry.fSubRun = subRun;
-            geometry.fColor = color;
-            SkAutoTUnref<GrBatch> batch(BitmapTextBatch::Create(geometry, subRunColor, format,
-                                                                glyphCount,
-                                                                fContext->getBatchFontCache()));
-
-            target->drawBatch(&pipelineBuilder, batch, &blob->fRuns[run].fVertexBounds);
-        }
+    if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
+        return;
     }
 
-    // Now flush big glyphs
-    for (int i = 0; i < blob->fBigGlyphs.count(); i++) {
-        BitmapTextBlob::BigGlyph& bigGlyph = blob->fBigGlyphs[i];
+    runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
+
+    switch (it.positioning()) {
+        case SkTextBlob::kDefault_Positioning:
+            this->drawTextAsPath(runPaint, viewMatrix, (const char *)it.glyphs(),
+                                 textLen, x + offset.x(), y + offset.y(), clipBounds);
+            break;
+        case SkTextBlob::kHorizontal_Positioning:
+            this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
+                                    textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
+                                    clipBounds);
+            break;
+        case SkTextBlob::kFull_Positioning:
+            this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
+                                    textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
+            break;
+    }
+}
+
+inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder,
+                                         BitmapTextBlob* cacheBlob, int run, GrColor color,
+                                         uint8_t paintAlpha) {
+    for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
+        const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
+        int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
+        if (0 == glyphCount) {
+            continue;
+        }
+
+        GrMaskFormat format = info.fMaskFormat;
+        GrColor subRunColor = kARGB_GrMaskFormat == format ?
+                              SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha) :
+                              color;
+
+        BitmapTextBatch::Geometry geometry;
+        geometry.fBlob.reset(SkRef(cacheBlob));
+        geometry.fRun = run;
+        geometry.fSubRun = subRun;
+        geometry.fColor = subRunColor;
+        SkAutoTUnref<GrBatch> batch(BitmapTextBatch::Create(geometry, format, glyphCount,
+                                                            fContext->getBatchFontCache()));
+
+        target->drawBatch(pipelineBuilder, batch, &cacheBlob->fRuns[run].fVertexBounds);
+    }
+}
+
+inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
+                                               const GrPaint& grPaint, const GrClip& clip) {
+    for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
+        const BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
         SkMatrix translate;
         translate.setTranslate(SkIntToScalar(bigGlyph.fVx), SkIntToScalar(bigGlyph.fVy));
         SkPath tmpPath(bigGlyph.fPath);
         tmpPath.transform(translate);
         GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
-        fContext->drawPath(rt, clip, paint, SkMatrix::I(), tmpPath, strokeInfo);
+        fContext->drawPath(rt, clip, grPaint, SkMatrix::I(), tmpPath, strokeInfo);
     }
 }
+
+void GrAtlasTextContext::flush(GrDrawTarget* target,
+                               const SkTextBlob* blob,
+                               BitmapTextBlob* cacheBlob,
+                               GrRenderTarget* rt,
+                               const SkPaint& skPaint,
+                               const GrPaint& grPaint,
+                               SkDrawFilter* drawFilter,
+                               const GrClip& clip,
+                               const SkMatrix& viewMatrix,
+                               const SkIRect& clipBounds,
+                               SkScalar x,
+                               SkScalar y) {
+    // We loop through the runs of the blob, flushing each.  If any run is too large, then we flush
+    // it as paths
+    GrPipelineBuilder pipelineBuilder;
+    pipelineBuilder.setFromPaint(grPaint, rt, clip);
+
+    GrColor color = grPaint.getColor();
+    uint8_t paintAlpha = skPaint.getAlpha();
+
+    SkTextBlob::RunIterator it(blob);
+    for (int run = 0; !it.done(); it.next(), run++) {
+        if (cacheBlob->fRuns[run].fDrawAsPaths) {
+            this->flushRunAsPaths(it, skPaint, drawFilter, viewMatrix, clipBounds, x, y);
+            continue;
+        }
+        this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha);
+    }
+
+    // Now flush big glyphs
+    this->flushBigGlyphs(cacheBlob, rt, grPaint, clip);
+}
+
+void GrAtlasTextContext::flush(GrDrawTarget* target,
+                               BitmapTextBlob* cacheBlob,
+                               GrRenderTarget* rt,
+                               const SkPaint& skPaint,
+                               const GrPaint& grPaint,
+                               const GrClip& clip,
+                               const SkMatrix& viewMatrix) {
+    GrPipelineBuilder pipelineBuilder;
+    pipelineBuilder.setFromPaint(grPaint, rt, clip);
+
+    GrColor color = grPaint.getColor();
+    uint8_t paintAlpha = skPaint.getAlpha();
+    for (int run = 0; run < cacheBlob->fRunCount; run++) {
+        this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha);
+    }
+
+    // Now flush big glyphs
+    this->flushBigGlyphs(cacheBlob, rt, grPaint, clip);
+}
diff --git a/src/gpu/GrAtlasTextContext.h b/src/gpu/GrAtlasTextContext.h
index eb1abba..de07d1f 100644
--- a/src/gpu/GrAtlasTextContext.h
+++ b/src/gpu/GrAtlasTextContext.h
@@ -12,6 +12,7 @@
 
 #include "GrGeometryProcessor.h"
 #include "SkDescriptor.h"
+#include "SkTextBlob.h"
 #include "SkTHash.h"
 
 class GrBatchTextStrike;
@@ -45,9 +46,6 @@
                       const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y,
                       SkDrawFilter*, const SkIRect& clipBounds) override;
 
-    void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
-              const SkIRect& regionClipBounds);
-
     /*
      * A BitmapTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing
      * on the GPU.  These are initially created with valid positions and colors, but invalid
@@ -60,19 +58,32 @@
      * TODO this is currently a bug
      */
     struct BitmapTextBlob : public SkRefCnt {
-        // Each Run inside of the blob can have its texture coordinates regenerated if required.
-        // To determine if regeneration is necessary, fAtlasGeneration is used.  If there have been
-        // any evictions inside of the atlas, then we will simply regenerate Runs.  We could track
-        // this at a more fine grained level, but its not clear if this is worth it, as evictions
-        // should be fairly rare.
-        // One additional point, each run can contain glyphs with any of the three mask formats.
-        // We call these SubRuns.  Because a subrun must be a contiguous range, we have to create
-        // a new subrun each time the mask format changes in a run.  In theory, a run can have as
-        // many SubRuns as it has glyphs, ie if a run alternates between color emoji and A8.  In
-        // practice, the vast majority of runs have only a single subrun.
+        /*
+         * Each Run inside of the blob can have its texture coordinates regenerated if required.
+         * To determine if regeneration is necessary, fAtlasGeneration is used.  If there have been
+         * any evictions inside of the atlas, then we will simply regenerate Runs.  We could track
+         * this at a more fine grained level, but its not clear if this is worth it, as evictions
+         * should be fairly rare.
+         *
+         * One additional point, each run can contain glyphs with any of the three mask formats.
+         * We call these SubRuns.  Because a subrun must be a contiguous range, we have to create
+         * a new subrun each time the mask format changes in a run.  In theory, a run can have as
+         * many SubRuns as it has glyphs, ie if a run alternates between color emoji and A8.  In
+         * practice, the vast majority of runs have only a single subrun.
+         *
+         * Finally, for runs where the entire thing is too large for the GrAtlasTextContext to
+         * handle, we have a bit to mark the run as flusahable via rendering as paths.  It is worth
+         * pointing. It would be a bit expensive to figure out ahead of time whether or not a run
+         * can flush in this manner, so we always allocate vertices for the run, regardless of
+         * whether or not it is too large.  The benefit of this strategy is that we can always reuse
+         * a blob allocation regardless of viewmatrix changes.  We could store positions for these
+         * glyphs.  However, its not clear if this is a win because we'd still have to either go the
+         * glyph cache to get the path at flush time, or hold onto the path in the cache, which
+         * would greatly increase the memory of these cached items.
+         */
 
         struct Run {
-            Run() : fColor(GrColor_ILLEGAL), fInitialized(false) {
+            Run() : fColor(GrColor_ILLEGAL), fInitialized(false), fDrawAsPaths(false) {
                 fVertexBounds.setLargestInverted();
                 // We insert the first subrun to gurantee a run always has atleast one subrun.
                 // We do this to simplify things when we 'hand off' data from one subrun to the
@@ -99,6 +110,7 @@
             SkRect fVertexBounds;
             GrColor fColor;
             bool fInitialized;
+            bool fDrawAsPaths;
         };
 
         struct BigGlyph {
@@ -113,7 +125,7 @@
         SkScalar fX;
         SkScalar fY;
         SkPaint::Style fStyle;
-        uint32_t fRunCount;
+        int fRunCount;
 
         // all glyph / vertex offsets are into these pools.
         unsigned char* fVertices;
@@ -143,8 +155,21 @@
 
     void appendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top,
                      GrColor color, GrFontScaler*, const SkIRect& clipRect);
-    void flush(GrDrawTarget*, BitmapTextBlob*, GrRenderTarget*, const GrPaint&, const GrClip&,
-               const SkMatrix& viewMatrix, int paintAlpha);
+
+    inline void flushRunAsPaths(const SkTextBlob::RunIterator&, const SkPaint&, SkDrawFilter*,
+                                const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x,
+                                SkScalar y);
+    inline void flushRun(GrDrawTarget*, GrPipelineBuilder*, BitmapTextBlob*, int run, GrColor,
+                         uint8_t paintAlpha);
+    inline void flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
+                               const GrPaint& grPaint, const GrClip& clip);
+
+    // We have to flush SkTextBlobs differently from drawText / drawPosText
+    void flush(GrDrawTarget*, const SkTextBlob*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&,
+               const GrPaint&, SkDrawFilter*, const GrClip&, const SkMatrix& viewMatrix,
+               const SkIRect& clipBounds, SkScalar x, SkScalar y);
+    void flush(GrDrawTarget*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&,
+               const GrPaint&, const GrClip&, const SkMatrix& viewMatrix);
 
     void internalDrawText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
                           const SkMatrix& viewMatrix, const char text[], size_t byteLength,