Add full scaling for color emoji.

Bug: skia:7562
Change-Id: If6788a5004fe060b42a4e437f9b8c18459623225
Reviewed-on: https://skia-review.googlesource.com/106821
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp
index 66cc0d4..06282e1 100644
--- a/src/gpu/text/GrAtlasTextContext.cpp
+++ b/src/gpu/text/GrAtlasTextContext.cpp
@@ -18,7 +18,6 @@
 #include "SkMakeUnique.h"
 #include "SkMaskFilterBase.h"
 #include "SkTextMapStateProc.h"
-#include "SkTextToPathIter.h"
 
 #include "ops/GrMeshDrawOp.h"
 
@@ -242,12 +241,12 @@
                 case SkTextBlob::kHorizontal_Positioning:
                     DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
                                    viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1,
-                                   SkPoint::Make(x, y + offset.y()), SK_Scalar1);
+                                   SkPoint::Make(x, y + offset.y()));
                     break;
                 case SkTextBlob::kFull_Positioning:
                     DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
                                    viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2,
-                                   SkPoint::Make(x, y), SK_Scalar1);
+                                   SkPoint::Make(x, y));
                     break;
             }
         }
@@ -307,7 +306,7 @@
                             text, byteLength, pos, scalarsPerPosition, offset);
     } else {
         DrawBmpPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
-                       byteLength, pos, scalarsPerPosition, offset, SK_Scalar1);
+                       byteLength, pos, scalarsPerPosition, offset);
     }
     return blob;
 }
@@ -378,7 +377,8 @@
     blob->setHasBitmap();
 
     if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) {
-        DrawBmpTextAsPaths(blob, runIndex, paint, text, byteLength, x, y);
+        DrawBmpTextAsPaths(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix,
+                           text, byteLength, x, y);
         return;
     }
     GrAtlasTextStrike* currStrike = nullptr;
@@ -403,8 +403,7 @@
                                         SkScalerContextFlags scalerContextFlags,
                                         const SkMatrix& viewMatrix,
                                         const char text[], size_t byteLength, const SkScalar pos[],
-                                        int scalarsPerPosition, const SkPoint& offset,
-                                        SkScalar textRatio) {
+                                        int scalarsPerPosition, const SkPoint& offset) {
     SkASSERT(byteLength == 0 || text != nullptr);
     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
 
@@ -417,8 +416,8 @@
     blob->setHasBitmap();
 
     if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) {
-        DrawBmpPosTextAsPaths(blob, runIndex, props, paint, text, byteLength,
-                              pos, scalarsPerPosition, offset, textRatio);
+        DrawBmpPosTextAsPaths(blob, runIndex, fontCache, props, paint, scalerContextFlags,
+                              viewMatrix, text, byteLength, pos, scalarsPerPosition, offset);
         return;
     }
 
@@ -433,14 +432,18 @@
                 BmpAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph,
                                SkScalarFloorToScalar(position.fX),
                                SkScalarFloorToScalar(position.fY),
-                               paint.filteredPremulColor(), cache, textRatio);
+                               paint.filteredPremulColor(), cache, SK_Scalar1);
             });
 
     SkGlyphCache::AttachCache(cache);
 }
 
 void GrAtlasTextContext::DrawBmpTextAsPaths(GrAtlasTextBlob* blob, int runIndex,
-                                            const GrTextUtils::Paint& origPaint, const char text[],
+                                            GrAtlasGlyphCache* fontCache,
+                                            const SkSurfaceProps& props,
+                                            const GrTextUtils::Paint& origPaint,
+                                            SkScalerContextFlags scalerContextFlags,
+                                            const SkMatrix& viewMatrix, const char text[],
                                             size_t byteLength, SkScalar x, SkScalar y) {
     // nothing to draw
     if (text == nullptr || byteLength == 0) {
@@ -448,26 +451,41 @@
     }
 
     // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
-    SkPaint paint(origPaint);
-    paint.setStyle(SkPaint::kFill_Style);
-    paint.setPathEffect(nullptr);
+    SkPaint pathPaint(origPaint);
+    pathPaint.setStyle(SkPaint::kFill_Style);
+    pathPaint.setPathEffect(nullptr);
 
-    SkTextToPathIter iter(text, byteLength, paint, true);
+    GrTextUtils::PathTextIter iter(text, byteLength, pathPaint, true);
+    FallbackTextHelper fallbackTextHelper(viewMatrix, pathPaint.getTextSize(),
+                                          fontCache->getGlyphSizeLimit(),
+                                          iter.getPathScale());
+
+    const SkGlyph* iterGlyph;
     const SkPath* iterPath;
     SkScalar xpos = 0;
-    while (iter.next(&iterPath, &xpos)) {
-        if (iterPath) {
+    const char* lastText = text;
+    while (iter.next(&iterGlyph, &iterPath, &xpos)) {
+        if (iterGlyph) {
+            SkPoint pos = SkPoint::Make(xpos + x, y);
+            fallbackTextHelper.appendText(*iterGlyph, iter.getText() - lastText, lastText, pos);
+        } else if (iterPath) {
             blob->appendPathGlyph(runIndex, *iterPath, xpos + x, y, iter.getPathScale(), false);
         }
+        lastText = iter.getText();
     }
+
+    fallbackTextHelper.drawText(blob, runIndex, fontCache, props, origPaint, scalerContextFlags);
 }
 
 void GrAtlasTextContext::DrawBmpPosTextAsPaths(GrAtlasTextBlob* blob, int runIndex,
+                                               GrAtlasGlyphCache* fontCache,
                                                const SkSurfaceProps& props,
                                                const GrTextUtils::Paint& origPaint,
+                                               SkScalerContextFlags scalerContextFlags,
+                                               const SkMatrix& viewMatrix,
                                                const char text[], size_t byteLength,
                                                const SkScalar pos[], int scalarsPerPosition,
-                                               const SkPoint& offset, SkScalar textRatio) {
+                                               const SkPoint& offset) {
     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
 
     // nothing to draw
@@ -476,38 +494,47 @@
     }
 
     // setup our std paint, in hopes of getting hits in the cache
-    SkPaint paint(origPaint);
-    SkScalar matrixScale = paint.setupForAsPaths();
-    matrixScale *= textRatio;
+    SkPaint pathPaint(origPaint);
+    SkScalar matrixScale = pathPaint.setupForAsPaths();
+    FallbackTextHelper fallbackTextHelper(viewMatrix, pathPaint.getTextSize(), matrixScale,
+                                          fontCache->getGlyphSizeLimit());
 
     // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
-    paint.setStyle(SkPaint::kFill_Style);
-    paint.setPathEffect(nullptr);
+    pathPaint.setStyle(SkPaint::kFill_Style);
+    pathPaint.setPathEffect(nullptr);
 
-    SkPaint::GlyphCacheProc    glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
-                                                                           paint.isDevKernText(),
-                                                                           true);
-    SkAutoGlyphCache           autoCache(paint, &props, nullptr);
+    SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(pathPaint.getTextEncoding(),
+                                                                        pathPaint.isDevKernText(),
+                                                                        true);
+    SkAutoGlyphCache           autoCache(pathPaint, &props, nullptr);
     SkGlyphCache*              cache = autoCache.getCache();
 
     const char*        stop = text + byteLength;
-    SkTextAlignProc    alignProc(paint.getTextAlign());
+    const char*        lastText = text;
+    SkTextAlignProc    alignProc(pathPaint.getTextAlign());
     SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
 
     while (text < stop) {
         const SkGlyph& glyph = glyphCacheProc(cache, &text);
         if (glyph.fWidth) {
-            const SkPath* path = cache->findPath(glyph);
-            if (path) {
-                SkPoint tmsLoc;
-                tmsProc(pos, &tmsLoc);
-                SkPoint loc;
-                alignProc(tmsLoc, glyph, &loc);
-                blob->appendPathGlyph(runIndex, *path, loc.fX, loc.fY, matrixScale, false);
+            SkPoint tmsLoc;
+            tmsProc(pos, &tmsLoc);
+            SkPoint loc;
+            alignProc(tmsLoc, glyph, &loc);
+            if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
+                fallbackTextHelper.appendText(glyph, text - lastText, lastText, loc);
+            } else {
+                const SkPath* path = cache->findPath(glyph);
+                if (path) {
+                    blob->appendPathGlyph(runIndex, *path, loc.fX, loc.fY, matrixScale, false);
+                }
             }
         }
+        lastText = text;
         pos += scalarsPerPosition;
     }
+
+    fallbackTextHelper.drawText(blob, runIndex, fontCache, props, origPaint, scalerContextFlags);
 }
 
 void GrAtlasTextContext::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
@@ -732,15 +759,6 @@
         return;
     }
 
-    SkTDArray<char> fallbackTxt;
-    SkTDArray<SkScalar> fallbackPos;
-
-    bool useScaledFallback = false;
-    SkScalar textSize = paint.skPaint().getTextSize();
-    SkScalar maxTextSize = fontCache->getGlyphSizeLimit();
-    SkScalar scaledFallbackTextSize = maxTextSize;
-    SkScalar maxScale = viewMatrix.getMaxScale();
-
     bool hasWCoord = viewMatrix.hasPerspective() || fDistanceFieldVerticesAlwaysHaveW;
 
     // Setup distance field paint and text ratio
@@ -751,6 +769,11 @@
     blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
                                      paint.skPaint().isAntiAlias(), hasWCoord);
 
+    FallbackTextHelper fallbackTextHelper(viewMatrix,
+                                          paint.skPaint().getTextSize(),
+                                          fontCache->getGlyphSizeLimit(),
+                                          textRatio);
+
     GrAtlasTextStrike* currStrike = nullptr;
 
     // We apply the fake-gamma by altering the distance in the shader, so we ignore the
@@ -771,46 +794,17 @@
         const SkGlyph& glyph = glyphCacheProc(cache, &text);
 
         if (glyph.fWidth) {
-            SkScalar x = offset.x() + pos[0];
-            SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
-
-            SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
-            SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
+            SkPoint glyphPos(offset);
+            glyphPos.fX += pos[0] - SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
+            glyphPos.fY += (2 == scalarsPerPosition ? pos[1] : 0) -
+                           SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
 
             if (glyph.fMaskFormat != SkMask::kARGB32_Format) {
-                DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
-                              y - advanceY, paint.filteredPremulColor(), cache, textRatio);
+                DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, glyphPos.fX,
+                              glyphPos.fY, paint.filteredPremulColor(), cache, textRatio);
             } else {
                 // can't append color glyph to SDF batch, send to fallback
-
-                // all fallback glyphs need to use the same descriptor, so once
-                // we have to scale one, we have to scale all of them
-                SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*textRatio;
-                if (!useScaledFallback) {
-                    SkScalar scaledGlyphSize = maxDim * maxScale;
-                    if (!viewMatrix.hasPerspective() && scaledGlyphSize > maxTextSize) {
-                        useScaledFallback = true;
-                        // rescale previous glyph positions to match text scale
-                        for (int i = 0; i < fallbackPos.count(); ++i) {
-                            fallbackPos[i] *= maxScale;
-                        }
-                    }
-                }
-
-                fallbackTxt.append(SkToInt(text - lastText), lastText);
-                if (useScaledFallback) {
-                    *fallbackPos.append() = maxScale*pos[0];
-                    if (2 == scalarsPerPosition) {
-                        *fallbackPos.append() = maxScale*pos[1];
-                    }
-                    SkScalar glyphTextSize = SkScalarFloorToScalar(maxTextSize*textSize/maxDim);
-                    scaledFallbackTextSize = SkTMin(glyphTextSize, scaledFallbackTextSize);
-                } else {
-                    *fallbackPos.append() = pos[0];
-                    if (2 == scalarsPerPosition) {
-                        *fallbackPos.append() = pos[1];
-                    }
-                }
+                fallbackTextHelper.appendText(glyph, SkToInt(text - lastText), lastText, glyphPos);
             }
         }
         pos += scalarsPerPosition;
@@ -818,34 +812,8 @@
 
     SkGlyphCache::AttachCache(cache);
 
-    if (fallbackTxt.count()) {
-        blob->initOverride(runIndex);
-        if (useScaledFallback) {
-            // Set up paint and matrix to scale glyphs
-            SkPaint scaledPaint(paint);
-            scaledPaint.setTextSize(scaledFallbackTextSize);
-            // remove maxScale from viewMatrix and move it into textRatio
-            // this keeps the base glyph size consistent regardless of matrix scale
-            SkMatrix modMatrix(viewMatrix);
-            SkScalar invScale = SkScalarInvert(maxScale);
-            modMatrix.preScale(invScale, invScale);
-            SkScalar scaledFallbackTextRatio = textSize * maxScale / scaledFallbackTextSize;
-            SkPoint modOffset(offset);
-            modOffset *= maxScale;
-            GrTextUtils::Paint textPaint(&scaledPaint, paint.dstColorSpaceInfo());
-            GrAtlasTextContext::DrawBmpPosText(blob, runIndex, fontCache, props, textPaint,
-                                               scalerContextFlags, modMatrix, fallbackTxt.begin(),
-                                               fallbackTxt.count(), fallbackPos.begin(),
-                                               scalarsPerPosition, modOffset,
-                                               scaledFallbackTextRatio);
-        } else {
-            GrAtlasTextContext::DrawBmpPosText(blob, runIndex, fontCache, props, paint,
-                                               scalerContextFlags, viewMatrix, fallbackTxt.begin(),
-                                               fallbackTxt.count(), fallbackPos.begin(),
-                                               scalarsPerPosition, offset, SK_Scalar1);
-        }
-    }
-
+    fallbackTextHelper.drawText(blob, runIndex, fontCache, props, paint,
+                                scalerContextFlags);
 }
 
 // TODO: merge with BmpAppendGlyph
@@ -884,6 +852,75 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+void GrAtlasTextContext::FallbackTextHelper::appendText(const SkGlyph& glyph, int count,
+                                                        const char* text, SkPoint glyphPos) {
+    SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*fTextRatio;
+    if (!fUseScaledFallback) {
+        SkScalar scaledGlyphSize = maxDim * fMaxScale;
+        if (!fViewMatrix.hasPerspective() && scaledGlyphSize > fMaxTextSize) {
+            fUseScaledFallback = true;
+        }
+    }
+
+    fFallbackTxt.append(count, text);
+    if (fUseScaledFallback) {
+        SkScalar glyphTextSize = SkScalarFloorToScalar(fMaxTextSize*fTextSize / maxDim);
+        fScaledFallbackTextSize = SkTMin(glyphTextSize, fScaledFallbackTextSize);
+    }
+    *fFallbackPos.append() = glyphPos;
+}
+
+void GrAtlasTextContext::FallbackTextHelper::drawText(GrAtlasTextBlob* blob, int runIndex,
+                                                      GrAtlasGlyphCache* fontCache,
+                                                      const SkSurfaceProps& props,
+                                                      const GrTextUtils::Paint& paint,
+                                                      SkScalerContextFlags scalerContextFlags) {
+    if (fFallbackTxt.count()) {
+        blob->initOverride(runIndex);
+        blob->setHasBitmap();
+        SkGlyphCache* cache = nullptr;
+        const SkPaint& skPaint = paint.skPaint();
+        SkPaint::GlyphCacheProc glyphCacheProc =
+            SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(),
+                                       skPaint.isDevKernText(), true);
+        SkColor textColor = paint.filteredPremulColor();
+        SkScalar textRatio = SK_Scalar1;
+        fViewMatrix.mapPoints(fFallbackPos.begin(), fFallbackPos.count());
+        if (fUseScaledFallback) {
+            // Set up paint and matrix to scale glyphs
+            SkPaint scaledPaint(skPaint);
+            scaledPaint.setTextSize(fScaledFallbackTextSize);
+            // remove maxScale from viewMatrix and move it into textRatio
+            // this keeps the base glyph size consistent regardless of matrix scale
+            SkMatrix modMatrix(fViewMatrix);
+            SkScalar invScale = SkScalarInvert(fMaxScale);
+            modMatrix.preScale(invScale, invScale);
+            textRatio = fTextSize * fMaxScale / fScaledFallbackTextSize;
+            cache = blob->setupCache(runIndex, props, scalerContextFlags, scaledPaint,
+                                     &modMatrix);
+        } else {
+            cache = blob->setupCache(runIndex, props, scalerContextFlags, paint,
+                                     &fViewMatrix);
+        }
+
+        GrAtlasTextStrike* currStrike = nullptr;
+        const char* text = fFallbackTxt.begin();
+        const char* stop = text + fFallbackTxt.count();
+        SkPoint* glyphPos = fFallbackPos.begin();
+        while (text < stop) {
+            const SkGlyph& glyph = glyphCacheProc(cache, &text);
+            GrAtlasTextContext::BmpAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph,
+                                               glyphPos->fX, glyphPos->fY, textColor,
+                                               cache, textRatio);
+            glyphPos++;
+        }
+
+        SkGlyphCache::AttachCache(cache);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 #if GR_TEST_UTILS
 
 #include "GrRenderTargetContext.h"
diff --git a/src/gpu/text/GrAtlasTextContext.h b/src/gpu/text/GrAtlasTextContext.h
index 360f6dd..207dd67 100644
--- a/src/gpu/text/GrAtlasTextContext.h
+++ b/src/gpu/text/GrAtlasTextContext.h
@@ -58,6 +58,39 @@
 private:
     GrAtlasTextContext(const Options& options);
 
+    class FallbackTextHelper {
+    public:
+        FallbackTextHelper(const SkMatrix& viewMatrix,
+                           SkScalar textSize,
+                           SkScalar maxTextSize,
+                           SkScalar textRatio)
+            : fViewMatrix(viewMatrix)
+            , fTextRatio(textRatio)
+            , fTextSize(textSize)
+            , fMaxTextSize(maxTextSize)
+            , fScaledFallbackTextSize(maxTextSize)
+            , fUseScaledFallback(false) {
+            fMaxScale = viewMatrix.getMaxScale();
+        }
+
+        void appendText(const SkGlyph& glyph, int count, const char* text, SkPoint glyphPos);
+        void drawText(GrAtlasTextBlob* blob, int runIndex,
+                      GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
+                      const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags);
+
+    private:
+        SkTDArray<char> fFallbackTxt;
+        SkTDArray<SkPoint> fFallbackPos;
+
+        const SkMatrix& fViewMatrix;
+        SkScalar fTextRatio;
+        SkScalar fTextSize;
+        SkScalar fMaxTextSize;
+        SkScalar fScaledFallbackTextSize;
+        SkScalar fMaxScale;
+        bool fUseScaledFallback;
+    };
+
     // sets up the descriptor on the blob and returns a detached cache.  Client must attach
     static SkColor ComputeCanonicalColor(const SkPaint&, bool lcd);
     // Determines if we need to use fake gamma (and contrast boost):
@@ -104,18 +137,21 @@
                                const SkSurfaceProps&, const GrTextUtils::Paint& paint,
                                SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix,
                                const char text[], size_t byteLength, const SkScalar pos[],
-                               int scalarsPerPosition, const SkPoint& offset,
-                               SkScalar textRatio);
+                               int scalarsPerPosition, const SkPoint& offset);
 
-    static void DrawBmpTextAsPaths(GrAtlasTextBlob* blob, int runIndex,
-                                   const GrTextUtils::Paint& paint, const char text[],
+    static void DrawBmpTextAsPaths(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
+                                   const SkSurfaceProps&, const GrTextUtils::Paint& paint,
+                                   SkScalerContextFlags scalerContextFlags,
+                                   const SkMatrix& viewMatrix, const char text[],
                                    size_t byteLength, SkScalar x, SkScalar y);
 
-    static void DrawBmpPosTextAsPaths(GrAtlasTextBlob*, int runIndex,
-                                      const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
+    static void DrawBmpPosTextAsPaths(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
+                                      const SkSurfaceProps&, const GrTextUtils::Paint& paint,
+                                      SkScalerContextFlags scalerContextFlags,
+                                      const SkMatrix& viewMatrix,
                                       const char text[], size_t byteLength,
                                       const SkScalar pos[], int scalarsPerPosition,
-                                      const SkPoint& offset, SkScalar textRatio);
+                                      const SkPoint& offset);
 
     // functions for appending distance field text
     bool canDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
diff --git a/src/gpu/text/GrTextUtils.cpp b/src/gpu/text/GrTextUtils.cpp
index 56c1d33..680e262 100644
--- a/src/gpu/text/GrTextUtils.cpp
+++ b/src/gpu/text/GrTextUtils.cpp
@@ -75,3 +75,32 @@
     return paint.getMaskFilter() || paint.getPathEffect() ||
            paint.isFakeBoldText() || paint.getStyle() != SkPaint::kFill_Style;
 }
+
+bool GrTextUtils::PathTextIter::next(const SkGlyph** skGlyph, const SkPath** path, SkScalar* xpos) {
+    SkASSERT(skGlyph);
+    SkASSERT(path);
+    SkASSERT(xpos);
+    if (fText < fStop) {
+        const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
+
+        fXPos += (fPrevAdvance + fAutoKern.adjust(glyph)) * fScale;
+        SkASSERT(0 == fXYIndex || 1 == fXYIndex);
+        fPrevAdvance = SkFloatToScalar((&glyph.fAdvanceX)[fXYIndex]);
+
+        if (glyph.fWidth) {
+            if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
+                *skGlyph = &glyph;
+                *path = nullptr;
+            } else {
+                *skGlyph = nullptr;
+                *path = fCache->findPath(glyph);
+            }
+        } else {
+            *skGlyph = nullptr;
+            *path = nullptr;
+        }
+        *xpos = fXPos;
+        return true;
+    }
+    return false;
+}
diff --git a/src/gpu/text/GrTextUtils.h b/src/gpu/text/GrTextUtils.h
index 58318e8..5fe38fe 100644
--- a/src/gpu/text/GrTextUtils.h
+++ b/src/gpu/text/GrTextUtils.h
@@ -13,6 +13,7 @@
 #include "SkColorFilter.h"
 #include "SkPaint.h"
 #include "SkScalar.h"
+#include "SkTextToPathIter.h"
 #include "SkTLazy.h"
 
 class GrAtlasGlyphCache;
@@ -127,6 +128,25 @@
     static uint32_t FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint);
 
     static bool ShouldDisableLCD(const SkPaint& paint);
+
+    class PathTextIter : SkTextBaseIter {
+    public:
+        PathTextIter(const char text[], size_t length, const SkPaint& paint,
+                     bool applyStrokeAndPathEffects)
+            : SkTextBaseIter(text, length, paint, applyStrokeAndPathEffects) {
+        }
+
+        const SkPaint&  getPaint() const { return fPaint; }
+        SkScalar        getPathScale() const { return fScale; }
+        const char*     getText() const { return fText; }
+
+        /**
+         *  Returns false when all of the text has been consumed
+         *  Will set skGlyph if the maskformat is ARGB, and path otherwise. The other will be null.
+         *  If the glyph is zero-width, both will be null.
+         */
+        bool next(const SkGlyph** skGlyph, const SkPath** path, SkScalar* xpos);
+    };
 };
 
 #endif