Revert "Revert "Return POD from generateMetrics() rather than mutate SkGlyph""

This reverts commit 3b83ce679ed656c3a347aec594f111170e0077de.

Fix was to copy over extraBits from glyph to glyphmetrics

Change-Id: Iaf771d2461544936b380911f7dca65cbbbb444bb
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/729800
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index b7b26ac..7b784cc 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -231,26 +231,43 @@
         glyph.fWidth    = 0;
         glyph.fHeight   = 0;
     };
+
     SkGlyph glyph{packedID};
-    glyph.fMaskFormat = format;
-    // Must call to allow the subclass to determine the glyph representation to use.
-    this->generateMetrics(&glyph, alloc);
-    SkDEBUGCODE(glyph.fAdvancesBoundsFormatAndInitialPathDone = true;)
-    if (fGenerateImageFromPath) {
+    glyph.fMaskFormat = format; // subclass may return a different value
+    const auto mx = this->generateMetrics(glyph, alloc);
+    SkASSERT(!mx.neverRequestPath || !mx.computeFromPath);
+
+    glyph.fAdvanceX = mx.advance.fX;
+    glyph.fAdvanceY = mx.advance.fY;
+    glyph.fMaskFormat = mx.maskFormat;
+    glyph.fScalerContextBits = mx.extraBits;
+
+    if (mx.computeFromPath || (fGenerateImageFromPath && !mx.neverRequestPath)) {
+        SkDEBUGCODE(glyph.fAdvancesBoundsFormatAndInitialPathDone = true;)
         this->internalGetPath(glyph, alloc);
         const SkPath* devPath = glyph.path();
         if (devPath) {
-            // generateMetrics may have modified the glyph fMaskFormat.
-            glyph.fMaskFormat = format;
             const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
             const bool a8LCD = SkToBool(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag);
             const bool hairline = glyph.pathIsHairline();
             if (!GenerateMetricsFromPath(&glyph, *devPath, format, doVert, a8LCD, hairline)) {
                 zeroBounds(glyph);
-                return glyph;
             }
         }
+    } else {
+        if (!SkRectPriv::Is16Bit(mx.bounds)) {
+            zeroBounds(glyph);
+        } else {
+            glyph.fLeft   = SkTo<int16_t>( mx.bounds.fLeft);
+            glyph.fTop    = SkTo<int16_t>( mx.bounds.fTop);
+            glyph.fWidth  = SkTo<uint16_t>(mx.bounds.width());
+            glyph.fHeight = SkTo<uint16_t>(mx.bounds.height());
+        }
+        if (mx.neverRequestPath) {
+            glyph.setPath(alloc, nullptr, false);
+        }
     }
+    SkDEBUGCODE(glyph.fAdvancesBoundsFormatAndInitialPathDone = true;)
 
     // if either dimension is empty, zap the image bounds of the glyph
     if (0 == glyph.fWidth || 0 == glyph.fHeight) {
@@ -1254,9 +1271,8 @@
                 : SkScalerContext(std::move(typeface), effects, desc) {}
 
     protected:
-        void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override {
-            glyph->fMaskFormat = fRec.fMaskFormat;
-            glyph->zeroMetrics();
+        GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override {
+            return {glyph.maskFormat()};
         }
         void generateImage(const SkGlyph& glyph) override {}
         bool generatePath(const SkGlyph& glyph, SkPath* path) override {
diff --git a/src/core/SkScalerContext.h b/src/core/SkScalerContext.h
index e8a8a71..fbe82f4 100644
--- a/src/core/SkScalerContext.h
+++ b/src/core/SkScalerContext.h
@@ -363,11 +363,26 @@
 protected:
     SkScalerContextRec fRec;
 
-    /** Generates the contents of glyph.fWidth, fHeight, fTop, fLeft,
-     *  as well as fAdvanceX and fAdvanceY if not already set.
-     *  The fMaskFormat will already be set to a requested format but may be changed.
-     */
-    virtual void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) = 0;
+    struct GlyphMetrics {
+        SkVector       advance;
+        SkIRect        bounds;
+        SkMask::Format maskFormat;
+        uint16_t       extraBits;
+        bool           neverRequestPath;
+        bool           computeFromPath;
+
+        GlyphMetrics(SkMask::Format format)
+            : advance{0, 0}
+            , bounds{0, 0, 0, 0}
+            , maskFormat(format)
+            , extraBits(0)
+            , neverRequestPath(false)
+            , computeFromPath(false)
+        {}
+    };
+
+    virtual GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) = 0;
+
     static bool GenerateMetricsFromPath(
         SkGlyph* glyph, const SkPath& path, SkMask::Format format,
         bool verticalLCD, bool a8FromLCD, bool hairline);
diff --git a/src/core/SkTypeface_remote.cpp b/src/core/SkTypeface_remote.cpp
index 2f227a5..a7930b2 100644
--- a/src/core/SkTypeface_remote.cpp
+++ b/src/core/SkTypeface_remote.cpp
@@ -23,17 +23,18 @@
         : SkScalerContext{std::move(tf), effects, desc}
         , fDiscardableManager{std::move(manager)} {}
 
-void SkScalerContextProxy::generateMetrics(SkGlyph* glyph, SkArenaAlloc*) {
+SkScalerContext::GlyphMetrics SkScalerContextProxy::generateMetrics(const SkGlyph& glyph,
+                                                                    SkArenaAlloc*) {
     TRACE_EVENT1("skia", "generateMetrics", "rec", TRACE_STR_COPY(this->getRec().dump().c_str()));
     if (this->getProxyTypeface()->isLogging()) {
         SkDebugf("GlyphCacheMiss generateMetrics looking for glyph: %x\n  generateMetrics: %s\n",
-                 glyph->getPackedID().value(), this->getRec().dump().c_str());
+                 glyph.getPackedID().value(), this->getRec().dump().c_str());
     }
 
-    glyph->fMaskFormat = fRec.fMaskFormat;
-    glyph->zeroMetrics();
     fDiscardableManager->notifyCacheMiss(
-            SkStrikeClient::CacheMissType::kGlyphMetrics, fRec.fTextSize);
+                                         SkStrikeClient::CacheMissType::kGlyphMetrics, fRec.fTextSize);
+
+    return {glyph.maskFormat()};
 }
 
 void SkScalerContextProxy::generateImage(const SkGlyph& glyph) {
diff --git a/src/core/SkTypeface_remote.h b/src/core/SkTypeface_remote.h
index 9a7142b..32cbe66 100644
--- a/src/core/SkTypeface_remote.h
+++ b/src/core/SkTypeface_remote.h
@@ -30,9 +30,9 @@
                          sk_sp<SkStrikeClient::DiscardableHandleManager> manager);
 
 protected:
-    void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override;
+    GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override;
     void generateImage(const SkGlyph& glyph) override;
-    bool generatePath(const SkGlyph& glyphID, SkPath* path) override;
+    bool generatePath(const SkGlyph& glyph, SkPath* path) override;
     sk_sp<SkDrawable> generateDrawable(const SkGlyph&) override;
     void generateFontMetrics(SkFontMetrics* metrics) override;
     SkTypefaceProxy* getProxyTypeface() const;
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index 39b4157..967609e 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -444,7 +444,7 @@
     }
 
 protected:
-    void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override;
+    GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override;
     void generateImage(const SkGlyph& glyph) override;
     bool generatePath(const SkGlyph& glyph, SkPath* path) override;
     sk_sp<SkDrawable> generateDrawable(const SkGlyph&) override;
@@ -472,10 +472,10 @@
 
     FT_Error setupSize();
     static bool getBoundsOfCurrentOutlineGlyph(FT_GlyphSlot glyph, SkRect* bounds);
-    static void setGlyphBounds(SkGlyph* glyph, SkRect* bounds, bool subpixel);
+    static SkIRect computeGlyphBounds(const SkGlyph&, SkRect* bounds, bool subpixel);
     bool getCBoxForLetter(char letter, FT_BBox* bbox);
     // Caller must lock f_t_mutex() before calling this function.
-    void updateGlyphBoundsIfLCD(SkGlyph* glyph);
+    void updateGlyphBoundsIfLCD(GlyphMetrics* mx);
     // Caller must lock f_t_mutex() before calling this function.
     // update FreeType2 glyph slot with glyph emboldened
     void emboldenIfNeeded(FT_Face face, FT_GlyphSlot glyph, SkGlyphID gid);
@@ -1105,41 +1105,31 @@
     return true;
 }
 
-void SkScalerContext_FreeType::setGlyphBounds(SkGlyph* glyph, SkRect* bounds, bool subpixel) {
+SkIRect SkScalerContext_FreeType::computeGlyphBounds(const SkGlyph& glyph, SkRect* bounds,
+                                                     bool subpixel) {
     SkIRect irect;
     if (bounds->isEmpty()) {
         irect = SkIRect::MakeEmpty();
     } else {
         if (subpixel) {
-            bounds->offset(SkFixedToScalar(glyph->getSubXFixed()),
-                           SkFixedToScalar(glyph->getSubYFixed()));
+            bounds->offset(SkFixedToScalar(glyph.getSubXFixed()),
+                           SkFixedToScalar(glyph.getSubYFixed()));
         }
-
         irect = bounds->roundOut();
-        if (!SkTFitsIn<decltype(glyph->fWidth )>(irect.width ()) ||
-            !SkTFitsIn<decltype(glyph->fHeight)>(irect.height()) ||
-            !SkTFitsIn<decltype(glyph->fTop   )>(irect.top   ()) ||
-            !SkTFitsIn<decltype(glyph->fLeft  )>(irect.left  ())  )
-        {
-            irect = SkIRect::MakeEmpty();
-        }
     }
-    glyph->fWidth  = SkToU16(irect.width ());
-    glyph->fHeight = SkToU16(irect.height());
-    glyph->fTop    = SkToS16(irect.top   ());
-    glyph->fLeft   = SkToS16(irect.left  ());
+    return irect;
 }
 
-void SkScalerContext_FreeType::updateGlyphBoundsIfLCD(SkGlyph* glyph) {
-    if (glyph->fMaskFormat == SkMask::kLCD16_Format &&
-        glyph->fWidth > 0 && glyph->fHeight > 0)
+void SkScalerContext_FreeType::updateGlyphBoundsIfLCD(GlyphMetrics* mx) {
+    if (mx->maskFormat == SkMask::kLCD16_Format &&
+        mx->bounds.width() > 0 && mx->bounds.height() > 0)
     {
         if (fLCDIsVert) {
-            glyph->fHeight += 2;
-            glyph->fTop -= 1;
+            mx->bounds.fBottom += 1;
+            mx->bounds.fTop -= 1;
         } else {
-            glyph->fWidth += 2;
-            glyph->fLeft -= 1;
+            mx->bounds.fRight += 1;
+            mx->bounds.fLeft -= 1;
         }
     }
 }
@@ -1160,12 +1150,14 @@
     return mechanism && policy;
 }
 
-void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) {
+SkScalerContext::GlyphMetrics SkScalerContext_FreeType::generateMetrics(const SkGlyph& glyph,
+                                                                        SkArenaAlloc* alloc) {
     SkAutoMutexExclusive  ac(f_t_mutex());
 
+    GlyphMetrics mx(glyph.maskFormat());
+
     if (this->setupSize()) {
-        glyph->zeroMetrics();
-        return;
+        return mx;
     }
 
     FT_Bool haveLayers = false;
@@ -1175,14 +1167,14 @@
         SkRect bounds = SkRect::MakeEmpty();
 #ifdef TT_SUPPORT_COLRV1
         FT_OpaquePaint opaqueLayerPaint{nullptr, 1};
-        if (FT_Get_Color_Glyph_Paint(fFace, glyph->getGlyphID(),
+        if (FT_Get_Color_Glyph_Paint(fFace, glyph.getGlyphID(),
                                      FT_COLOR_INCLUDE_ROOT_TRANSFORM, &opaqueLayerPaint)) {
             haveLayers = true;
-            glyph->fScalerContextBits = ScalerContextBits::COLRv1;
+            mx.extraBits = ScalerContextBits::COLRv1;
 
             // COLRv1 optionally provides a ClipBox.
             FT_ClipBox clipBox;
-            if (FT_Get_Color_Glyph_ClipBox(fFace, glyph->getGlyphID(), &clipBox)) {
+            if (FT_Get_Color_Glyph_ClipBox(fFace, glyph.getGlyphID(), &clipBox)) {
                 // Find bounding box of clip box corner points, needed when clipbox is transformed.
                 FT_BBox bbox;
                 bbox.xMin = clipBox.bottom_left.x;
@@ -1201,11 +1193,10 @@
                 // Traverse the glyph graph with a focus on measuring the required bounding box.
                 // The call to computeColrV1GlyphBoundingBox may modify the face.
                 // Reset the face to load the base glyph for metrics.
-                if (!computeColrV1GlyphBoundingBox(fFace, glyph->getGlyphID(), &bounds) ||
+                if (!computeColrV1GlyphBoundingBox(fFace, glyph.getGlyphID(), &bounds) ||
                     this->setupSize())
                 {
-                    glyph->zeroMetrics();
-                    return;
+                    return mx;
                 }
             }
         }
@@ -1221,12 +1212,11 @@
             flags &= ~FT_LOAD_RENDER;  // Don't scan convert.
             flags &= ~FT_LOAD_COLOR;  // Ignore SVG.
             // For COLRv0 compute the glyph bounding box from the union of layer bounding boxes.
-            while (FT_Get_Color_Glyph_Layer(fFace, glyph->getGlyphID(), &layerGlyphIndex,
+            while (FT_Get_Color_Glyph_Layer(fFace, glyph.getGlyphID(), &layerGlyphIndex,
                                             &layerColorIndex, &layerIterator)) {
                 haveLayers = true;
                 if (FT_Load_Glyph(fFace, layerGlyphIndex, flags)) {
-                    glyph->zeroMetrics();
-                    return;
+                    return mx;
                 }
 
                 SkRect currentBounds;
@@ -1235,37 +1225,36 @@
                 }
             }
             if (haveLayers) {
-                glyph->fScalerContextBits = ScalerContextBits::COLRv0;
+                mx.extraBits = ScalerContextBits::COLRv0;
             }
         }
 
         if (haveLayers) {
-            glyph->fMaskFormat = SkMask::kARGB32_Format;
-            glyph->setPath(alloc, nullptr, false);
-            setGlyphBounds(glyph, &bounds, this->isSubpixel());
+            mx.maskFormat = SkMask::kARGB32_Format;
+            mx.neverRequestPath = true;
+            mx.bounds = computeGlyphBounds(glyph, &bounds, this->isSubpixel());
         }
     }
 #endif  //FT_COLOR_H
 
     // Even if haveLayers, the base glyph must be loaded to get the metrics.
-    if (FT_Load_Glyph(fFace, glyph->getGlyphID(), fLoadGlyphFlags | FT_LOAD_BITMAP_METRICS_ONLY)) {
-        glyph->zeroMetrics();
-        return;
+    if (FT_Load_Glyph(fFace, glyph.getGlyphID(), fLoadGlyphFlags | FT_LOAD_BITMAP_METRICS_ONLY)) {
+        return mx;
     }
 
     if (!haveLayers) {
-        emboldenIfNeeded(fFace, fFace->glyph, glyph->getGlyphID());
+        emboldenIfNeeded(fFace, fFace->glyph, glyph.getGlyphID());
 
         if (fFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
             SkRect bounds;
             if (!getBoundsOfCurrentOutlineGlyph(fFace->glyph, &bounds)) {
                 bounds = SkRect::MakeEmpty();
             }
-            setGlyphBounds(glyph, &bounds, this->isSubpixel());
-            updateGlyphBoundsIfLCD(glyph);
+            mx.bounds = computeGlyphBounds(glyph, &bounds, this->isSubpixel());
+            updateGlyphBoundsIfLCD(&mx);
 
         } else if (fFace->glyph->format == FT_GLYPH_FORMAT_BITMAP) {
-            glyph->setPath(alloc, nullptr, false);
+            mx.neverRequestPath = true;
 
             if (this->isVertical()) {
                 FT_Vector vector;
@@ -1277,7 +1266,7 @@
             }
 
             if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
-                glyph->fMaskFormat = SkMask::kARGB32_Format;
+                mx.maskFormat = SkMask::kARGB32_Format;
             }
 
             SkRect bounds = SkRect::MakeXYWH(SkIntToScalar(fFace->glyph->bitmap_left ),
@@ -1285,13 +1274,14 @@
                                              SkIntToScalar(fFace->glyph->bitmap.width),
                                              SkIntToScalar(fFace->glyph->bitmap.rows ));
             fMatrix22Scalar.mapRect(&bounds);
-            setGlyphBounds(glyph, &bounds, this->shouldSubpixelBitmap(*glyph, fMatrix22Scalar));
+            mx.bounds = computeGlyphBounds(glyph, &bounds,
+                                           this->shouldSubpixelBitmap(glyph, fMatrix22Scalar));
 
 #if defined(FT_CONFIG_OPTION_SVG)
         } else if (fFace->glyph->format == FT_GLYPH_FORMAT_SVG) {
-            glyph->fScalerContextBits = ScalerContextBits::SVG;
-            glyph->fMaskFormat = SkMask::kARGB32_Format;
-            glyph->setPath(alloc, nullptr, false);
+            mx.extraBits = ScalerContextBits::SVG;
+            mx.maskFormat = SkMask::kARGB32_Format;
+            mx.neverRequestPath = true;
 
             SkPictureRecorder recorder;
             SkRect infiniteRect = SkRect::MakeLTRB(-SK_ScalarInfinity, -SK_ScalarInfinity,
@@ -1299,48 +1289,47 @@
             sk_sp<SkBBoxHierarchy> bboxh = SkRTreeFactory()();
             SkSpan<SkColor> palette(fFaceRec->fSkPalette.get(), fFaceRec->fFTPaletteEntryCount);
             SkCanvas* recordingCanvas = recorder.beginRecording(infiniteRect, bboxh);
-            if (!this->drawSVGGlyph(fFace, *glyph, fLoadGlyphFlags, palette, recordingCanvas)) {
-                glyph->zeroMetrics();
-                return;
+            if (!this->drawSVGGlyph(fFace, glyph, fLoadGlyphFlags, palette, recordingCanvas)) {
+                return mx;
             }
             sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
             SkRect bounds = pic->cullRect();
             SkASSERT(bounds.isFinite());
 
             // drawSVGGlyph already applied the subpixel positioning.
-            setGlyphBounds(glyph, &bounds, false);
+            mx.bounds = computeGlyphBounds(glyph, &bounds, false);
 #endif  // FT_CONFIG_OPTION_SVG
 
         } else {
             SkDEBUGFAIL("unknown glyph format");
-            glyph->zeroMetrics();
-            return;
+            return mx;
         }
     }
 
     if (this->isVertical()) {
         if (fDoLinearMetrics) {
             const SkScalar advanceScalar = SkFT_FixedToScalar(fFace->glyph->linearVertAdvance);
-            glyph->fAdvanceX = SkScalarToFloat(fMatrix22Scalar.getSkewX() * advanceScalar);
-            glyph->fAdvanceY = SkScalarToFloat(fMatrix22Scalar.getScaleY() * advanceScalar);
+            mx.advance.fX = SkScalarToFloat(fMatrix22Scalar.getSkewX() * advanceScalar);
+            mx.advance.fY = SkScalarToFloat(fMatrix22Scalar.getScaleY() * advanceScalar);
         } else {
-            glyph->fAdvanceX = -SkFDot6ToFloat(fFace->glyph->advance.x);
-            glyph->fAdvanceY =  SkFDot6ToFloat(fFace->glyph->advance.y);
+            mx.advance.fX = -SkFDot6ToFloat(fFace->glyph->advance.x);
+            mx.advance.fY =  SkFDot6ToFloat(fFace->glyph->advance.y);
         }
     } else {
         if (fDoLinearMetrics) {
             const SkScalar advanceScalar = SkFT_FixedToScalar(fFace->glyph->linearHoriAdvance);
-            glyph->fAdvanceX = SkScalarToFloat(fMatrix22Scalar.getScaleX() * advanceScalar);
-            glyph->fAdvanceY = SkScalarToFloat(fMatrix22Scalar.getSkewY() * advanceScalar);
+            mx.advance.fX = SkScalarToFloat(fMatrix22Scalar.getScaleX() * advanceScalar);
+            mx.advance.fY = SkScalarToFloat(fMatrix22Scalar.getSkewY() * advanceScalar);
         } else {
-            glyph->fAdvanceX =  SkFDot6ToFloat(fFace->glyph->advance.x);
-            glyph->fAdvanceY = -SkFDot6ToFloat(fFace->glyph->advance.y);
+            mx.advance.fX =  SkFDot6ToFloat(fFace->glyph->advance.x);
+            mx.advance.fY = -SkFDot6ToFloat(fFace->glyph->advance.y);
         }
     }
 
 #ifdef ENABLE_GLYPH_SPEW
     LOG_INFO("Metrics(glyph:%d flags:0x%x) w:%d\n", glyph->getGlyphID(), fLoadGlyphFlags, glyph->fWidth);
 #endif
+    return mx;
 }
 
 void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index 5b17b6a..cf32f03 100644
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -572,7 +572,7 @@
     bool isValid() const;
 
 protected:
-    void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override;
+    GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override;
     void generateImage(const SkGlyph& glyph) override;
     bool generatePath(const SkGlyph& glyph, SkPath* path) override;
     void generateFontMetrics(SkFontMetrics*) override;
@@ -805,56 +805,53 @@
     return fDDC && fFont;
 }
 
-void SkScalerContext_GDI::generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) {
+SkScalerContext::GlyphMetrics SkScalerContext_GDI::generateMetrics(const SkGlyph& glyph,
+                                                                   SkArenaAlloc*) {
     SkASSERT(fDDC);
 
-    glyph->fMaskFormat = fRec.fMaskFormat;
+    GlyphMetrics mx(glyph.maskFormat());
 
     if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
         SIZE size;
-        WORD glyphs = glyph->getGlyphID();
+        WORD glyphs = glyph.getGlyphID();
+        int width, height;
         if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
-            glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
-            glyph->fHeight = SkToS16(fTM.tmHeight);
+            width = fTM.tmMaxCharWidth;
+            height = fTM.tmHeight;
         } else {
-            glyph->fWidth = SkToS16(size.cx);
-            glyph->fHeight = SkToS16(size.cy);
+            width = size.cx;
+            height = size.cy;
         }
 
-        glyph->fTop = SkToS16(-fTM.tmAscent);
         // Bitmap FON cannot underhang, but vector FON may.
         // There appears no means of determining underhang of vector FON.
-        glyph->fLeft = SkToS16(0);
-        glyph->fAdvanceX = glyph->width();
-        glyph->fAdvanceY = 0;
+        int left = 0;
+        int top = -fTM.tmAscent;
+
+        mx.bounds = SkIRect::MakeXYWH(left, top, width, height);
+        mx.advance = SkVector{(float)width, 0};
 
         // Vector FON will transform nicely, but bitmap FON do not.
         if (fType == SkScalerContext_GDI::kLine_Type) {
-            SkRect bounds = SkRect::MakeXYWH(glyph->fLeft, glyph->fTop,
-                                             glyph->width(), glyph->height());
+            SkRect bounds = SkRect::MakeXYWH(left, top, width, height);
             SkMatrix m;
             m.setAll(SkFIXEDToScalar(fMat22.eM11), -SkFIXEDToScalar(fMat22.eM21), 0,
                      -SkFIXEDToScalar(fMat22.eM12), SkFIXEDToScalar(fMat22.eM22), 0,
                      0,  0, 1);
             m.mapRect(&bounds);
-            bounds.roundOut(&bounds);
-            glyph->fLeft = SkScalarTruncToInt(bounds.fLeft);
-            glyph->fTop = SkScalarTruncToInt(bounds.fTop);
-            glyph->fWidth = SkScalarTruncToInt(bounds.width());
-            glyph->fHeight = SkScalarTruncToInt(bounds.height());
+            bounds.roundOut(&mx.bounds);
         }
 
         // Apply matrix to advance.
-        glyph->fAdvanceY = -SkFIXEDToFloat(fMat22.eM12) * glyph->fAdvanceX;
-        glyph->fAdvanceX *= SkFIXEDToFloat(fMat22.eM11);
+        mx.advance.fY = -SkFIXEDToFloat(fMat22.eM12) * mx.advance.fX;
+        mx.advance.fX *= SkFIXEDToFloat(fMat22.eM11);
 
         // These do not have an outline path at all.
-        glyph->setPath(alloc, nullptr, false);
-
-        return;
+        mx.neverRequestPath = true;
+        return mx;
     }
 
-    UINT glyphId = glyph->getGlyphID();
+    UINT glyphId = glyph.getGlyphID();
 
     GLYPHMETRICS gm;
     sk_bzero(&gm, sizeof(gm));
@@ -864,8 +861,7 @@
         LogFontTypeface::EnsureAccessible(this->getTypeface());
         status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
         if (GDI_ERROR == status) {
-            glyph->zeroMetrics();
-            return;
+            return mx;
         }
     }
 
@@ -879,43 +875,34 @@
         empty = (0 == bufferSize);
     }
 
-    glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
-    glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
-    if (empty) {
-        glyph->fWidth = 0;
-        glyph->fHeight = 0;
-    } else {
+
+    if (!empty) {
+        int y  = -gm.gmptGlyphOrigin.y;
+        int x =  gm.gmptGlyphOrigin.x;
         // Outset, since the image may bleed out of the black box.
         // For embedded bitmaps the black box should be exact.
         // For outlines we need to outset by 1 in all directions for bleed.
         // For ClearType we need to outset by 2 for bleed.
-        glyph->fWidth = gm.gmBlackBoxX + 4;
-        glyph->fHeight = gm.gmBlackBoxY + 4;
-        glyph->fTop -= 2;
-        glyph->fLeft -= 2;
+        mx.bounds = SkIRect::MakeXYWH(x, y, gm.gmBlackBoxX, gm.gmBlackBoxY).makeOutset(2, 2);
     }
     // TODO(benjaminwagner): What is the type of gm.gmCellInc[XY]?
-    glyph->fAdvanceX = (float)((int)gm.gmCellIncX);
-    glyph->fAdvanceY = (float)((int)gm.gmCellIncY);
+    mx.advance.fX = (float)((int)gm.gmCellIncX);
+    mx.advance.fY = (float)((int)gm.gmCellIncY);
 
     if ((fTM.tmPitchAndFamily & TMPF_VECTOR) && this->isLinearMetrics()) {
         sk_bzero(&gm, sizeof(gm));
         status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fHighResMat22);
         if (GDI_ERROR != status) {
-            SkPoint advance;
-            fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
-            glyph->fAdvanceX = SkScalarToFloat(advance.fX);
-            glyph->fAdvanceY = SkScalarToFloat(advance.fY);
+            mx.advance = fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX),
+                                            SkIntToScalar(gm.gmCellIncY));
         }
     } else if (!isAxisAligned(this->fRec)) {
         status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fGsA);
         if (GDI_ERROR != status) {
-            SkPoint advance;
-            fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
-            glyph->fAdvanceX = SkScalarToFloat(advance.fX);
-            glyph->fAdvanceY = SkScalarToFloat(advance.fY);
+            mx.advance = fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY));
         }
     }
+    return mx;
 }
 
 static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
diff --git a/src/ports/SkScalerContext_mac_ct.cpp b/src/ports/SkScalerContext_mac_ct.cpp
index a788660..5743814 100644
--- a/src/ports/SkScalerContext_mac_ct.cpp
+++ b/src/ports/SkScalerContext_mac_ct.cpp
@@ -293,23 +293,21 @@
     return image;
 }
 
-void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) {
-    glyph->fMaskFormat = fRec.fMaskFormat;
+SkScalerContext::GlyphMetrics SkScalerContext_Mac::generateMetrics(const SkGlyph& glyph,
+                                                                   SkArenaAlloc*) {
+    GlyphMetrics mx(glyph.maskFormat());
 
-    if (((SkTypeface_Mac*)this->getTypeface())->fHasColorGlyphs) {
-        glyph->setPath(alloc, nullptr, false);
-    }
+    mx.neverRequestPath = ((SkTypeface_Mac*)this->getTypeface())->fHasColorGlyphs;
 
-    const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
-    glyph->zeroMetrics();
+    const CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID();
 
     // The following block produces cgAdvance in CG units (pixels, y up).
     CGSize cgAdvance;
     CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
                                &cgGlyph, &cgAdvance, 1);
     cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
-    glyph->fAdvanceX =  SkFloatFromCGFloat(cgAdvance.width);
-    glyph->fAdvanceY = -SkFloatFromCGFloat(cgAdvance.height);
+    mx.advance.fX =  SkFloatFromCGFloat(cgAdvance.width);
+    mx.advance.fY = -SkFloatFromCGFloat(cgAdvance.height);
 
     // The following produces skBounds in SkGlyph units (pixels, y down),
     // or returns early if skBounds would be empty.
@@ -335,12 +333,12 @@
         if (0 == cgAdvance.width && 0 == cgAdvance.height) {
             SkUniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph,nullptr));
             if (!path || CGPathIsEmpty(path.get())) {
-                return;
+                return mx;
             }
         }
 
         if (SkCGRectIsEmpty(cgBounds)) {
-            return;
+            return mx;
         }
 
         // Convert cgBounds to SkGlyph units (pixels, y down).
@@ -351,27 +349,17 @@
     // Currently the bounds are based on being rendered at (0,0).
     // The top left must not move, since that is the base from which subpixel positioning is offset.
     if (fDoSubPosition) {
-        skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
-        skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
+        skBounds.fRight += SkFixedToFloat(glyph.getSubXFixed());
+        skBounds.fBottom += SkFixedToFloat(glyph.getSubYFixed());
     }
 
-    // We're trying to pack left and top into int16_t,
-    // and width and height into uint16_t, after outsetting by 1.
-    if (!SkRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(skBounds)) {
-        return;
-    }
-
-    SkIRect skIBounds;
-    skBounds.roundOut(&skIBounds);
+    skBounds.roundOut(&mx.bounds);
     // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
     // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
     // is not currently known, as CG dilates the outlines by some percentage.
     // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
-    skIBounds.outset(1, 1);
-    glyph->fLeft = SkToS16(skIBounds.fLeft);
-    glyph->fTop = SkToS16(skIBounds.fTop);
-    glyph->fWidth = SkToU16(skIBounds.width());
-    glyph->fHeight = SkToU16(skIBounds.height());
+    mx.bounds.outset(1, 1);
+    return mx;
 }
 
 static constexpr uint8_t sk_pow2_table(size_t i) {
diff --git a/src/ports/SkScalerContext_mac_ct.h b/src/ports/SkScalerContext_mac_ct.h
index 5e96a0b..1bd50f8 100644
--- a/src/ports/SkScalerContext_mac_ct.h
+++ b/src/ports/SkScalerContext_mac_ct.h
@@ -44,7 +44,7 @@
     SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
 
 protected:
-    void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override;
+    GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override;
     void generateImage(const SkGlyph& glyph) override;
     bool generatePath(const SkGlyph& glyph, SkPath* path) override;
     void generateFontMetrics(SkFontMetrics*) override;
diff --git a/src/ports/SkScalerContext_win_dw.cpp b/src/ports/SkScalerContext_win_dw.cpp
index f3aee45..4af558a 100644
--- a/src/ports/SkScalerContext_win_dw.cpp
+++ b/src/ports/SkScalerContext_win_dw.cpp
@@ -1391,13 +1391,13 @@
     }
 }
 
-bool SkScalerContext_DW::generateColorV1Metrics(SkGlyph* glyph) {
+bool SkScalerContext_DW::generateColorV1Metrics(const SkGlyph& glyph, SkIRect* ibounds) {
     DWriteFontTypeface* typeface = this->getDWriteTypeface();
     IDWriteFontFace7* fontFace = typeface->fDWriteFontFace7/*.get()*/;
     if (!fontFace) {
         return false;
     }
-    UINT32 glyphIndex = glyph->getGlyphID();
+    UINT32 glyphIndex = glyph.getGlyphID();
 
     SkTScopedComPtr<IDWritePaintReader> paintReader;
     HRESULT hr;
@@ -1428,8 +1428,8 @@
     SkScalar scale = fTextSizeRender;
     matrix.preScale(scale, scale);
     if (this->isSubpixel()) {
-        matrix.postTranslate(SkFixedToScalar(glyph->getSubXFixed()),
-                             SkFixedToScalar(glyph->getSubYFixed()));
+        matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
+                             SkFixedToScalar(glyph.getSubYFixed()));
     }
 
     SkRect r;
@@ -1443,22 +1443,21 @@
         r = sk_rect_from(clipBox);
         matrix.mapRect(&r);
     }
-    SetGlyphBounds(glyph, r);
+    r.roundOut(ibounds);
     return true;
 }
 
 #else  // DWRITE_CORE || (defined(NTDDI_WIN11_ZN) && NTDDI_VERSION >= NTDDI_WIN11_ZN)
 
-bool SkScalerContext_DW::generateColorV1Metrics(SkGlyph*) { return false; }
+bool SkScalerContext_DW::generateColorV1Metrics(const SkGlyph&, SkIRect*) { return false; }
 bool SkScalerContext_DW::generateColorV1Image(const SkGlyph&) { return false; }
 bool SkScalerContext_DW::drawColorV1Image(const SkGlyph&, SkCanvas&) { return false; }
 
 #endif  // DWRITE_CORE || (defined(NTDDI_WIN11_ZN) && NTDDI_VERSION >= NTDDI_WIN11_ZN)
 
-bool SkScalerContext_DW::setAdvance(SkGlyph* glyph) {
-    glyph->fAdvanceX = 0;
-    glyph->fAdvanceY = 0;
-    uint16_t glyphId = glyph->getGlyphID();
+bool SkScalerContext_DW::setAdvance(const SkGlyph& glyph, SkVector* advance) {
+    *advance = {0, 0};
+    uint16_t glyphId = glyph.getGlyphID();
     DWriteFontTypeface* typeface = this->getDWriteTypeface();
 
     // DirectWrite treats all out of bounds glyph ids as having the same data as glyph 0.
@@ -1496,34 +1495,32 @@
     }
     SkScalar advanceX = fTextSizeMeasure * gm.advanceWidth / dwfm.designUnitsPerEm;
 
-    SkVector advance = { advanceX, 0 };
+    *advance = { advanceX, 0 };
     if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
         DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
     {
         // DirectWrite produced 'compatible' metrics, but while close,
         // the end result is not always an integer as it would be with GDI.
-        advance.fX = SkScalarRoundToScalar(advance.fX);
+        advance->fX = SkScalarRoundToScalar(advance->fX);
     }
-    fSkXform.mapVectors(&advance, 1);
-
-    glyph->fAdvanceX = SkScalarToFloat(advance.fX);
-    glyph->fAdvanceY = SkScalarToFloat(advance.fY);
+    fSkXform.mapVectors(advance, 1);
     return true;
 }
 
-bool SkScalerContext_DW::generateDWMetrics(SkGlyph* glyph,
+bool SkScalerContext_DW::generateDWMetrics(const SkGlyph& glyph,
                                            DWRITE_RENDERING_MODE renderingMode,
-                                           DWRITE_TEXTURE_TYPE textureType)
+                                           DWRITE_TEXTURE_TYPE textureType,
+                                           SkIRect* ibounds)
 {
     DWriteFontTypeface* typeface = this->getDWriteTypeface();
 
     //Measure raster size.
-    fXform.dx = SkFixedToFloat(glyph->getSubXFixed());
-    fXform.dy = SkFixedToFloat(glyph->getSubYFixed());
+    fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
+    fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
 
     FLOAT advance = 0;
 
-    UINT16 glyphId = glyph->getGlyphID();
+    UINT16 glyphId = glyph.getGlyphID();
 
     DWRITE_GLYPH_OFFSET offset;
     offset.advanceOffset = 0.0f;
@@ -1585,17 +1582,7 @@
         return false;
     }
 
-    // We're trying to pack left and top into int16_t,
-    // and width and height into uint16_t, after outsetting by 1.
-    if (!SkIRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(
-        SkIRect::MakeLTRB(bbox.left, bbox.top, bbox.right, bbox.bottom))) {
-        return false;
-    }
-
-    glyph->fWidth = SkToU16(bbox.right - bbox.left);
-    glyph->fHeight = SkToU16(bbox.bottom - bbox.top);
-    glyph->fLeft = SkToS16(bbox.left);
-    glyph->fTop = SkToS16(bbox.top);
+    *ibounds = SkIRect::MakeLTRB(bbox.left, bbox.top, bbox.right, bbox.bottom);
     return true;
 }
 
@@ -1628,26 +1615,9 @@
     return true;
 }
 
-void SkScalerContext_DW::SetGlyphBounds(SkGlyph* glyph, const SkRect& bounds) {
-    SkIRect ibounds = bounds.roundOut();
-
-    if (!SkTFitsIn<decltype(glyph->fWidth )>(ibounds.width ()) ||
-        !SkTFitsIn<decltype(glyph->fHeight)>(ibounds.height()) ||
-        !SkTFitsIn<decltype(glyph->fTop   )>(ibounds.top   ()) ||
-        !SkTFitsIn<decltype(glyph->fLeft  )>(ibounds.left  ())  )
-    {
-        ibounds = SkIRect::MakeEmpty();
-    }
-
-    glyph->fWidth  = SkToU16(ibounds.width ());
-    glyph->fHeight = SkToU16(ibounds.height());
-    glyph->fTop    = SkToS16(ibounds.top   ());
-    glyph->fLeft   = SkToS16(ibounds.left  ());
-}
-
-bool SkScalerContext_DW::generateColorMetrics(SkGlyph* glyph) {
+bool SkScalerContext_DW::generateColorMetrics(const SkGlyph& glyph, SkIRect* ibounds) {
     SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayers;
-    if (!getColorGlyphRun(*glyph, &colorLayers)) {
+    if (!getColorGlyphRun(glyph, &colorLayers)) {
         return false;
     }
     SkASSERT(colorLayers.get());
@@ -1679,28 +1649,27 @@
     }
     SkMatrix matrix = fSkXform;
     if (this->isSubpixel()) {
-        matrix.postTranslate(SkFixedToScalar(glyph->getSubXFixed()),
-                             SkFixedToScalar(glyph->getSubYFixed()));
+        matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
+                             SkFixedToScalar(glyph.getSubYFixed()));
     }
     matrix.mapRect(&bounds);
-    SetGlyphBounds(glyph, bounds);
+    bounds.roundOut(ibounds);
     return true;
 }
 
-bool SkScalerContext_DW::generateSVGMetrics(SkGlyph* glyph) {
+bool SkScalerContext_DW::generateSVGMetrics(const SkGlyph& glyph, SkIRect* ibounds) {
     SkPictureRecorder recorder;
     SkRect infiniteRect = SkRect::MakeLTRB(-SK_ScalarInfinity, -SK_ScalarInfinity,
                                             SK_ScalarInfinity,  SK_ScalarInfinity);
     sk_sp<SkBBoxHierarchy> bboxh = SkRTreeFactory()();
     SkCanvas* recordingCanvas = recorder.beginRecording(infiniteRect, bboxh);
-    if (!this->drawSVGImage(*glyph, *recordingCanvas)) {
+    if (!this->drawSVGImage(glyph, *recordingCanvas)) {
         return false;
     }
     sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
     SkRect bounds = pic->cullRect();
     SkASSERT(bounds.isFinite());
-
-    SetGlyphBounds(glyph, bounds);
+    bounds.roundOut(ibounds);
     return true;
 }
 
@@ -1721,14 +1690,14 @@
 }
 }
 
-bool SkScalerContext_DW::generatePngMetrics(SkGlyph* glyph) {
+bool SkScalerContext_DW::generatePngMetrics(const SkGlyph& glyph, SkIRect* ibounds) {
     IDWriteFontFace4* fontFace4 = this->getDWriteTypeface()->fDWriteFontFace4.get();
     if (!fontFace4) {
         return false;
     }
 
     DWRITE_GLYPH_IMAGE_FORMATS imageFormats;
-    HRBM(fontFace4->GetGlyphImageFormats(glyph->getGlyphID(), 0, UINT32_MAX, &imageFormats),
+    HRBM(fontFace4->GetGlyphImageFormats(glyph.getGlyphID(), 0, UINT32_MAX, &imageFormats),
          "Cannot get glyph image formats.");
     if (!(imageFormats & DWRITE_GLYPH_IMAGE_FORMATS_PNG)) {
         return false;
@@ -1736,7 +1705,7 @@
 
     DWRITE_GLYPH_IMAGE_DATA glyphData;
     void* glyphDataContext;
-    HRBM(fontFace4->GetGlyphImageData(glyph->getGlyphID(),
+    HRBM(fontFace4->GetGlyphImageData(glyph.getGlyphID(),
                                       fTextSizeRender,
                                       DWRITE_GLYPH_IMAGE_FORMATS_PNG,
                                       &glyphData,
@@ -1765,59 +1734,58 @@
     matrix.preScale(scale, scale);
     matrix.preTranslate(-glyphData.horizontalLeftOrigin.x, -glyphData.horizontalLeftOrigin.y);
     if (this->isSubpixel()) {
-        matrix.postTranslate(SkFixedToScalar(glyph->getSubXFixed()),
-                             SkFixedToScalar(glyph->getSubYFixed()));
+        matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
+                             SkFixedToScalar(glyph.getSubYFixed()));
     }
     matrix.mapRect(&bounds);
-    SetGlyphBounds(glyph, bounds);
+    bounds.roundOut(ibounds);
     return true;
 }
 
-void SkScalerContext_DW::generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) {
-    glyph->fWidth = 0;
-    glyph->fHeight = 0;
-    glyph->fLeft = 0;
-    glyph->fTop = 0;
-    glyph->fScalerContextBits = ScalerContextBits::NONE;
+SkScalerContext::GlyphMetrics SkScalerContext_DW::generateMetrics(const SkGlyph& glyph,
+                                                                  SkArenaAlloc* alloc) {
+    GlyphMetrics mx(glyph.maskFormat());
 
-    if (!this->setAdvance(glyph)) {
-        return;
+    mx.extraBits = ScalerContextBits::NONE;
+
+    if (!this->setAdvance(glyph, &mx.advance)) {
+        return mx;
     }
 
     DWriteFontTypeface* typeface = this->getDWriteTypeface();
     if (typeface->fIsColorFont) {
-        if (generateColorV1Metrics(glyph)) {
-            glyph->fMaskFormat = SkMask::kARGB32_Format;
-            glyph->fScalerContextBits |= ScalerContextBits::COLRv1;
-            glyph->setPath(alloc, nullptr, false);
-            return;
+        if (generateColorV1Metrics(glyph, &mx.bounds)) {
+            mx.maskFormat = SkMask::kARGB32_Format;
+            mx.extraBits |= ScalerContextBits::COLRv1;
+            mx.neverRequestPath = true;
+            return mx;
         }
 
-        if (generateColorMetrics(glyph)) {
-            glyph->fMaskFormat = SkMask::kARGB32_Format;
-            glyph->fScalerContextBits |= ScalerContextBits::COLR;
-            glyph->setPath(alloc, nullptr, false);
-            return;
+        if (generateColorMetrics(glyph, &mx.bounds)) {
+            mx.maskFormat = SkMask::kARGB32_Format;
+            mx.extraBits |= ScalerContextBits::COLR;
+            mx.neverRequestPath = true;
+            return mx;
         }
 
-        if (generateSVGMetrics(glyph)) {
-            glyph->fMaskFormat = SkMask::kARGB32_Format;
-            glyph->fScalerContextBits |= ScalerContextBits::SVG;
-            glyph->setPath(alloc, nullptr, false);
-            return;
+        if (generateSVGMetrics(glyph, &mx.bounds)) {
+            mx.maskFormat = SkMask::kARGB32_Format;
+            mx.extraBits |= ScalerContextBits::SVG;
+            mx.neverRequestPath = true;
+            return mx;
         }
 
-        if (generatePngMetrics(glyph)) {
-            glyph->fMaskFormat = SkMask::kARGB32_Format;
-            glyph->fScalerContextBits |= ScalerContextBits::PNG;
-            glyph->setPath(alloc, nullptr, false);
-            return;
+        if (generatePngMetrics(glyph, &mx.bounds)) {
+            mx.maskFormat = SkMask::kARGB32_Format;
+            mx.extraBits |= ScalerContextBits::PNG;
+            mx.neverRequestPath = true;
+            return mx;
         }
     }
 
-    if (this->generateDWMetrics(glyph, fRenderingMode, fTextureType)) {
-        glyph->fScalerContextBits = ScalerContextBits::DW;
-        return;
+    if (this->generateDWMetrics(glyph, fRenderingMode, fTextureType, &mx.bounds)) {
+        mx.extraBits = ScalerContextBits::DW;
+        return mx;
     }
 
     // GetAlphaTextureBounds succeeds but returns an empty RECT if there are no
@@ -1828,30 +1796,21 @@
     {
         if (this->generateDWMetrics(glyph,
                                     DWRITE_RENDERING_MODE_ALIASED,
-                                    DWRITE_TEXTURE_ALIASED_1x1))
+                                    DWRITE_TEXTURE_ALIASED_1x1,
+                                    &mx.bounds))
         {
-            glyph->fMaskFormat = SkMask::kBW_Format;
-            glyph->fScalerContextBits = ScalerContextBits::DW_1;
-            return;
+            mx.maskFormat = SkMask::kBW_Format;
+            mx.extraBits = ScalerContextBits::DW_1;
+            return mx;
         }
     }
     // TODO: Try DWRITE_TEXTURE_CLEARTYPE_3x1 if DWRITE_TEXTURE_ALIASED_1x1 fails
 
     // GetAlphaTextureBounds can fail for various reasons.
     // As a fallback, attempt to generate the metrics and image from the path.
-    SkDEBUGCODE(glyph->fAdvancesBoundsFormatAndInitialPathDone = true;)
-    this->getPath(*glyph, alloc);
-    const SkPath* devPath = glyph->path();
-    if (devPath) {
-        // Sometimes all the above fails. If so, try to create the glyph from path.
-        const SkMask::Format format = glyph->maskFormat();
-        const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
-        const bool a8LCD = SkToBool(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag);
-        const bool hairline = glyph->pathIsHairline();
-        if (GenerateMetricsFromPath(glyph, *devPath, format, doVert, a8LCD, hairline)) {
-            glyph->fScalerContextBits = ScalerContextBits::PATH;
-        }
-    }
+    mx.computeFromPath = true;
+    mx.extraBits = ScalerContextBits::PATH;
+    return mx;
 }
 
 void SkScalerContext_DW::generateFontMetrics(SkFontMetrics* metrics) {
diff --git a/src/ports/SkScalerContext_win_dw.h b/src/ports/SkScalerContext_win_dw.h
index 01f3e4e..494bb44 100644
--- a/src/ports/SkScalerContext_win_dw.h
+++ b/src/ports/SkScalerContext_win_dw.h
@@ -32,14 +32,14 @@
     ~SkScalerContext_DW() override;
 
 protected:
-    void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override;
+    GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override;
     void generateImage(const SkGlyph& glyph) override;
     bool generatePath(const SkGlyph&, SkPath*) override;
     sk_sp<SkDrawable> generateDrawable(const SkGlyph&) override;
     void generateFontMetrics(SkFontMetrics*) override;
 
 private:
-    bool setAdvance(SkGlyph* glyph);
+    bool setAdvance(const SkGlyph&, SkVector*);
 
     struct ScalerContextBits {
         using value_type = decltype(SkGlyph::fScalerContextBits);
@@ -74,30 +74,28 @@
     }
 
     bool generateColorV1PaintBounds(SkMatrix*, SkRect*, IDWritePaintReader&, DWRITE_PAINT_ELEMENT const &);
-    bool generateColorV1Metrics(SkGlyph*);
+    bool generateColorV1Metrics(const SkGlyph&, SkIRect*);
     bool generateColorV1Image(const SkGlyph&);
     bool drawColorV1Paint(SkCanvas&, IDWritePaintReader&, DWRITE_PAINT_ELEMENT const &);
     bool drawColorV1Image(const SkGlyph&, SkCanvas&);
 
     bool getColorGlyphRun(const SkGlyph&, IDWriteColorGlyphRunEnumerator**);
-    bool generateColorMetrics(SkGlyph*);
+    bool generateColorMetrics(const SkGlyph&, SkIRect*);
     bool generateColorImage(const SkGlyph&);
     bool drawColorImage(const SkGlyph&, SkCanvas&);
 
-    bool generateSVGMetrics(SkGlyph*);
+    bool generateSVGMetrics(const SkGlyph&, SkIRect*);
     bool generateSVGImage(const SkGlyph&);
     bool drawSVGImage(const SkGlyph&, SkCanvas&);
 
-    bool generatePngMetrics(SkGlyph*);
+    bool generatePngMetrics(const SkGlyph&, SkIRect*);
     bool generatePngImage(const SkGlyph&);
     bool drawPngImage(const SkGlyph&, SkCanvas&);
 
-    bool generateDWMetrics(SkGlyph*, DWRITE_RENDERING_MODE, DWRITE_TEXTURE_TYPE);
+    bool generateDWMetrics(const SkGlyph&, DWRITE_RENDERING_MODE, DWRITE_TEXTURE_TYPE, SkIRect*);
     const void* getDWMaskBits(const SkGlyph&, DWRITE_RENDERING_MODE, DWRITE_TEXTURE_TYPE);
     bool generateDWImage(const SkGlyph&);
 
-    static void SetGlyphBounds(SkGlyph* glyph, const SkRect& bounds);
-
     SkTDArray<uint8_t> fBits;
     /** The total matrix without the text height scale. */
     SkMatrix fSkXform;
diff --git a/src/ports/SkTypeface_fontations.cpp b/src/ports/SkTypeface_fontations.cpp
index 874de15..98a7d0a 100644
--- a/src/ports/SkTypeface_fontations.cpp
+++ b/src/ports/SkTypeface_fontations.cpp
@@ -159,26 +159,22 @@
     }
 
 protected:
-    void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override {
-        glyph->fMaskFormat = fRec.fMaskFormat;
-        glyph->zeroMetrics();
+    GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override {
+        GlyphMetrics mx(fRec.fMaskFormat);
 
         SkVector scale;
         SkMatrix remainingMatrix;
-        if (!glyph ||
-            !fRec.computeMatrices(
+        if (!fRec.computeMatrices(
                     SkScalerContextRec::PreMatrixScale::kVertical, &scale, &remainingMatrix)) {
-            return false;
+            return mx;
         }
         float x_advance = 0.0f;
         x_advance = fontations_ffi::advance_width_or_zero(
-                fBridgeFontRef, scale.y(), fBridgeNormalizedCoords, glyph->getGlyphID());
+                fBridgeFontRef, scale.y(), fBridgeNormalizedCoords, glyph.getGlyphID());
         // TODO(drott): y-advance?
-        const SkVector advance = remainingMatrix.mapXY(x_advance, SkFloatToScalar(0.f));
-        glyph->fAdvanceX = SkScalarToFloat(advance.fX);
-        glyph->fAdvanceY = SkScalarToFloat(advance.fY);
-
-        // Always generates from paths, so SkScalerContext::makeGlyph will figure the bounds.
+        mx.advance = remainingMatrix.mapXY(x_advance, SkFloatToScalar(0.f));
+        mx.computeFromPath = true;
+        return mx;
     }
 
     void generateImage(const SkGlyph&) override { SK_ABORT("Should have generated from path."); }
diff --git a/src/utils/SkCustomTypeface.cpp b/src/utils/SkCustomTypeface.cpp
index 255dd1f..a574c7c 100644
--- a/src/utils/SkCustomTypeface.cpp
+++ b/src/utils/SkCustomTypeface.cpp
@@ -253,33 +253,25 @@
     }
 
 protected:
-    void generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) override {
-        glyph->zeroMetrics();
+    GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override {
+        GlyphMetrics mx(glyph.maskFormat());
 
         const SkUserTypeface* tf = this->userTF();
-        auto advance = fMatrix.mapXY(tf->fGlyphRecs[glyph->getGlyphID()].fAdvance, 0);
+        mx.advance = fMatrix.mapXY(tf->fGlyphRecs[glyph.getGlyphID()].fAdvance, 0);
 
-        glyph->fAdvanceX = advance.fX;
-        glyph->fAdvanceY = advance.fY;
-
-        const auto& rec = tf->fGlyphRecs[glyph->getGlyphID()];
+        const auto& rec = tf->fGlyphRecs[glyph.getGlyphID()];
         if (rec.isDrawable()) {
-            glyph->fMaskFormat = SkMask::kARGB32_Format;
+            mx.maskFormat = SkMask::kARGB32_Format;
 
             SkRect bounds = fMatrix.mapRect(rec.fBounds);
-            bounds.offset(SkFixedToScalar(glyph->getSubXFixed()),
-                          SkFixedToScalar(glyph->getSubYFixed()));
-
-            SkIRect ibounds;
-            bounds.roundOut(&ibounds);
-            glyph->fLeft   = ibounds.fLeft;
-            glyph->fTop    = ibounds.fTop;
-            glyph->fWidth  = ibounds.width();
-            glyph->fHeight = ibounds.height();
+            bounds.offset(SkFixedToScalar(glyph.getSubXFixed()),
+                          SkFixedToScalar(glyph.getSubYFixed()));
+            bounds.roundOut(&mx.bounds);
 
             // These do not have an outline path.
-            glyph->setPath(alloc, nullptr, false);
+            mx.neverRequestPath = true;
         }
+        return mx;
     }
 
     void generateImage(const SkGlyph& glyph) override {
diff --git a/tools/fonts/RandomScalerContext.cpp b/tools/fonts/RandomScalerContext.cpp
index f25aa66..69fd6c1 100644
--- a/tools/fonts/RandomScalerContext.cpp
+++ b/tools/fonts/RandomScalerContext.cpp
@@ -26,7 +26,7 @@
                         bool fFakeIt);
 
 protected:
-    void     generateMetrics(SkGlyph*, SkArenaAlloc*) override;
+    GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override;
     void     generateImage(const SkGlyph&) override;
     bool     generatePath(const SkGlyph&, SkPath*) override;
     sk_sp<SkDrawable> generateDrawable(const SkGlyph&) override;
@@ -53,49 +53,58 @@
     fProxy->forceGenerateImageFromPath();
 }
 
-void RandomScalerContext::generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) {
+SkScalerContext::GlyphMetrics RandomScalerContext::generateMetrics(const SkGlyph& origGlyph,
+                                                                   SkArenaAlloc* alloc) {
     // Here we will change the mask format of the glyph
     // NOTE: this may be overridden by the base class (e.g. if a mask filter is applied).
     SkMask::Format format = SkMask::kA8_Format;
-    switch (glyph->getGlyphID() % 4) {
+    switch (origGlyph.getGlyphID() % 4) {
         case 0: format = SkMask::kLCD16_Format; break;
         case 1: format = SkMask::kA8_Format; break;
         case 2: format = SkMask::kARGB32_Format; break;
         case 3: format = SkMask::kBW_Format; break;
     }
 
-    *glyph = fProxy->internalMakeGlyph(glyph->getPackedID(), format, alloc);
+    auto glyph = fProxy->internalMakeGlyph(origGlyph.getPackedID(), format, alloc);
 
-    if (fFakeIt || (glyph->getGlyphID() % 4) != 2) {
-        return;
+    GlyphMetrics mx(SkMask::kA8_Format);
+    mx.advance.fX = glyph.fAdvanceX;
+    mx.advance.fY = glyph.fAdvanceY;
+    mx.bounds = SkIRect::MakeXYWH(glyph.left(), glyph.top(), glyph.width(), glyph.height());
+    mx.maskFormat = glyph.maskFormat();
+    mx.extraBits = glyph.fScalerContextBits;
+
+    if (fFakeIt || (glyph.getGlyphID() % 4) != 2) {
+        mx.neverRequestPath = glyph.setPathHasBeenCalled() && !glyph.path();
+        mx.computeFromPath = !mx.neverRequestPath;
+        return mx;
     }
 
-    fProxy->getPath(*glyph, alloc);
-    if (!glyph->path()) {
-        return;
+    fProxy->getPath(glyph, alloc);
+    if (!glyph.path()) {
+        mx.neverRequestPath = true;
+        return mx;
     }
 
     // The proxy glyph has a path, but this glyph does not.
     // Stash the proxy glyph so it can be used later.
-    const SkGlyph* proxyGlyph = fProxyGlyphs.set(glyph->getPackedID(), std::move(*glyph));
+    const auto packedID = glyph.getPackedID();
+    const SkGlyph* proxyGlyph = fProxyGlyphs.set(packedID, std::move(glyph));
     const SkPath& proxyPath = *proxyGlyph->path();
 
-    *glyph = SkGlyph(glyph->getPackedID());
-    glyph->setPath(alloc, nullptr, false);
-    glyph->fMaskFormat = SkMask::kARGB32_Format;
-    glyph->fAdvanceX = proxyGlyph->fAdvanceX;
-    glyph->fAdvanceY = proxyGlyph->fAdvanceY;
+    mx.neverRequestPath = true;
+    mx.maskFormat = SkMask::kARGB32_Format;
+    mx.advance.fX = proxyGlyph->fAdvanceX;
+    mx.advance.fY = proxyGlyph->fAdvanceY;
+    mx.extraBits = proxyGlyph->fScalerContextBits;
 
     SkRect         storage;
     const SkPaint& paint = this->getRandomTypeface()->paint();
     const SkRect&  newBounds =
             paint.doComputeFastBounds(proxyPath.getBounds(), &storage, SkPaint::kFill_Style);
-    SkIRect ibounds;
-    newBounds.roundOut(&ibounds);
-    glyph->fLeft   = ibounds.fLeft;
-    glyph->fTop    = ibounds.fTop;
-    glyph->fWidth  = ibounds.width();
-    glyph->fHeight = ibounds.height();
+    newBounds.roundOut(&mx.bounds);
+
+    return mx;
 }
 
 void RandomScalerContext::generateImage(const SkGlyph& glyph) {
diff --git a/tools/fonts/TestSVGTypeface.cpp b/tools/fonts/TestSVGTypeface.cpp
index 0f716c2..eae5744 100644
--- a/tools/fonts/TestSVGTypeface.cpp
+++ b/tools/fonts/TestSVGTypeface.cpp
@@ -122,12 +122,9 @@
 TestSVGTypeface::Glyph::Glyph() : fOrigin{0, 0}, fAdvance(0) {}
 TestSVGTypeface::Glyph::~Glyph() {}
 
-void TestSVGTypeface::getAdvance(SkGlyph* glyph) const {
-    SkGlyphID glyphID = glyph->getGlyphID();
-    glyphID           = glyphID < fGlyphCount ? glyphID : 0;
-
-    glyph->fAdvanceX = fGlyphs[glyphID].fAdvance;
-    glyph->fAdvanceY = 0;
+SkVector TestSVGTypeface::getAdvance(SkGlyphID glyphID) const {
+    glyphID = glyphID < fGlyphCount ? glyphID : 0;
+    return {fGlyphs[glyphID].fAdvance, 0};
 }
 
 void TestSVGTypeface::getFontMetrics(SkFontMetrics* metrics) const { *metrics = fFontMetrics; }
@@ -189,23 +186,18 @@
         return static_cast<TestSVGTypeface*>(this->getTypeface());
     }
 
-    void setAdvance(SkGlyph* glyph) {
-        this->getTestSVGTypeface()->getAdvance(glyph);
-
-        const SkVector advance =
-                fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), SkFloatToScalar(glyph->fAdvanceY));
-        glyph->fAdvanceX = SkScalarToFloat(advance.fX);
-        glyph->fAdvanceY = SkScalarToFloat(advance.fY);
+    SkVector computeAdvance(SkGlyphID glyphID) {
+        auto advance = this->getTestSVGTypeface()->getAdvance(glyphID);
+        return fMatrix.mapXY(advance.fX, advance.fY);
     }
 
-    void generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) override {
-        SkGlyphID glyphID = glyph->getGlyphID();
+    GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override {
+        SkGlyphID glyphID = glyph.getGlyphID();
         glyphID           = glyphID < this->getTestSVGTypeface()->fGlyphCount ? glyphID : 0;
 
-        glyph->zeroMetrics();
-        glyph->fMaskFormat = SkMask::kARGB32_Format;
-        glyph->setPath(alloc, nullptr, false);
-        this->setAdvance(glyph);
+        GlyphMetrics mx(SkMask::kARGB32_Format);
+        mx.neverRequestPath = true;
+        mx.advance = this->computeAdvance(glyph.getGlyphID());
 
         TestSVGTypeface::Glyph& glyphData = this->getTestSVGTypeface()->fGlyphs[glyphID];
 
@@ -215,16 +207,11 @@
                                             containerSize.fWidth,
                                             containerSize.fHeight);
         fMatrix.mapRect(&newBounds);
-        SkScalar dx = SkFixedToScalar(glyph->getSubXFixed());
-        SkScalar dy = SkFixedToScalar(glyph->getSubYFixed());
+        SkScalar dx = SkFixedToScalar(glyph.getSubXFixed());
+        SkScalar dy = SkFixedToScalar(glyph.getSubYFixed());
         newBounds.offset(dx, dy);
-
-        SkIRect ibounds;
-        newBounds.roundOut(&ibounds);
-        glyph->fLeft   = ibounds.fLeft;
-        glyph->fTop    = ibounds.fTop;
-        glyph->fWidth  = ibounds.width();
-        glyph->fHeight = ibounds.height();
+        newBounds.roundOut(&mx.bounds);
+        return mx;
     }
 
     void generateImage(const SkGlyph& glyph) override {
diff --git a/tools/fonts/TestSVGTypeface.h b/tools/fonts/TestSVGTypeface.h
index f4f8ee0..1901945 100644
--- a/tools/fonts/TestSVGTypeface.h
+++ b/tools/fonts/TestSVGTypeface.h
@@ -52,7 +52,7 @@
 class TestSVGTypeface : public SkTypeface {
 public:
     ~TestSVGTypeface() override;
-    void getAdvance(SkGlyph* glyph) const;
+    SkVector getAdvance(SkGlyphID) const;
     void getFontMetrics(SkFontMetrics* metrics) const;
 
     static sk_sp<TestSVGTypeface> Default();
diff --git a/tools/fonts/TestTypeface.cpp b/tools/fonts/TestTypeface.cpp
index f4029a5..008f3ca 100644
--- a/tools/fonts/TestTypeface.cpp
+++ b/tools/fonts/TestTypeface.cpp
@@ -127,13 +127,11 @@
 TestTypeface::TestTypeface(sk_sp<SkTestFont> testFont, const SkFontStyle& style)
         : SkTypeface(style, false), fTestFont(std::move(testFont)) {}
 
-void TestTypeface::getAdvance(SkGlyph* glyph) {
-    SkGlyphID glyphID = glyph->getGlyphID();
-    glyphID           = glyphID < fTestFont->fCharCodesCount ? glyphID : 0;
+SkVector TestTypeface::getAdvance(SkGlyphID glyphID) const {
+    glyphID = glyphID < fTestFont->fCharCodesCount ? glyphID : 0;
 
     // TODO(benjaminwagner): Update users to use floats.
-    glyph->fAdvanceX = SkFixedToFloat(fTestFont->fWidths[glyphID]);
-    glyph->fAdvanceY = 0;
+    return {SkFixedToFloat(fTestFont->fWidths[glyphID]), 0};
 }
 
 void TestTypeface::getFontMetrics(SkFontMetrics* metrics) { *metrics = fTestFont->fMetrics; }
@@ -260,15 +258,13 @@
         return static_cast<TestTypeface*>(this->getTypeface());
     }
 
-    void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override {
-        glyph->zeroMetrics();
+    GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override {
+        GlyphMetrics mx(glyph.maskFormat());
 
-        this->getTestTypeface()->getAdvance(glyph);
+        auto advance = this->getTestTypeface()->getAdvance(glyph.getGlyphID());
 
-        const SkVector advance =
-                fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), SkFloatToScalar(glyph->fAdvanceY));
-        glyph->fAdvanceX = SkScalarToFloat(advance.fX);
-        glyph->fAdvanceY = SkScalarToFloat(advance.fY);
+        mx.advance = fMatrix.mapXY(advance.fX, advance.fY);
+        return mx;
 
         // Always generates from paths, so SkScalerContext::makeGlyph will figure the bounds.
     }
diff --git a/tools/fonts/TestTypeface.h b/tools/fonts/TestTypeface.h
index de4077c..a93bed2 100644
--- a/tools/fonts/TestTypeface.h
+++ b/tools/fonts/TestTypeface.h
@@ -78,7 +78,7 @@
     };
     static const List& Typefaces();
 
-    void getAdvance(SkGlyph* glyph);
+    SkVector getAdvance(SkGlyphID) const;
     void getFontMetrics(SkFontMetrics* metrics);
     SkPath getPath(SkGlyphID glyph);