Reland "Have draw(Text|PosText|PosTextH) use a single entry on the device"

This reverts commit 74b390d6b136a60f1df15ac5ecd19bd8ad5a394b.

Reason for revert: reverting to add patch for valgrind

Original change's description:
> Revert "Have draw(Text|PosText|PosTextH) use a single entry on the device"
> 
> This reverts commit 4225b3220ef4bf50f0d9403f812ea94d50c4ee59.
> 
> Reason for revert: made valgrind unhappy.
> 
> Original change's description:
> > Have draw(Text|PosText|PosTextH) use a single entry on the device
> > 
> > Handle the positioning of drawText at the canvas layer. Simplify
> > the code by removing similar implementations.
> > 
> > Change-Id: I8b711783435072f560e29fca1dd934fa2e345ed2
> > Reviewed-on: https://skia-review.googlesource.com/127131
> > Reviewed-by: Ben Wagner <bungeman@google.com>
> > Commit-Queue: Herb Derby <herb@google.com>
> 
> TBR=jvanverth@google.com,bungeman@google.com,herb@google.com
> 
> Change-Id: I65c9d30ae6ecb1f87e8660e56d8f8ce5daab7551
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://skia-review.googlesource.com/132403
> Reviewed-by: Hal Canary <halcanary@google.com>
> Commit-Queue: Hal Canary <halcanary@google.com>

TBR=jvanverth@google.com,halcanary@google.com,bungeman@google.com,herb@google.com

Change-Id: I9bbb73aac447b51eb8215ac42331759fa4c9fa45
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/132580
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 71b85af..cbd9a7c 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -565,12 +565,6 @@
     BDDraw(this).drawSprite(bitmap, x, y, paint);
 }
 
-void SkBitmapDevice::drawText(const void* text, size_t len,
-                              SkScalar x, SkScalar y, const SkPaint& paint) {
-    SkBitmapDeviceFilteredSurfaceProps props(fBitmap, paint, fSurfaceProps);
-    LOOP_TILER( drawText((const char*)text, len, x, y, paint, &props()), nullptr)
-}
-
 void SkBitmapDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
                                  int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
     SkBitmapDeviceFilteredSurfaceProps props(fBitmap, paint, fSurfaceProps);
diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h
index c36bc28..ba7846c 100644
--- a/src/core/SkBitmapDevice.h
+++ b/src/core/SkBitmapDevice.h
@@ -111,8 +111,6 @@
      *  Does not handle text decoration.
      *  Decorations (underline and stike-thru) will be handled by SkCanvas.
      */
-    void drawText(const void* text, size_t len, SkScalar x, SkScalar y,
-                  const SkPaint&) override;
     void drawPosText(const void* text, size_t len, const SkScalar pos[],
                      int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override;
     void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 8230fb3..dcff1b0 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -15,6 +15,8 @@
 #include "SkDrawable.h"
 #include "SkDrawFilter.h"
 #include "SkDrawLooper.h"
+#include "SkGlyphCache.h"
+#include "SkGlyphRun.h"
 #include "SkImage.h"
 #include "SkImage_Base.h"
 #include "SkImageFilter.h"
@@ -34,6 +36,7 @@
 #include "SkRasterHandleAllocator.h"
 #include "SkRRect.h"
 #include "SkSpecialImage.h"
+#include "SkStrikeCache.h"
 #include "SkString.h"
 #include "SkSurface_Base.h"
 #include "SkTextBlob.h"
@@ -1075,7 +1078,8 @@
         const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
                                      (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
         const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
-        const bool trackCoverage = SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
+        const bool trackCoverage =
+                SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
         const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
                                                                              preserveLCDText,
                                                                              trackCoverage,
@@ -2439,10 +2443,13 @@
 
 void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
                           const SkPaint& paint) {
+
     LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
 
     while (iter.next()) {
-        iter.fDevice->drawText(text, byteLength, x, y, looper.paint());
+        auto glyphRun = SkGlyphRun::MakeFromDrawText(
+                looper.paint(), text, byteLength, SkPoint::Make(x, y));
+        iter.fDevice->drawGlyphRun(looper.paint(), &glyphRun);
     }
 
     LOOPER_END
@@ -2450,12 +2457,12 @@
 
 void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
                              const SkPaint& paint) {
-    SkPoint textOffset = SkPoint::Make(0, 0);
 
     LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
 
     while (iter.next()) {
-        iter.fDevice->drawPosText(text, byteLength, &pos->fX, 2, textOffset, looper.paint());
+        auto glyphRun = SkGlyphRun::MakeFromDrawPosText(looper.paint(), text, byteLength, pos);
+        iter.fDevice->drawGlyphRun(looper.paint(), &glyphRun);
     }
 
     LOOPER_END
@@ -2464,12 +2471,12 @@
 void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
                               SkScalar constY, const SkPaint& paint) {
 
-    SkPoint textOffset = SkPoint::Make(0, constY);
-
     LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
 
     while (iter.next()) {
-        iter.fDevice->drawPosText(text, byteLength, xpos, 1, textOffset, looper.paint());
+        auto glyphRun =
+                SkGlyphRun::MakeFromDrawPosTextH(looper.paint(), text, byteLength, xpos, constY);
+        iter.fDevice->drawGlyphRun(looper.paint(), &glyphRun);
     }
 
     LOOPER_END
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 35cfc9a..1bad6db 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -9,12 +9,14 @@
 #include "SkColorFilter.h"
 #include "SkDraw.h"
 #include "SkDrawFilter.h"
+#include "SkGlyphRun.h"
 #include "SkImageFilter.h"
 #include "SkImageFilterCache.h"
 #include "SkImagePriv.h"
 #include "SkImage_Base.h"
 #include "SkLatticeIter.h"
 #include "SkLocalMatrixShader.h"
+#include "SkMakeUnique.h"
 #include "SkMatrixPriv.h"
 #include "SkPatchUtils.h"
 #include "SkPathMeasure.h"
@@ -157,9 +159,16 @@
         }
 
         switch (it.positioning()) {
-        case SkTextBlob::kDefault_Positioning:
-            this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
-            break;
+        case SkTextBlob::kDefault_Positioning: {
+            auto origin = SkPoint::Make(x + offset.x(), y + offset.y());
+            auto glyphRun =
+                    SkGlyphRun::MakeFromDrawText(runPaint,
+                                                 (const char*) it.glyphs(), textLen, origin);
+            this->drawPosText(
+                    it.glyphs(), textLen, glyphRun.getPositions(), 2,
+                    SkPoint::Make(0, 0), runPaint);
+        }
+        break;
         case SkTextBlob::kHorizontal_Positioning:
             this->drawPosText(it.glyphs(), textLen, it.pos(), 1,
                               SkPoint::Make(x, y + offset.y()), runPaint);
@@ -243,6 +252,17 @@
     }
 }
 
+void SkBaseDevice::drawGlyphRun(const SkPaint& paint, SkGlyphRun* info) {
+    SkPaint glyphPaint(paint);
+    glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    auto glyphs = info->copyGlyphIDs();
+
+    this->drawPosText(
+            glyphs.get(), info->runSize() * 2,
+            info->getPositions(), 2, SkPoint::Make(0, 0), glyphPaint);
+}
+
 void SkBaseDevice::drawBitmapLattice(const SkBitmap& bitmap,
                                      const SkCanvas::Lattice& lattice, const SkRect& dst,
                                      const SkPaint& paint) {
@@ -496,6 +516,8 @@
 
     SkPaint localPaint(paint);
     SkShader* shader = paint.getShader();
+    SkScalar pos[2] = {0.0f, 0.0f};
+    SkPoint origin = SkPoint::Make(0, 0);
 
     SkMatrix localM, currM;
     const void* stopText = (const char*)text + len;
@@ -517,7 +539,7 @@
         }
 
         int subLen = proc((const char*)text);
-        this->drawText(text, subLen, 0, 0, localPaint);
+        this->drawPosText(text, subLen, pos, 2, origin, localPaint);
         text = (const char*)text + subLen;
     }
 }
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index 40b9589..d36dc4d 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -17,6 +17,7 @@
 class SkBitmap;
 class SkDrawFilter;
 struct SkDrawShadowRec;
+class SkGlyphRun;
 class SkImageFilterCache;
 struct SkIRect;
 class SkMatrix;
@@ -222,11 +223,7 @@
      *  Does not handle text decoration.
      *  Decorations (underline and stike-thru) will be handled by SkCanvas.
      */
-    virtual void drawText(const void* text, size_t len,
-                          SkScalar x, SkScalar y, const SkPaint& paint) = 0;
-    virtual void drawPosText(const void* text, size_t len,
-                             const SkScalar pos[], int scalarsPerPos,
-                             const SkPoint& offset, const SkPaint& paint) = 0;
+    virtual void drawGlyphRun(const SkPaint& paint, SkGlyphRun* info);
     virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) = 0;
     virtual void drawShadow(const SkPath&, const SkDrawShadowRec&);
 
@@ -349,6 +346,10 @@
     friend class SkSurface_Raster;
     friend class DeviceTestingAccess;
 
+    virtual void drawPosText(const void* text, size_t len,
+                             const SkScalar pos[], int scalarsPerPos,
+                             const SkPoint& offset, const SkPaint& paint) = 0;
+
     // used to change the backend's pixels (and possibly config/rowbytes)
     // but cannot change the width/height, so there should be no change to
     // any clip information.
@@ -427,7 +428,6 @@
     void drawSprite(const SkBitmap&, int, int, const SkPaint&) override {}
     void drawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint&,
                         SkCanvas::SrcRectConstraint) override {}
-    void drawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&) override {}
     void drawPosText(const void*, size_t, const SkScalar[], int, const SkPoint&,
                      const SkPaint&) override {}
     void drawDevice(SkBaseDevice*, int, int, const SkPaint&) override {}
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index b7150f8..e975ea2 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -1537,37 +1537,6 @@
     return flags;
 }
 
-void SkDraw::drawText(const char text[], size_t byteLength, SkScalar x, SkScalar y,
-                      const SkPaint& paint, const SkSurfaceProps* props) const {
-    SkASSERT(byteLength == 0 || text != nullptr);
-
-    SkDEBUGCODE(this->validate();)
-
-    // nothing to draw
-    if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
-        return;
-    }
-
-    // SkScalarRec doesn't currently have a way of representing hairline stroke and
-    // will fill if its frame-width is 0.
-    if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
-        this->drawText_asPaths(text, byteLength, x, y, paint);
-        return;
-    }
-
-    auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(
-            paint, props, this->scalerContextFlags(), fMatrix);
-
-    // The Blitter Choose needs to be live while using the blitter below.
-    SkAutoBlitterChoose    blitterChooser(*this, nullptr, paint);
-    SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
-    DrawOneGlyph           drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
-
-    SkFindAndPlaceGlyph::ProcessText(
-        paint.getTextEncoding(), text, byteLength,
-        {x, y}, *fMatrix, paint.getTextAlign(), cache.get(), drawOneGlyph);
-}
-
 //////////////////////////////////////////////////////////////////////////////
 
 void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength, const SkScalar pos[],
diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h
index eabccf0..266960e 100644
--- a/src/core/SkDraw.h
+++ b/src/core/SkDraw.h
@@ -62,8 +62,6 @@
     void    drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect* dstOrNull,
                        const SkPaint&) const;
     void    drawSprite(const SkBitmap&, int x, int y, const SkPaint&) const;
-    void    drawText(const char text[], size_t byteLength, SkScalar x,
-                     SkScalar y, const SkPaint& paint, const SkSurfaceProps*) const;
     void    drawPosText(const char text[], size_t byteLength,
                         const SkScalar pos[], int scalarsPerPosition,
                         const SkPoint& offset, const SkPaint&, const SkSurfaceProps*) const;
diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp
index bf5f77c..3734b96 100644
--- a/src/core/SkGlyphCache.cpp
+++ b/src/core/SkGlyphCache.cpp
@@ -130,6 +130,13 @@
     return *this->lookupByPackedGlyphID(packedGlyphID, kFull_MetricsType);
 }
 
+void SkGlyphCache::getAdvances(SkSpan<SkGlyphID> glyphIDs, SkPoint advances[]) {
+    for (ptrdiff_t i = 0; i < glyphIDs.size(); i++) {
+        auto glyph = this->getGlyphIDAdvance(glyphIDs[i]);
+        advances[i] = SkPoint::Make(glyph.fAdvanceX, glyph.fAdvanceY);
+    }
+}
+
 SkGlyph* SkGlyphCache::lookupByChar(SkUnichar charCode, MetricsType type, SkFixed x, SkFixed y) {
     SkPackedUnicharID id(charCode, x, y);
     CharGlyphRec* rec = this->getCharGlyphRec(id);
diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h
index 2e73557..e904c6e 100644
--- a/src/core/SkGlyphCache.h
+++ b/src/core/SkGlyphCache.h
@@ -10,6 +10,7 @@
 #include "SkArenaAlloc.h"
 #include "SkDescriptor.h"
 #include "SkGlyph.h"
+#include "SkGlyphRun.h"
 #include "SkPaint.h"
 #include "SkTHash.h"
 #include "SkScalerContext.h"
@@ -67,6 +68,8 @@
     const SkGlyph& getUnicharMetrics(SkUnichar, SkFixed x, SkFixed y);
     const SkGlyph& getGlyphIDMetrics(uint16_t, SkFixed x, SkFixed y);
 
+    void getAdvances(SkSpan<SkGlyphID>, SkPoint[]);
+
     /** Return the glyphID for the specified Unichar. If the char has already been seen, use the
         existing cache entry. If not, ask the scalercontext to compute it for us.
     */
diff --git a/src/core/SkGlyphRun.cpp b/src/core/SkGlyphRun.cpp
new file mode 100644
index 0000000..a25b9af
--- /dev/null
+++ b/src/core/SkGlyphRun.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkGlyphRun.h"
+
+#include <algorithm>
+#include <tuple>
+
+#include "SkDraw.h"
+#include "SkGlyphCache.h"
+#include "SkMakeUnique.h"
+#include "SkMSAN.h"
+#include "SkPaint.h"
+#include "SkPaintPriv.h"
+#include "SkStrikeCache.h"
+#include "SkUtils.h"
+
+namespace {
+
+// A faster set implementation that does not need any initialization, and reading the set items
+// is order the number of items, and not the size of the universe.
+// This implementation is based on the paper by Briggs and Torczon, "An Efficient Representation
+// for Sparse Sets"
+class GlyphSet {
+public:
+    GlyphSet(uint32_t glyphUniverseSize)
+    : fUniverseSize{glyphUniverseSize}
+    , fIndexes{skstd::make_unique_default<uint16_t[]>(2 * glyphUniverseSize)}
+    , fUniqueGlyphIDs{&fIndexes[glyphUniverseSize]} {
+        SkASSERT(glyphUniverseSize <= (1 << 16));
+        sk_msan_mark_initialized(fIndexes.get(), &fIndexes[glyphUniverseSize], "works with uninited");
+    }
+
+    uint16_t add(SkGlyphID glyphID) {
+        if (glyphID >= fUniverseSize) {
+            glyphID = kUndefGlyph;
+        }
+        auto index = fIndexes[glyphID];
+        if (index < fUniqueCount && fUniqueGlyphIDs[index] == glyphID) {
+            return index;
+        }
+
+        fUniqueGlyphIDs[fUniqueCount] = glyphID;
+        fIndexes[glyphID] = fUniqueCount;
+        fUniqueCount += 1;
+        return fUniqueCount - 1;
+    }
+
+    std::tuple<uint16_t, std::unique_ptr<SkGlyphID[]>> uniqueGlyphIDs() const {
+        auto uniqueGlyphs = skstd::make_unique_default<SkGlyphID[]>(fUniqueCount);
+        memcpy(uniqueGlyphs.get(), fUniqueGlyphIDs, fUniqueCount * sizeof(SkGlyphID));
+        return std::make_tuple(fUniqueCount, std::move(uniqueGlyphs));
+    }
+
+private:
+    static constexpr SkGlyphID  kUndefGlyph{0};
+    const uint32_t              fUniverseSize;
+    uint16_t                    fUniqueCount{0};
+    std::unique_ptr<uint16_t[]> fIndexes;
+    SkGlyphID*                  fUniqueGlyphIDs;
+ };
+
+template<typename T>
+bool is_aligned(const void* ptr) {
+    uintptr_t bits = reinterpret_cast<uintptr_t>(ptr);
+    return (bits & (alignof(T) - 1)) == 0;
+}
+
+template<typename T>
+bool is_aligned_size(size_t size) {
+    return size % sizeof(T) == 0;
+}
+
+SkTypeface::Encoding convert_encoding(SkPaint::TextEncoding encoding) {
+    switch (encoding) {
+        case  SkPaint::kUTF8_TextEncoding: return SkTypeface::kUTF8_Encoding;
+        case SkPaint::kUTF16_TextEncoding: return SkTypeface::kUTF16_Encoding;
+        case SkPaint::kUTF32_TextEncoding: return SkTypeface::kUTF32_Encoding;
+        default: return SkTypeface::kUTF32_Encoding;
+    }
+}
+
+using Core = std::tuple<size_t,   std::unique_ptr<uint16_t[]>,
+                        uint16_t, std::unique_ptr<SkGlyphID[]>>;
+
+Core make_from_glyphids(size_t glyphCount, const SkGlyphID* glyphs, SkGlyphID maxGlyphID) {
+    if (glyphCount == 0) { return Core(0, nullptr, 0, nullptr); }
+
+    GlyphSet glyphSet{maxGlyphID};
+
+    auto denseIndex = skstd::make_unique_default<uint16_t[]>(glyphCount);
+    for (size_t i = 0; i < glyphCount; i++) {
+        denseIndex[i] = glyphSet.add(glyphs[i]);
+    }
+
+    std::unique_ptr<SkGlyphID[]> uniqueGlyphIDs;
+    uint16_t uniqueCount;
+    std::tie(uniqueCount, uniqueGlyphIDs) = glyphSet.uniqueGlyphIDs();
+
+    return Core(glyphCount, std::move(denseIndex), uniqueCount, std::move(uniqueGlyphIDs));
+}
+
+Core make_from_utfn(size_t byteLength, const void* utfN, const SkTypeface& typeface,
+                    SkTypeface::Encoding encoding) {
+    auto count = SkUTFN_CountUnichars(encoding, utfN, byteLength);
+
+    if (count <= 0) {
+        return Core(0, nullptr, 0, nullptr);
+    }
+
+    auto glyphs = skstd::make_unique_default<SkGlyphID[]>(count);
+
+    // TODO: move to using cached version.
+    typeface.charsToGlyphs(utfN, encoding, glyphs.get(), count);
+
+    return make_from_glyphids(count, glyphs.get(), typeface.countGlyphs());
+}
+
+Core make_core(const SkPaint& paint, const void* bytes, size_t byteLength) {
+    auto encoding = paint.getTextEncoding();
+    auto typeface = SkPaintPriv::GetTypefaceOrDefault(paint);
+    if (encoding == SkPaint::kGlyphID_TextEncoding) {
+        return make_from_glyphids(
+                byteLength / 2, reinterpret_cast<const SkGlyphID*>(bytes), typeface->countGlyphs());
+    } else {
+        return make_from_utfn(byteLength, bytes, *typeface, convert_encoding(encoding));
+    }
+}
+
+}  // namespace
+
+SkGlyphRun SkGlyphRun::MakeFromDrawText(
+        const SkPaint& paint, const void* bytes, size_t byteLength,
+        const SkPoint origin) {
+    size_t runSize;
+    std::unique_ptr<uint16_t[]> denseIndex;
+    uint16_t uniqueSize;
+    std::unique_ptr<SkGlyphID[]> uniqueGlyphIDs;
+    std::tie(runSize, denseIndex, uniqueSize, uniqueGlyphIDs) = make_core(paint, bytes, byteLength);
+
+    if (runSize == 0) { return SkGlyphRun{}; }
+
+    auto advances = skstd::make_unique_default<SkPoint[]>(uniqueSize);
+
+    {
+        auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(paint);
+        cache->getAdvances(SkSpan<SkGlyphID>{uniqueGlyphIDs.get(), uniqueSize}, advances.get());
+    }
+
+    auto positions = skstd::make_unique_default<SkPoint[]>(runSize);
+
+    SkPoint endOfLastGlyph = origin;
+
+    for (size_t i = 0; i < runSize; i++) {
+        positions[i] = endOfLastGlyph;
+        endOfLastGlyph += advances[denseIndex[i]];
+    }
+
+    if (paint.getTextAlign() != SkPaint::kLeft_Align) {
+        SkVector len = endOfLastGlyph - origin;
+        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+            len.scale(SK_ScalarHalf);
+        }
+        for (size_t i = 0; i < runSize; i++) {
+            positions[i] -= len;
+        }
+    }
+
+    return SkGlyphRun{
+        runSize, std::move(denseIndex), std::move(positions), uniqueSize, std::move(uniqueGlyphIDs)};
+}
+
+SkGlyphRun SkGlyphRun::MakeFromDrawPosTextH(
+        const SkPaint& paint, const void* bytes, size_t byteLength,
+        const SkScalar xpos[], SkScalar constY) {
+    size_t runSize;
+    std::unique_ptr<uint16_t[]> denseIndex;
+    uint16_t uniqueSize;
+    std::unique_ptr<SkGlyphID[]> uniqueGlyphIDs;
+    std::tie(runSize, denseIndex, uniqueSize, uniqueGlyphIDs) = make_core(paint, bytes, byteLength);
+
+    if (runSize == 0) { return SkGlyphRun{}; }
+
+    auto positions = skstd::make_unique_default<SkPoint[]>(runSize);
+
+    for (size_t i = 0; i < runSize; i++) {
+        positions[i] = SkPoint::Make(xpos[i], constY);
+    }
+
+    return SkGlyphRun{
+        runSize, std::move(denseIndex), std::move(positions), uniqueSize, std::move(uniqueGlyphIDs)};
+}
+
+SkGlyphRun SkGlyphRun::MakeFromDrawPosText(
+        const SkPaint& paint, const void* bytes, size_t byteLength,
+        const SkPoint pos[]) {
+    size_t runSize;
+    std::unique_ptr<uint16_t[]> denseIndex;
+    uint16_t uniqueSize;
+    std::unique_ptr<SkGlyphID[]> uniqueGlyphIDs;
+    std::tie(runSize, denseIndex, uniqueSize, uniqueGlyphIDs) = make_core(paint, bytes, byteLength);
+
+    if (runSize == 0) { return SkGlyphRun{}; }
+
+    auto positions = skstd::make_unique_default<SkPoint[]>(runSize);
+
+    memcpy(positions.get(), pos, sizeof(SkPoint) * runSize);
+
+    return SkGlyphRun{
+        runSize, std::move(denseIndex), std::move(positions), uniqueSize, std::move(uniqueGlyphIDs)};
+}
+
+std::unique_ptr<SkGlyphID[]> SkGlyphRun::copyGlyphIDs() const {
+    auto glyphs = skstd::make_unique_default<SkGlyphID[]>(fRunSize);
+
+    for (size_t i = 0; i < fRunSize; i++) {
+        glyphs[i] = fUniqueGlyphs[fDenseIndex[i]];
+    }
+
+    return glyphs;
+}
+
+SkGlyphRun::SkGlyphRun(size_t runSize,
+                       std::unique_ptr<uint16_t[]>&& denseIndex,
+                       std::unique_ptr<SkPoint[]>&& positions,
+                       uint16_t uniqueSize,
+                       std::unique_ptr<SkGlyphID[]>&& uniqueGlyphIDs)
+    : fDenseIndex{std::move(denseIndex)}
+    , fPositions{std::move(positions)}
+    , fUniqueGlyphs{std::move(uniqueGlyphIDs)}
+    , fRunSize{runSize}
+    , fUniqueSize{uniqueSize} { }
diff --git a/src/core/SkGlyphRun.h b/src/core/SkGlyphRun.h
new file mode 100644
index 0000000..17b4297
--- /dev/null
+++ b/src/core/SkGlyphRun.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkGlyphRunInfo_DEFINED
+#define SkGlyphRunInfo_DEFINED
+
+#include <memory>
+#include <vector>
+
+#include "SkDescriptor.h"
+#include "SkMask.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkTypes.h"
+
+class SkGlyphRun {
+public:
+    SkGlyphRun() = default;
+    SkGlyphRun(SkGlyphRun&&) = default;
+    static SkGlyphRun MakeFromDrawText(
+            const SkPaint& paint, const void* bytes, size_t byteLength,
+            SkPoint origin);
+    static SkGlyphRun MakeFromDrawPosTextH(
+            const SkPaint& paint, const void* bytes, size_t byteLength,
+            const SkScalar xpos[], SkScalar constY);
+    static SkGlyphRun MakeFromDrawPosText(
+            const SkPaint& paint, const void* bytes, size_t byteLength,
+            const SkPoint pos[]);
+
+    size_t runSize() const { return fRunSize; }
+    uint16_t uniqueSize() const { return fUniqueSize; }
+
+    // copyGlyphIDs is temporary glue to work with the existing system. Don't use with new code.
+    std::unique_ptr<SkGlyphID[]> copyGlyphIDs() const;
+    const SkScalar* getPositions() const {
+        return reinterpret_cast<const SkScalar*>(fPositions.get());
+    }
+
+private:
+    SkGlyphRun(size_t runSize,
+               std::unique_ptr<uint16_t[]>&& denseIndex,
+               std::unique_ptr<SkPoint[]>&& positions,
+               uint16_t uniqueSize,
+               std::unique_ptr<SkGlyphID[]>&& uniqueGlyphIDs);
+
+    std::unique_ptr<uint16_t[]>  fDenseIndex;
+    std::unique_ptr<SkPoint[]>   fPositions;
+    std::unique_ptr<SkGlyphID[]> fUniqueGlyphs;
+    const size_t                 fRunSize{0};
+    const uint16_t               fUniqueSize{0};
+};
+
+template <typename T>
+class SkSpan {
+public:
+    SkSpan(const T* ptr, size_t size) : fPtr{ptr}, fSize{size} {}
+    SkSpan(const std::vector<T>& v) : fPtr{v.data()}, fSize{v.size()} {}
+    const T& operator [] (ptrdiff_t i) const { return fPtr[i]; }
+    const T* begin() const { return fPtr; }
+    const T* end() const { return fPtr + fSize; }
+    ptrdiff_t size() const { return fSize; }
+
+private:
+    const T* fPtr;
+    size_t fSize;
+};
+
+#endif  // SkGlyphRunInfo_DEFINED
diff --git a/src/core/SkThreadedBMPDevice.cpp b/src/core/SkThreadedBMPDevice.cpp
index b75e90d..10cd824 100644
--- a/src/core/SkThreadedBMPDevice.cpp
+++ b/src/core/SkThreadedBMPDevice.cpp
@@ -210,16 +210,6 @@
     });
 }
 
-void SkThreadedBMPDevice::drawText(const void* text, size_t len, SkScalar x, SkScalar y,
-        const SkPaint& paint) {
-    char* clonedText = this->cloneArray((const char*)text, len);
-    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
-    SkSurfaceProps prop(SkBitmapDeviceFilteredSurfaceProps(fBitmap, paint, this->surfaceProps())());
-    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
-        TileDraw(ds, tileBounds).drawText(clonedText, len, x, y, paint, &prop);
-    });
-}
-
 void SkThreadedBMPDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
         int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
     char* clonedText = this->cloneArray((const char*)text, len);
diff --git a/src/core/SkThreadedBMPDevice.h b/src/core/SkThreadedBMPDevice.h
index d9e27b4..6defce5 100644
--- a/src/core/SkThreadedBMPDevice.h
+++ b/src/core/SkThreadedBMPDevice.h
@@ -32,9 +32,6 @@
     void drawPath(const SkPath&, const SkPaint&, const SkMatrix* prePathMatrix,
                   bool pathIsMutable) override;
     void drawSprite(const SkBitmap&, int x, int y, const SkPaint&) override;
-
-    void drawText(const void* text, size_t len, SkScalar x, SkScalar y,
-                  const SkPaint&) override;
     void drawPosText(const void* text, size_t len, const SkScalar pos[],
                      int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override;
     void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;