impl SkFont::getPath

Replacement for SkPaint::getTextPath and getPosTextPath
- only works with glyphIDs
- doesn't try to do positioning
- doesn't force caller to consolidate all the glyphs into one giant path

Much of the time is spent transforming the path from the cache's size to the callers.
Might consider passing the raw path + matrix rather than scaling it for them???

Bug: skia:
Change-Id: Ie13015c61ebe410eaec084282d600338cfccb51a
Reviewed-on: https://skia-review.googlesource.com/c/170881
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Mike Reed <reed@google.com>
diff --git a/bench/FontCacheBench.cpp b/bench/FontCacheBench.cpp
index de629f6..98caa2b 100644
--- a/bench/FontCacheBench.cpp
+++ b/bench/FontCacheBench.cpp
@@ -8,7 +8,9 @@
 #include "Benchmark.h"
 #include "SkCanvas.h"
 #include "SkChecksum.h"
+#include "SkFont.h"
 #include "SkPaint.h"
+#include "SkPath.h"
 #include "SkString.h"
 #include "SkTemplates.h"
 
@@ -139,10 +141,60 @@
 private:
     typedef Benchmark INHERITED;
 };
-
-///////////////////////////////////////////////////////////////////////////////
-
 DEF_BENCH( return new FontCacheBench(); )
 
 // undefine this to run the efficiency test
 //DEF_BENCH( return new FontCacheEfficiency(); )
+
+///////////////////////////////////////////////////////////////////////////////
+
+class FontPathBench : public Benchmark {
+    SkFont fFont;
+    uint16_t fGlyphs[100];
+    SkString fName;
+    const bool fOneAtATime;
+
+public:
+    FontPathBench(bool oneAtATime) : fOneAtATime(oneAtATime) {
+        fName.printf("font-path-%s", oneAtATime ? "loop" : "batch");
+    }
+
+protected:
+    const char* onGetName() override {
+        return fName.c_str();
+    }
+
+    bool isSuitableFor(Backend backend) override {
+        return backend == kNonRendering_Backend;
+    }
+
+    void onDelayedSetup() override {
+        fFont.setSize(32);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fGlyphs); ++i) {
+            fGlyphs[i] = i;
+        }
+    }
+
+    void onDraw(int loops, SkCanvas* canvas) override {
+        SkPath path;
+        for (int i = 0; i < loops; ++i) {
+            if (fOneAtATime) {
+                for (size_t i = 0; i < SK_ARRAY_COUNT(fGlyphs); ++i) {
+                    fFont.getPath(fGlyphs[i], &path);
+                }
+            } else {
+                fFont.getPaths(fGlyphs, SK_ARRAY_COUNT(fGlyphs),
+                               [](uint16_t, const SkPath* src, void* ctx) {
+                                   if (src) {
+                                       *static_cast<SkPath*>(ctx) = *src;
+                                   }
+                               }, &path);
+            }
+        }
+    }
+
+private:
+    typedef Benchmark INHERITED;
+};
+DEF_BENCH( return new FontPathBench(true); )
+DEF_BENCH( return new FontPathBench(false); )
diff --git a/include/core/SkFont.h b/include/core/SkFont.h
index 13cfaa1..9d69a4e 100644
--- a/include/core/SkFont.h
+++ b/include/core/SkFont.h
@@ -16,6 +16,7 @@
 #define SK_SUPPORT_LEGACY_FONT_FLAGS
 
 class SkPaint;
+class SkPath;
 struct SkFontMetrics;
 
 class SK_API SkFont {
@@ -152,7 +153,7 @@
         return fTypeface->unicharToGlyph(uni);
     }
 
-    int countText(const void* text, size_t byteLength, SkTextEncoding encoding) {
+    int countText(const void* text, size_t byteLength, SkTextEncoding encoding) const {
         return this->textToGlyphs(text, byteLength, encoding, nullptr, 0);
     }
 
@@ -162,6 +163,16 @@
     void getWidths(const uint16_t glyphs[], int count, SkScalar widths[],
                    SkRect bounds[] = nullptr) const;
 
+    /**
+     *  Returns true if the glyph has an outline (even if its empty), and sets the path.
+     *  If the glyph does not have an outline (e.g. it is a bitmap), this returns false
+     *  and ignores the path parameter.
+     */
+    bool getPath(uint16_t glyphID, SkPath* path) const;
+    void getPaths(const uint16_t glyphIDs[], int count,
+                  void (*GlyphPathProc)(uint16_t glyphID, const SkPath* pathOrNull, void* ctx),
+                  void* ctx) const;
+
     SkScalar getMetrics(SkFontMetrics* metrics) const;
     SkScalar getSpacing() const { return this->getMetrics(nullptr); }
 
diff --git a/src/core/SkFont.cpp b/src/core/SkFont.cpp
index b04226a..62edeb9 100644
--- a/src/core/SkFont.cpp
+++ b/src/core/SkFont.cpp
@@ -9,6 +9,7 @@
 #include "SkFontPriv.h"
 #include "SkGlyphCache.h"
 #include "SkPaint.h"
+#include "SkPath.h"
 #include "SkScalerContext.h"
 #include "SkStrikeCache.h"
 #include "SkTo.h"
@@ -334,6 +335,48 @@
     }
 }
 
+void SkFont::getPaths(const uint16_t glyphs[], int count,
+                      void (*proc)(uint16_t, const SkPath*, void*), void* ctx) const {
+    SkFont font(*this);
+    SkScalar scale = font.setupForAsPaths(nullptr);
+
+    SkAutoDescriptor ad;
+    SkScalerContextEffects effects;
+    auto desc = SkScalerContext::CreateDescriptorAndEffectsUsingDefaultPaint(font,
+                         SkSurfaceProps(0, kUnknown_SkPixelGeometry), SkScalerContextFlags::kNone,
+                         SkMatrix::I(), &ad, &effects);
+    auto typeface = SkFontPriv::GetTypefaceOrDefault(font);
+    auto exclusive = SkStrikeCache::FindOrCreateStrikeExclusive(*desc, effects, *typeface);
+    auto cache = exclusive.get();
+
+    for (int i = 0; i < count; ++i) {
+        const SkPath* orig = cache->findPath(cache->getGlyphIDMetrics(glyphs[i]));
+        if (orig && scale) {
+            SkPath tmp;
+            orig->transform(SkMatrix::MakeScale(scale, scale), &tmp);
+            proc(glyphs[i], &tmp, ctx);
+        } else {
+            proc(glyphs[i], orig, ctx);
+        }
+    }
+}
+
+bool SkFont::getPath(uint16_t glyphID, SkPath* path) const {
+    struct Pair {
+        SkPath* fPath;
+        bool    fWasSet;
+    } pair = { path, false };
+
+    this->getPaths(&glyphID, 1, [](uint16_t, const SkPath* orig, void* ctx) {
+        Pair* pair = static_cast<Pair*>(ctx);
+        if (orig) {
+            *pair->fPath = *orig;
+            pair->fWasSet = true;
+        }
+    }, &pair);
+    return pair.fWasSet;
+}
+
 SkScalar SkFont::getMetrics(SkFontMetrics* metrics) const {
     SkCanonicalizeFont canon(*this);
     const SkFont& font = canon.getFont();