fonts: Add support for distance field text to font remoting.

R=jvanverth@google.com, herb@google.com

Bug: skia:7913
Change-Id: Id3f5b3e75005be9a7234df774268359b406c99a8
Reviewed-on: https://skia-review.googlesource.com/128970
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Khusal Sagar <khushalsagar@chromium.org>
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 6a52c5c..525e5d7 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -282,6 +282,8 @@
     // Chrome is using this!
     void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
 
+    bool supportsDistanceFieldText() const;
+
 protected:
     GrContext(GrBackend, int32_t id = SK_InvalidGenID);
 
diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp
index c7f1f44..d5e96fc 100644
--- a/src/core/SkRemoteGlyphCache.cpp
+++ b/src/core/SkRemoteGlyphCache.cpp
@@ -23,6 +23,7 @@
 
 #if SK_SUPPORT_GPU
 #include "GrDrawOpAtlas.h"
+#include "text/GrAtlasTextContext.h"
 #endif
 
 static SkDescriptor* auto_descriptor_from_desc(const SkDescriptor* source_desc,
@@ -188,14 +189,18 @@
 };
 
 // -- SkTextBlobCacheDiffCanvas -------------------------------------------------------------------
+SkTextBlobCacheDiffCanvas::Settings::Settings() = default;
+SkTextBlobCacheDiffCanvas::Settings::~Settings() = default;
+
 SkTextBlobCacheDiffCanvas::SkTextBlobCacheDiffCanvas(int width, int height,
                                                      const SkMatrix& deviceMatrix,
                                                      const SkSurfaceProps& props,
-                                                     SkStrikeServer* strikeSever)
+                                                     SkStrikeServer* strikeSever, Settings settings)
         : SkNoDrawCanvas{sk_make_sp<TrackLayerDevice>(SkIRect::MakeWH(width, height), props)}
         , fDeviceMatrix{deviceMatrix}
         , fSurfaceProps{props}
-        , fStrikeServer{strikeSever} {
+        , fStrikeServer{strikeSever}
+        , fSettings{settings} {
     SkASSERT(fStrikeServer);
 }
 
@@ -274,6 +279,23 @@
     runMatrix.preTranslate(position.x(), position.y());
     runMatrix.preTranslate(it.offset().x(), it.offset().y());
 
+#if SK_SUPPORT_GPU
+    GrAtlasTextContext::Options options;
+    options.fMinDistanceFieldFontSize = fSettings.fMinDistanceFieldFontSize;
+    options.fMaxDistanceFieldFontSize = fSettings.fMaxDistanceFieldFontSize;
+    GrAtlasTextContext::SanitizeOptions(&options);
+    if (GrAtlasTextContext::CanDrawAsDistanceFields(runPaint, runMatrix, fSurfaceProps,
+                                                    fSettings.fContextSupportsDistanceFieldText,
+                                                    options)) {
+        SkScalar textRatio;
+        SkPaint dfPaint(runPaint);
+        SkScalerContextFlags flags;
+        GrAtlasTextContext::InitDistanceFieldPaint(nullptr, &dfPaint, runMatrix, options,
+                                                   &textRatio, &flags);
+        this->processGlyphRunForDFT(it, dfPaint, flags);
+    }
+#endif
+
     // If the matrix has perspective, we fall back to using distance field text or paths.
     // TODO: Add distance field text support, and FallbackTextHelper logic from GrAtlasTextContext.
     if (SkDraw::ShouldDrawTextAsPaths(runPaint, runMatrix)) {
@@ -334,9 +356,11 @@
 
     SkScalerContextRec deviceSpecificRec;
     SkScalerContextEffects effects;
-    auto* glyphCacheState = static_cast<SkStrikeServer*>(fStrikeServer)
-                                    ->getOrCreateCache(runPaint, &fSurfaceProps, &runMatrix,
-                                                       &deviceSpecificRec, &effects);
+    auto* glyphCacheState =
+            static_cast<SkStrikeServer*>(fStrikeServer)
+                    ->getOrCreateCache(runPaint, &fSurfaceProps, &runMatrix,
+                                       SkScalerContextFlags::kFakeGammaAndBoostContrast,
+                                       &deviceSpecificRec, &effects);
     SkASSERT(glyphCacheState);
 
     const bool asPath = false;
@@ -371,9 +395,11 @@
 
     SkScalerContextRec deviceSpecificRec;
     SkScalerContextEffects effects;
-    auto* glyphCacheState = static_cast<SkStrikeServer*>(fStrikeServer)
-                                    ->getOrCreateCache(pathPaint, &fSurfaceProps, nullptr,
-                                                       &deviceSpecificRec, &effects);
+    auto* glyphCacheState =
+            static_cast<SkStrikeServer*>(fStrikeServer)
+                    ->getOrCreateCache(pathPaint, &fSurfaceProps, nullptr,
+                                       SkScalerContextFlags::kFakeGammaAndBoostContrast,
+                                       &deviceSpecificRec, &effects);
 
     const bool asPath = true;
     const SkIPoint subPixelPos{0, 0};
@@ -386,6 +412,26 @@
     }
 }
 
+void SkTextBlobCacheDiffCanvas::processGlyphRunForDFT(const SkTextBlobRunIterator& it,
+                                                      const SkPaint& runPaint,
+                                                      SkScalerContextFlags flags) {
+    SkScalerContextRec deviceSpecificRec;
+    SkScalerContextEffects effects;
+    auto* glyphCacheState = static_cast<SkStrikeServer*>(fStrikeServer)
+                                    ->getOrCreateCache(runPaint, &fSurfaceProps, nullptr, flags,
+                                                       &deviceSpecificRec, &effects);
+
+    const bool asPath = false;
+    const SkIPoint subPixelPos{0, 0};
+    const uint16_t* glyphs = it.glyphs();
+    for (uint32_t index = 0; index < it.glyphCount(); index++) {
+        glyphCacheState->addGlyph(runPaint.getTypeface(),
+                                  effects,
+                                  SkPackedGlyphID(glyphs[index], subPixelPos.x(), subPixelPos.y()),
+                                  asPath);
+    }
+}
+
 struct StrikeSpec {
     StrikeSpec() {}
     StrikeSpec(SkFontID typefaceID_, SkDiscardableHandleId discardableHandleId_)
@@ -451,15 +497,12 @@
         const SkPaint& paint,
         const SkSurfaceProps* props,
         const SkMatrix* matrix,
+        SkScalerContextFlags flags,
         SkScalerContextRec* deviceRec,
         SkScalerContextEffects* effects) {
     SkScalerContextRec keyRec;
-    SkScalerContext::MakeRecAndEffects(paint, props, matrix,
-                                       SkScalerContextFlags::kFakeGammaAndBoostContrast, deviceRec,
-                                       effects, true);
-    SkScalerContext::MakeRecAndEffects(paint, props, matrix,
-                                       SkScalerContextFlags::kFakeGammaAndBoostContrast, &keyRec,
-                                       effects, false);
+    SkScalerContext::MakeRecAndEffects(paint, props, matrix, flags, deviceRec, effects, true);
+    SkScalerContext::MakeRecAndEffects(paint, props, matrix, flags, &keyRec, effects, false);
     TRACE_EVENT1("skia", "RecForDesc", "rec", TRACE_STR_COPY(keyRec.dump().c_str()));
 
     // TODO: possible perf improvement - don't recompute the device desc on cache hit.
diff --git a/src/core/SkRemoteGlyphCache.h b/src/core/SkRemoteGlyphCache.h
index 60d8807..d1119cf 100644
--- a/src/core/SkRemoteGlyphCache.h
+++ b/src/core/SkRemoteGlyphCache.h
@@ -28,6 +28,7 @@
 class SkDescriptor;
 class SkGlyphCache;
 struct SkPackedGlyphID;
+enum SkScalerContextFlags : uint32_t;
 class SkScalerContextRecDescriptor;
 class SkTextBlobRunIterator;
 class SkTypefaceProxy;
@@ -51,8 +52,17 @@
 // which will be serialized and renderered using the SkStrikeClient.
 class SK_API SkTextBlobCacheDiffCanvas : public SkNoDrawCanvas {
 public:
+    struct SK_API Settings {
+        Settings();
+        ~Settings();
+
+        bool fContextSupportsDistanceFieldText = true;
+        SkScalar fMinDistanceFieldFontSize = -1.f;
+        SkScalar fMaxDistanceFieldFontSize = -1.f;
+    };
     SkTextBlobCacheDiffCanvas(int width, int height, const SkMatrix& deviceMatrix,
-                              const SkSurfaceProps& props, SkStrikeServer* strikeserver);
+                              const SkSurfaceProps& props, SkStrikeServer* strikeserver,
+                              Settings settings = Settings());
     ~SkTextBlobCacheDiffCanvas() override;
 
 protected:
@@ -70,10 +80,13 @@
                          const SkTextBlobRunIterator& it,
                          const SkPaint& runPaint);
     void processGlyphRunForPaths(const SkTextBlobRunIterator& it, const SkPaint& runPaint);
+    void processGlyphRunForDFT(const SkTextBlobRunIterator& it, const SkPaint& runPaint,
+                               SkScalerContextFlags flags);
 
     const SkMatrix fDeviceMatrix;
     const SkSurfaceProps fSurfaceProps;
     SkStrikeServer* const fStrikeServer;
+    const Settings fSettings;
 };
 
 using SkDiscardableHandleId = uint32_t;
@@ -161,7 +174,7 @@
     };
 
     SkGlyphCacheState* getOrCreateCache(const SkPaint&, const SkSurfaceProps*, const SkMatrix*,
-                                        SkScalerContextRec* deviceRec,
+                                        SkScalerContextFlags flags, SkScalerContextRec* deviceRec,
                                         SkScalerContextEffects* effects);
 
 private:
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 5be510d..24c28e6 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -1514,6 +1514,10 @@
     return fPMUPMConversionsRoundTrip;
 }
 
+bool GrContext::supportsDistanceFieldText() const {
+    return fCaps->shaderCaps()->supportsDistanceFieldText();
+}
+
 //////////////////////////////////////////////////////////////////////////////
 
 // DDL TODO: remove 'maxResources'
diff --git a/src/gpu/GrShaderCaps.h b/src/gpu/GrShaderCaps.h
index 2f2d277..3e85b6c 100644
--- a/src/gpu/GrShaderCaps.h
+++ b/src/gpu/GrShaderCaps.h
@@ -37,6 +37,8 @@
 
     void dumpJSON(SkJSONWriter*) const;
 
+    bool supportsDistanceFieldText() const { return fShaderDerivativeSupport; }
+
     bool shaderDerivativeSupport() const { return fShaderDerivativeSupport; }
     bool geometryShaderSupport() const { return fGeometryShaderSupport; }
     bool gsInvocationsSupport() const { return fGSInvocationsSupport; }
diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp
index 8c964ba..1f06cfe 100644
--- a/src/gpu/text/GrAtlasTextContext.cpp
+++ b/src/gpu/text/GrAtlasTextContext.cpp
@@ -41,14 +41,8 @@
 #endif
 
 GrAtlasTextContext::GrAtlasTextContext(const Options& options)
-        : fDistanceAdjustTable(new GrDistanceFieldAdjustTable) {
-    fMaxDistanceFieldFontSize = options.fMaxDistanceFieldFontSize < 0.f
-                                        ? kDefaultMaxDistanceFieldFontSize
-                                        : options.fMaxDistanceFieldFontSize;
-    fMinDistanceFieldFontSize = options.fMinDistanceFieldFontSize < 0.f
-                                        ? kDefaultMinDistanceFieldFontSize
-                                        : options.fMinDistanceFieldFontSize;
-    fDistanceFieldVerticesAlwaysHaveW = options.fDistanceFieldVerticesAlwaysHaveW;
+        : fDistanceAdjustTable(new GrDistanceFieldAdjustTable), fOptions(options) {
+    SanitizeOptions(&fOptions);
 }
 
 std::unique_ptr<GrAtlasTextContext> GrAtlasTextContext::Make(const Options& options) {
@@ -211,7 +205,8 @@
         }
         cacheBlob->setRunPaintFlags(run, runPaint.skPaint().getFlags());
 
-        if (this->canDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) {
+        if (CanDrawAsDistanceFields(runPaint, viewMatrix, props,
+                                    shaderCaps.supportsDistanceFieldText(), fOptions)) {
             switch (it.positioning()) {
                 case SkTextBlob::kDefault_Positioning: {
                     this->drawDFText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
@@ -274,7 +269,8 @@
     blob->initThrowawayBlob(viewMatrix, x, y);
     blob->setRunPaintFlags(0, paint.skPaint().getFlags());
 
-    if (this->canDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
+    if (CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps.supportsDistanceFieldText(),
+                                fOptions)) {
         this->drawDFText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
                          text, byteLength, x, y);
     } else {
@@ -304,7 +300,8 @@
     blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y());
     blob->setRunPaintFlags(0, paint.skPaint().getFlags());
 
-    if (this->canDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
+    if (CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps.supportsDistanceFieldText(),
+                                fOptions)) {
         this->drawDFPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
                             text, byteLength, pos, scalarsPerPosition, offset);
     } else {
@@ -565,16 +562,26 @@
                       textRatio, true);
 }
 
-bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
+void GrAtlasTextContext::SanitizeOptions(Options* options) {
+    if (options->fMaxDistanceFieldFontSize < 0.f) {
+        options->fMaxDistanceFieldFontSize = kDefaultMaxDistanceFieldFontSize;
+    }
+    if (options->fMinDistanceFieldFontSize < 0.f) {
+        options->fMinDistanceFieldFontSize = kDefaultMinDistanceFieldFontSize;
+    }
+}
+
+bool GrAtlasTextContext::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
                                                  const SkSurfaceProps& props,
-                                                 const GrShaderCaps& caps) const {
+                                                 bool contextSupportsDistanceFieldText,
+                                                 const Options& options) {
     if (!viewMatrix.hasPerspective()) {
         SkScalar maxScale = viewMatrix.getMaxScale();
         SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
         // Hinted text looks far better at small resolutions
         // Scaling up beyond 2x yields undesireable artifacts
-        if (scaledTextSize < fMinDistanceFieldFontSize ||
-            scaledTextSize > fMaxDistanceFieldFontSize) {
+        if (scaledTextSize < options.fMinDistanceFieldFontSize ||
+            scaledTextSize > options.fMaxDistanceFieldFontSize) {
             return false;
         }
 
@@ -589,7 +596,7 @@
     }
 
     // mask filters modify alpha, which doesn't translate well to distance
-    if (skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
+    if (skPaint.getMaskFilter() || !contextSupportsDistanceFieldText) {
         return false;
     }
 
@@ -601,10 +608,12 @@
     return true;
 }
 
-void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
+void GrAtlasTextContext::InitDistanceFieldPaint(GrAtlasTextBlob* blob,
                                                 SkPaint* skPaint,
+                                                const SkMatrix& viewMatrix,
+                                                const Options& options,
                                                 SkScalar* textRatio,
-                                                const SkMatrix& viewMatrix) const {
+                                                SkScalerContextFlags* flags) {
     SkScalar textSize = skPaint->getTextSize();
     SkScalar scaledTextSize = textSize;
 
@@ -627,7 +636,7 @@
     SkScalar dfMaskScaleFloor;
     SkScalar dfMaskScaleCeil;
     if (scaledTextSize <= kSmallDFFontLimit) {
-        dfMaskScaleFloor = fMinDistanceFieldFontSize;
+        dfMaskScaleFloor = options.fMinDistanceFieldFontSize;
         dfMaskScaleCeil = kSmallDFFontLimit;
         *textRatio = textSize / kSmallDFFontSize;
         skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
@@ -638,7 +647,7 @@
         skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
     } else {
         dfMaskScaleFloor = kMediumDFFontLimit;
-        dfMaskScaleCeil = fMaxDistanceFieldFontSize;
+        dfMaskScaleCeil = options.fMaxDistanceFieldFontSize;
         *textRatio = textSize / kLargeDFFontSize;
         skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
     }
@@ -651,7 +660,10 @@
     // against these values to decide if we can reuse or not(ie, will a given scale change our mip
     // level)
     SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
-    blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
+    if (blob) {
+        blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize,
+                                dfMaskScaleCeil / scaledTextSize);
+    }
 
     skPaint->setAntiAlias(true);
     skPaint->setLCDRenderText(false);
@@ -660,6 +672,10 @@
     skPaint->setSubpixelText(true);
 
     skPaint->setMaskFilter(GrSDFMaskFilter::Make());
+
+    // We apply the fake-gamma by altering the distance in the shader, so we ignore the
+    // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
+    *flags = SkScalerContextFlags::kNone;
 }
 
 void GrAtlasTextContext::drawDFText(GrAtlasTextBlob* blob, int runIndex,
@@ -745,12 +761,13 @@
         return;
     }
 
-    bool hasWCoord = viewMatrix.hasPerspective() || fDistanceFieldVerticesAlwaysHaveW;
+    bool hasWCoord = viewMatrix.hasPerspective() || fOptions.fDistanceFieldVerticesAlwaysHaveW;
 
     // Setup distance field paint and text ratio
     SkScalar textRatio;
     SkPaint dfPaint(paint);
-    this->initDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
+    SkScalerContextFlags flags;
+    InitDistanceFieldPaint(blob, &dfPaint, viewMatrix, fOptions, &textRatio, &flags);
     blob->setHasDistanceField();
     blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
                                      paint.skPaint().isAntiAlias(), hasWCoord);
@@ -760,10 +777,7 @@
     sk_sp<GrTextStrike> currStrike;
 
     {
-        // We apply the fake-gamma by altering the distance in the shader, so we ignore the
-        // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
-        auto cache = blob->setupCache(runIndex, props, SkScalerContextFlags::kNone, dfPaint,
-                                      nullptr);
+        auto cache = blob->setupCache(runIndex, props, flags, dfPaint, nullptr);
         SkPaint::GlyphCacheProc glyphCacheProc =
             SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(), true);
 
diff --git a/src/gpu/text/GrAtlasTextContext.h b/src/gpu/text/GrAtlasTextContext.h
index 1342ee7..e2531b3 100644
--- a/src/gpu/text/GrAtlasTextContext.h
+++ b/src/gpu/text/GrAtlasTextContext.h
@@ -60,6 +60,18 @@
                                                    const SkMatrix& viewMatrix, const char* text,
                                                    int x, int y);
 
+    static void SanitizeOptions(Options* options);
+    static bool CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
+                                        const SkSurfaceProps& props,
+                                        bool contextSupportsDistanceFieldText,
+                                        const Options& options);
+    static void InitDistanceFieldPaint(GrAtlasTextBlob* blob,
+                                       SkPaint* skPaint,
+                                       const SkMatrix& viewMatrix,
+                                       const Options& options,
+                                       SkScalar* textRatio,
+                                       SkScalerContextFlags* flags);
+
 private:
     GrAtlasTextContext(const Options& options);
 
@@ -158,9 +170,6 @@
                                       const SkPoint& offset);
 
     // functions for appending distance field text
-    bool canDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
-                                 const SkSurfaceProps& props, const GrShaderCaps& caps) const;
-
     void drawDFText(GrAtlasTextBlob* blob, int runIndex, GrGlyphCache*, const SkSurfaceProps&,
                     const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags,
                     const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x,
@@ -173,11 +182,6 @@
                        size_t byteLength, const SkScalar pos[], int scalarsPerPosition,
                        const SkPoint& offset) const;
 
-    void initDistanceFieldPaint(GrAtlasTextBlob* blob,
-                                SkPaint* skPaint,
-                                SkScalar* textRatio,
-                                const SkMatrix& viewMatrix) const;
-
     static void BmpAppendGlyph(GrAtlasTextBlob*, int runIndex, GrGlyphCache*,
                                sk_sp<GrTextStrike>*, const SkGlyph&, SkScalar sx, SkScalar sy,
                                GrColor color, SkGlyphCache*, SkScalar textRatio);
@@ -190,9 +194,7 @@
 
     sk_sp<const GrDistanceFieldAdjustTable> fDistanceAdjustTable;
 
-    SkScalar fMinDistanceFieldFontSize;
-    SkScalar fMaxDistanceFieldFontSize;
-    bool fDistanceFieldVerticesAlwaysHaveW;
+    Options fOptions;
 
 #if GR_TEST_UTILS
     static const SkScalerContextFlags kTextBlobOpScalerContextFlags =
diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp
index 811bf1b..5d4b9ff 100644
--- a/tests/SkRemoteGlyphCacheTest.cpp
+++ b/tests/SkRemoteGlyphCacheTest.cpp
@@ -15,6 +15,10 @@
 #include "SkTypeface_remote.h"
 #include "Test.h"
 
+#if SK_SUPPORT_GPU
+#include "text/GrAtlasTextContext.h"
+#endif
+
 class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
                            public SkStrikeClient::DiscardableHandleManager {
 public:
@@ -79,14 +83,26 @@
         }                                                                                \
     }
 
-SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint) {
-    auto surface = SkSurface::MakeRasterN32Premul(width, height);
+#if SK_SUPPORT_GPU
+SkTextBlobCacheDiffCanvas::Settings MakeSettings(GrContext* context) {
+    SkTextBlobCacheDiffCanvas::Settings settings;
+    settings.fContextSupportsDistanceFieldText = context->supportsDistanceFieldText();
+    return settings;
+}
+
+SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint,
+                    GrContext* context, const SkMatrix* matrix = nullptr) {
+    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);
     SkBitmap bitmap;
     bitmap.allocN32Pixels(width, height);
     surface->readPixels(bitmap, 0, 0);
     return bitmap;
 }
+#endif
 
 DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization, reporter) {
     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
@@ -102,7 +118,8 @@
                                       server_tf->uniqueID());
 }
 
-DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) {
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization, reporter, ctxInfo) {
     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
     SkStrikeServer server(discardableManager.get());
     SkStrikeClient client(discardableManager);
@@ -115,7 +132,8 @@
     int glyphCount = 10;
     auto serverBlob = buildTextBlob(serverTf, glyphCount);
     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
-    SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
+    SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
+                                                MakeSettings(ctxInfo.grContext()));
     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
 
     std::vector<uint8_t> serverStrikeData;
@@ -127,10 +145,11 @@
                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
     auto clientBlob = buildTextBlob(clientTf, glyphCount);
 
-    SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint);
-    SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint);
+    SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
+    SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
     COMPARE_BLOBS(expected, actual, reporter);
 }
+#endif
 
 DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
@@ -250,7 +269,8 @@
     SkStrikeCache::Validate();
 }
 
-DEF_TEST(SkRemoteGlyphCache_DrawTextAsPath, reporter) {
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath, reporter, ctxInfo) {
     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
     SkStrikeServer server(discardableManager.get());
     SkStrikeClient client(discardableManager);
@@ -266,7 +286,8 @@
     int glyphCount = 10;
     auto serverBlob = buildTextBlob(serverTf, glyphCount);
     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
-    SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
+    SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
+                                                MakeSettings(ctxInfo.grContext()));
     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
 
     std::vector<uint8_t> serverStrikeData;
@@ -278,8 +299,54 @@
                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
     auto clientBlob = buildTextBlob(clientTf, glyphCount);
 
-    SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint);
-    SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint);
+    SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
+    SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
     COMPARE_BLOBS(expected, actual, reporter);
     SkStrikeCache::Validate();
 }
+#endif
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT, reporter, ctxInfo) {
+    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
+    SkStrikeServer server(discardableManager.get());
+    SkStrikeClient client(discardableManager);
+    SkPaint paint;
+
+    // A perspective transform forces fallback to dft.
+    SkMatrix matrix = SkMatrix::I();
+    matrix[SkMatrix::kMPersp0] = 0.5f;
+    REPORTER_ASSERT(reporter, matrix.hasPerspective());
+    SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
+    GrAtlasTextContext::Options options;
+    GrAtlasTextContext::SanitizeOptions(&options);
+    REPORTER_ASSERT(reporter, GrAtlasTextContext::CanDrawAsDistanceFields(
+                                      paint, matrix, surfaceProps, true, options));
+
+    // 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.concat(matrix);
+    cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 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(), &matrix);
+    SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
+    COMPARE_BLOBS(expected, actual, reporter);
+    SkStrikeCache::Validate();
+}
+#endif