Convert prepareForDrawing to glyph buffers

* Have prepareForDrawing leave empty glyphs in.
  This allows the SkSourceGlyphBuffer to track indexes properly.
  Rename prepareForDrawingRemoving empty to prepareForDrawing.
  This is only temporary until the new unified drawing CL is submitted.
* Make the device fallback case properly support subpixel positioning.
* Add calls to SkDrawableGlyphBuffer to handle the glyph flow of the
  prepareForDrawing system.
* Remove most of the old buffering system except for path handling.

Change-Id: I3e51c96ec041896e07dd4a8f580c207e857911f7
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/249221
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/src/core/SkGlyphBuffer.h b/src/core/SkGlyphBuffer.h
index abfd617..f284814 100644
--- a/src/core/SkGlyphBuffer.h
+++ b/src/core/SkGlyphBuffer.h
@@ -159,6 +159,23 @@
         return SkZip<SkGlyphVariant, SkPoint>{fInputSize, fMultiBuffer, fPositions};
     }
 
+    // DO NOT USE. This is only used to work around how prepareForDrawing works.
+    void flipDrawableToInput() {
+        SkASSERT(fPhase == kProcess);
+        fInputSize = fDrawableSize;
+        fDrawableSize = 0;
+        SkDEBUGCODE(fPhase = kInput);
+    }
+
+    // DO NOT USE. This is only used to work around how prepareForDrawing works.
+    void push_back(size_t from) {
+        SkASSERT(fPhase == kProcess);
+        SkASSERT(fDrawableSize <= from);
+        fPositions[fDrawableSize] = fPositions[from];
+        fMultiBuffer[fDrawableSize] = fMultiBuffer[from];
+        fDrawableSize++;
+    }
+
     // Store the glyph in the next drawable slot, using the position information located at index
     // from.
     void push_back(SkGlyph* glyph, size_t from) {
diff --git a/src/core/SkGlyphRunPainter.cpp b/src/core/SkGlyphRunPainter.cpp
index cb63a0f..dfb60b8 100644
--- a/src/core/SkGlyphRunPainter.cpp
+++ b/src/core/SkGlyphRunPainter.cpp
@@ -26,6 +26,7 @@
 #include "src/core/SkDevice.h"
 #include "src/core/SkDistanceFieldGen.h"
 #include "src/core/SkDraw.h"
+#include "src/core/SkEnumerate.h"
 #include "src/core/SkFontPriv.h"
 #include "src/core/SkRasterClip.h"
 #include "src/core/SkStrike.h"
@@ -76,52 +77,6 @@
 
 #endif
 
-SkSpan<const SkPackedGlyphID> SkGlyphRunListPainter::DeviceSpacePackedGlyphIDs(
-        const SkGlyphPositionRoundingSpec& roundingSpec,
-        const SkMatrix& viewMatrix,
-        const SkPoint& origin,
-        int n,
-        const SkGlyphID* glyphIDs,
-        const SkPoint* positions,
-        SkPoint* mappedPositions,
-        SkPackedGlyphID* results) {
-    // Add rounding and origin.
-    SkMatrix matrix = viewMatrix;
-    matrix.preTranslate(origin.x(), origin.y());
-    SkPoint rounding = roundingSpec.halfAxisSampleFreq;
-    matrix.postTranslate(rounding.x(), rounding.y());
-    matrix.mapPoints(mappedPositions, positions, n);
-
-    SkIPoint mask = roundingSpec.ignorePositionMask;
-
-    for (int i = 0; i < n; i++) {
-        SkFixed subX = SkScalarToFixed(mappedPositions[i].x()) & mask.x(),
-                subY = SkScalarToFixed(mappedPositions[i].y()) & mask.y();
-        results[i] = SkPackedGlyphID{glyphIDs[i], subX, subY};
-    }
-
-    return SkSpan<const SkPackedGlyphID>{results, SkTo<size_t>(n)};
-}
-
-SkSpan<const SkPackedGlyphID> SkGlyphRunListPainter::SourceSpacePackedGlyphIDs(
-        const SkPoint& origin,
-        int n,
-        const SkGlyphID* glyphIDs,
-        const SkPoint* positions,
-        SkPoint* mappedPositions,
-        SkPackedGlyphID* results) {
-
-    SkMatrix::MakeTrans(origin.x(), origin.y()).mapPoints(
-            mappedPositions, positions, n);
-
-    SkPackedGlyphID* cursor = results;
-    for (int i = 0; i < n; i++) {
-        *cursor++ = SkPackedGlyphID{glyphIDs[i]};
-    }
-
-    return SkSpan<const SkPackedGlyphID>{results, SkTo<size_t>(n)};
-}
-
 void SkGlyphRunListPainter::drawForBitmapDevice(
         const SkGlyphRunList& glyphRunList, const SkMatrix& deviceMatrix,
         const BitmapDevicePainter* bitmapDevice) {
@@ -191,10 +146,9 @@
 void SkGlyphRunListPainter::processARGBFallback(SkScalar maxSourceGlyphDimension,
                                                 const SkPaint& runPaint,
                                                 const SkFont& runFont,
+                                                SkPoint origin,
                                                 const SkMatrix& viewMatrix,
                                                 SkGlyphRunPainterInterface* process) {
-    SkASSERT(!fARGBGlyphsIDs.empty());
-
     // if maxSourceGlyphDimension then no pixels will change.
     if (maxSourceGlyphDimension == 0) { return; }
 
@@ -215,33 +169,18 @@
     // fit in the atlas. If a glyph will not fit in the atlas, then the general transform case is
     // used to render the glyphs.
     if (useDeviceCache) {
-        // Translate the positions to device space.
-        // TODO: this code is dubious
-        viewMatrix.mapPoints(fARGBPositions.data(), fARGBPositions.size());
-        for (SkPoint& point : fARGBPositions) {
-            point.fX =  SkScalarFloorToScalar(point.fX);
-            point.fY =  SkScalarFloorToScalar(point.fY);
-        }
-
         SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
                 runFont, runPaint, fDeviceProps, fScalerContextFlags, viewMatrix);
 
         SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
 
-        SkPackedGlyphID* cursor = fPackedGlyphIDs;
-        for (auto glyphID : fARGBGlyphsIDs) {
-            *cursor++ = SkPackedGlyphID{glyphID};
-        }
+        fDrawable.startDevice(fRejects.source(), origin, viewMatrix, strike->roundingSpec());
 
-        SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
-                fPackedGlyphIDs,
-                fARGBPositions.data(),
-                fARGBGlyphsIDs.size(),
-                SkStrikeCommon::kSkSideTooBigForAtlas,
-                fGlyphPos);
+        strike->prepareForDrawing(
+                SkStrikeCommon::kSkSideTooBigForAtlas, &fDrawable);
 
         if (process) {
-            process->processDeviceFallback(glyphPosSpan, strikeSpec);
+            process->processDeviceFallback(fDrawable.drawable(), strikeSpec);
         }
 
     } else {
@@ -253,21 +192,14 @@
 
         SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
 
-        SkPackedGlyphID* cursor = fPackedGlyphIDs;
-        for (auto glyphID : fARGBGlyphsIDs) {
-            *cursor++ = SkPackedGlyphID{glyphID};
-        }
+        fDrawable.startSource(fRejects.source(), origin);
 
-        auto glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
-                fPackedGlyphIDs,
-                fARGBPositions.data(),
-                fARGBGlyphsIDs.size(),
-                SkStrikeCommon::kSkSideTooBigForAtlas,
-                fGlyphPos);
+        strike->prepareForDrawing(
+                SkStrikeCommon::kSkSideTooBigForAtlas, &fDrawable);
 
         if (process) {
             process->processSourceFallback(
-                    glyphPosSpan,
+                    fDrawable.drawable(),
                     strikeSpec,
                     viewMatrix.hasPerspective());
         }
@@ -284,21 +216,14 @@
 
     SkPoint origin = glyphRunList.origin();
     const SkPaint& runPaint = glyphRunList.paint();
+    ScopedBuffers _ = this->ensureBuffers(glyphRunList);
 
     for (const auto& glyphRun : glyphRunList) {
-        SkScalar maxFallbackDimension{-SK_ScalarInfinity};
-        ScopedBuffers _ = this->ensureBuffers(glyphRun);
-
-        auto addFallback = [this, &maxFallbackDimension]
-                (const SkGlyph& glyph, SkPoint sourcePosition) {
-            maxFallbackDimension = std::max(maxFallbackDimension,
-                                            SkIntToScalar(glyph.maxDimension()));
-            fARGBGlyphsIDs.push_back(glyph.getGlyphID());
-            fARGBPositions.push_back(sourcePosition);
-        };
-
+        fRejects.setSource(glyphRun.source());
+        fPaths.clear();
         const SkFont& runFont = glyphRun.font();
 
+
         bool useSDFT = GrTextContext::CanDrawAsDistanceFields(
                 runPaint, runFont, viewMatrix, props, contextSupportsDistanceFieldText, options);
         if (process) {
@@ -314,42 +239,37 @@
 
             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
 
-            auto packedGlyphIDs = SourceSpacePackedGlyphIDs(
-                    origin,
-                    glyphRun.runSize(),
-                    glyphRun.glyphsIDs().data(),
-                    glyphRun.positions().data(),
-                    fPositions,
-                    fPackedGlyphIDs);
+            fDrawable.startSource(fRejects.source(), origin);
+            strike->prepareForDrawing(SkStrikeCommon::kSkSideTooBigForAtlas, &fDrawable);
 
-            SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
-                    packedGlyphIDs.data(),
-                    fPositions,
-                    glyphRun.runSize(),
-                    SkStrikeCommon::kSkSideTooBigForAtlas,
-                    fGlyphPos);
-
-            size_t glyphsWithMaskCount = 0;
-            for (const SkGlyphPos& glyphPos : glyphPosSpan) {
-                const SkGlyph& glyph = *glyphPos.glyph;
-                SkPoint position = glyphPos.position;
+            fDrawable.flipDrawableToInput();
+            for (auto t : SkMakeEnumerate(fDrawable.input())) {
+                size_t i; SkGlyphVariant glyphVariant; SkPoint pos;
+                std::forward_as_tuple(i, std::tie(glyphVariant, pos)) = t;
+                const SkGlyph& glyph = *glyphVariant.glyph();
 
                 // The SDF scaler context system ensures that a glyph is empty, kSDF_Format, or
                 // kARGB32_Format. The following if statements use this assumption.
-                SkASSERT(glyph.maskFormat() == SkMask::kSDF_Format || glyph.isColor());
+                SkASSERT(glyph.maskFormat() == SkMask::kSDF_Format
+                         || glyph.isColor()
+                         || glyph.isEmpty());
 
-                if (SkStrikeForGPU::CanDrawAsSDFT(glyph)) {
-                    // SDF mask will work.
-                    fGlyphPos[glyphsWithMaskCount++] = glyphPos;
-                } else if (SkStrikeForGPU::CanDrawAsPath(glyph)) {
-                    // If not color but too big, use a path.
-                    fPaths.push_back(glyphPos);
-                } else {
-                    // If no path, or it is color, then fallback.
-                    addFallback(glyph, position);
+                if (!glyph.isEmpty()) {
+                    if (SkStrikeForGPU::CanDrawAsSDFT(glyph)) {
+                        // SDF mask will work.
+                        fDrawable.push_back(i);
+                    } else if (SkStrikeForGPU::CanDrawAsPath(glyph)) {
+                        // If not color but too big, use a path.
+                        fPaths.push_back(SkGlyphPos{i, &glyph, pos});
+                    } else {
+                        // If no path, or it is color, then fallback.
+                        fRejects.reject(i, glyph.maxDimension());
+                    }
                 }
             }
 
+            fRejects.flipRejectsToSource();
+
             if (process) {
                 bool hasWCoord =
                         viewMatrix.hasPerspective() || options.fDistanceFieldVerticesAlwaysHaveW;
@@ -357,7 +277,7 @@
                 // processSourceSDFT must be called even if there are no glyphs to make sure runs
                 // are set correctly.
                 process->processSourceSDFT(
-                        SkSpan<const SkGlyphPos>{fGlyphPos, glyphsWithMaskCount},
+                        fDrawable.drawable(),
                         strikeSpec,
                         runFont,
                         minScale,
@@ -365,16 +285,15 @@
                         hasWCoord);
 
                 if (!fPaths.empty()) {
-                    process->processSourcePaths(
-                            SkMakeSpan(fPaths),
-                            strikeSpec);
+                    process->processSourcePaths(SkMakeSpan(fPaths), strikeSpec);
                 }
             }
 
             // fGlyphPos will be reused here.
-            if (!fARGBGlyphsIDs.empty()) {
-                this->processARGBFallback(maxFallbackDimension * strikeSpec.strikeToSourceRatio(),
-                                          runPaint, runFont, viewMatrix, process);
+            if (!fRejects.source().empty()) {
+                this->processARGBFallback(
+                        fRejects.rejectedMaxDimension() * strikeSpec.strikeToSourceRatio(),
+                        runPaint, runFont, origin, viewMatrix, process);
             }
         } else if (SkStrikeSpec::ShouldDrawAsPath(runPaint, runFont, viewMatrix)) {
             SkStrikeSpec strikeSpec = SkStrikeSpec::MakePath(
@@ -382,46 +301,37 @@
 
             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
 
-            auto packedGlyphIDs = SourceSpacePackedGlyphIDs(
-                    origin,
-                    glyphRun.runSize(),
-                    glyphRun.glyphsIDs().data(),
-                    glyphRun.positions().data(),
-                    fPositions,
-                    fPackedGlyphIDs);
+            fDrawable.startSource(fRejects.source(), origin);
+            strike->prepareForDrawing(0, &fDrawable);
 
-            SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
-                    packedGlyphIDs.data(),
-                    fPositions,
-                    glyphRun.runSize(),
-                    0,
-                    fGlyphPos);
-
-            // As opposed to SDF and mask, path handling puts paths in fGlyphPos instead of fPaths.
-            size_t glyphsWithPathCount = 0;
-            for (const SkGlyphPos& glyphPos : glyphPosSpan) {
-                const SkGlyph& glyph = *glyphPos.glyph;
-                SkPoint position = glyphPos.position;
-                if (SkStrikeForGPU::CanDrawAsPath(glyph)) {
-                    // Place paths in fGlyphPos
-                    fGlyphPos[glyphsWithPathCount++] = glyphPos;
-                } else {
-                    addFallback(glyph, position);
+            fDrawable.flipDrawableToInput();
+            for (auto t : SkMakeEnumerate(fDrawable.input())) {
+                size_t i; SkGlyphVariant glyphVariant; SkPoint pos;
+                std::forward_as_tuple(i, std::tie(glyphVariant, pos)) = t;
+                const SkGlyph& glyph = *glyphVariant.glyph();
+                if (!glyph.isEmpty()) {
+                    if (SkStrikeForGPU::CanDrawAsPath(glyph)) {
+                        // Place paths in fGlyphPos
+                        fPaths.push_back(SkGlyphPos{i, &glyph, pos});
+                    } else {
+                        fRejects.reject(i, glyph.maxDimension());
+                    }
                 }
             }
 
+            fRejects.flipRejectsToSource();
+
             if (process) {
                 // processSourcePaths must be called even if there are no glyphs to make sure runs
                 // are set correctly.
-                process->processSourcePaths(
-                        SkSpan<const SkGlyphPos>{fGlyphPos, glyphsWithPathCount},
-                        strikeSpec);
+                process->processSourcePaths(SkMakeSpan(fPaths), strikeSpec);
             }
 
             // fGlyphPos will be reused here.
-            if (!fARGBGlyphsIDs.empty()) {
-                this->processARGBFallback(maxFallbackDimension * strikeSpec.strikeToSourceRatio(),
-                                          runPaint, runFont, viewMatrix, process);
+            if (!fRejects.source().empty()) {
+                this->processARGBFallback(
+                        fRejects.rejectedMaxDimension() * strikeSpec.strikeToSourceRatio(),
+                        runPaint, runFont, origin, viewMatrix, process);
             }
         } else {
             SkStrikeSpec strikeSpec =
@@ -430,56 +340,47 @@
 
             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
 
-            auto packedGlyphIDs = DeviceSpacePackedGlyphIDs(
-                    strike->roundingSpec(),
-                    viewMatrix,
-                    origin,
-                    glyphRun.runSize(),
-                    glyphRun.glyphsIDs().data(),
-                    glyphRun.positions().data(),
-                    fPositions,
-                    fPackedGlyphIDs);
+            fDrawable.startDevice(fRejects.source(), origin, viewMatrix, strike->roundingSpec());
 
-            // Lookup all the glyphs from the cache. Strip empty glyphs.
-            SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
-                    packedGlyphIDs.data(),
-                    fPositions,
-                    glyphRun.runSize(),
-                    SkStrikeCommon::kSkSideTooBigForAtlas,
-                    fGlyphPos);
+            strike->prepareForDrawing(
+                    SkStrikeCommon::kSkSideTooBigForAtlas, &fDrawable);
 
             // Sort glyphs into the three bins: mask (fGlyphPos), path (fPaths), and fallback.
-            size_t glyphsWithMaskCount = 0;
-            for (const SkGlyphPos& glyphPos : glyphPosSpan) {
-                const SkGlyph& glyph = *glyphPos.glyph;
-                const SkPoint position = glyphPos.position;
-
-                // Does the glyph have work to do or is the code able to position the glyph?
-                if (!SkScalarsAreFinite(position.x(), position.y())) {
-                    // Do nothing;
-                } else if (SkStrikeForGPU::CanDrawAsMask(glyph)) {
-                    fGlyphPos[glyphsWithMaskCount++] = glyphPos;
-                } else if (SkStrikeForGPU::CanDrawAsPath(glyph)) {
-                    fPaths.push_back(glyphPos);
-                } else {
-                    addFallback(glyph, origin + glyphRun.positions()[glyphPos.index]);
+            fDrawable.flipDrawableToInput();
+            for (auto t : SkMakeEnumerate(fDrawable.input())) {
+                size_t i; SkGlyphVariant glyphVariant; SkPoint pos;
+                std::forward_as_tuple(i, std::tie(glyphVariant, pos)) = t;
+                SkGlyph* glyph = glyphVariant.glyph();
+                if (!glyph->isEmpty()) {
+                    // Does the glyph have work to do or is the code able to position the glyph?
+                    if (!SkScalarsAreFinite(pos.x(), pos.y())) {
+                        // Do nothing;
+                    } else if (SkStrikeForGPU::CanDrawAsMask(*glyph)) {
+                        fDrawable.push_back(i);
+                    } else if (SkStrikeForGPU::CanDrawAsPath(*glyph)) {
+                        fPaths.push_back(SkGlyphPos{i, glyph, pos});
+                    } else {
+                        fRejects.reject(i, glyph->maxDimension());
+                    }
                 }
             }
 
+            fRejects.flipRejectsToSource();
+
             if (process) {
                 // processDeviceMasks must be called even if there are no glyphs to make sure runs
                 // are set correctly.
-                process->processDeviceMasks(
-                        SkSpan<const SkGlyphPos>{fGlyphPos, glyphsWithMaskCount}, strikeSpec);
+                process->processDeviceMasks(fDrawable.drawable(), strikeSpec);
                 if (!fPaths.empty()) {
                     process->processDevicePaths(SkMakeSpan(fPaths));
                 }
             }
 
             // fGlyphPos will be reused here.
-            if (!fARGBGlyphsIDs.empty()) {
-                this->processARGBFallback(maxFallbackDimension / viewMatrix.getMaxScale(),
-                                          runPaint, runFont, viewMatrix, process);
+            if (!fRejects.source().empty()) {
+                this->processARGBFallback(
+                        fRejects.rejectedMaxDimension() / viewMatrix.getMaxScale(),
+                        runPaint, runFont, origin, viewMatrix, process);
             }
         }  // Mask case
     }  // For all glyph runs
@@ -729,16 +630,18 @@
     run->setRunFontAntiAlias(glyphRun.font().hasSomeAntiAliasing());
 }
 
-void GrTextBlob::processDeviceMasks(SkSpan<const SkGlyphPos> masks,
+void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                     const SkStrikeSpec& strikeSpec) {
     Run* run = this->currentRun();
     this->setHasBitmap();
     run->setupFont(strikeSpec);
     sk_sp<GrTextStrike> currStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
-    for (const auto& mask : masks) {
-        SkPoint pt{SkScalarFloorToScalar(mask.position.fX),
-                   SkScalarFloorToScalar(mask.position.fY)};
-        run->appendDeviceSpaceGlyph(currStrike, *mask.glyph, pt);
+    for (auto t : drawables) {
+        SkGlyph* glyph; SkPoint pos;
+        std::tie(glyph, pos) = t;
+        SkPoint pt{SkScalarFloorToScalar(pos.fX),
+                   SkScalarFloorToScalar(pos.fY)};
+        run->appendDeviceSpaceGlyph(currStrike, *glyph, pt);
     }
 }
 
@@ -768,7 +671,7 @@
     }
 }
 
-void GrTextBlob::processSourceSDFT(SkSpan<const SkGlyphPos> masks,
+void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                    const SkStrikeSpec& strikeSpec,
                                    const SkFont& runFont,
                                    SkScalar minScale,
@@ -783,13 +686,14 @@
     this->setMinAndMaxScale(minScale, maxScale);
     run->setupFont(strikeSpec);
     sk_sp<GrTextStrike> currStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
-    for (const auto& mask : masks) {
-        run->appendSourceSpaceGlyph(
-                currStrike, *mask.glyph, mask.position, strikeSpec.strikeToSourceRatio());
+    for (auto t : drawables) {
+        SkGlyph* glyph; SkPoint pos;
+        std::tie(glyph, pos) = t;
+        run->appendSourceSpaceGlyph(currStrike, *glyph, pos, strikeSpec.strikeToSourceRatio());
     }
 }
 
-void GrTextBlob::processSourceFallback(SkSpan<const SkGlyphPos> masks,
+void GrTextBlob::processSourceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                        const SkStrikeSpec& strikeSpec,
                                        bool hasW) {
     Run* run = this->currentRun();
@@ -801,13 +705,14 @@
 
     this->setHasBitmap();
     run->setupFont(strikeSpec);
-    for (const auto& mask : masks) {
-        run->appendSourceSpaceGlyph
-                (grStrike, *mask.glyph, mask.position, strikeSpec.strikeToSourceRatio());
+    for (auto t : drawables) {
+        SkGlyph* glyph; SkPoint pos;
+        std::tie(glyph, pos) = t;
+        run->appendSourceSpaceGlyph(grStrike, *glyph, pos, strikeSpec.strikeToSourceRatio());
     }
 }
 
-void GrTextBlob::processDeviceFallback(SkSpan<const SkGlyphPos> masks,
+void GrTextBlob::processDeviceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                        const SkStrikeSpec& strikeSpec) {
     Run* run = this->currentRun();
     this->setHasBitmap();
@@ -815,8 +720,12 @@
     auto subRun = run->initARGBFallback();
     run->setupFont(strikeSpec);
     subRun->setStrike(grStrike);
-    for (const auto& mask : masks) {
-        run->appendDeviceSpaceGlyph(grStrike, *mask.glyph, mask.position);
+    for (auto t : drawables) {
+        SkGlyph* glyph; SkPoint pos;
+        std::tie(glyph, pos) = t;
+        SkPoint pt{SkScalarFloorToScalar(pos.fX),
+                   SkScalarFloorToScalar(pos.fY)};
+        run->appendDeviceSpaceGlyph(grStrike, *glyph, pt);
     }
 }
 
@@ -876,27 +785,17 @@
     fPainter->fDrawable.ensureSize(size);
     if (fPainter->fMaxRunSize < size) {
         fPainter->fMaxRunSize = size;
-
-        fPainter->fPositions.reset(size);
-        fPainter->fPackedGlyphIDs.reset(size);
-        fPainter->fGlyphPos.reset(size);
     }
 }
 
 SkGlyphRunListPainter::ScopedBuffers::~ScopedBuffers() {
     fPainter->fDrawable.reset();
+    fPainter->fRejects.reset();
     fPainter->fPaths.clear();
-    fPainter->fARGBGlyphsIDs.clear();
-    fPainter->fARGBPositions.clear();
 
     if (fPainter->fMaxRunSize > 200) {
         fPainter->fMaxRunSize = 0;
-        fPainter->fPositions.reset();
-        fPainter->fPackedGlyphIDs.reset();
-        fPainter->fGlyphPos.reset();
         fPainter->fPaths.shrink_to_fit();
-        fPainter->fARGBGlyphsIDs.shrink_to_fit();
-        fPainter->fARGBPositions.shrink_to_fit();
     }
 }
 
diff --git a/src/core/SkGlyphRunPainter.h b/src/core/SkGlyphRunPainter.h
index a5aff15..5c27791 100644
--- a/src/core/SkGlyphRunPainter.h
+++ b/src/core/SkGlyphRunPainter.h
@@ -113,27 +113,10 @@
     void processARGBFallback(SkScalar maxSourceGlyphDimension,
                              const SkPaint& runPaint,
                              const SkFont& runFont,
+                             SkPoint origin,
                              const SkMatrix& viewMatrix,
                              SkGlyphRunPainterInterface* process);
 
-    static SkSpan<const SkPackedGlyphID> DeviceSpacePackedGlyphIDs(
-            const SkGlyphPositionRoundingSpec& roundingSpec,
-            const SkMatrix& viewMatrix,
-            const SkPoint& origin,
-            int n,
-            const SkGlyphID* glyphIDs,
-            const SkPoint* positions,
-            SkPoint* mappedPositions,
-            SkPackedGlyphID* results);
-
-    static SkSpan<const SkPackedGlyphID> SourceSpacePackedGlyphIDs(
-            const SkPoint& origin,
-            int n,
-            const SkGlyphID* glyphIDs,
-            const SkPoint* positions,
-            SkPoint* mappedPositions,
-            SkPackedGlyphID* results);
-
     // The props as on the actual device.
     const SkSurfaceProps fDeviceProps;
     // The props for when the bitmap device can't draw LCD text.
@@ -144,17 +127,11 @@
     SkStrikeForGPUCacheInterface* const fStrikeCache;
 
     SkDrawableGlyphBuffer fDrawable;
+    SkSourceGlyphBuffer fRejects;
 
     size_t fMaxRunSize{0};
-    SkAutoTMalloc<SkPoint> fPositions;
-    SkAutoTMalloc<SkPackedGlyphID> fPackedGlyphIDs;
-    SkAutoTMalloc<SkGlyphPos> fGlyphPos;
 
     std::vector<SkGlyphPos> fPaths;
-
-    // Vectors for tracking ARGB fallback information.
-    std::vector<SkGlyphID> fARGBGlyphsIDs;
-    std::vector<SkPoint>   fARGBPositions;
 };
 
 // SkGlyphRunPainterInterface are all the ways that Ganesh generates glyphs. The first
@@ -175,7 +152,7 @@
 
     virtual void startRun(const SkGlyphRun& glyphRun, bool useSDFT) = 0;
 
-    virtual void processDeviceMasks(SkSpan<const SkGlyphPos> masks,
+    virtual void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                     const SkStrikeSpec& strikeSpec) = 0;
 
     virtual void processSourcePaths(SkSpan<const SkGlyphPos> paths,
@@ -183,18 +160,18 @@
 
     virtual void processDevicePaths(SkSpan<const SkGlyphPos> paths) = 0;
 
-    virtual void processSourceSDFT(SkSpan<const SkGlyphPos> masks,
+    virtual void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                    const SkStrikeSpec& strikeSpec,
                                    const SkFont& runFont,
                                    SkScalar minScale,
                                    SkScalar maxScale,
                                    bool hasWCoord) = 0;
 
-    virtual void processSourceFallback(SkSpan<const SkGlyphPos> masks,
+    virtual void processSourceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                        const SkStrikeSpec& strikeSpec,
                                        bool hasW) = 0;
 
-    virtual void processDeviceFallback(SkSpan<const SkGlyphPos> masks,
+    virtual void processDeviceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                        const SkStrikeSpec& strikeSpec) = 0;
 
 };
diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp
index 3f6a8ac..021a476 100644
--- a/src/core/SkRemoteGlyphCache.cpp
+++ b/src/core/SkRemoteGlyphCache.cpp
@@ -15,6 +15,7 @@
 
 #include "src/core/SkDevice.h"
 #include "src/core/SkDraw.h"
+#include "src/core/SkEnumerate.h"
 #include "src/core/SkGlyphRun.h"
 #include "src/core/SkStrike.h"
 #include "src/core/SkStrikeCache.h"
@@ -207,12 +208,7 @@
         return fRoundingSpec;
     }
 
-    SkSpan<const SkGlyphPos>
-    prepareForDrawingRemoveEmpty(
-            const SkPackedGlyphID packedGlyphIDs[],
-            const SkPoint positions[], size_t n,
-            int maxDimension,
-            SkGlyphPos results[]) override;
+    void prepareForDrawing(int maxDimension, SkDrawableGlyphBuffer* drawables) override;
 
     void onAboutToExitScope() override {}
 
@@ -656,30 +652,21 @@
 }
 
 
-// Be sure to read and understand the comment for prepareForDrawingRemoveEmpty in
+// Be sure to read and understand the comment for prepareForDrawing in
 // SkStrikeForGPU.h before working on this code.
-SkSpan<const SkGlyphPos>
-SkStrikeServer::RemoteStrike::prepareForDrawingRemoveEmpty(
-        const SkPackedGlyphID packedGlyphIDs[],
-        const SkPoint positions[], size_t n,
-        int maxDimension,
-        SkGlyphPos results[]) {
-    size_t drawableGlyphCount = 0;
-    for (size_t i = 0; i < n; i++) {
-        SkPoint glyphPos = positions[i];
-
-        // Check the cache for the glyph.
-        SkGlyph* glyphPtr = fGlyphMap.findOrNull(packedGlyphIDs[i]);
-
+void SkStrikeServer::RemoteStrike::prepareForDrawing(
+        int maxDimension, SkDrawableGlyphBuffer* drawables) {
+    for (auto t : SkMakeEnumerate(drawables->input())) {
+        size_t i; SkGlyphVariant packedID; SkPoint pos;
+        std::forward_as_tuple(i, std::tie(packedID, pos)) = t;
+        SkGlyph* glyphPtr = fGlyphMap.findOrNull(packedID);
         // Has this glyph ever been seen before?
         if (glyphPtr == nullptr) {
-
             // Never seen before. Make a new glyph.
-            glyphPtr = fAlloc.make<SkGlyph>(packedGlyphIDs[i]);
+            glyphPtr = fAlloc.make<SkGlyph>(packedID);
             fGlyphMap.set(glyphPtr);
             this->ensureScalerContext();
             fContext->getMetrics(glyphPtr);
-
             if (glyphPtr->maxDimension() <= maxDimension) {
                 // do nothing
             } else if (!glyphPtr->isColor()) {
@@ -695,20 +682,14 @@
                 // This will be handled by the fallback strike.
                 SkASSERT(glyphPtr->maxDimension() > maxDimension && glyphPtr->isColor());
             }
-
             // Make sure to send the glyph to the GPU because we always send the image for a glyph.
-            fCachedGlyphImages.add(packedGlyphIDs[i]);
-            fPendingGlyphImages.push_back(packedGlyphIDs[i]);
+            fCachedGlyphImages.add(packedID);
+            fPendingGlyphImages.push_back(packedID);
         }
 
-        // Each non-empty glyph needs to be added as per the contract for
-        // prepareForDrawingRemoveEmpty.
         // TODO(herb): Change the code to only send the glyphs for fallback?
-        if (!glyphPtr->isEmpty()) {
-            results[drawableGlyphCount++] = {i, glyphPtr, glyphPos};
-        }
+        drawables->push_back(glyphPtr, i);
     }
-    return SkMakeSpan(results, drawableGlyphCount);
 }
 
 // SkStrikeClient ----------------------------------------------------------------------------------
diff --git a/src/core/SkStrike.cpp b/src/core/SkStrike.cpp
index 1c444c8..0241239 100644
--- a/src/core/SkStrike.cpp
+++ b/src/core/SkStrike.cpp
@@ -213,33 +213,24 @@
 
 // N.B. This glyphMetrics call culls all the glyphs which will not display based on a non-finite
 // position or that there are no mask pixels.
-SkSpan<const SkGlyphPos>
-SkStrike::prepareForDrawingRemoveEmpty(const SkPackedGlyphID packedGlyphIDs[],
-                                       const SkPoint positions[],
-                                       size_t n,
-                                       int maxDimension,
-                                       SkGlyphPos results[]) {
-    size_t drawableGlyphCount = 0;
-    for (size_t i = 0; i < n; i++) {
-        SkPoint pos = positions[i];
+void SkStrike::prepareForDrawing(int maxDimension, SkDrawableGlyphBuffer* drawables) {
+    for (auto t : SkMakeEnumerate(drawables->input())) {
+        size_t i; SkGlyphVariant packedID; SkPoint pos;
+        std::forward_as_tuple(i, std::tie(packedID, pos)) = t;
         if (SkScalarsAreFinite(pos.x(), pos.y())) {
-            SkGlyph* glyphPtr = this->glyph(packedGlyphIDs[i]);
-            if (!glyphPtr->isEmpty()) {
-                results[drawableGlyphCount++] = {i, glyphPtr, pos};
-                if (glyphPtr->maxDimension() <= maxDimension) {
-                    // The glyph fits. Prepare image later.
-                } else if (!glyphPtr->isColor()) {
-                    // The out of atlas glyph is not color so we can draw it using paths.
-                    this->preparePath(glyphPtr);
-                } else {
-                    // This will be handled by the fallback strike.
-                    SkASSERT(glyphPtr->maxDimension() > maxDimension && glyphPtr->isColor());
-                }
+            SkGlyph* glyphPtr = this->glyph(packedID);
+            drawables->push_back(glyphPtr, i);
+            if (glyphPtr->maxDimension() <= maxDimension) {
+                // The glyph fits. Prepare image later.
+            } else if (!glyphPtr->isColor()) {
+                // The out of atlas glyph is not color so we can draw it using paths.
+                this->preparePath(glyphPtr);
+            } else {
+                // This will be handled by the fallback strike.
+                SkASSERT(glyphPtr->maxDimension() > maxDimension && glyphPtr->isColor());
             }
         }
     }
-
-    return SkSpan<const SkGlyphPos>{results, drawableGlyphCount};
 }
 
 void SkStrike::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
diff --git a/src/core/SkStrike.h b/src/core/SkStrike.h
index f893835..469f44c 100644
--- a/src/core/SkStrike.h
+++ b/src/core/SkStrike.h
@@ -115,11 +115,8 @@
     void prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* drawables);
 
     void prepareForDrawingPathsCPU(SkDrawableGlyphBuffer* drawables);
-    SkSpan<const SkGlyphPos> prepareForDrawingRemoveEmpty(const SkPackedGlyphID packedGlyphIDs[],
-                                                          const SkPoint positions[],
-                                                          size_t n,
-                                                          int maxDimension,
-                                                          SkGlyphPos results[]) override;
+    void  prepareForDrawing(
+            int maxDimension, SkDrawableGlyphBuffer* drawables)override;
 
     void onAboutToExitScope() override;
 
diff --git a/src/core/SkStrikeCache.cpp b/src/core/SkStrikeCache.cpp
index 1bcbdd4..d5b0591 100644
--- a/src/core/SkStrikeCache.cpp
+++ b/src/core/SkStrikeCache.cpp
@@ -32,17 +32,8 @@
         return fStrike.roundingSpec();
     }
 
-    SkSpan<const SkGlyphPos>
-    prepareForDrawingRemoveEmpty(const SkPackedGlyphID packedGlyphIDs[],
-                                 const SkPoint positions[],
-                                 size_t n,
-                                 int maxDimension,
-                                 SkGlyphPos results[]) override {
-        return fStrike.prepareForDrawingRemoveEmpty(packedGlyphIDs,
-                                                    positions,
-                                                    n,
-                                                    maxDimension,
-                                                    results);
+    void prepareForDrawing(int maxDimension, SkDrawableGlyphBuffer* drawbles) override {
+        fStrike.prepareForDrawing(maxDimension, drawbles);
     }
 
     const SkDescriptor& getDescriptor() const override {
diff --git a/src/core/SkStrikeForGPU.h b/src/core/SkStrikeForGPU.h
index a72ea35..bb46b30 100644
--- a/src/core/SkStrikeForGPU.h
+++ b/src/core/SkStrikeForGPU.h
@@ -17,6 +17,7 @@
 #include <memory>
 
 class SkDescriptor;
+class SkDrawableGlyphBuffer;
 class SkGlyph;
 class SkMaskFilter;
 class SkPathEffect;
@@ -30,28 +31,19 @@
     SkPoint position;
 };
 
-struct SkPathPos {
-    const SkPath* path;
-    SkPoint position;
-};
-
 class SkStrikeForGPU {
 public:
     virtual ~SkStrikeForGPU() = default;
     virtual const SkDescriptor& getDescriptor() const = 0;
 
-    // prepareForDrawingRemoveEmpty takes glyphIDs, and position, and returns a list of SkGlyphs
-    // and positions where all the data to draw the glyph has been created. The maxDimension
+    // prepareForDrawing takes glyphIDs, and position in the form of
+    // SkDrawableGlyphBuffer, and returns a list of SkGlyphs and positions where all the data to
+    // draw the glyph has been created by adjusting the SkDrawableGlyphBuffer. The maxDimension
     // parameter determines if the mask/SDF version will be created, or an alternate drawing
     // format should be used. For path-only drawing set maxDimension to 0, and for bitmap-device
     // drawing (where there is no upper limit to the glyph in the cache) use INT_MAX.
-    // prepareForDrawingRemoveEmpty should remove all empty glyphs from the returned span.
-    virtual SkSpan<const SkGlyphPos>
-    prepareForDrawingRemoveEmpty(const SkPackedGlyphID packedGlyphIDs[],
-                                 const SkPoint positions[],
-                                 size_t n,
-                                 int maxDimension,
-                                 SkGlyphPos results[]) = 0;
+    virtual void
+    prepareForDrawing(int maxGlyphDimension, SkDrawableGlyphBuffer* drawables) = 0;
 
     virtual const SkGlyphPositionRoundingSpec& roundingSpec() const = 0;
 
diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h
index ddea88d..4405f36 100644
--- a/src/gpu/text/GrTextBlob.h
+++ b/src/gpu/text/GrTextBlob.h
@@ -517,7 +517,7 @@
 
     void startRun(const SkGlyphRun& glyphRun, bool useSDFT) override;
 
-    void processDeviceMasks(SkSpan<const SkGlyphPos> masks,
+    void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                             const SkStrikeSpec& strikeSpec) override;
 
     void processSourcePaths(SkSpan<const SkGlyphPos> paths,
@@ -525,18 +525,18 @@
 
     void processDevicePaths(SkSpan<const SkGlyphPos> paths) override;
 
-    void processSourceSDFT(SkSpan<const SkGlyphPos> masks,
+    void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                            const SkStrikeSpec& strikeSpec,
                            const SkFont& runFont,
                            SkScalar minScale,
                            SkScalar maxScale,
                            bool hasWCoord) override;
 
-    void processSourceFallback(SkSpan<const SkGlyphPos> masks,
+    void processSourceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                const SkStrikeSpec& strikeSpec,
                                bool hasW) override;
 
-    void processDeviceFallback(SkSpan<const SkGlyphPos> masks,
+    void processDeviceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                const SkStrikeSpec& strikeSpec) override;
 
     struct StrokeInfo {