Reland "Use bulk interface for paths"

This is a reland of 5b83d05bf4505f180364424ef94e3e1126622ffc

In the original, the outline was calculated using the glyph's
position, which included the subpixel adjustment. It should have
been calculated at {0, 0} because all transformations including
subpixel placmenet happen while drawing.

Original change's description:
> Use bulk interface for paths
>
> Change-Id: I3a73f3b181542588470e3ce2c03d147423fef5cd
> Reviewed-on: https://skia-review.googlesource.com/c/192424
> Reviewed-by: Mike Klein <mtklein@google.com>
> Commit-Queue: Herb Derby <herb@google.com>

Change-Id: Id4987a8d0122ce73cae9ade33b853b1a9d080d1f
Reviewed-on: https://skia-review.googlesource.com/c/193021
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/src/core/SkGlyphRunPainter.cpp b/src/core/SkGlyphRunPainter.cpp
index b8ddd17..ae81733 100644
--- a/src/core/SkGlyphRunPainter.cpp
+++ b/src/core/SkGlyphRunPainter.cpp
@@ -331,28 +331,41 @@
 
 // Beware! The following code will end up holding two glyph caches at the same time, but they
 // will not be the same cache (which would cause two separate caches to be created).
-template <typename PerEmptyT, typename PerPathT>
+template <typename ProcessPathsT>
 void SkGlyphRunListPainter::drawGlyphRunAsPathWithARGBFallback(
-        SkStrikeInterface* pathCache, const SkGlyphRun& glyphRun,
+        SkStrikeInterface* strike, const SkGlyphRun& glyphRun,
         SkPoint origin, const SkPaint& runPaint, const SkMatrix& viewMatrix, SkScalar textScale,
-        PerEmptyT&& perEmpty, PerPathT&& perPath, ARGBFallback&& argbFallback) {
+        ProcessPathsT&& processPaths, ARGBFallback&& argbFallback) {
     fARGBGlyphsIDs.clear();
     fARGBPositions.clear();
+    ScopedBuffers _ = ensureBuffers(glyphRun);
     SkScalar maxFallbackDimension{-SK_ScalarInfinity};
 
+    // Four empty glyphs are expected; one for each horizontal subpixel position.
+    SkSTArray<4, const SkGlyph*> emptyGlyphs;
+
+    int glyphCount = 0;
     const SkPoint* positionCursor = glyphRun.positions().data();
     for (auto glyphID : glyphRun.glyphsIDs()) {
         SkPoint glyphPos = origin + *positionCursor++;
-        const SkGlyph& glyph = pathCache->getGlyphMetrics(glyphID, {0, 0});
+
+        if (std::any_of(emptyGlyphs.begin(), emptyGlyphs.end(),
+                        [glyphID](const SkGlyph* g) { return g->getGlyphID() == glyphID; })) {
+            continue;
+        }
+
+        // Use outline from {0, 0} because all transforms including subpixel translation happen
+        // during drawing.
+        const SkGlyph& glyph = strike->getGlyphMetrics(glyphID, {0, 0});
         if (glyph.isEmpty()) {
-            perEmpty(glyph, glyphPos);
+            emptyGlyphs.push_back(&glyph);
         } else if (glyph.fMaskFormat != SkMask::kARGB32_Format) {
-            if (pathCache->decideCouldDrawFromPath(glyph)) {
-                perPath(glyph, glyphPos);
+            if (strike->decideCouldDrawFromPath(glyph)) {
+                fGlyphPos[glyphCount++] = {&glyph, glyphPos};
             } else {
-                // This happens when a bitmap-only font has a very large glyph compared to the
-                // rest of the glyphs. This doesn't happen in practice.
-                perEmpty(glyph, glyphPos);
+                // This happens when a bitmap-only font is forced to scale very large. This
+                // doesn't happen in practice.
+                emptyGlyphs.push_back(&glyph);
             }
         } else {
             SkScalar largestDimension = std::max(glyph.fWidth, glyph.fHeight);
@@ -362,6 +375,13 @@
         }
     }
 
+    if (glyphCount > 0) {
+        processPaths(SkSpan<const GlyphAndPos>{fGlyphPos, SkTo<size_t>(glyphCount)},
+                    strike,
+                    textScale);
+    }
+
+
     if (!fARGBGlyphsIDs.empty()) {
         this->processARGBFallback(
                 maxFallbackDimension, runPaint, glyphRun.font(), viewMatrix, textScale,
@@ -420,14 +440,14 @@
                 }
             } else {
                 // If the glyph is not empty, then it will have a pointer to mask data.
-                fMasks[glyphCount++] = {&glyph, mappedPt};
+                fGlyphPos[glyphCount++] = {&glyph, mappedPt};
             }
         }
     }
 
     if (glyphCount > 0) {
         mapping.mapPoints(fPositions, glyphCount);
-        processMasks(SkSpan<const GlyphAndPos>{fMasks, SkTo<size_t>(glyphCount)}, strike.get());
+        processMasks(SkSpan<const GlyphAndPos>{fGlyphPos, SkTo<size_t>(glyphCount)}, strike.get());
     }
     if (!fPaths.empty()) {
         processPaths(SkSpan<const GlyphAndPos>{fPaths});
@@ -813,14 +833,15 @@
                                 pathFont, pathPaint, props,
                                 scalerContextFlags, SkMatrix::I());
 
-            auto perEmpty = [](const SkGlyph&, SkPoint) {};
-
             // Given a glyph that is not ARGB, draw it.
-            auto perPath = [textScale, run]
-                           (const SkGlyph& glyph, SkPoint position) {
-                // TODO: path should always be set. Remove when proven.
-                if (const SkPath* glyphPath = glyph.path()) {
-                    run->appendPathGlyph(*glyphPath, position, textScale, false);
+            auto processPath = [run](
+                    SkSpan<const SkGlyphRunListPainter::GlyphAndPos> paths,
+                    SkStrikeInterface* strike, SkScalar textScale) {
+                run->setupFont(strike->strikeSpec());
+                for (const auto& path : paths) {
+                    if (const SkPath* glyphPath = path.glyph->path()) {
+                        run->appendPathGlyph(*glyphPath, path.position, textScale, false);
+                    }
                 }
             };
 
@@ -828,7 +849,7 @@
 
             glyphPainter->drawGlyphRunAsPathWithARGBFallback(
                 pathCache.get(), glyphRun, origin, runPaint, viewMatrix, textScale,
-                std::move(perEmpty), std::move(perPath), std::move(argbFallback));
+                std::move(processPath), std::move(argbFallback));
         } else {
             // Ensure the blob is set for bitmaptext
             this->setHasBitmap();
@@ -1012,20 +1033,16 @@
             pathPaint, pathFont, this->surfaceProps(), SkMatrix::I(),
             SkScalerContextFlags::kFakeGammaAndBoostContrast, &effects);
 
-    auto perEmpty = [glyphCacheState] (const SkGlyph& glyph, SkPoint mappedPt) {
-        glyphCacheState->addGlyph(glyph.getPackedID(), false);
-    };
-
-    auto perPath = [glyphCacheState](const SkGlyph& glyph, SkPoint position) {
-        const bool asPath = true;
-        glyphCacheState->addGlyph(glyph.getGlyphID(), asPath);
-    };
+    // This processor is empty because all changes to the cache are tracked through
+    // getGlyphMetrics and decideCouldDrawFromPath.
+    auto processPaths = [](
+            SkSpan<const SkGlyphRunListPainter::GlyphAndPos>, SkStrikeInterface*, SkScalar) { };
 
     ARGBHelper argbFallback{runMatrix, surfaceProps(), fStrikeServer};
 
     fPainter.drawGlyphRunAsPathWithARGBFallback(
             glyphCacheState, glyphRun, origin, runPaint, runMatrix, textScale,
-            std::move(perEmpty), std::move(perPath), std::move(argbFallback));
+            std::move(processPaths), std::move(argbFallback));
 }
 
 #if SK_SUPPORT_GPU
@@ -1095,7 +1112,7 @@
         fPainter->fMaxRunSize = size;
 
         fPainter->fPositions.reset(size);
-        fPainter->fMasks.reset(size);
+        fPainter->fGlyphPos.reset(size);
     }
 }
 
@@ -1107,7 +1124,7 @@
     if (fPainter->fMaxRunSize > 200) {
         fPainter->fMaxRunSize = 0;
         fPainter->fPositions.reset();
-        fPainter->fMasks.reset();
+        fPainter->fGlyphPos.reset();
         fPainter->fPaths.shrink_to_fit();
         fPainter->fARGBGlyphsIDs.shrink_to_fit();
         fPainter->fARGBPositions.shrink_to_fit();
diff --git a/src/core/SkGlyphRunPainter.h b/src/core/SkGlyphRunPainter.h
index a918b30..f6417a6 100644
--- a/src/core/SkGlyphRunPainter.h
+++ b/src/core/SkGlyphRunPainter.h
@@ -131,11 +131,11 @@
     // For each glyph that is not ARGB call perPath. If the glyph is ARGB then store the glyphID
     // and the position in fallback vectors. After all the glyphs are processed, pass the
     // fallback glyphIDs and positions to fallbackARGB.
-    template <typename PerEmptyT, typename PerPath>
+    template <typename ProcessPathsT>
     void drawGlyphRunAsPathWithARGBFallback(
             SkStrikeInterface* cache, const SkGlyphRun& glyphRun,
             SkPoint origin, const SkPaint& paint, const SkMatrix& viewMatrix, SkScalar textScale,
-            PerEmptyT&& perEmpty, PerPath&& perPath, ARGBFallback&& fallbackARGB);
+            ProcessPathsT&& processPaths, ARGBFallback&& fallbackARGB);
 
     template <typename PerEmptyT, typename PerSDFT, typename PerPathT>
     void drawGlyphRunAsSDFWithARGBFallback(
@@ -171,7 +171,7 @@
 
     int fMaxRunSize{0};
     SkAutoTMalloc<SkPoint> fPositions;
-    SkAutoTMalloc<GlyphAndPos> fMasks;
+    SkAutoTMalloc<GlyphAndPos> fGlyphPos;
 
     std::vector<GlyphAndPos> fPaths;