Remove drawTextBlob from device use drawGlyphRunList

Convert all backends to use GlyphRunList instead of
text blobs. If the device did not originally implement
drawTextBlob it will be simulated by drawPosText on the
device.

Other changes:
Change to using an origin from absolulte positioning. The GPU
code uses origin change to update blobs under translation.

Change cluster to use const uint32_t instead of just
uint32_t.

Add SkPaint to runs.

The draw filter is hosted up to the canavas level and applied there.

Change-Id: Ib105b6bd26b67db55f1c954e37c79fbdcaa9d4a2
Reviewed-on: https://skia-review.googlesource.com/137224
Reviewed-by: Herb Derby <herb@google.com>
Reviewed-by: Khusal Sagar <khushalsagar@chromium.org>
Reviewed-by: Hal Canary <halcanary@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/include/core/SkTextBlob.h b/include/core/SkTextBlob.h
index 59151d3..9f4f10f 100644
--- a/include/core/SkTextBlob.h
+++ b/include/core/SkTextBlob.h
@@ -14,6 +14,7 @@
 #include "SkString.h"
 #include "SkRefCnt.h"
 
+class SkGlyphRunList;
 class SkReadBuffer;
 class SkWriteBuffer;
 
@@ -110,6 +111,7 @@
         fCacheID.store(cacheID);
     }
 
+    friend class SkGlyphRunList;
     friend class GrTextBlobCache;
     friend class SkTextBlobBuilder;
     friend class SkTextBlobRunIterator;
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index b297f98..c590d77 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -2453,8 +2453,7 @@
     while (iter.next()) {
         fScratchGlyphRunBuilder->prepareDrawText(
                 looper.paint(), text, byteLength, SkPoint::Make(x, y));
-        auto glyphRun = fScratchGlyphRunBuilder->useGlyphRun();
-        iter.fDevice->drawGlyphRun(looper.paint(), glyphRun);
+        fScratchGlyphRunBuilder->draw(iter.fDevice);
     }
 
     LOOPER_END
@@ -2467,8 +2466,7 @@
 
     while (iter.next()) {
         fScratchGlyphRunBuilder->prepareDrawPosText(looper.paint(), text, byteLength, pos);
-        auto glyphRun = fScratchGlyphRunBuilder->useGlyphRun();
-        iter.fDevice->drawGlyphRun(looper.paint(), glyphRun);
+        fScratchGlyphRunBuilder->draw(iter.fDevice);
     }
 
     LOOPER_END
@@ -2482,8 +2480,7 @@
     while (iter.next()) {
         fScratchGlyphRunBuilder->prepareDrawPosTextH(
                 looper.paint(), text, byteLength, xpos, constY);
-        const auto& glyphRun = fScratchGlyphRunBuilder->useGlyphRun();
-        iter.fDevice->drawGlyphRun(looper.paint(), glyphRun);
+        fScratchGlyphRunBuilder->draw(iter.fDevice);
     }
 
     LOOPER_END
@@ -2536,7 +2533,9 @@
     LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
 
     while (iter.next()) {
-        iter.fDevice->drawTextBlob(blob, x, y, looper.paint(), drawFilter);
+        fScratchGlyphRunBuilder->prepareTextBlob(
+                looper.paint(), *blob, SkPoint::Make(x, y), drawFilter);
+        fScratchGlyphRunBuilder->draw(iter.fDevice);
     }
 
     LOOPER_END
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 46404b2..9fd61c9 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -141,53 +141,6 @@
     }
 }
 
-void SkBaseDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                const SkPaint &paint, SkDrawFilter* drawFilter) {
-
-    SkPaint runPaint = paint;
-
-    SkTextBlobRunIterator it(blob);
-    for (;!it.done(); it.next()) {
-        size_t textLen = it.glyphCount() * sizeof(uint16_t);
-        const SkPoint& offset = it.offset();
-        // applyFontToPaint() always overwrites the exact same attributes,
-        // so it is safe to not re-seed the paint for this reason.
-        it.applyFontToPaint(&runPaint);
-
-        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
-            // A false return from filter() means we should abort the current draw.
-            runPaint = paint;
-            continue;
-        }
-
-        switch (it.positioning()) {
-        case SkTextBlob::kDefault_Positioning: {
-            auto origin = SkPoint::Make(x + offset.x(), y + offset.y());
-            SkGlyphRunBuilder builder;
-            builder.prepareDrawText(runPaint, (const char*) it.glyphs(), textLen, origin);
-            auto glyphRun = builder.useGlyphRun();
-            glyphRun->temporaryShuntToDrawPosText(runPaint, this);
-        }
-        break;
-        case SkTextBlob::kHorizontal_Positioning:
-            this->drawPosText(it.glyphs(), textLen, it.pos(), 1,
-                              SkPoint::Make(x, y + offset.y()), runPaint);
-            break;
-        case SkTextBlob::kFull_Positioning:
-            this->drawPosText(it.glyphs(), textLen, it.pos(), 2,
-                              SkPoint::Make(x, y), runPaint);
-            break;
-        default:
-            SK_ABORT("unhandled positioning mode");
-        }
-
-        if (drawFilter) {
-            // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
-            runPaint = paint;
-        }
-    }
-}
-
 void SkBaseDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y,
                              const SkPaint& paint) {
     SkBitmap bm;
@@ -252,11 +205,10 @@
     }
 }
 
-void SkBaseDevice::drawGlyphRun(const SkPaint& paint, SkGlyphRun* glyphRun) {
-    SkPaint glyphPaint(paint);
-    glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
-    glyphRun->temporaryShuntToDrawPosText(glyphPaint, this);
+void SkBaseDevice::drawGlyphRunList(SkGlyphRunList* glyphRunList) {
+    for (auto& glyphRun : *glyphRunList) {
+        glyphRun.temporaryShuntToDrawPosText(this, glyphRunList->origin());
+    }
 }
 
 void SkBaseDevice::drawBitmapLattice(const SkBitmap& bitmap,
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index f840cf6..9a4086e 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -18,6 +18,7 @@
 class SkDrawFilter;
 struct SkDrawShadowRec;
 class SkGlyphRun;
+class SkGlyphRunList;
 class SkGlyphRunBuilder;
 class SkImageFilterCache;
 struct SkIRect;
@@ -224,13 +225,10 @@
      *  Does not handle text decoration.
      *  Decorations (underline and stike-thru) will be handled by SkCanvas.
      */
-    virtual void drawGlyphRun(const SkPaint& paint, SkGlyphRun* glyphRun);
+    virtual void drawGlyphRunList(SkGlyphRunList* glyphRunList);
     virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) = 0;
     virtual void drawShadow(const SkPath&, const SkDrawShadowRec&);
 
-    // default implementation unrolls the blob runs.
-    virtual void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y,
-                              const SkPaint& paint, SkDrawFilter* drawFilter);
     // default implementation calls drawVertices
     virtual void drawPatch(const SkPoint cubics[12], const SkColor colors[4],
                            const SkPoint texCoords[4], SkBlendMode, bool interpColorsLinearly,
@@ -349,6 +347,7 @@
 
     // Temporarily friend the SkGlyphRunBuilder until drawPosText is gone.
     friend class SkGlyphRun;
+    friend class SkGlyphRunBuilder;
     virtual void drawPosText(const void* text, size_t len,
                              const SkScalar pos[], int scalarsPerPos,
                              const SkPoint& offset, const SkPaint& paint) = 0;
diff --git a/src/core/SkGlyphRun.cpp b/src/core/SkGlyphRun.cpp
index ce1c336..583d676 100644
--- a/src/core/SkGlyphRun.cpp
+++ b/src/core/SkGlyphRun.cpp
@@ -12,7 +12,7 @@
 #include <tuple>
 
 #include "SkDevice.h"
-#include "SkDraw.h"
+#include "SkDrawFilter.h"
 #include "SkGlyphCache.h"
 #include "SkMSAN.h"
 #include "SkMakeUnique.h"
@@ -90,22 +90,23 @@
                        size_t uniqueOffset, uint16_t uniqueSize,
                        SkSpan<SkGlyphID>  scratchGlyphs,
                        SkSpan<const char> text,
-                       SkSpan<uint32_t>   clusters)
+                       SkSpan<const uint32_t> clusters,
+                       SkPaint&&          runPaint)
         : fRunInfo{runInfo}
         , fDenseOffset{denseOffset}, fDenseSize{denseSize}
         , fUniqueOffset{uniqueOffset}, fUniqueSize{uniqueSize}
         , fTemporaryShuntGlyphIDs{scratchGlyphs}
         , fText{text}
-        , fClusters{clusters} { }
+        , fClusters{clusters}
+        , fRunPaint{std::move(runPaint)} {}
 
-
-void SkGlyphRun::temporaryShuntToDrawPosText(const SkPaint& paint, SkBaseDevice* device) {
+void SkGlyphRun::temporaryShuntToDrawPosText(SkBaseDevice* device, SkPoint origin) {
 
     auto pos = (const SkScalar*) this->positions().data();
 
     device->drawPosText(
             fTemporaryShuntGlyphIDs.data(), fDenseSize * sizeof(SkGlyphID),
-            pos, 2, SkPoint::Make(0, 0), paint);
+            pos, 2, origin, fRunPaint);
 }
 
 void SkGlyphRun::temporaryShuntToCallback(TemporaryShuntCallback callback) {
@@ -115,19 +116,40 @@
 }
 
 // -- SkGlyphRunList -------------------------------------------------------------------------------
-SkGlyphRunList::SkGlyphRunList(SkSpan<SkGlyphRun> glyphRuns, uint64_t uniqueID)
-        : fUniqueID{uniqueID}
-        , fGlyphRuns{glyphRuns} { }
+SkGlyphRunList::SkGlyphRunList(
+        SkSpan<SkGlyphRun> glyphRuns, SkPoint origin, const SkTextBlob* textBlob)
+        : fGlyphRuns{glyphRuns}
+        , fOrigin{origin}
+        , fTemporaryTextBlobShunt{textBlob} { }
+
+uint64_t SkGlyphRunList::uniqueID() const {
+    return fTemporaryTextBlobShunt != nullptr ? fTemporaryTextBlobShunt->uniqueID()
+                                              : SK_InvalidUniqueID;
+}
+
+bool SkGlyphRunList::anyRunsLCD() const {
+    for (const auto& r : fGlyphRuns) {
+        if (r.paint().isLCDRenderText()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void SkGlyphRunList::temporaryShuntBlobnotifyAddedToCache(uint32_t cacheID) const {
+    SkASSERT(fTemporaryTextBlobShunt != nullptr);
+    fTemporaryTextBlobShunt->notifyAddedToCache(cacheID);
+}
+
+// -- SkGlyphRunListIterator -----------------------------------------------------------------------
+constexpr SkPoint SkGlyphRunListIterator::fZero;
 
 // -- SkGlyphRunBuilder ----------------------------------------------------------------------------
 void SkGlyphRunBuilder::prepareDrawText(
         const SkPaint& paint, const void* bytes, size_t byteLength, SkPoint origin) {
     this->initialize();
-    SkSpan<const char> originalText((const char*)bytes, byteLength);
-    if (paint.getTextEncoding() != SkPaint::kUTF8_TextEncoding) {
-        originalText = SkSpan<const char>();
-    }
-    this->drawText(paint, bytes, byteLength, origin, originalText, SkSpan<uint32_t>());
+    this->drawText(
+            paint, bytes, byteLength, origin, SkSpan<const char>(), SkSpan<const uint32_t>());
 }
 
 void SkGlyphRunBuilder::prepareDrawPosTextH(const SkPaint& paint, const void* bytes,
@@ -135,19 +157,22 @@
                                             SkScalar constY) {
     this->initialize();
     this->drawPosTextH(
-            paint, bytes, byteLength, xpos, constY, SkSpan<const char>(), SkSpan<uint32_t>());
+            paint, bytes, byteLength, xpos, constY,
+            SkSpan<const char>(), SkSpan<const uint32_t>());
 }
 
 void SkGlyphRunBuilder::prepareDrawPosText(const SkPaint& paint, const void* bytes,
                                            size_t byteLength, const SkPoint* pos) {
     this->initialize();
-    this->drawPosText(paint, bytes, byteLength, pos, SkSpan<const char>(), SkSpan<uint32_t>());
+    this->drawPosText(paint, bytes, byteLength, pos,
+            SkSpan<const char>(), SkSpan<const uint32_t>());
 }
 
 void SkGlyphRunBuilder::prepareTextBlob(
-        const SkPaint& paint, const SkTextBlob& blob, SkPoint origin) {
+        const SkPaint& paint, const SkTextBlob& blob, SkPoint origin, SkDrawFilter* drawFilter) {
     this->initialize();
-    fUniqueID = blob.uniqueID();
+    fTemporaryTextBlobShunt = &blob;
+    fOrigin = origin;
 
     SkPaint runPaint = paint;
 
@@ -156,33 +181,44 @@
         // so it is safe to not re-seed the paint for this reason.
         it.applyFontToPaint(&runPaint);
 
+        if (drawFilter != nullptr && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
+            // A false return from filter() means we should abort the current draw.
+            runPaint = paint;
+            continue;
+        }
+
         // These better be glyphs
         SkASSERT(runPaint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
 
         auto text = SkSpan<const char>(it.text(), it.textSize());
-        auto clusters = SkSpan<uint32_t>(it.clusters(), it.glyphCount());
+        auto clusters = SkSpan<const uint32_t>(it.clusters(), it.glyphCount());
         size_t glyphLen = it.glyphCount() * sizeof(SkGlyphID);
         const SkPoint& offset = it.offset();
 
         switch (it.positioning()) {
             case SkTextBlob::kDefault_Positioning: {
-                auto dtOrigin = origin + offset;
-                this->drawText(runPaint, it.glyphs(), glyphLen, dtOrigin, text, clusters);
+                this->drawText(runPaint, it.glyphs(), glyphLen, offset, text, clusters);
             }
             break;
             case SkTextBlob::kHorizontal_Positioning: {
-                auto constY = origin.y() + offset.y();
+                auto constY = offset.y();
                 this->drawPosTextH(
                         runPaint, it.glyphs(), glyphLen, it.pos(), constY, text, clusters);
             }
             break;
             case SkTextBlob::kFull_Positioning:
                 this->drawPosText(
-                        runPaint, it.glyphs(), glyphLen, (const SkPoint*)it.pos(), text, clusters);
+                        runPaint, it.glyphs(), glyphLen, (const SkPoint*)it.pos(), text,
+                        clusters);
             break;
             default:
                 SK_ABORT("unhandled positioning mode");
         }
+
+        if (drawFilter != nullptr) {
+            // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
+            runPaint = paint;
+        }
     }
 }
 
@@ -193,22 +229,29 @@
 }
 
 SkGlyphRunList* SkGlyphRunBuilder::useGlyphRunList() {
-    new ((void*)&fScratchGlyphRunList) SkGlyphRunList{SkSpan<SkGlyphRun>(fGlyphRuns), fUniqueID};
+    new ((void*)&fScratchGlyphRunList)
+        SkGlyphRunList{SkSpan<SkGlyphRun>(fGlyphRuns), fOrigin, fTemporaryTextBlobShunt};
     return &fScratchGlyphRunList;
 }
 
+void SkGlyphRunBuilder::draw(SkBaseDevice* device) {
+    auto glyphRunList = this->useGlyphRunList();
+    device->drawGlyphRunList(glyphRunList);
+}
+
 size_t SkGlyphRunBuilder::runSize() const { return fDenseIndex.size() - fLastDenseIndex; }
 
 size_t SkGlyphRunBuilder::uniqueSize() const { return fUniqueGlyphIDs.size() - fLastUniqueIndex; }
 
 void SkGlyphRunBuilder::initialize() {
-    fUniqueID = 0;
+    fTemporaryTextBlobShunt = nullptr;
     fDenseIndex.clear();
     fPositions.clear();
     fUniqueGlyphIDs.clear();
     fGlyphRuns.clear();
     fLastDenseIndex = 0;
     fLastUniqueIndex = 0;
+    fOrigin = {0, 0};
 }
 
 SkGlyphID* SkGlyphRunBuilder::addDenseAndUnique(
@@ -245,10 +288,19 @@
 }
 
 void SkGlyphRunBuilder::addGlyphRunToList(
-        SkGlyphID* temporaryShuntGlyphIDs, SkSpan<const char> text, SkSpan<uint32_t> clusters) {
+        const SkPaint& runPaint,
+        SkGlyphID* temporaryShuntGlyphIDs,
+        SkSpan<const char> text,
+        SkSpan<const uint32_t> clusters) {
 
     // Ignore empty runs.
     if (fDenseIndex.size() != fLastDenseIndex) {
+        SkPaint modifiedRunPaint{runPaint};
+
+        // TODO: remove these once the text stack has all the encoding and align code removed.
+        modifiedRunPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        modifiedRunPaint.setTextAlign(SkPaint::kLeft_Align);
+
         auto runSize = this->runSize();
         auto uniqueSize = this->uniqueSize();
 
@@ -258,7 +310,8 @@
                 fLastUniqueIndex, SkTo<uint16_t>(uniqueSize),
                 SkSpan<SkGlyphID>(temporaryShuntGlyphIDs, runSize),
                 text,
-                clusters);
+                clusters,
+                std::move(modifiedRunPaint));
 
         fLastDenseIndex = fDenseIndex.size();
         fLastUniqueIndex = fUniqueGlyphIDs.size();
@@ -267,7 +320,7 @@
 
 void SkGlyphRunBuilder::drawText(
         const SkPaint& paint, const void* bytes, size_t byteLength, SkPoint origin,
-        SkSpan<const char> text, SkSpan<uint32_t> clusters) {
+        SkSpan<const char> text, SkSpan<const uint32_t> clusters) {
 
     SkGlyphID* temporaryShuntGlyphIDs = this->addDenseAndUnique(paint, bytes, byteLength);
 
@@ -286,22 +339,22 @@
     }
 
     if (paint.getTextAlign() != SkPaint::kLeft_Align) {
-        SkVector len = endOfLastGlyph - origin;
+        SkVector runWidth = endOfLastGlyph - origin;
         if (paint.getTextAlign() == SkPaint::kCenter_Align) {
-            len.scale(SK_ScalarHalf);
+            runWidth.scale(SK_ScalarHalf);
         }
         for (size_t i = fLastDenseIndex; i < this->runSize(); i++) {
-            fPositions[i] -= len;
+            fPositions[i] -= runWidth;
         }
     }
 
-    this->addGlyphRunToList(temporaryShuntGlyphIDs, text, clusters);
+    this->addGlyphRunToList(paint, temporaryShuntGlyphIDs, text, clusters);
 }
 
-void SkGlyphRunBuilder::drawPosTextH(const SkPaint& paint, const void* bytes,
-                                     size_t byteLength, const SkScalar* xpos,
-                                     SkScalar constY,
-                                     SkSpan<const char> text, SkSpan<uint32_t> clusters) {
+void SkGlyphRunBuilder::drawPosTextH(
+        const SkPaint& paint, const void* bytes, size_t byteLength,
+        const SkScalar* xpos, SkScalar constY,
+        SkSpan<const char> text, SkSpan<const uint32_t> clusters) {
 
     SkGlyphID* temporaryShuntGlyphIDs = this->addDenseAndUnique(paint, bytes, byteLength);
 
@@ -309,19 +362,20 @@
         fPositions.push_back(SkPoint::Make(xpos[i], constY));
     }
 
-    this->addGlyphRunToList(temporaryShuntGlyphIDs, text, clusters);
+    this->addGlyphRunToList(paint, temporaryShuntGlyphIDs, text, clusters);
 }
 
-void SkGlyphRunBuilder::drawPosText(const SkPaint& paint, const void* bytes,
-                                    size_t byteLength, const SkPoint* pos,
-                                    SkSpan<const char> text, SkSpan<uint32_t> clusters) {
+void SkGlyphRunBuilder::drawPosText(
+        const SkPaint& paint, const void* bytes,
+        size_t byteLength, const SkPoint* pos,
+        SkSpan<const char> text, SkSpan<const uint32_t> clusters) {
     SkGlyphID* temporaryShuntGlyphIDs = this->addDenseAndUnique(paint, bytes, byteLength);
 
     for (size_t i = 0; i < runSize(); i++) {
         fPositions.push_back(pos[i]);
     }
 
-    this->addGlyphRunToList(temporaryShuntGlyphIDs, text, clusters);
+    this->addGlyphRunToList(paint, temporaryShuntGlyphIDs, text, clusters);
 }
 
 
diff --git a/src/core/SkGlyphRun.h b/src/core/SkGlyphRun.h
index 739ad0b..6f9041c 100644
--- a/src/core/SkGlyphRun.h
+++ b/src/core/SkGlyphRun.h
@@ -16,9 +16,11 @@
 #include "SkMask.h"
 #include "SkPath.h"
 #include "SkPoint.h"
+#include "SkTextBlob.h"
 #include "SkTypes.h"
 
 class SkBaseDevice;
+class SkDrawFilter;
 
 template <typename T>
 class SkSpan {
@@ -74,11 +76,12 @@
                size_t fUniqueOffset, uint16_t fUniqueSize,
                SkSpan<SkGlyphID>  scratchGlyphs,
                SkSpan<const char> text,
-               SkSpan<uint32_t>   clusters);
+               SkSpan<const uint32_t>   clusters,
+               SkPaint&&          runPaint);
 
     // The temporaryShunt calls are to allow inter-operating with existing code while glyph runs
     // are developed.
-    void temporaryShuntToDrawPosText(const SkPaint& paint, SkBaseDevice* device);
+    void temporaryShuntToDrawPosText(SkBaseDevice* device, SkPoint origin);
     using TemporaryShuntCallback = std::function<void(size_t, const char*, const SkScalar*)>;
     void temporaryShuntToCallback(TemporaryShuntCallback callback);
 
@@ -90,6 +93,21 @@
     SkSpan<const SkGlyphID> uniqueGlyphIDs() const {
         return fRunInfo.uniqueGlyphIDs(fUniqueOffset, fUniqueSize);
     }
+    SkSpan<SkGlyphID> shuntGlyphsIDs() const {
+        return fTemporaryShuntGlyphIDs;
+    }
+
+    const SkPaint& paint() const {
+        return fRunPaint;
+    }
+
+    SkSpan<const uint32_t> clusters() const {
+        return fClusters;
+    }
+
+    SkSpan<const char> text() const {
+        return fText;
+    }
 
 private:
     const SkIndexedRunInfo& fRunInfo;
@@ -103,18 +121,39 @@
     // Original text from SkTextBlob if present. Will be empty of not present.
     const SkSpan<const char> fText;
     // Original clusters from SkTextBlob if present. Will be empty if not present.
-    const SkSpan<uint32_t>   fClusters;
+    const SkSpan<const uint32_t>   fClusters;
+    // Paint for this run modified to have glyph encoding and left alignment.
+    const SkPaint fRunPaint;
 };
 
 class SkGlyphRunList {
-    const uint64_t     fUniqueID{0};
     SkSpan<SkGlyphRun> fGlyphRuns;
+    SkPoint            fOrigin = {0, 0};
+
+    // The text blob is needed to hookup the call back that the SkTextBlob destructor calls. It
+    // should be used for nothing else
+    const SkTextBlob*  fTemporaryTextBlobShunt{nullptr};
 
 public:
     SkGlyphRunList() = default;
-    SkGlyphRunList(SkSpan<SkGlyphRun> glyphRuns, uint64_t uniqueID);
+    // Blob maybe null.
+    SkGlyphRunList(SkSpan<SkGlyphRun> glyphRuns, SkPoint origin, const SkTextBlob* blob);
 
-    uint64_t uniqueID() const { return fUniqueID; }
+    uint64_t uniqueID() const;
+    bool anyRunsLCD() const;
+    void temporaryShuntBlobnotifyAddedToCache(uint32_t cacheID) const;
+
+    bool canCache() const { return fTemporaryTextBlobShunt != nullptr; }
+    ptrdiff_t runCount() const { return fGlyphRuns.size(); }
+    size_t totalGlyphCount() const {
+        size_t glyphCount = 0;
+        for(const auto& run : fGlyphRuns) {
+            glyphCount += run.runSize();
+        }
+        return glyphCount;
+    }
+
+    SkPoint origin() const { return fOrigin; }
 
     auto begin() -> decltype(fGlyphRuns.begin())               { return fGlyphRuns.begin(); }
     auto end()   -> decltype(fGlyphRuns.end())                 { return fGlyphRuns.end();   }
@@ -122,6 +161,30 @@
     auto operator [] (ptrdiff_t i) -> decltype(fGlyphRuns[i])  { return fGlyphRuns[i];      }
 };
 
+class SkGlyphRunListIterator {
+public:
+    explicit SkGlyphRunListIterator(SkGlyphRunList* list) : fList{*list} {}
+
+    bool done() const { return fIndex == fList.size(); }
+    void next() { fIndex += 1;}
+    uint32_t glyphCount() const { return fList[fIndex].runSize(); }
+    const uint16_t* glyphs() const { return fList[fIndex].shuntGlyphsIDs().data(); }
+    const SkScalar* pos() const { return (const SkScalar*)fList[fIndex].positions().data(); }
+    const SkPoint& offset() const { return fZero; }
+    void applyFontToPaint(SkPaint* paint) const { *paint = fList[fIndex].paint(); }
+    SkTextBlob::GlyphPositioning positioning() const { return SkTextBlob::kFull_Positioning; }
+    const uint32_t* clusters() const { return fList[fIndex].clusters().data(); }
+    uint32_t textSize() const { return fList[fIndex].text().size(); }
+    const char* text() const { return fList[fIndex].text().data(); }
+
+    bool isLCD() const { return fList[fIndex].paint().isLCDRenderText(); }
+
+private:
+    static constexpr SkPoint fZero{0,0};
+    ptrdiff_t fIndex{0};
+    SkGlyphRunList& fList;
+};
+
 // 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
@@ -154,10 +217,14 @@
             const SkScalar xpos[], SkScalar constY);
     void prepareDrawPosText(
             const SkPaint& paint, const void* bytes, size_t byteLength, const SkPoint pos[]);
-    void prepareTextBlob(const SkPaint& paint, const SkTextBlob& blob, SkPoint origin);
+    void prepareTextBlob(
+            const SkPaint& paint, const SkTextBlob& blob, SkPoint origin, SkDrawFilter* filter);
 
-    SkGlyphRunList* useGlyphRunList();
+    void draw(SkBaseDevice* device);
+
+    // Public for testing only.
     SkGlyphRun* useGlyphRun();
+    SkGlyphRunList* useGlyphRunList();
 
 private:
     size_t runSize() const;
@@ -165,20 +232,27 @@
     void initialize();
     SkGlyphID* addDenseAndUnique(const SkPaint& paint, const void* bytes, size_t byteLength);
     void addGlyphRunToList(
-            SkGlyphID* temporaryShuntGlyphIDs, SkSpan<const char> text, SkSpan<uint32_t> clusters);
+            const SkPaint& runPaint,
+            SkGlyphID* temporaryShuntGlyphIDs,
+            SkSpan<const char> text,
+            SkSpan<const uint32_t> clusters);
 
     void drawText(
             const SkPaint& paint, const void* bytes, size_t byteLength, SkPoint origin,
-            SkSpan<const char> text, SkSpan<uint32_t> clusters);
+            SkSpan<const char> text, SkSpan<const uint32_t> clusters);
     void drawPosTextH(
             const SkPaint& paint, const void* bytes, size_t byteLength,
             const SkScalar* xpos, SkScalar constY,
-            SkSpan<const char> text, SkSpan<uint32_t> clusters);
+            SkSpan<const char> text, SkSpan<const uint32_t> clusters);
     void drawPosText(
             const SkPaint& paint, const void* bytes, size_t byteLength, const SkPoint* pos,
-            SkSpan<const char> text, SkSpan<uint32_t> clusters);
+            SkSpan<const char> text, SkSpan<const uint32_t> clusters);
 
-    uint64_t               fUniqueID{0};
+    const SkTextBlob*      fTemporaryTextBlobShunt{nullptr};
+
+    // The point passed into drawTextBlob. This allows the GPU back end to detect and adjust for
+    // translations.
+    SkPoint                fOrigin;
 
     std::vector<uint16_t>  fDenseIndex;
     std::vector<SkPoint>   fPositions;
diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp
index 774a1eb..18e47f3 100644
--- a/src/core/SkRemoteGlyphCache.cpp
+++ b/src/core/SkRemoteGlyphCache.cpp
@@ -16,9 +16,9 @@
 #include "SkDevice.h"
 #include "SkDraw.h"
 #include "SkFindAndPlaceGlyph.h"
+#include "SkGlyphRun.h"
 #include "SkPathEffect.h"
 #include "SkStrikeCache.h"
-#include "SkTextBlobRunIterator.h"
 #include "SkTraceEvent.h"
 #include "SkTypeface_remote.h"
 
@@ -240,27 +240,20 @@
     }
 
 protected:
-    void drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint,
-                      SkDrawFilter* drawFilter) override {
-        // The looper should be applied by the SkCanvas.
-        SkASSERT(paint.getDrawLooper() == nullptr);
-        // We don't support SkDrawFilter.
-        SkASSERT(drawFilter == nullptr);
-
-        SkPoint position{x, y};
-        SkPaint runPaint{paint};
-        SkTextBlobRunIterator it(blob);
+    void drawGlyphRunList(SkGlyphRunList* glyphRunList) override {
+        SkPaint runPaint;
+        SkGlyphRunListIterator it(glyphRunList);
         for (; !it.done(); it.next()) {
             // applyFontToPaint() always overwrites the exact same attributes,
             // so it is safe to not re-seed the paint for this reason.
             it.applyFontToPaint(&runPaint);
-            this->processGlyphRun(position, it, runPaint);
+            this->processGlyphRun(glyphRunList->origin(), it, runPaint);
         }
     }
 
 private:
     void processGlyphRun(const SkPoint& position,
-                         const SkTextBlobRunIterator& it,
+                         const SkGlyphRunListIterator& it,
                          const SkPaint& runPaint) {
         TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRun");
 
@@ -344,7 +337,7 @@
         }
     }
 
-    void processGlyphRunForPaths(const SkTextBlobRunIterator& it,
+    void processGlyphRunForPaths(const SkGlyphRunListIterator& it,
                                  const SkPaint& runPaint,
                                  const SkMatrix& runMatrix) {
         TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRunForPaths");
@@ -394,7 +387,7 @@
     }
 
 #if SK_SUPPORT_GPU
-    bool processGlyphRunForDFT(const SkTextBlobRunIterator& it,
+    bool processGlyphRunForDFT(const SkGlyphRunListIterator& it,
                                const SkPaint& runPaint,
                                const SkMatrix& runMatrix) {
         TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRunForDFT");
diff --git a/src/core/SkTypeface_remote.h b/src/core/SkTypeface_remote.h
index 47a68fd..f7326cc 100644
--- a/src/core/SkTypeface_remote.h
+++ b/src/core/SkTypeface_remote.h
@@ -123,8 +123,7 @@
         return 0;
     }
     int onCountGlyphs() const override {
-        SK_ABORT("Should never be called.");
-        return 0;
+        return this->glyphCount();
     }
 
     void* onGetCTFontRef() const override {
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 1dbde17..9d31a0d 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -237,18 +237,17 @@
                                   clipBounds);
 }
 
-void GrRenderTargetContext::drawTextBlob(const GrClip& clip, const SkPaint& paint,
-                                         const SkMatrix& viewMatrix, const SkTextBlob* blob,
-                                         SkScalar x, SkScalar y, SkDrawFilter* filter,
-                                         const SkIRect& clipBounds) {
+void GrRenderTargetContext::drawGlyphRunList(
+        const GrClip& clip, const SkMatrix& viewMatrix,
+        SkGlyphRunList* blob, const SkIRect& clipBounds) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
-    GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTextBlob", fContext);
+    GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawGlyphRunList", fContext);
 
     GrTextContext* atlasTextContext = this->drawingManager()->getTextContext();
-    atlasTextContext->drawTextBlob(fContext, fTextTarget.get(), clip, paint, viewMatrix,
-                                   fSurfaceProps, blob, x, y, filter, clipBounds);
+    atlasTextContext->drawGlyphRunList(fContext, fTextTarget.get(), clip, viewMatrix,
+                                       fSurfaceProps, blob, clipBounds);
 }
 
 void GrRenderTargetContext::discard() {
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 783b351..b529398 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -37,6 +37,7 @@
 struct GrUserStencilSettings;
 class SkDrawFilter;
 struct SkDrawShadowRec;
+class SkGlyphRunList;
 struct SkIPoint;
 struct SkIRect;
 class SkLatticeIter;
@@ -67,10 +68,9 @@
                              const char text[], size_t byteLength, const SkScalar pos[],
                              int scalarsPerPosition, const SkPoint& offset,
                              const SkIRect& clipBounds);
-    virtual void drawTextBlob(const GrClip&, const SkPaint&,
-                              const SkMatrix& viewMatrix, const SkTextBlob*,
-                              SkScalar x, SkScalar y,
-                              SkDrawFilter*, const SkIRect& clipBounds);
+    virtual void drawGlyphRunList(const GrClip&,
+                                  const SkMatrix& viewMatrix, SkGlyphRunList*,
+                                  const SkIRect& clipBounds);
 
     /**
      * Provides a perfomance hint that the render target's contents are allowed
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 819acd2..d4f329d 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1624,14 +1624,13 @@
                                       this->devClipBounds());
 }
 
-void SkGpuDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                               const SkPaint& paint, SkDrawFilter* drawFilter) {
+void SkGpuDevice::drawGlyphRunList(SkGlyphRunList* list) {
     ASSERT_SINGLE_OWNER
-    GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawTextBlob", fContext.get());
+    GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawGlyphRunList", fContext.get());
     SkDEBUGCODE(this->validate();)
 
-    fRenderTargetContext->drawTextBlob(this->clip(), paint, this->ctm(), blob, x, y, drawFilter,
-                                       this->devClipBounds());
+    fRenderTargetContext->drawGlyphRunList(
+            this->clip(), this->ctm(), list, this->devClipBounds());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 7c7030e..0bd1c80 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -89,8 +89,7 @@
                     const SkPaint& paint) override;
     void drawPosText(const void* text, size_t len, const SkScalar pos[],
                      int scalarsPerPos, const SkPoint& offset, const SkPaint&) override;
-    void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y,
-                      const SkPaint& paint, SkDrawFilter* drawFilter) override;
+    void drawGlyphRunList(SkGlyphRunList* list) override;
     void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
     void drawShadow(const SkPath&, const SkDrawShadowRec&) override;
     void drawAtlas(const SkImage* atlas, const SkRSXform[], const SkRect[],
diff --git a/src/gpu/text/GrTextBlobCache.h b/src/gpu/text/GrTextBlobCache.h
index b41d401..7a8ea0b 100644
--- a/src/gpu/text/GrTextBlobCache.h
+++ b/src/gpu/text/GrTextBlobCache.h
@@ -55,6 +55,20 @@
         blob->notifyAddedToCache(fUniqueID);
         return cacheBlob;
     }
+    sk_sp<GrTextBlob> makeBlob(SkGlyphRunList* glyphRunList) {
+        return GrTextBlob::Make(glyphRunList->totalGlyphCount(), glyphRunList->size());
+    }
+
+    sk_sp<GrTextBlob> makeCachedBlob(SkGlyphRunList* glyphRunList,
+                                     const GrTextBlob::Key& key,
+                                     const SkMaskFilterBase::BlurRec& blurRec,
+                                     const SkPaint& paint) {
+        sk_sp<GrTextBlob> cacheBlob(makeBlob(glyphRunList));
+        cacheBlob->setupKey(key, blurRec, paint);
+        this->add(cacheBlob);
+        glyphRunList->temporaryShuntBlobnotifyAddedToCache(fUniqueID);
+        return cacheBlob;
+    }
 
     sk_sp<GrTextBlob> find(const GrTextBlob::Key& key) const {
         const auto* idEntry = fBlobIDCache.find(key.fUniqueID);
diff --git a/src/gpu/text/GrTextContext.cpp b/src/gpu/text/GrTextContext.cpp
index a20ba44..3309d22 100644
--- a/src/gpu/text/GrTextContext.cpp
+++ b/src/gpu/text/GrTextContext.cpp
@@ -84,58 +84,48 @@
     }
 }
 
-// TODO if this function ever shows up in profiling, then we can compute this value when the
-// textblob is being built and cache it.  However, for the time being textblobs mostly only have 1
-// run so this is not a big deal to compute here.
-bool GrTextContext::HasLCD(const SkTextBlob* blob) {
-    SkTextBlobRunIterator it(blob);
-    for (; !it.done(); it.next()) {
-        if (it.isLCD()) {
-            return true;
-        }
-    }
-    return false;
-}
+void GrTextContext::drawGlyphRunList(
+        GrContext* context, GrTextUtils::Target* target, const GrClip& clip,
+        const SkMatrix& viewMatrix, const SkSurfaceProps& props, SkGlyphRunList* glyphRunList,
+        const SkIRect& clipBounds) {
+    SkPoint origin = glyphRunList->origin();
 
-void GrTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* target,
-                                 const GrClip& clip, const SkPaint& skPaint,
-                                 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
-                                 const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
+    // Get the first paint to use as the key paint.
+    const SkPaint& skPaint = (*glyphRunList)[0].paint();
+
     // If we have been abandoned, then don't draw
     if (context->abandoned()) {
         return;
     }
 
-    sk_sp<GrTextBlob> cacheBlob;
     SkMaskFilterBase::BlurRec blurRec;
-    GrTextBlob::Key key;
     // It might be worth caching these things, but its not clear at this time
     // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
     const SkMaskFilter* mf = skPaint.getMaskFilter();
-    bool canCache = !(skPaint.getPathEffect() ||
-                      (mf && !as_MFB(mf)->asABlur(&blurRec)) ||
-                      drawFilter);
+    bool canCache = glyphRunList->canCache() && !(skPaint.getPathEffect() ||
+                      (mf && !as_MFB(mf)->asABlur(&blurRec)));
     SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo());
 
     auto glyphCache = context->contextPriv().getGlyphCache();
     GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache();
 
+    sk_sp<GrTextBlob> cacheBlob;
+    GrTextBlob::Key key;
     if (canCache) {
-        bool hasLCD = HasLCD(blob);
+        bool hasLCD = glyphRunList->anyRunsLCD();
 
         // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
         SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
-                                                 kUnknown_SkPixelGeometry;
+                                        kUnknown_SkPixelGeometry;
 
         // TODO we want to figure out a way to be able to use the canonical color on LCD text,
         // see the note on ComputeCanonicalColor above.  We pick a dummy value for LCD text to
         // ensure we always match the same key
         GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
-                                          ComputeCanonicalColor(skPaint, hasLCD);
+                                 ComputeCanonicalColor(skPaint, hasLCD);
 
         key.fPixelGeometry = pixelGeometry;
-        key.fUniqueID = blob->uniqueID();
+        key.fUniqueID = glyphRunList->uniqueID();
         key.fStyle = skPaint.getStyle();
         key.fHasBlur = SkToBool(mf);
         key.fCanonicalColor = canonicalColor;
@@ -145,135 +135,77 @@
 
     GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
     if (cacheBlob) {
-        if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, x, y)) {
+        if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, origin.x(), origin.y())) {
             // We have to remake the blob because changes may invalidate our masks.
             // TODO we could probably get away reuse most of the time if the pointer is unique,
             // but we'd have to clear the subrun information
             textBlobCache->remove(cacheBlob.get());
-            cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
-            this->regenerateTextBlob(cacheBlob.get(), glyphCache,
+            cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, skPaint);
+            this->regenerateGlyphRunList(cacheBlob.get(), glyphCache,
                                      *context->contextPriv().caps()->shaderCaps(), paint,
-                                     scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter);
+                                     scalerContextFlags, viewMatrix, props, glyphRunList);
         } else {
             textBlobCache->makeMRU(cacheBlob.get());
 
             if (CACHE_SANITY_CHECK) {
-                int glyphCount = 0;
-                int runCount = 0;
-                GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
+                int glyphCount = glyphRunList->totalGlyphCount();
+                int runCount = glyphRunList->runCount();
                 sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount));
                 sanityBlob->setupKey(key, blurRec, skPaint);
-                this->regenerateTextBlob(
+                this->regenerateGlyphRunList(
                         sanityBlob.get(), glyphCache, *context->contextPriv().caps()->shaderCaps(),
-                        paint, scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter);
+                        paint, scalerContextFlags, viewMatrix, props, glyphRunList);
                 GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
             }
         }
     } else {
         if (canCache) {
-            cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
+            cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, skPaint);
         } else {
-            cacheBlob = textBlobCache->makeBlob(blob);
+            cacheBlob = textBlobCache->makeBlob(glyphRunList);
         }
-        this->regenerateTextBlob(cacheBlob.get(), glyphCache,
+        this->regenerateGlyphRunList(cacheBlob.get(), glyphCache,
                                  *context->contextPriv().caps()->shaderCaps(), paint,
-                                 scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter);
+                                 scalerContextFlags, viewMatrix, props, glyphRunList);
     }
 
     cacheBlob->flush(target, props, fDistanceAdjustTable.get(), paint,
-                     clip, viewMatrix, clipBounds, x, y);
+                     clip, viewMatrix, clipBounds, origin.x(), origin.y());
 }
 
-void GrTextContext::regenerateTextBlob(GrTextBlob* cacheBlob,
+void GrTextContext::regenerateGlyphRunList(GrTextBlob* cacheBlob,
                                        GrGlyphCache* glyphCache,
                                        const GrShaderCaps& shaderCaps,
                                        const GrTextUtils::Paint& paint,
                                        SkScalerContextFlags scalerContextFlags,
                                        const SkMatrix& viewMatrix,
-                                       const SkSurfaceProps& props, const SkTextBlob* blob,
-                                       SkScalar x, SkScalar y,
-                                       SkDrawFilter* drawFilter) const {
-    cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, x, y);
+                                       const SkSurfaceProps& props,
+                                       SkGlyphRunList* glyphRunList) const {
+    SkPoint origin = glyphRunList->origin();
+    cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, origin.x(), origin.y());
 
     // Regenerate textblob
-    SkTextBlobRunIterator it(blob);
-    GrTextUtils::RunPaint runPaint(&paint, drawFilter);
+    SkGlyphRunListIterator it(glyphRunList);
+    GrTextUtils::RunPaint runPaint(&paint, nullptr);
     for (int run = 0; !it.done(); it.next(), run++) {
         int glyphCount = it.glyphCount();
         size_t textLen = glyphCount * sizeof(uint16_t);
-        const SkPoint& offset = it.offset();
         cacheBlob->push_back_run(run);
         if (!runPaint.modifyForRun([it](SkPaint* p) { it.applyFontToPaint(p); })) {
             continue;
         }
         cacheBlob->setRunPaintFlags(run, runPaint.skPaint().getFlags());
 
+        SkASSERT(it.positioning() == SkTextBlob::kFull_Positioning);
         if (CanDrawAsDistanceFields(runPaint, viewMatrix, props,
                                     shaderCaps.supportsDistanceFieldText(), fOptions)) {
-            switch (it.positioning()) {
-                case SkTextBlob::kDefault_Positioning: {
-                    auto origin = SkPoint::Make(x + offset.x(), y + offset.y());
-                    SkGlyphRunBuilder builder;
-                    builder.prepareDrawText(runPaint.skPaint(),
-                                            (const char*)it.glyphs(), textLen, origin);
-
-                    auto glyphRun = builder.useGlyphRun();
-
-                    glyphRun->temporaryShuntToCallback(
-                            [&](size_t runSize, const char* glyphIDs, const SkScalar* pos) {
-                                this->drawDFPosText(
-                                    cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
-                                    viewMatrix, glyphIDs, 2 * runSize, pos, 2,
-                                    SkPoint::Make(0,0));
-                            });
-                    break;
-                }
-
-                case SkTextBlob::kHorizontal_Positioning: {
-                    SkPoint dfOffset = SkPoint::Make(x, y + offset.y());
-                    this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint,
-                                        scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
-                                        textLen, it.pos(), 1, dfOffset);
-                    break;
-                }
-                case SkTextBlob::kFull_Positioning: {
-                    SkPoint dfOffset = SkPoint::Make(x, y);
-                    this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint,
-                                        scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
-                                        textLen, it.pos(), 2, dfOffset);
-                    break;
-                }
-            }
+            this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint,
+                                scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
+                                textLen, it.pos(), 2, origin);
         } else {
-            switch (it.positioning()) {
-                case SkTextBlob::kDefault_Positioning: {
-                    auto origin = SkPoint::Make(x + offset.x(), y + offset.y());
-                    SkGlyphRunBuilder builder;
-                    builder.prepareDrawText(runPaint.skPaint(),
-                                            (const char*)it.glyphs(), textLen, origin);
-
-                    auto glyphRun = builder.useGlyphRun();
-
-                    glyphRun->temporaryShuntToCallback(
-                            [&](size_t runSize, const char* glyphIDs, const SkScalar* pos) {
-                                this->DrawBmpPosText(
-                                    cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
-                                    viewMatrix, glyphIDs, 2 * runSize,
-                                    pos, 2, SkPoint::Make(0, 0));
-                            });
-                    break;
-                }
-                case SkTextBlob::kHorizontal_Positioning:
-                    DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
-                                   viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1,
-                                   SkPoint::Make(x, y + offset.y()));
-                    break;
-                case SkTextBlob::kFull_Positioning:
-                    DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
-                                   viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2,
-                                   SkPoint::Make(x, y));
-                    break;
-            }
+            DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
+                           viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2,
+                           origin);
         }
     }
 }
@@ -333,7 +265,6 @@
     }
 }
 
-
 void GrTextContext::DrawBmpPosText(GrTextBlob* blob, int runIndex,
                                    GrGlyphCache* glyphCache, const SkSurfaceProps& props,
                                    const GrTextUtils::Paint& paint,
@@ -786,6 +717,7 @@
     builder.prepareDrawText(skPaint, text, textLen, origin);
     sk_sp<GrTextBlob> blob;
 
+    // TODO - remove shunt call when removing drawPosText from device.
     auto glyphRun = builder.useGlyphRun();
     // Use the text and textLen below, because we don't want to mess with the paint.
     glyphRun->temporaryShuntToCallback(
diff --git a/src/gpu/text/GrTextContext.h b/src/gpu/text/GrTextContext.h
index dd82b3c..25aeea0 100644
--- a/src/gpu/text/GrTextContext.h
+++ b/src/gpu/text/GrTextContext.h
@@ -48,9 +48,9 @@
                      const SkMatrix& viewMatrix, const SkSurfaceProps&, const char text[],
                      size_t byteLength, const SkScalar pos[], int scalarsPerPosition,
                      const SkPoint& offset, const SkIRect& regionClipBounds);
-    void drawTextBlob(GrContext*, GrTextUtils::Target*, const GrClip&, const SkPaint&,
-                      const SkMatrix& viewMatrix, const SkSurfaceProps&, const SkTextBlob*,
-                      SkScalar x, SkScalar y, SkDrawFilter*, const SkIRect& clipBounds);
+    void drawGlyphRunList(GrContext*, GrTextUtils::Target*, const GrClip&,
+                          const SkMatrix& viewMatrix, const SkSurfaceProps&, SkGlyphRunList*,
+                          const SkIRect& clipBounds);
 
     std::unique_ptr<GrDrawOp> createOp_TestingOnly(GrContext*,
                                                    GrTextContext*,
@@ -115,17 +115,15 @@
     static SkColor ComputeCanonicalColor(const SkPaint&, bool lcd);
     // Determines if we need to use fake gamma (and contrast boost):
     static SkScalerContextFlags ComputeScalerContextFlags(const GrColorSpaceInfo&);
-    void regenerateTextBlob(GrTextBlob* bmp,
+
+    void regenerateGlyphRunList(GrTextBlob* bmp,
                             GrGlyphCache*,
                             const GrShaderCaps&,
                             const GrTextUtils::Paint&,
                             SkScalerContextFlags scalerContextFlags,
                             const SkMatrix& viewMatrix,
                             const SkSurfaceProps&,
-                            const SkTextBlob* blob, SkScalar x, SkScalar y,
-                            SkDrawFilter* drawFilter) const;
-
-    static bool HasLCD(const SkTextBlob*);
+                            SkGlyphRunList* glyphRunList) const;
 
     sk_sp<GrTextBlob> makeDrawPosTextBlob(GrTextBlobCache*, GrGlyphCache*,
                                                const GrShaderCaps&,
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 45a9b95..72d543c 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -19,6 +19,7 @@
 #include "SkDraw.h"
 #include "SkDrawFilter.h"
 #include "SkGlyphCache.h"
+#include "SkGlyphRun.h"
 #include "SkImageFilterCache.h"
 #include "SkJpegEncoder.h"
 #include "SkMakeUnique.h"
@@ -1458,17 +1459,12 @@
                            offset, paint, nullptr, 0, nullptr);
 }
 
-void SkPDFDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                               const SkPaint &paint, SkDrawFilter* drawFilter) {
-    for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
-        SkPaint runPaint(paint);
+void SkPDFDevice::drawGlyphRunList(SkGlyphRunList* glyphRunList) {
+    for (SkGlyphRunListIterator it(glyphRunList); !it.done(); it.next()) {
+        SkPaint runPaint;
         it.applyFontToPaint(&runPaint);
-        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
-            continue;
-        }
-        SkPoint offset = it.offset() + SkPoint{x, y};
         this->internalDrawText(it.glyphs(), sizeof(SkGlyphID) * it.glyphCount(),
-                               it.pos(), it.positioning(), offset, runPaint,
+                               it.pos(), it.positioning(), glyphRunList->origin(), runPaint,
                                it.clusters(), it.textSize(), it.text());
     }
 }
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 33d0e70..90f4b8e 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -22,6 +22,7 @@
 #include "SkTextBlob.h"
 #include "SkKeyedImage.h"
 
+class SkGlyphRunList;
 class SkKeyedImage;
 class SkPath;
 class SkPDFArray;
@@ -98,8 +99,7 @@
     void drawPosText(const void* text, size_t len,
                      const SkScalar pos[], int scalarsPerPos,
                      const SkPoint& offset, const SkPaint&) override;
-    void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y,
-                      const SkPaint &, SkDrawFilter*) override;
+    void drawGlyphRunList(SkGlyphRunList* glyphRunList) override;
     void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
     void drawDevice(SkBaseDevice*, int x, int y,
                     const SkPaint&) override;
diff --git a/tests/GlyphRunTest.cpp b/tests/GlyphRunTest.cpp
index cd2a221..8107f20 100644
--- a/tests/GlyphRunTest.cpp
+++ b/tests/GlyphRunTest.cpp
@@ -75,7 +75,7 @@
     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
     SkGlyphRunBuilder runBuilder;
-    runBuilder.prepareTextBlob(font, *blob, SkPoint::Make(0, 0));
+    runBuilder.prepareTextBlob(font, *blob, SkPoint::Make(0, 0), nullptr);
 
     auto runList = runBuilder.useGlyphRunList();
 
@@ -96,4 +96,4 @@
 
         runIndex += 1;
     }
-}
\ No newline at end of file
+}
diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp
index 4a7bd86..6986a7b 100644
--- a/tests/SkRemoteGlyphCacheTest.cpp
+++ b/tests/SkRemoteGlyphCacheTest.cpp
@@ -69,6 +69,8 @@
     font.setStyle(SkPaint::kFill_Style);
     font.setHinting(SkPaint::kNormal_Hinting);
     font.setTextSize(1u);
+    font.setAntiAlias(true);
+    font.setSubpixelText(true);
 
     SkTextBlobBuilder builder;
     SkRect bounds = SkRect::MakeWH(10, 10);
@@ -99,12 +101,13 @@
 }
 
 SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint,
-                    GrContext* context, const SkMatrix* matrix = nullptr) {
+                    GrContext* context, const SkMatrix* matrix = nullptr,
+                    SkScalar x = 0) {
     const SkImageInfo info =
             SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
     auto surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
     if (matrix) surface->getCanvas()->concat(*matrix);
-    surface->getCanvas()->drawTextBlob(blob.get(), 0u, 0u, paint);
+    surface->getCanvas()->drawTextBlob(blob.get(), x, 0, paint);
     SkBitmap bitmap;
     bitmap.allocN32Pixels(width, height);
     surface->readPixels(bitmap, 0, 0);
@@ -332,6 +335,45 @@
     discardableManager->unlockAndDeleteAll();
 }
 
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY, reporter, ctxInfo) {
+    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
+    SkStrikeServer server(discardableManager.get());
+    SkStrikeClient client(discardableManager, false);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setSubpixelText(true);
+    paint.setLCDRenderText(true);
+
+    // Server.
+    auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
+    auto serverTfData = server.serializeTypeface(serverTf.get());
+
+    int glyphCount = 10;
+    auto serverBlob = buildTextBlob(serverTf, glyphCount);
+    const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
+    SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
+                                                MakeSettings(ctxInfo.grContext()));
+    cache_diff_canvas.drawTextBlob(serverBlob.get(), 0.5, 0, paint);
+
+    std::vector<uint8_t> serverStrikeData;
+    server.writeStrikeData(&serverStrikeData);
+
+    // Client.
+    auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
+    REPORTER_ASSERT(reporter,
+                    client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
+    auto clientBlob = buildTextBlob(clientTf, glyphCount);
+
+    SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5);
+    SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5);
+    COMPARE_BLOBS(expected, actual, reporter);
+    REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
+    SkStrikeCache::ValidateGlyphCacheDataSize();
+
+    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
+    discardableManager->unlockAndDeleteAll();
+}
+
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT, reporter, ctxInfo) {
     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
     SkStrikeServer server(discardableManager.get());