Revert "Simplify SkTypeface::charsToGlyphs API to require UTF32 input"

This reverts commit fde841de44a63701b0a4396da35893f348559ce9.

Reason for revert: used in headless on google3, need to add a guard

Original change's description:
> Simplify SkTypeface::charsToGlyphs API to require UTF32 input
> 
> Change-Id: I486713c496c40103eef13fa6068ac4d69e32f606
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/207865
> Commit-Queue: Ben Wagner <bungeman@google.com>
> Reviewed-by: Ben Wagner <bungeman@google.com>

TBR=bungeman@google.com,herb@google.com,reed@google.com

Change-Id: I1d28ed5c31deaa76aa3c4b627454b0ad3356a6fc
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/208800
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/bench/CmapBench.cpp b/bench/CmapBench.cpp
index cc34af1..3b90e35 100644
--- a/bench/CmapBench.cpp
+++ b/bench/CmapBench.cpp
@@ -44,7 +44,7 @@
 
     SkTypeface* face = r.fFont.getTypefaceOrDefault();
     for (int i = 0; i < r.fLoops; ++i) {
-        face->unicharsToGlyphs(r.fText, r.fCount, glyphs);
+        face->charsToGlyphs(r.fText, SkTypeface::kUTF32_Encoding, glyphs, r.fCount);
     }
 }
 
diff --git a/bench/TypefaceBench.cpp b/bench/TypefaceBench.cpp
index 00b19d2..cce5cdb 100644
--- a/bench/TypefaceBench.cpp
+++ b/bench/TypefaceBench.cpp
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "Benchmark.h"
-#include "SkFont.h"
+#include "SkFontTypes.h"
 #include "SkMakeUnique.h"
 #include "SkTypeface.h"
 #include "SkUTF.h"
@@ -238,12 +238,12 @@
     }
 
     void onDraw(int loops, SkCanvas* canvas) override {
-        SkFont font(fTypeface);
         // Do more loops to reduce variance.
         for (int i = 0; i < loops * 3; ++i) {
             for (auto& line : fLines) {
-                font.textToGlyphs(line->utf.data(), line->utf.size(), fEncoding,
-                                  fGlyphIds.data(), line->glyphCount);
+                fTypeface->charsToGlyphs(line->utf.data(),
+                                  (SkTypeface::Encoding)fEncoding, fGlyphIds.data(),
+                                  line->glyphCount);
             }
         }
     }
diff --git a/gm/atlastext.cpp b/gm/atlastext.cpp
index 1584ea4..328f398 100644
--- a/gm/atlastext.cpp
+++ b/gm/atlastext.cpp
@@ -33,14 +33,12 @@
     auto atlas_font = SkAtlasTextFont::Make(typeface, size);
     int cnt = SkUTF::CountUTF8(text.c_str(), text.size());
     std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[cnt]);
+    typeface->charsToGlyphs(text.c_str(), SkTypeface::Encoding::kUTF8_Encoding, glyphs.get(), cnt);
 
     // Using a paint to get the positions for each glyph.
     SkFont font;
     font.setSize(size);
     font.setTypeface(std::move(typeface));
-
-    font.textToGlyphs(text.c_str(), text.size(), SkTextEncoding::kUTF8, glyphs.get(), cnt);
-
     std::unique_ptr<SkScalar[]> widths(new SkScalar[cnt]);
     font.getWidths(glyphs.get(), cnt, widths.get());
 
diff --git a/include/core/SkFont.h b/include/core/SkFont.h
index a64c4bb..74b7df5 100644
--- a/include/core/SkFont.h
+++ b/include/core/SkFont.h
@@ -302,8 +302,6 @@
     */
     SkGlyphID unicharToGlyph(SkUnichar uni) const;
 
-    void unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const;
-
     /** Returns number of glyphs represented by text.
 
         If encoding is kUTF8_SkTextEncoding, kUTF16_SkTextEncoding, or
diff --git a/include/core/SkFontTypes.h b/include/core/SkFontTypes.h
index 6796451..bb1b03f 100644
--- a/include/core/SkFontTypes.h
+++ b/include/core/SkFontTypes.h
@@ -9,6 +9,8 @@
 #define SkFontTypes_DEFINED
 
 #include "SkTypes.h"
+// remove me once google3 uses IWYU
+#include "SkTypeface.h"
 
 enum class SkTextEncoding {
     kUTF8,      //!< uses bytes to represent UTF-8 or ASCII
@@ -34,7 +36,4 @@
 #define kNormal_SkFontHinting   SkFontHinting::kNormal
 #define kFull_SkFontHinting     SkFontHinting::kFull
 
-// remove me once google3 uses IWYU
-#include "SkTypeface.h"
-
 #endif
diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h
index 1fba06c..7f5af8c 100644
--- a/include/core/SkTypeface.h
+++ b/include/core/SkTypeface.h
@@ -14,7 +14,6 @@
 #include "SkFontArguments.h"
 #include "SkFontParameters.h"
 #include "SkFontStyle.h"
-#include "SkFontTypes.h"
 #include "SkRect.h"
 #include "SkString.h"
 
@@ -177,20 +176,36 @@
      */
     static sk_sp<SkTypeface> MakeDeserialize(SkStream*);
 
+    enum Encoding : uint8_t {
+        kUTF8_Encoding,
+        kUTF16_Encoding,
+        kUTF32_Encoding
+    };
+
     /**
-     *  Given an array of UTF32 character codes, return their corresponding glyph IDs.
+     *  Given an array of character codes, of the specified encoding,
+     *  optionally return their corresponding glyph IDs (if glyphs is not NULL).
      *
-     *  @param chars pointer to the array of UTF32 chars
-     *  @param number of chars and glyphs
-     *  @param glyphs returns the corresponding glyph IDs for each character.
+     *  @param chars pointer to the array of character codes
+     *  @param encoding how the characters are encoded
+     *  @param glyphs (optional) returns the corresponding glyph IDs for each
+     *          character code, up to glyphCount values. If a character code is
+     *          not found in the typeface, the corresponding glyph ID will be 0.
+     *  @param glyphCount number of code points in 'chars' to process. If glyphs
+     *          is not NULL, then it must point sufficient memory to write
+     *          glyphCount values into it.
+     *  @return the number of number of continuous non-zero glyph IDs computed
+     *          from the beginning of chars. This value is valid, even if the
+     *          glyphs parameter is NULL.
      */
-    void unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const;
+    int charsToGlyphs(const void* chars, Encoding encoding, SkGlyphID glyphs[],
+                      int glyphCount) const;
 
     /**
      *  Return the glyphID that corresponds to the specified unicode code-point
      *  (in UTF32 encoding). If the unichar is not supported, returns 0.
      *
-     *  This is a short-cut for calling unicharsToGlyphs().
+     *  This is a short-cut for calling charsToGlyphs() with kUTF32_Encoding for one code-point.
      */
     SkGlyphID unicharToGlyph(SkUnichar unichar) const;
 
@@ -378,7 +393,8 @@
 
     virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const = 0;
 
-    virtual void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const = 0;
+    virtual int onCharsToGlyphs(const void* chars, Encoding, SkGlyphID glyphs[],
+                                int glyphCount) const = 0;
     virtual int onCountGlyphs() const = 0;
 
     virtual int onGetUPEM() const = 0;
diff --git a/src/core/SkFont.cpp b/src/core/SkFont.cpp
index 29c8229..5ab8447 100644
--- a/src/core/SkFont.cpp
+++ b/src/core/SkFont.cpp
@@ -170,46 +170,6 @@
     return this->getTypefaceOrDefault()->unicharToGlyph(uni);
 }
 
-void SkFont::unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
-    this->getTypefaceOrDefault()->unicharsToGlyphs(uni, count, glyphs);
-}
-
-class SkConvertToUTF32 {
-public:
-    SkConvertToUTF32() {}
-
-    const SkUnichar* convert(const void* text, size_t byteLength, SkTextEncoding encoding) {
-        const SkUnichar* uni;
-        switch (encoding) {
-            case SkTextEncoding::kUTF8: {
-                uni = fStorage.reset(byteLength);
-                const char* ptr = (const char*)text;
-                const char* end = ptr + byteLength;
-                for (int i = 0; ptr < end; ++i) {
-                    fStorage[i] = SkUTF::NextUTF8(&ptr, end);
-                }
-            } break;
-            case SkTextEncoding::kUTF16: {
-                uni = fStorage.reset(byteLength);
-                const uint16_t* ptr = (const uint16_t*)text;
-                const uint16_t* end = ptr + (byteLength >> 1);
-                for (int i = 0; ptr < end; ++i) {
-                    fStorage[i] = SkUTF::NextUTF16(&ptr, end);
-                }
-            } break;
-            case SkTextEncoding::kUTF32:
-                uni = (const SkUnichar*)text;
-                break;
-            default:
-                SK_ABORT("unexpected enum");
-        }
-        return uni;
-    }
-
-private:
-    SkAutoSTMalloc<256, SkUnichar> fStorage;
-};
-
 int SkFont::textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding,
                          SkGlyphID glyphs[], int maxGlyphCount) const {
     if (0 == byteLength) {
@@ -223,15 +183,26 @@
         return count;
     }
 
-    if (encoding == SkTextEncoding::kGlyphID) {
-        memcpy(glyphs, text, count << 1);
-        return count;
+    // TODO: unify/eliminate SkTypeface::Encoding with SkTextEncoding
+    SkTypeface::Encoding typefaceEncoding;
+    switch (encoding) {
+        case kUTF8_SkTextEncoding:
+            typefaceEncoding = SkTypeface::kUTF8_Encoding;
+            break;
+        case kUTF16_SkTextEncoding:
+            typefaceEncoding = SkTypeface::kUTF16_Encoding;
+            break;
+        case kUTF32_SkTextEncoding:
+            typefaceEncoding = SkTypeface::kUTF32_Encoding;
+            break;
+        default:
+            SkASSERT(kGlyphID_SkTextEncoding == encoding);
+            // we can early exit, since we already have glyphIDs
+            memcpy(glyphs, text, count << 1);
+            return count;
     }
 
-    SkConvertToUTF32 storage;
-    const SkUnichar* uni = storage.convert(text, byteLength, encoding);
-
-    this->getTypefaceOrDefault()->unicharsToGlyphs(uni, count, glyphs);
+    (void) this->getTypefaceOrDefault()->charsToGlyphs(text, typefaceEncoding, glyphs,count);
     return count;
 }
 
diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp
index 7786b78..3950e5d 100644
--- a/src/core/SkTypeface.cpp
+++ b/src/core/SkTypeface.cpp
@@ -57,8 +57,12 @@
         return nullptr;
     }
     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
-    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override {
-        sk_bzero(glyphs, count * sizeof(glyphs[0]));
+    virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
+                                uint16_t glyphs[], int glyphCount) const override {
+        if (glyphs && glyphCount > 0) {
+            sk_bzero(glyphs, glyphCount * sizeof(glyphs[0]));
+        }
+        return 0;
     }
     int onCountGlyphs() const override { return 0; }
     int onGetUPEM() const override { return 0; }
@@ -281,16 +285,23 @@
     return skstd::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
 };
 
-void SkTypeface::unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
-    if (count > 0 && glyphs && uni) {
-        this->onCharsToGlyphs(uni, count, glyphs);
+int SkTypeface::charsToGlyphs(const void* chars, Encoding encoding,
+                              uint16_t glyphs[], int glyphCount) const {
+    if (glyphCount <= 0) {
+        return 0;
     }
+    if (nullptr == chars || (unsigned)encoding > kUTF32_Encoding) {
+        if (glyphs) {
+            sk_bzero(glyphs, glyphCount * sizeof(glyphs[0]));
+        }
+        return 0;
+    }
+    return this->onCharsToGlyphs(chars, encoding, glyphs, glyphCount);
 }
 
 SkGlyphID SkTypeface::unicharToGlyph(SkUnichar uni) const {
-    SkGlyphID glyphs[1] = { 0 };
-    this->onCharsToGlyphs(&uni, 1, glyphs);
-    return glyphs[0];
+    SkGlyphID glyphs[1];
+    return this->onCharsToGlyphs(&uni, kUTF32_Encoding, glyphs, 1) == 1 ? glyphs[0] : 0;
 }
 
 int SkTypeface::countGlyphs() const {
diff --git a/src/core/SkTypeface_remote.h b/src/core/SkTypeface_remote.h
index dde0ff5..46908c5 100644
--- a/src/core/SkTypeface_remote.h
+++ b/src/core/SkTypeface_remote.h
@@ -122,8 +122,10 @@
         SK_ABORT("Should never be called.");
         return nullptr;
     }
-    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override {
+    int onCharsToGlyphs(const void* chars, Encoding,
+                        uint16_t glyphs[], int glyphCount) const override {
         SK_ABORT("Should never be called.");
+        return 0;
     }
     int onCountGlyphs() const override {
         return this->glyphCount();
diff --git a/src/core/SkUtils.h b/src/core/SkUtils.h
index 1597c98..cfbdaae 100644
--- a/src/core/SkUtils.h
+++ b/src/core/SkUtils.h
@@ -9,7 +9,7 @@
 #define SkUtils_DEFINED
 
 #include "SkOpts.h"
-#include "SkFontTypes.h"
+#include "SkTypeface.h"
 #include "../utils/SkUTF.h"
 
 /** Similar to memset(), but it assigns a 16, 32, or 64-bit value into the buffer.
@@ -42,22 +42,23 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static inline int SkUTFN_CountUnichars(SkTextEncoding enc, const void* utfN, size_t bytes) {
+static inline int SkUTFN_CountUnichars(SkTypeface::Encoding enc, const void* utfN, size_t bytes) {
     switch (enc) {
-        case SkTextEncoding::kUTF8:  return SkUTF::CountUTF8((const char*)utfN, bytes);
-        case SkTextEncoding::kUTF16: return SkUTF::CountUTF16((const uint16_t*)utfN, bytes);
-        case SkTextEncoding::kUTF32: return SkUTF::CountUTF32((const int32_t*)utfN, bytes);
+        case SkTypeface::kUTF8_Encoding:  return SkUTF::CountUTF8((const char*)utfN, bytes);
+        case SkTypeface::kUTF16_Encoding: return SkUTF::CountUTF16((const uint16_t*)utfN, bytes);
+        case SkTypeface::kUTF32_Encoding: return SkUTF::CountUTF32((const int32_t*)utfN, bytes);
         default: SkDEBUGFAIL("unknown text encoding"); return -1;
     }
 }
 
-static inline SkUnichar SkUTFN_Next(SkTextEncoding enc, const void** ptr, const void* stop) {
+static inline SkUnichar SkUTFN_Next(SkTypeface::Encoding enc,
+                                    const void** ptr, const void* stop) {
     switch (enc) {
-        case SkTextEncoding::kUTF8:
+        case SkTypeface::kUTF8_Encoding:
             return SkUTF::NextUTF8((const char**)ptr, (const char*)stop);
-        case SkTextEncoding::kUTF16:
+        case SkTypeface::kUTF16_Encoding:
             return SkUTF::NextUTF16((const uint16_t**)ptr, (const uint16_t*)stop);
-        case SkTextEncoding::kUTF32:
+        case SkTypeface::kUTF32_Encoding:
             return SkUTF::NextUTF32((const int32_t**)ptr, (const int32_t*)stop);
         default: SkDEBUGFAIL("unknown text encoding"); return -1;
     }
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index dd0b800..c21f884 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -1551,17 +1551,62 @@
 
 #include "SkUtils.h"
 
-void SkTypeface_FreeType::onCharsToGlyphs(const SkUnichar uni[], int count,
-                                          SkGlyphID glyphs[]) const {
+static SkUnichar next_utf8(const void** chars) {
+    return SkUTF8_NextUnichar((const char**)chars);
+}
+
+static SkUnichar next_utf16(const void** chars) {
+    return SkUTF16_NextUnichar((const uint16_t**)chars);
+}
+
+static SkUnichar next_utf32(const void** chars) {
+    const SkUnichar** uniChars = (const SkUnichar**)chars;
+    SkUnichar uni = **uniChars;
+    *uniChars += 1;
+    return uni;
+}
+
+typedef SkUnichar (*EncodingProc)(const void**);
+
+static EncodingProc find_encoding_proc(SkTypeface::Encoding enc) {
+    static const EncodingProc gProcs[] = {
+        next_utf8, next_utf16, next_utf32
+    };
+    SkASSERT((size_t)enc < SK_ARRAY_COUNT(gProcs));
+    return gProcs[enc];
+}
+
+int SkTypeface_FreeType::onCharsToGlyphs(const void* chars, Encoding encoding,
+                                         uint16_t glyphs[], int glyphCount) const
+{
     AutoFTAccess fta(this);
     FT_Face face = fta.face();
     if (!face) {
-        sk_bzero(glyphs, count * sizeof(glyphs[0]));
-        return;
+        if (glyphs) {
+            sk_bzero(glyphs, glyphCount * sizeof(glyphs[0]));
+        }
+        return 0;
     }
 
-    for (int i = 0; i < count; ++i) {
-        glyphs[i] = SkToU16(FT_Get_Char_Index(face, uni[i]));
+    EncodingProc next_uni_proc = find_encoding_proc(encoding);
+
+    if (nullptr == glyphs) {
+        for (int i = 0; i < glyphCount; ++i) {
+            if (0 == FT_Get_Char_Index(face, next_uni_proc(&chars))) {
+                return i;
+            }
+        }
+        return glyphCount;
+    } else {
+        int first = glyphCount;
+        for (int i = 0; i < glyphCount; ++i) {
+            unsigned id = FT_Get_Char_Index(face, next_uni_proc(&chars));
+            glyphs[i] = SkToU16(id);
+            if (0 == id && i < first) {
+                first = i;
+            }
+        }
+        return first;
     }
 }
 
diff --git a/src/ports/SkFontHost_FreeType_common.h b/src/ports/SkFontHost_FreeType_common.h
index 064b1a8..02cdb60 100644
--- a/src/ports/SkFontHost_FreeType_common.h
+++ b/src/ports/SkFontHost_FreeType_common.h
@@ -102,7 +102,8 @@
     int onGetUPEM() const override;
     bool onGetKerningPairAdjustments(const uint16_t glyphs[], int count,
                                      int32_t adjustments[]) const override;
-    void onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const override;
+    int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
+                        int glyphCount) const override;
     int onCountGlyphs() const override;
 
     LocalizedStrings* onCreateFamilyNameIterator() const override;
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index 870ee30..5f9cbcf 100644
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -742,7 +742,8 @@
     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
     void getGlyphToUnicodeMap(SkUnichar*) const override;
     std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
-    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
+    int onCharsToGlyphs(const void* chars, Encoding,
+                        uint16_t glyphs[], int glyphCount) const override;
     int onCountGlyphs() const override;
 
     void* onGetCTFontRef() const override { return (void*)fFontRef.get(); }
@@ -2332,7 +2333,9 @@
     *isLocalStream = fIsFromStream;
 }
 
-void SkTypeface_Mac::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
+int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
+                                    uint16_t glyphs[], int glyphCount) const
+{
     // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
     // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
     // It is documented that if a mapping is unavailable, the glyph will be set to 0.
@@ -2340,38 +2343,81 @@
     SkAutoSTMalloc<1024, UniChar> charStorage;
     const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
     int srcCount;
-    const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(uni);
-    UniChar* utf16 = charStorage.reset(2 * count);
-    src = utf16;
-    for (int i = 0; i < count; ++i) {
-        utf16 += SkUTF::ToUTF16(utf32[i], utf16);
+    switch (encoding) {
+        case kUTF8_Encoding: {
+            const char* utf8 = reinterpret_cast<const char*>(chars);
+            UniChar* utf16 = charStorage.reset(2 * glyphCount);
+            src = utf16;
+            for (int i = 0; i < glyphCount; ++i) {
+                SkUnichar uni = SkUTF8_NextUnichar(&utf8);
+                utf16 += SkUTF::ToUTF16(uni, utf16);
+            }
+            srcCount = SkToInt(utf16 - src);
+            break;
+        }
+        case kUTF16_Encoding: {
+            src = reinterpret_cast<const UniChar*>(chars);
+            int extra = 0;
+            for (int i = 0; i < glyphCount; ++i) {
+                if (SkUTF16_IsLeadingSurrogate(src[i + extra])) {
+                    ++extra;
+                }
+            }
+            srcCount = glyphCount + extra;
+            break;
+        }
+        case kUTF32_Encoding: {
+            const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
+            UniChar* utf16 = charStorage.reset(2 * glyphCount);
+            src = utf16;
+            for (int i = 0; i < glyphCount; ++i) {
+                utf16 += SkUTF::ToUTF16(utf32[i], utf16);
+            }
+            srcCount = SkToInt(utf16 - src);
+            break;
+        }
     }
-    srcCount = SkToInt(utf16 - src);
 
-    // If there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
+    // If glyphs is nullptr, CT still needs glyph storage for finding the first failure.
+    // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
     SkAutoSTMalloc<1024, uint16_t> glyphStorage;
     uint16_t* macGlyphs = glyphs;
-    if (srcCount > count) {
+    if (nullptr == macGlyphs || srcCount > glyphCount) {
         macGlyphs = glyphStorage.reset(srcCount);
     }
 
-    CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
+    bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
 
     // If there were any non-bmp, then copy and compact.
-    // If all are bmp, 'glyphs' already contains the compact glyphs.
-    // If some are non-bmp, copy and compact into 'glyphs'.
-    if (srcCount > count) {
-        SkASSERT(glyphs != macGlyphs);
+    // If 'glyphs' is nullptr, then compact glyphStorage in-place.
+    // If all are bmp and 'glyphs' is non-nullptr, 'glyphs' already contains the compact glyphs.
+    // If some are non-bmp and 'glyphs' is non-nullptr, copy and compact into 'glyphs'.
+    uint16_t* compactedGlyphs = glyphs;
+    if (nullptr == compactedGlyphs) {
+        compactedGlyphs = macGlyphs;
+    }
+    if (srcCount > glyphCount) {
         int extra = 0;
-        for (int i = 0; i < count; ++i) {
-            glyphs[i] = macGlyphs[i + extra];
+        for (int i = 0; i < glyphCount; ++i) {
+            compactedGlyphs[i] = macGlyphs[i + extra];
             if (SkUTF16_IsLeadingSurrogate(src[i + extra])) {
                 ++extra;
             }
         }
-    } else {
-        SkASSERT(glyphs == macGlyphs);
     }
+
+    if (allEncoded) {
+        return glyphCount;
+    }
+
+    // If we got false, then we need to manually look for first failure.
+    for (int i = 0; i < glyphCount; ++i) {
+        if (0 == compactedGlyphs[i]) {
+            return i;
+        }
+    }
+    // Odd to get here, as we expected CT to have returned true up front.
+    return glyphCount;
 }
 
 int SkTypeface_Mac::onCountGlyphs() const {
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index bb76fc5..bbb37c7 100644
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -267,7 +267,8 @@
     void getGlyphToUnicodeMap(SkUnichar*) const override;
     std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
-    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
+    int onCharsToGlyphs(const void* chars, Encoding encoding,
+                        uint16_t glyphs[], int glyphCount) const override;
     int onCountGlyphs() const override;
     int onGetUPEM() const override;
     void onGetFamilyName(SkString* familyName) const override;
@@ -1916,8 +1917,8 @@
 };
 #define SkAutoHDC(...) SK_REQUIRE_LOCAL_VAR(SkAutoHDC)
 
-void LogFontTypeface::onCharsToGlyphs(const SkUnichar uni[], int glyphCount,
-                                      SkGlyphID glyphs[]) const
+int LogFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
+                                     uint16_t userGlyphs[], int glyphCount) const
 {
     SkAutoHDC hdc(fLogFont);
 
@@ -1930,35 +1931,119 @@
     }
     bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */;
 
-    SCRIPT_CACHE sc = 0;
-    static const int scratchCount = 256;
-    WCHAR scratch[scratchCount];
-    int glyphIndex = 0;
-    const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(uni);
-    while (glyphIndex < glyphCount) {
-        // Try a run of bmp.
-        int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
-        int runLength = 0;
-        while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) {
-            scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]);
-            ++runLength;
-        }
-        if (runLength) {
-            bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
-            glyphIndex += runLength;
-        }
+    SkAutoSTMalloc<256, uint16_t> scratchGlyphs;
+    uint16_t* glyphs;
+    if (userGlyphs != nullptr) {
+        glyphs = userGlyphs;
+    } else {
+        glyphs = scratchGlyphs.reset(glyphCount);
+    }
 
-        // Try a run of non-bmp.
-        while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) {
-            SkUTF::ToUTF16(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch));
-            glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
-            ++glyphIndex;
+    SCRIPT_CACHE sc = 0;
+    switch (encoding) {
+    case SkTypeface::kUTF8_Encoding: {
+        static const int scratchCount = 256;
+        WCHAR scratch[scratchCount];
+        int glyphIndex = 0;
+        const char* currentUtf8 = reinterpret_cast<const char*>(chars);
+        SkUnichar currentChar = 0;
+        if (glyphCount) {
+            currentChar = SkUTF8_NextUnichar(&currentUtf8);
         }
+        while (glyphIndex < glyphCount) {
+            // Try a run of bmp.
+            int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
+            int runLength = 0;
+            while (runLength < glyphsLeft && currentChar <= 0xFFFF) {
+                scratch[runLength] = static_cast<WCHAR>(currentChar);
+                ++runLength;
+                if (runLength < glyphsLeft) {
+                    currentChar = SkUTF8_NextUnichar(&currentUtf8);
+                }
+            }
+            if (runLength) {
+                bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
+                glyphIndex += runLength;
+            }
+
+            // Try a run of non-bmp.
+            while (glyphIndex < glyphCount && currentChar > 0xFFFF) {
+                SkUTF::ToUTF16(currentChar, reinterpret_cast<uint16_t*>(scratch));
+                glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
+                ++glyphIndex;
+                if (glyphIndex < glyphCount) {
+                    currentChar = SkUTF8_NextUnichar(&currentUtf8);
+                }
+            }
+        }
+        break;
+    }
+    case SkTypeface::kUTF16_Encoding: {
+        int glyphIndex = 0;
+        const WCHAR* currentUtf16 = reinterpret_cast<const WCHAR*>(chars);
+        while (glyphIndex < glyphCount) {
+            // Try a run of bmp.
+            int glyphsLeft = glyphCount - glyphIndex;
+            int runLength = 0;
+            while (runLength < glyphsLeft && !SkUTF16_IsLeadingSurrogate(currentUtf16[runLength])) {
+                ++runLength;
+            }
+            if (runLength) {
+                bmpCharsToGlyphs(hdc, currentUtf16, runLength, &glyphs[glyphIndex], Ox1FHack);
+                glyphIndex += runLength;
+                currentUtf16 += runLength;
+            }
+
+            // Try a run of non-bmp.
+            while (glyphIndex < glyphCount && SkUTF16_IsLeadingSurrogate(*currentUtf16)) {
+                glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, currentUtf16);
+                ++glyphIndex;
+                currentUtf16 += 2;
+            }
+        }
+        break;
+    }
+    case SkTypeface::kUTF32_Encoding: {
+        static const int scratchCount = 256;
+        WCHAR scratch[scratchCount];
+        int glyphIndex = 0;
+        const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(chars);
+        while (glyphIndex < glyphCount) {
+            // Try a run of bmp.
+            int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
+            int runLength = 0;
+            while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) {
+                scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]);
+                ++runLength;
+            }
+            if (runLength) {
+                bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
+                glyphIndex += runLength;
+            }
+
+            // Try a run of non-bmp.
+            while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) {
+                SkUTF::ToUTF16(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch));
+                glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
+                ++glyphIndex;
+            }
+        }
+        break;
+    }
+    default:
+        SK_ABORT("Invalid Text Encoding");
     }
 
     if (sc) {
         ::ScriptFreeCache(&sc);
     }
+
+    for (int i = 0; i < glyphCount; ++i) {
+        if (0 == glyphs[i]) {
+            return i;
+        }
+    }
+    return glyphCount;
 }
 
 int LogFontTypeface::onCountGlyphs() const {
diff --git a/src/ports/SkTypeface_win_dw.cpp b/src/ports/SkTypeface_win_dw.cpp
index bcb9d7d..c7802aa 100644
--- a/src/ports/SkTypeface_win_dw.cpp
+++ b/src/ports/SkTypeface_win_dw.cpp
@@ -52,9 +52,78 @@
     *isLocalStream = SkToBool(fDWriteFontFileLoader.get());
 }
 
-void DWriteFontTypeface::onCharsToGlyphs(const SkUnichar uni[], int count,
-                                         SkGlyphID glyphs[]) const {
-    fDWriteFontFace->GetGlyphIndices((const UINT32*)uni, count, glyphs);
+static SkUnichar next_utf8(const void** chars) {
+    return SkUTF8_NextUnichar((const char**)chars);
+}
+
+static SkUnichar next_utf16(const void** chars) {
+    return SkUTF16_NextUnichar((const uint16_t**)chars);
+}
+
+static SkUnichar next_utf32(const void** chars) {
+    const SkUnichar** uniChars = (const SkUnichar**)chars;
+    SkUnichar uni = **uniChars;
+    *uniChars += 1;
+    return uni;
+}
+
+typedef SkUnichar (*EncodingProc)(const void**);
+
+static EncodingProc find_encoding_proc(SkTypeface::Encoding enc) {
+    static const EncodingProc gProcs[] = {
+        next_utf8, next_utf16, next_utf32
+    };
+    SkASSERT((size_t)enc < SK_ARRAY_COUNT(gProcs));
+    return gProcs[enc];
+}
+
+int DWriteFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
+                                        uint16_t glyphs[], int glyphCount) const
+{
+    if (nullptr == glyphs) {
+        EncodingProc next_ucs4_proc = find_encoding_proc(encoding);
+        for (int i = 0; i < glyphCount; ++i) {
+            const SkUnichar c = next_ucs4_proc(&chars);
+            BOOL exists;
+            fDWriteFont->HasCharacter(c, &exists);
+            if (!exists) {
+                return i;
+            }
+        }
+        return glyphCount;
+    }
+
+    switch (encoding) {
+    case SkTypeface::kUTF8_Encoding:
+    case SkTypeface::kUTF16_Encoding: {
+        static const int scratchCount = 256;
+        UINT32 scratch[scratchCount];
+        EncodingProc next_ucs4_proc = find_encoding_proc(encoding);
+        for (int baseGlyph = 0; baseGlyph < glyphCount; baseGlyph += scratchCount) {
+            int glyphsLeft = glyphCount - baseGlyph;
+            int limit = SkTMin(glyphsLeft, scratchCount);
+            for (int i = 0; i < limit; ++i) {
+                scratch[i] = next_ucs4_proc(&chars);
+            }
+            fDWriteFontFace->GetGlyphIndices(scratch, limit, &glyphs[baseGlyph]);
+        }
+        break;
+    }
+    case SkTypeface::kUTF32_Encoding: {
+        const UINT32* utf32 = reinterpret_cast<const UINT32*>(chars);
+        fDWriteFontFace->GetGlyphIndices(utf32, glyphCount, glyphs);
+        break;
+    }
+    default:
+        SK_ABORT("Invalid Text Encoding");
+    }
+
+    for (int i = 0; i < glyphCount; ++i) {
+        if (0 == glyphs[i]) {
+            return i;
+        }
+    }
+    return glyphCount;
 }
 
 int DWriteFontTypeface::onCountGlyphs() const {
diff --git a/src/ports/SkTypeface_win_dw.h b/src/ports/SkTypeface_win_dw.h
index 0e11333..855ef79 100644
--- a/src/ports/SkTypeface_win_dw.h
+++ b/src/ports/SkTypeface_win_dw.h
@@ -116,7 +116,8 @@
     void getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const override;
     std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
-    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
+    int onCharsToGlyphs(const void* chars, Encoding encoding,
+                        uint16_t glyphs[], int glyphCount) const override;
     int onCountGlyphs() const override;
     int onGetUPEM() const override;
     void onGetFamilyName(SkString* familyName) const override;
diff --git a/tests/FontHostTest.cpp b/tests/FontHostTest.cpp
index 0b70ac6..c9846a6 100644
--- a/tests/FontHostTest.cpp
+++ b/tests/FontHostTest.cpp
@@ -69,6 +69,50 @@
     }
 }
 
+// The following three are all the same code points in various encodings.
+// a中Яיו𝄞a𠮟
+static uint8_t utf8Chars[] = { 0x61, 0xE4,0xB8,0xAD, 0xD0,0xAF, 0xD7,0x99, 0xD7,0x95, 0xF0,0x9D,0x84,0x9E, 0x61, 0xF0,0xA0,0xAE,0x9F };
+static uint16_t utf16Chars[] = { 0x0061, 0x4E2D, 0x042F, 0x05D9, 0x05D5, 0xD834,0xDD1E, 0x0061, 0xD842,0xDF9F };
+static uint32_t utf32Chars[] = { 0x00000061, 0x00004E2D, 0x0000042F, 0x000005D9, 0x000005D5, 0x0001D11E, 0x00000061, 0x00020B9F };
+
+struct CharsToGlyphs_TestData {
+    const void* chars;
+    int charCount;
+    size_t charsByteLength;
+    SkTypeface::Encoding typefaceEncoding;
+    const char* name;
+} static charsToGlyphs_TestData[] = {
+    { utf8Chars, 8, sizeof(utf8Chars), SkTypeface::kUTF8_Encoding, "Simple UTF-8" },
+    { utf16Chars, 8, sizeof(utf16Chars), SkTypeface::kUTF16_Encoding, "Simple UTF-16" },
+    { utf32Chars, 8, sizeof(utf32Chars), SkTypeface::kUTF32_Encoding, "Simple UTF-32" },
+};
+
+// Test that SkPaint::textToGlyphs agrees with SkTypeface::charsToGlyphs.
+static void test_charsToGlyphs(skiatest::Reporter* reporter, sk_sp<SkTypeface> face) {
+    uint16_t paintGlyphIds[256];
+    uint16_t faceGlyphIds[256];
+
+    for (size_t testIndex = 0; testIndex < SK_ARRAY_COUNT(charsToGlyphs_TestData); ++testIndex) {
+        CharsToGlyphs_TestData& test = charsToGlyphs_TestData[testIndex];
+        SkTextEncoding encoding = static_cast<SkTextEncoding>(test.typefaceEncoding);
+
+        SkFont font(face);
+        font.textToGlyphs(test.chars, test.charsByteLength, encoding,
+                          paintGlyphIds, SK_ARRAY_COUNT(paintGlyphIds));
+
+        face->charsToGlyphs(test.chars, test.typefaceEncoding, faceGlyphIds, test.charCount);
+
+        for (int i = 0; i < test.charCount; ++i) {
+            SkString name;
+            face->getFamilyName(&name);
+            SkString a;
+            a.appendf("%s, paintGlyphIds[%d] = %d, faceGlyphIds[%d] = %d, face = %s",
+                      test.name, i, (int)paintGlyphIds[i], i, (int)faceGlyphIds[i], name.c_str());
+            REPORTER_ASSERT(reporter, paintGlyphIds[i] == faceGlyphIds[i], a.c_str());
+        }
+    }
+}
+
 static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream, int ttcIndex) {
     int n = SkFontStream::GetTableTags(stream, ttcIndex, nullptr);
     SkAutoTArray<SkFontTableTag> array(n);
@@ -186,6 +230,7 @@
             test_tables(reporter, face);
             test_unitsPerEm(reporter, face);
             test_countGlyphs(reporter, face);
+            test_charsToGlyphs(reporter, face);
         }
     }
 }
diff --git a/tests/FontMgrTest.cpp b/tests/FontMgrTest.cpp
index c0d8b49..67867b5 100644
--- a/tests/FontMgrTest.cpp
+++ b/tests/FontMgrTest.cpp
@@ -136,8 +136,12 @@
             return nullptr;
         }
         void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
-        void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override {
-            sk_bzero(glyphs, count * sizeof(glyphs[0]));
+        virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
+            uint16_t glyphs[], int glyphCount) const override {
+            if (glyphs && glyphCount > 0) {
+                sk_bzero(glyphs, glyphCount * sizeof(glyphs[0]));
+            }
+            return 0;
         }
         int onCountGlyphs() const override { return 0; }
         int onGetUPEM() const override { return 0; }
diff --git a/tests/PaintTest.cpp b/tests/PaintTest.cpp
index 503bd49..f22aaf5 100644
--- a/tests/PaintTest.cpp
+++ b/tests/PaintTest.cpp
@@ -21,6 +21,90 @@
 #include "Test.h"
 #undef ASSERT
 
+static size_t uni_to_utf8(const SkUnichar src[], void* dst, int count) {
+    char* u8 = (char*)dst;
+    for (int i = 0; i < count; ++i) {
+        int n = SkToInt(SkUTF::ToUTF8(src[i], u8));
+        u8 += n;
+    }
+    return u8 - (char*)dst;
+}
+
+static size_t uni_to_utf16(const SkUnichar src[], void* dst, int count) {
+    uint16_t* u16 = (uint16_t*)dst;
+    for (int i = 0; i < count; ++i) {
+        int n = SkToInt(SkUTF::ToUTF16(src[i], u16));
+        u16 += n;
+    }
+    return (char*)u16 - (char*)dst;
+}
+
+static size_t uni_to_utf32(const SkUnichar src[], void* dst, int count) {
+    SkUnichar* u32 = (SkUnichar*)dst;
+    if (src != u32) {
+        memcpy(u32, src, count * sizeof(SkUnichar));
+    }
+    return count * sizeof(SkUnichar);
+}
+
+static int find_first_zero(const uint16_t glyphs[], int count) {
+    for (int i = 0; i < count; ++i) {
+        if (0 == glyphs[i]) {
+            return i;
+        }
+    }
+    return count;
+}
+
+DEF_TEST(Paint_cmap, reporter) {
+    // need to implement charsToGlyphs on other backends (e.g. linux, win)
+    // before we can run this tests everywhere
+    return;
+
+    static const int NGLYPHS = 64;
+
+    SkUnichar src[NGLYPHS];
+    SkUnichar dst[NGLYPHS]; // used for utf8, utf16, utf32 storage
+
+    static const struct {
+        size_t (*fSeedTextProc)(const SkUnichar[], void* dst, int count);
+        SkTextEncoding   fEncoding;
+    } gRec[] = {
+        { uni_to_utf8,  kUTF8_SkTextEncoding },
+        { uni_to_utf16, kUTF16_SkTextEncoding },
+        { uni_to_utf32, kUTF32_SkTextEncoding },
+    };
+
+    SkRandom rand;
+    SkFont font;
+    font.setTypeface(SkTypeface::MakeDefault());
+    SkTypeface* face = font.getTypefaceOrDefault();
+
+    for (int i = 0; i < 1000; ++i) {
+        // generate some random text
+        for (int j = 0; j < NGLYPHS; ++j) {
+            src[j] = ' ' + j;
+        }
+        // inject some random chars, to sometimes abort early
+        src[rand.nextU() & 63] = rand.nextU() & 0xFFF;
+
+        for (size_t k = 0; k < SK_ARRAY_COUNT(gRec); ++k) {
+            size_t len = gRec[k].fSeedTextProc(src, dst, NGLYPHS);
+
+            uint16_t    glyphs0[NGLYPHS], glyphs1[NGLYPHS];
+
+            int nglyphs = font.textToGlyphs(dst, len, gRec[k].fEncoding, glyphs0, NGLYPHS);
+            int first = face->charsToGlyphs(dst, (SkTypeface::Encoding)gRec[k].fEncoding,
+                                            glyphs1, NGLYPHS);
+            int index = find_first_zero(glyphs1, NGLYPHS);
+
+            REPORTER_ASSERT(reporter, NGLYPHS == nglyphs);
+            REPORTER_ASSERT(reporter, index == first);
+            REPORTER_ASSERT(reporter, 0 == memcmp(glyphs0, glyphs1, NGLYPHS * sizeof(uint16_t)));
+        }
+    }
+}
+
 // temparary api for bicubic, just be sure we can set/clear it
 DEF_TEST(Paint_filterQuality, reporter) {
     SkPaint p0, p1;
diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp
index 7e6cdec..07cad45 100644
--- a/tests/SkRemoteGlyphCacheTest.cpp
+++ b/tests/SkRemoteGlyphCacheTest.cpp
@@ -496,8 +496,7 @@
     SkASSERT(runBuffer.utf8text == nullptr);
     SkASSERT(runBuffer.clusters == nullptr);
 
-    SkFont(sk_ref_sp(glyphTf)).textToGlyphs(s, strlen(s), SkTextEncoding::kUTF8,
-                                            runBuffer.glyphs, runSize);
+    glyphTf->charsToGlyphs(s, SkTypeface::kUTF8_Encoding, runBuffer.glyphs, runSize);
 
     SkRect glyphBounds;
     font.getWidths(runBuffer.glyphs, 1, nullptr, &glyphBounds);
diff --git a/tools/fonts/RandomScalerContext.cpp b/tools/fonts/RandomScalerContext.cpp
index d20bf2b..3431c93 100644
--- a/tools/fonts/RandomScalerContext.cpp
+++ b/tools/fonts/RandomScalerContext.cpp
@@ -182,8 +182,11 @@
     fProxy->getFontDescriptor(desc, isLocal);
 }
 
-void SkRandomTypeface::onCharsToGlyphs(const SkUnichar* uni, int count, SkGlyphID glyphs[]) const {
-    fProxy->unicharsToGlyphs(uni, count, glyphs);
+int SkRandomTypeface::onCharsToGlyphs(const void* chars,
+                                      Encoding    encoding,
+                                      uint16_t    glyphs[],
+                                      int         glyphCount) const {
+    return fProxy->charsToGlyphs(chars, encoding, glyphs, glyphCount);
 }
 
 int SkRandomTypeface::onCountGlyphs() const { return fProxy->countGlyphs(); }
diff --git a/tools/fonts/RandomScalerContext.h b/tools/fonts/RandomScalerContext.h
index d0eb02c..ea1b655 100644
--- a/tools/fonts/RandomScalerContext.h
+++ b/tools/fonts/RandomScalerContext.h
@@ -33,7 +33,10 @@
     sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override;
     void              onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const override;
 
-    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
+    int onCharsToGlyphs(const void* chars,
+                        Encoding    encoding,
+                        uint16_t    glyphs[],
+                        int         glyphCount) const override;
     int onCountGlyphs() const override;
     int onGetUPEM() const override;
 
diff --git a/tools/fonts/TestEmptyTypeface.h b/tools/fonts/TestEmptyTypeface.h
index dc358a7..bd630d3 100644
--- a/tools/fonts/TestEmptyTypeface.h
+++ b/tools/fonts/TestEmptyTypeface.h
@@ -30,8 +30,14 @@
         return nullptr;
     }
     void        onGetFontDescriptor(SkFontDescriptor*, bool*) const override {}
-    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override {
-        sk_bzero(glyphs, count * sizeof(glyphs[0]));
+    virtual int onCharsToGlyphs(const void* chars,
+                                Encoding    encoding,
+                                uint16_t    glyphs[],
+                                int         glyphCount) const override {
+        if (glyphs && glyphCount > 0) {
+            sk_bzero(glyphs, glyphCount * sizeof(glyphs[0]));
+        }
+        return 0;
     }
     int onCountGlyphs() const override { return 0; }
     int onGetUPEM() const override { return 0; }
diff --git a/tools/fonts/TestSVGTypeface.cpp b/tools/fonts/TestSVGTypeface.cpp
index e180f96..80ee05a 100644
--- a/tools/fonts/TestSVGTypeface.cpp
+++ b/tools/fonts/TestSVGTypeface.cpp
@@ -150,11 +150,27 @@
     *isLocal = false;
 }
 
-void TestSVGTypeface::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
-    for (int i = 0; i < count; i++) {
-        SkGlyphID* g = fCMap.find(uni[i]);
-        glyphs[i]    = g ? *g : 0;
+int TestSVGTypeface::onCharsToGlyphs(const void* chars,
+                                     Encoding    encoding,
+                                     uint16_t    glyphs[],
+                                     int         glyphCount) const {
+    auto utf8  = (const char*)chars;
+    auto utf16 = (const uint16_t*)chars;
+    auto utf32 = (const SkUnichar*)chars;
+
+    for (int i = 0; i < glyphCount; i++) {
+        SkUnichar ch;
+        switch (encoding) {
+            case kUTF8_Encoding: ch = SkUTF8_NextUnichar(&utf8); break;
+            case kUTF16_Encoding: ch = SkUTF16_NextUnichar(&utf16); break;
+            case kUTF32_Encoding: ch = *utf32++; break;
+        }
+        if (glyphs) {
+            SkGlyphID* g = fCMap.find(ch);
+            glyphs[i]    = g ? *g : 0;
+        }
     }
+    return glyphCount;
 }
 
 void TestSVGTypeface::onGetFamilyName(SkString* familyName) const { *familyName = fName; }
diff --git a/tools/fonts/TestSVGTypeface.h b/tools/fonts/TestSVGTypeface.h
index 592418a..ba0d789 100644
--- a/tools/fonts/TestSVGTypeface.h
+++ b/tools/fonts/TestSVGTypeface.h
@@ -94,7 +94,10 @@
 
     void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override;
 
-    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
+    int onCharsToGlyphs(const void* chars,
+                        Encoding    encoding,
+                        uint16_t    glyphs[],
+                        int         glyphCount) const override;
 
     int onCountGlyphs() const override { return fGlyphCount; }
 
diff --git a/tools/fonts/TestTypeface.cpp b/tools/fonts/TestTypeface.cpp
index 4641eef..05f5d79 100644
--- a/tools/fonts/TestTypeface.cpp
+++ b/tools/fonts/TestTypeface.cpp
@@ -132,10 +132,26 @@
     *isLocal = false;
 }
 
-void TestTypeface::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
-    for (int i = 0; i < count; ++i) {
-        glyphs[i] = fTestFont->glyphForUnichar(uni[i]);
+int TestTypeface::onCharsToGlyphs(const void* chars,
+                                  Encoding    encoding,
+                                  SkGlyphID   glyphs[],
+                                  int         glyphCount) const {
+    auto utf8  = (const char*)chars;
+    auto utf16 = (const uint16_t*)chars;
+    auto utf32 = (const SkUnichar*)chars;
+
+    for (int i = 0; i < glyphCount; ++i) {
+        SkUnichar ch;
+        switch (encoding) {
+            case kUTF8_Encoding: ch = SkUTF8_NextUnichar(&utf8); break;
+            case kUTF16_Encoding: ch = SkUTF16_NextUnichar(&utf16); break;
+            case kUTF32_Encoding: ch = *utf32++; break;
+        }
+        if (glyphs) {
+            glyphs[i] = fTestFont->glyphForUnichar(ch);
+        }
     }
+    return glyphCount;
 }
 
 void TestTypeface::onGetFamilyName(SkString* familyName) const { *familyName = fTestFont->fName; }
diff --git a/tools/fonts/TestTypeface.h b/tools/fonts/TestTypeface.h
index 7dcd574..8ea837f 100644
--- a/tools/fonts/TestTypeface.h
+++ b/tools/fonts/TestTypeface.h
@@ -83,7 +83,10 @@
 
     void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override;
 
-    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
+    int onCharsToGlyphs(const void* chars,
+                        Encoding    encoding,
+                        uint16_t    glyphs[],
+                        int         glyphCount) const override;
 
     int onCountGlyphs() const override { return (int)fTestFont->fCharCodesCount; }