Bug 1582231 - remove Moz2D dependency on Cairo glyph extents. r=jfkthame
authorLee Salzman <lsalzman@mozilla.com>
Fri, 20 Sep 2019 16:30:21 +0000
changeset 494311 a1b851fdff169264475d1b5160faefc7f2c803d2
parent 494310 7a80958fe601fe7e853d4c179c2e5c5d8293aa56
child 494312 3c87cd65ab8aa8a3c25d95164d9af671eda9cc66
push id114114
push userdluca@mozilla.com
push dateFri, 20 Sep 2019 22:00:08 +0000
treeherdermozilla-inbound@56e11fddf939 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1582231
milestone71.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1582231 - remove Moz2D dependency on Cairo glyph extents. r=jfkthame Differential Revision: https://phabricator.services.mozilla.com/D46332
gfx/2d/2D.h
gfx/2d/DrawTargetCairo.cpp
gfx/2d/DrawTargetCairo.h
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetD2D1.h
gfx/2d/ScaledFontBase.cpp
gfx/2d/ScaledFontBase.h
gfx/2d/ScaledFontDWrite.cpp
gfx/2d/ScaledFontDWrite.h
gfx/thebes/gfxDWriteFonts.cpp
gfx/thebes/gfxDWriteFonts.h
gfx/thebes/gfxFT2FontBase.cpp
gfx/thebes/gfxFT2FontBase.h
gfx/thebes/gfxFT2Fonts.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxGDIFont.cpp
gfx/thebes/gfxGDIFont.h
gfx/thebes/gfxMacFont.cpp
gfx/thebes/gfxMacFont.h
layout/generic/TextDrawTarget.h
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -725,35 +725,16 @@ static inline bool operator==(const Glyp
  * as roc suggested. But for now it's a simple container for a glyph vector.
  */
 struct GlyphBuffer {
   const Glyph*
       mGlyphs;  //!< A pointer to a buffer of glyphs. Managed by the caller.
   uint32_t mNumGlyphs;  //!< Number of glyphs mGlyphs points to.
 };
 
-struct GlyphMetrics {
-  // Horizontal distance from the origin to the leftmost side of the bounding
-  // box of the drawn glyph. This can be negative!
-  Float mXBearing;
-  // Horizontal distance from the origin of this glyph to the origin of the
-  // next glyph.
-  Float mXAdvance;
-  // Vertical distance from the origin to the topmost side of the bounding box
-  // of the drawn glyph.
-  Float mYBearing;
-  // Vertical distance from the origin of this glyph to the origin of the next
-  // glyph, this is used when drawing vertically and will typically be 0.
-  Float mYAdvance;
-  // Width of the glyph's black box.
-  Float mWidth;
-  // Height of the glyph's black box.
-  Float mHeight;
-};
-
 #ifdef MOZ_ENABLE_FREETYPE
 class SharedFTFace;
 
 /** SharedFTFaceData abstracts data that may be used to back a SharedFTFace.
  * Its main function is to manage the lifetime of the data and ensure that it
  * lasts as long as the face.
  */
 class SharedFTFaceData {
@@ -905,22 +886,16 @@ class ScaledFont : public SupportsThread
    * API rather than a generic API to append paths because it allows easier
    * implementation in some backends, and more efficient implementation in
    * others.
    */
   virtual void CopyGlyphsToBuilder(const GlyphBuffer& aBuffer,
                                    PathBuilder* aBuilder,
                                    const Matrix* aTransformHint = nullptr) = 0;
 
-  /* This gets the metrics of a set of glyphs for the current font face.
-   */
-  virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices,
-                                     uint32_t aNumGlyphs,
-                                     GlyphMetrics* aGlyphMetrics) = 0;
-
   typedef void (*FontInstanceDataOutput)(const uint8_t* aData, uint32_t aLength,
                                          const FontVariation* aVariations,
                                          uint32_t aNumVariations, void* aBaton);
 
   virtual bool GetFontInstanceData(FontInstanceDataOutput, void*) {
     return false;
   }
 
@@ -1487,35 +1462,16 @@ class DrawTarget : public external::Atom
    * inputs.
    *
    * @param aType Type of filter node to be created.
    */
   virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) = 0;
 
   Matrix GetTransform() const { return mTransform; }
 
-  /*
-   * Get the metrics of a glyph, including any additional spacing that is taken
-   * during rasterization to this backends (for example because of antialiasing
-   * filters.
-   *
-   * aScaledFont The scaled font used when drawing.
-   * aGlyphIndices An array of indices for the glyphs whose the metrics are
-   *               wanted
-   * aNumGlyphs The amount of elements in aGlyphIndices
-   * aGlyphMetrics The glyph metrics
-   */
-  virtual void GetGlyphRasterizationMetrics(ScaledFont* aScaledFont,
-                                            const uint16_t* aGlyphIndices,
-                                            uint32_t aNumGlyphs,
-                                            GlyphMetrics* aGlyphMetrics) {
-    aScaledFont->GetGlyphDesignMetrics(aGlyphIndices, aNumGlyphs,
-                                       aGlyphMetrics);
-  }
-
   /**
    * Set a transform on the surface, this transform is applied at drawing time
    * to both the mask and source of the operation.
    *
    * Performance note: For some backends it is expensive to change the current
    * transform (because transforms affect a lot of the parts of the pipeline,
    * so new transform change can result in a pipeline flush).  To get around
    * this, DrawTarget implementations buffer transform changes and try to only
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1574,42 +1574,16 @@ already_AddRefed<GradientStops> DrawTarg
     GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
   return MakeAndAddRef<GradientStopsCairo>(aStops, aNumStops, aExtendMode);
 }
 
 already_AddRefed<FilterNode> DrawTargetCairo::CreateFilter(FilterType aType) {
   return FilterNodeSoftware::Create(aType);
 }
 
-void DrawTargetCairo::GetGlyphRasterizationMetrics(
-    ScaledFont* aScaledFont, const uint16_t* aGlyphIndices, uint32_t aNumGlyphs,
-    GlyphMetrics* aGlyphMetrics) {
-  cairo_scaled_font_t* cairoFont = aScaledFont->GetCairoScaledFont();
-  if (!cairoFont ||
-      cairo_scaled_font_status(cairoFont) != CAIRO_STATUS_SUCCESS) {
-    return;
-  }
-  cairo_set_scaled_font(mContext, cairoFont);
-  for (uint32_t i = 0; i < aNumGlyphs; i++) {
-    cairo_glyph_t glyph;
-    cairo_text_extents_t extents;
-    glyph.index = aGlyphIndices[i];
-    glyph.x = 0;
-    glyph.y = 0;
-    cairo_glyph_extents(mContext, &glyph, 1, &extents);
-
-    aGlyphMetrics[i].mXBearing = extents.x_bearing;
-    aGlyphMetrics[i].mXAdvance = extents.x_advance;
-    aGlyphMetrics[i].mYBearing = extents.y_bearing;
-    aGlyphMetrics[i].mYAdvance = extents.y_advance;
-    aGlyphMetrics[i].mWidth = extents.width;
-    aGlyphMetrics[i].mHeight = extents.height;
-  }
-}
-
 already_AddRefed<SourceSurface> DrawTargetCairo::CreateSourceSurfaceFromData(
     unsigned char* aData, const IntSize& aSize, int32_t aStride,
     SurfaceFormat aFormat) const {
   if (!aData) {
     gfxWarning() << "DrawTargetCairo::CreateSourceSurfaceFromData null aData";
     return nullptr;
   }
 
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -147,20 +147,16 @@ class DrawTargetCairo final : public Dra
       const Rect& aBounds, SurfaceFormat aFormat) override;
 
   virtual already_AddRefed<GradientStops> CreateGradientStops(
       GradientStop* aStops, uint32_t aNumStops,
       ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
 
   virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
 
-  virtual void GetGlyphRasterizationMetrics(
-      ScaledFont* aScaledFont, const uint16_t* aGlyphIndices,
-      uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) override;
-
   virtual void* GetNativeSurface(NativeSurfaceType aType) override;
 
   bool Init(cairo_surface_t* aSurface, const IntSize& aSize,
             SurfaceFormat* aFormat = nullptr);
   bool Init(const IntSize& aSize, SurfaceFormat aFormat);
   bool Init(unsigned char* aData, const IntSize& aSize, int32_t aStride,
             SurfaceFormat aFormat);
 
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -126,17 +126,18 @@ bool DrawTargetD2D1::EnsureLuminanceEffe
   HRESULT hr = mDC->CreateEffect(CLSID_D2D1ColorMatrix,
                                  getter_AddRefs(mLuminanceEffect));
   if (FAILED(hr)) {
     gfxCriticalError() << "Failed to create luminance effect. Code: "
                        << hexa(hr);
     return false;
   }
 
-  mLuminanceEffect->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX, kLuminanceMatrix);
+  mLuminanceEffect->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX,
+                             kLuminanceMatrix);
   mLuminanceEffect->SetValue(D2D1_COLORMATRIX_PROP_ALPHA_MODE,
                              D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT);
   return true;
 }
 
 already_AddRefed<SourceSurface> DrawTargetD2D1::IntoLuminanceSource(
     LuminanceType aLuminanceType, float aOpacity) {
   if (!EnsureInitialized()) {
@@ -1203,34 +1204,16 @@ already_AddRefed<GradientStops> DrawTarg
 
 already_AddRefed<FilterNode> DrawTargetD2D1::CreateFilter(FilterType aType) {
   if (!EnsureInitialized()) {
     return nullptr;
   }
   return FilterNodeD2D1::Create(mDC, aType);
 }
 
-void DrawTargetD2D1::GetGlyphRasterizationMetrics(ScaledFont* aScaledFont,
-                                                  const uint16_t* aGlyphIndices,
-                                                  uint32_t aNumGlyphs,
-                                                  GlyphMetrics* aGlyphMetrics) {
-  MOZ_ASSERT(aScaledFont->GetType() == FontType::DWRITE);
-
-  aScaledFont->GetGlyphDesignMetrics(aGlyphIndices, aNumGlyphs, aGlyphMetrics);
-
-  // GetDesignGlyphMetrics returns 'ideal' glyph metrics, we need to pad to
-  // account for antialiasing.
-  for (uint32_t i = 0; i < aNumGlyphs; i++) {
-    if (aGlyphMetrics[i].mWidth > 0 && aGlyphMetrics[i].mHeight > 0) {
-      aGlyphMetrics[i].mWidth += 2.0f;
-      aGlyphMetrics[i].mXBearing -= 1.0f;
-    }
-  }
-}
-
 bool DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat) {
   RefPtr<ID2D1Device> device = Factory::GetD2D1Device(&mDeviceSeq);
   if (!device) {
     gfxCriticalNote << "[D2D1.1] Failed to obtain a device for "
                        "DrawTargetD2D1::Init(ID3D11Texture2D*, SurfaceFormat).";
     return false;
   }
 
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -134,20 +134,16 @@ class DrawTargetD2D1 : public DrawTarget
   }
 
   virtual void* GetNativeSurface(NativeSurfaceType aType) override {
     return nullptr;
   }
 
   virtual void DetachAllSnapshots() override { MarkChanged(); }
 
-  virtual void GetGlyphRasterizationMetrics(
-      ScaledFont* aScaledFont, const uint16_t* aGlyphIndices,
-      uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) override;
-
   bool Init(const IntSize& aSize, SurfaceFormat aFormat);
   bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat);
   uint32_t GetByteSize() const;
 
   // This function will get an image for a surface, it may adjust the source
   // transform for any transformation of the resulting image relative to the
   // oritingal SourceSurface. By default, the surface and its transform are
   // interpreted in user-space, but may be specified in device-space instead.
--- a/gfx/2d/ScaledFontBase.cpp
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -232,68 +232,16 @@ void ScaledFontBase::CopyGlyphsToBuilder
     RefPtr<Path> path = MakeAndAddRef<PathSkia>(skPath, FillRule::FILL_WINDING);
     path->StreamToSink(aBuilder);
     return;
   }
 #endif
   MOZ_ASSERT(false, "Path not being copied");
 }
 
-void ScaledFontBase::GetGlyphDesignMetrics(const uint16_t* aGlyphs,
-                                           uint32_t aNumGlyphs,
-                                           GlyphMetrics* aGlyphMetrics) {
-#ifdef USE_CAIRO_SCALED_FONT
-  if (mScaledFont) {
-    for (uint32_t i = 0; i < aNumGlyphs; i++) {
-      cairo_glyph_t glyph;
-      cairo_text_extents_t extents;
-      glyph.index = aGlyphs[i];
-      glyph.x = 0;
-      glyph.y = 0;
-
-      cairo_scaled_font_glyph_extents(mScaledFont, &glyph, 1, &extents);
-
-      aGlyphMetrics[i].mXBearing = extents.x_bearing;
-      aGlyphMetrics[i].mXAdvance = extents.x_advance;
-      aGlyphMetrics[i].mYBearing = extents.y_bearing;
-      aGlyphMetrics[i].mYAdvance = extents.y_advance;
-      aGlyphMetrics[i].mWidth = extents.width;
-      aGlyphMetrics[i].mHeight = extents.height;
-
-      cairo_font_options_t* options = cairo_font_options_create();
-      cairo_scaled_font_get_font_options(mScaledFont, options);
-
-      if (cairo_font_options_get_antialias(options) != CAIRO_ANTIALIAS_NONE) {
-        if (cairo_scaled_font_get_type(mScaledFont) == CAIRO_FONT_TYPE_WIN32) {
-          if (aGlyphMetrics[i].mWidth > 0 && aGlyphMetrics[i].mHeight > 0) {
-            aGlyphMetrics[i].mWidth -= 3.0f;
-            aGlyphMetrics[i].mXBearing += 1.0f;
-          }
-        }
-#  if defined(MOZ2D_HAS_MOZ_CAIRO) && defined(CAIRO_HAS_DWRITE_FONT)
-        else if (cairo_scaled_font_get_type(mScaledFont) ==
-                 CAIRO_FONT_TYPE_DWRITE) {
-          if (aGlyphMetrics[i].mWidth > 0 && aGlyphMetrics[i].mHeight > 0) {
-            aGlyphMetrics[i].mWidth -= 2.0f;
-            aGlyphMetrics[i].mXBearing += 1.0f;
-          }
-        }
-#  endif
-      }
-      cairo_font_options_destroy(options);
-    }
-    return;
-  }
-#endif
-
-  // Don't know how to get the glyph metrics...
-  MOZ_CRASH(
-      "The specific backend type is not supported for GetGlyphDesignMetrics.");
-}
-
 #ifdef USE_CAIRO_SCALED_FONT
 void ScaledFontBase::SetCairoScaledFont(cairo_scaled_font_t* font) {
   MOZ_ASSERT(!mScaledFont);
 
   if (font == mScaledFont) return;
 
   if (mScaledFont) cairo_scaled_font_destroy(mScaledFont);
 
--- a/gfx/2d/ScaledFontBase.h
+++ b/gfx/2d/ScaledFontBase.h
@@ -35,20 +35,16 @@ class ScaledFontBase : public ScaledFont
 
   virtual already_AddRefed<Path> GetPathForGlyphs(
       const GlyphBuffer& aBuffer, const DrawTarget* aTarget) override;
 
   virtual void CopyGlyphsToBuilder(const GlyphBuffer& aBuffer,
                                    PathBuilder* aBuilder,
                                    const Matrix* aTransformHint) override;
 
-  virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices,
-                                     uint32_t aNumGlyphs,
-                                     GlyphMetrics* aGlyphMetrics) override;
-
   virtual Float GetSize() const override { return mSize; }
 
 #ifdef USE_SKIA
   SkTypeface* GetSkTypeface();
   virtual void SetupSkFontDrawOptions(SkFont& aFont) {}
 #endif
 
 #ifdef USE_CAIRO_SCALED_FONT
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -218,44 +218,16 @@ void ScaledFontDWrite::CopyGlyphsToBuild
   if (pathBuilderD2D->IsFigureActive()) {
     gfxCriticalNote
         << "Attempting to copy glyphs to PathBuilderD2D with active figure.";
   }
 
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 }
 
-void ScaledFontDWrite::GetGlyphDesignMetrics(const uint16_t* aGlyphs,
-                                             uint32_t aNumGlyphs,
-                                             GlyphMetrics* aGlyphMetrics) {
-  DWRITE_FONT_METRICS fontMetrics;
-  mFontFace->GetMetrics(&fontMetrics);
-
-  std::vector<DWRITE_GLYPH_METRICS> metrics(aNumGlyphs);
-  mFontFace->GetDesignGlyphMetrics(aGlyphs, aNumGlyphs, &metrics.front());
-
-  Float scaleFactor = mSize / fontMetrics.designUnitsPerEm;
-
-  for (uint32_t i = 0; i < aNumGlyphs; i++) {
-    aGlyphMetrics[i].mXBearing = metrics[i].leftSideBearing * scaleFactor;
-    aGlyphMetrics[i].mXAdvance = metrics[i].advanceWidth * scaleFactor;
-    aGlyphMetrics[i].mYBearing =
-        (metrics[i].topSideBearing - metrics[i].verticalOriginY) * scaleFactor;
-    aGlyphMetrics[i].mYAdvance = metrics[i].advanceHeight * scaleFactor;
-    aGlyphMetrics[i].mWidth =
-        (metrics[i].advanceWidth - metrics[i].leftSideBearing -
-         metrics[i].rightSideBearing) *
-        scaleFactor;
-    aGlyphMetrics[i].mHeight =
-        (metrics[i].advanceHeight - metrics[i].topSideBearing -
-         metrics[i].bottomSideBearing) *
-        scaleFactor;
-  }
-}
-
 void ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer& aBuffer,
                                         ID2D1SimplifiedGeometrySink* aSink) {
   std::vector<UINT16> indices;
   std::vector<FLOAT> advances;
   std::vector<DWRITE_GLYPH_OFFSET> offsets;
   indices.resize(aBuffer.mNumGlyphs);
   advances.resize(aBuffer.mNumGlyphs);
   offsets.resize(aBuffer.mNumGlyphs);
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -43,19 +43,16 @@ class ScaledFontDWrite final : public Sc
   already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer& aBuffer,
                                           const DrawTarget* aTarget) override;
   void CopyGlyphsToBuilder(const GlyphBuffer& aBuffer, PathBuilder* aBuilder,
                            const Matrix* aTransformHint) override;
 
   void CopyGlyphsToSink(const GlyphBuffer& aBuffer,
                         ID2D1SimplifiedGeometrySink* aSink);
 
-  void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs,
-                             GlyphMetrics* aGlyphMetrics) override;
-
   bool CanSerialize() override { return true; }
 
   bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;
 
   bool GetWRFontInstanceOptions(
       Maybe<wr::FontInstanceOptions>* aOutOptions,
       Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
       std::vector<FontVariation>* aOutVariations) override;
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -615,16 +615,36 @@ gfxFloat gfxDWriteFont::MeasureGlyphWidt
       if (SUCCEEDED(hr)) {
         return NS_lround(metrics.advanceWidth * mFUnitsConvFactor);
       }
     }
   }
   return 0.0;
 }
 
+bool gfxDWriteFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
+                                   bool aTight) {
+  DWRITE_GLYPH_METRICS m;
+  HRESULT hr = mFontFace->GetDesignGlyphMetrics(&aGID, 1, &m, FALSE);
+  if (FAILED(hr)) {
+    return false;
+  }
+  gfxRect bounds(m.leftSideBearing, m.topSideBearing - m.verticalOriginY,
+                 m.advanceWidth - m.leftSideBearing - m.rightSideBearing,
+                 m.advanceHeight - m.topSideBearing - m.bottomSideBearing);
+  bounds.Scale(mFUnitsConvFactor);
+  // GetDesignGlyphMetrics returns 'ideal' glyph metrics, we need to pad to
+  // account for antialiasing.
+  if (!aTight && !aBounds->IsEmpty()) {
+    bounds.Inflate(1.0, 0.0);
+  }
+  *aBounds = bounds;
+  return true;
+}
+
 void gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                            FontCacheSizes* aSizes) const {
   gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   aSizes->mFontInstances += aMallocSizeOf(mMetrics);
   if (mGlyphWidths) {
     aSizes->mFontInstances +=
         mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
   }
--- a/gfx/thebes/gfxDWriteFonts.h
+++ b/gfx/thebes/gfxDWriteFonts.h
@@ -52,16 +52,18 @@ class gfxDWriteFont : public gfxFont {
                      DrawTarget* aDrawTargetForTightBoundingBox,
                      Spacing* aSpacing,
                      mozilla::gfx::ShapedTextFlags aOrientation) override;
 
   bool ProvidesGlyphWidths() const override;
 
   int32_t GetGlyphWidth(uint16_t aGID) override;
 
+  bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) override;
+
   void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                               FontCacheSizes* aSizes) const override;
   void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                               FontCacheSizes* aSizes) const override;
 
   FontType GetType() const override { return FONT_TYPE_DWRITE; }
 
   already_AddRefed<mozilla::gfx::ScaledFont> GetScaledFont(
--- a/gfx/thebes/gfxFT2FontBase.cpp
+++ b/gfx/thebes/gfxFT2FontBase.cpp
@@ -136,43 +136,27 @@ static void SnapLineToPixels(gfxFloat& a
   // Snap offset
   aOffset = floor(offset + 0.5);
   aSize = snappedSize;
 }
 
 /**
  * Get extents for a simple character representable by a single glyph.
  * The return value is the glyph id of that glyph or zero if no such glyph
- * exists.  aExtents is only set when this returns a non-zero glyph id.
+ * exists.  aWidth/aBounds is only set when this returns a non-zero glyph id.
+ * This is just for use during initialization, and doesn't use the width cache.
  */
 uint32_t gfxFT2FontBase::GetCharExtents(char aChar, gfxFloat* aWidth,
-                                        gfxFloat* aHeight) {
+                                        gfxRect* aBounds) {
   FT_UInt gid = GetGlyph(aChar);
   int32_t width;
-  int32_t height;
-  if (gid && GetFTGlyphExtents(gid, &width, &height)) {
-    *aWidth = FLOAT_FROM_16_16(width);
-    *aHeight = FLOAT_FROM_26_6(height);
-    return gid;
-  } else {
-    return 0;
-  }
-}
-
-/**
- * Get glyph id and width for a simple character.
- * The return value is the glyph id of that glyph or zero if no such glyph
- * exists.  aWidth is only set when this returns a non-zero glyph id.
- * This is just for use during initialization, and doesn't use the width cache.
- */
-uint32_t gfxFT2FontBase::GetCharWidth(char aChar, gfxFloat* aWidth) {
-  FT_UInt gid = GetGlyph(aChar);
-  int32_t width;
-  if (gid && GetFTGlyphExtents(gid, &width)) {
-    *aWidth = FLOAT_FROM_16_16(width);
+  if (gid && GetFTGlyphExtents(gid, &width, aBounds)) {
+    if (aWidth) {
+      *aWidth = FLOAT_FROM_16_16(width);
+    }
     return gid;
   } else {
     return 0;
   }
 }
 
 /**
  * Find the closest available fixed strike size, if applicable, to the
@@ -397,42 +381,42 @@ void gfxFT2FontBase::InitMetrics() {
     mMetrics.capHeight = mMetrics.maxAscent;
   }
 
   // Release the face lock to safely load glyphs with GetCharExtents if
   // necessary without recursively locking.
   UnlockFTFace();
 
   gfxFloat width;
-  mSpaceGlyph = GetCharWidth(' ', &width);
+  mSpaceGlyph = GetCharExtents(' ', &width);
   if (mSpaceGlyph) {
     mMetrics.spaceWidth = width;
   } else {
     mMetrics.spaceWidth = mMetrics.maxAdvance;  // guess
   }
 
-  if (GetCharWidth('0', &width)) {
+  if (GetCharExtents('0', &width)) {
     mMetrics.zeroWidth = width;
   } else {
     mMetrics.zeroWidth = -1.0;  // indicates not found
   }
 
   // Prefering a measured x over sxHeight because sxHeight doesn't consider
   // hinting, but maybe the x extents are not quite right in some fancy
   // script fonts.  CSS 2.1 suggests possibly using the height of an "o",
   // which would have a more consistent glyph across fonts.
   gfxFloat xWidth;
-  gfxFloat xHeight;
-  if (GetCharExtents('x', &xWidth, &xHeight) && xHeight < 0.0) {
-    mMetrics.xHeight = -xHeight;
+  gfxRect xBounds;
+  if (GetCharExtents('x', &xWidth, &xBounds) && xBounds.y < 0.0) {
+    mMetrics.xHeight = -xBounds.y;
     mMetrics.aveCharWidth = std::max(mMetrics.aveCharWidth, xWidth);
   }
 
-  if (GetCharExtents('H', &xWidth, &xHeight) && xHeight < 0.0) {
-    mMetrics.capHeight = -xHeight;
+  if (GetCharExtents('H', nullptr, &xBounds) && xBounds.y < 0.0) {
+    mMetrics.capHeight = -xBounds.y;
   }
 
   mMetrics.aveCharWidth = std::max(mMetrics.aveCharWidth, mMetrics.zeroWidth);
   if (mMetrics.aveCharWidth == 0.0) {
     mMetrics.aveCharWidth = mMetrics.spaceWidth;
   }
   // Apparently hinting can mean that max_advance is not always accurate.
   mMetrics.maxAdvance = std::max(mMetrics.maxAdvance, mMetrics.aveCharWidth);
@@ -499,37 +483,37 @@ uint32_t gfxFT2FontBase::GetGlyph(uint32
       return GetGlyph(unicode);
     }
     return 0;
   }
 
   return GetGlyph(unicode);
 }
 
-FT_Fixed gfxFT2FontBase::GetEmboldenAdvance(FT_Face aFace, FT_Fixed aAdvance) {
-  // If freetype emboldening is being used, and it's not a zero-width glyph,
-  // adjust the advance to account for the increased width.
-  if (!mEmbolden || !aAdvance) {
-    return 0;
+FT_Vector gfxFT2FontBase::GetEmboldenStrength(FT_Face aFace) {
+  FT_Vector strength = { 0, 0 };
+  if (!mEmbolden) {
+    return strength;
   }
-  // This is the embolden "strength" used by FT_GlyphSlot_Embolden,
-  // converted from 26.6 to 16.16
-  FT_Fixed strength =
+  // This is the embolden "strength" used by FT_GlyphSlot_Embolden.
+  strength.x =
       FT_MulFix(aFace->units_per_EM, aFace->size->metrics.y_scale) / 24;
+  strength.y = strength.x;
   if (aFace->glyph->format == FT_GLYPH_FORMAT_BITMAP) {
-    strength &= -64;
-    if (!strength) {
-      strength = 64;
+    strength.x &= -64;
+    if (!strength.x) {
+      strength.x = 64;
     }
+    strength.y &= -64;
   }
-  return strength << 10;
+  return strength;
 }
 
 bool gfxFT2FontBase::GetFTGlyphExtents(uint16_t aGID, int32_t* aAdvance,
-                                       int32_t* aHeight) {
+                                       gfxRect* aBounds) {
   gfxFT2LockedFace face(this);
   MOZ_ASSERT(face.get());
   if (!face.get()) {
     // Failed to get the FT_Face? Give up already.
     NS_WARNING("failed to get FT_Face!");
     return false;
   }
 
@@ -542,42 +526,61 @@ bool gfxFT2FontBase::GetFTGlyphExtents(u
   }
 
   bool hintMetrics = ShouldHintMetrics();
 
   // Normalize out the loaded FT glyph size and then scale to the actually
   // desired size, in case these two sizes differ.
   gfxFloat extentsScale = GetAdjustedSize() / mFTSize;
 
+  FT_Vector bold = GetEmboldenStrength(face.get());
+
   // Due to freetype bug 52683 we MUST use the linearHoriAdvance field when
   // dealing with a variation font; also use it for scalable fonts when not
   // applying hinting. Otherwise, prefer hinted width from glyph->advance.x.
-  FT_Fixed advance;
-  if (face.get()->glyph->format == FT_GLYPH_FORMAT_OUTLINE &&
-      (!hintMetrics || FT_HAS_MULTIPLE_MASTERS(face.get()))) {
-    advance = face.get()->glyph->linearHoriAdvance;
-  } else {
-    advance = face.get()->glyph->advance.x << 10;  // convert 26.6 to 16.16
+  if (aAdvance) {
+    FT_Fixed advance;
+    if (face.get()->glyph->format == FT_GLYPH_FORMAT_OUTLINE &&
+        (!hintMetrics || FT_HAS_MULTIPLE_MASTERS(face.get()))) {
+      advance = face.get()->glyph->linearHoriAdvance;
+    } else {
+      advance = face.get()->glyph->advance.x << 10;  // convert 26.6 to 16.16
+    }
+    if (advance) {
+      advance += bold.x << 10;  // convert 26.6 to 16.16
+    }
+    // Hinting was requested, but FT did not apply any hinting to the metrics.
+    // Round the advance here to approximate hinting as Cairo does. This must
+    // happen BEFORE we apply the glyph extents scale, just like FT hinting
+    // would.
+    if (hintMetrics && (mFTLoadFlags & FT_LOAD_NO_HINTING)) {
+      advance = (advance + 0x8000) & 0xffff0000u;
+    }
+    *aAdvance = NS_lround(advance * extentsScale);
   }
-  advance += GetEmboldenAdvance(face.get(), advance);
-  // Hinting was requested, but FT did not apply any hinting to the metrics.
-  // Round the advance here to approximate hinting as Cairo does. This must
-  // happen BEFORE we apply the glyph extents scale, just like FT hinting
-  // would.
-  if (hintMetrics && (mFTLoadFlags & FT_LOAD_NO_HINTING)) {
-    advance = (advance + 0x8000) & 0xffff0000u;
-  }
-  *aAdvance = NS_lround(advance * extentsScale);
 
-  if (aHeight) {
-    FT_F26Dot6 height = -face.get()->glyph->metrics.horiBearingY;
+  if (aBounds) {
+    const FT_Glyph_Metrics& metrics = face.get()->glyph->metrics;
+    FT_F26Dot6 x = metrics.horiBearingX;
+    FT_F26Dot6 y = -metrics.horiBearingY;
+    FT_F26Dot6 x2 = x + metrics.width;
+    FT_F26Dot6 y2 = y + metrics.height;
+    // Synthetic bold moves the glyph top and right boundaries.
+    y -= bold.y;
+    x2 += bold.x;
     if (hintMetrics && (mFTLoadFlags & FT_LOAD_NO_HINTING)) {
-      height &= -64;
+      x &= -64;
+      y &= -64;
+      x2 = (x2 + 63) & -64;
+      y2 = (y2 + 63) & -64;
     }
-    *aHeight = NS_lround(height * extentsScale);
+    *aBounds = gfxRect(FLOAT_FROM_26_6(x) * extentsScale,
+                       FLOAT_FROM_26_6(y) * extentsScale,
+                       FLOAT_FROM_26_6(x2 - x) * extentsScale,
+                       FLOAT_FROM_26_6(y2 - y) * extentsScale);
   }
   return true;
 }
 
 int32_t gfxFT2FontBase::GetGlyphWidth(uint16_t aGID) {
   if (!mGlyphWidths) {
     mGlyphWidths =
         mozilla::MakeUnique<nsDataHashtable<nsUint32HashKey, int32_t>>(128);
@@ -591,16 +594,21 @@ int32_t gfxFT2FontBase::GetGlyphWidth(ui
   if (!GetFTGlyphExtents(aGID, &width)) {
     width = 0;
   }
   mGlyphWidths->Put(aGID, width);
 
   return width;
 }
 
+bool gfxFT2FontBase::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
+                                    bool aTight) {
+  return GetFTGlyphExtents(aGID, nullptr, aBounds);
+}
+
 // For variation fonts, figure out the variation coordinates to be applied
 // for each axis, in freetype's order (which may not match the order of
 // axes in mStyle.variationSettings, so we need to search by axis tag).
 /*static*/
 void gfxFT2FontBase::SetupVarCoords(
     FT_MM_Var* aMMVar, const nsTArray<gfxFontVariation>& aVariations,
     FT_Face aFTFace) {
   if (!aMMVar) {
--- a/gfx/thebes/gfxFT2FontBase.h
+++ b/gfx/thebes/gfxFT2FontBase.h
@@ -25,39 +25,40 @@ class gfxFT2FontBase : public gfxFont {
 
   uint32_t GetGlyph(uint32_t aCharCode);
   uint32_t GetSpaceGlyph() override;
   bool ProvidesGetGlyph() const override { return true; }
   virtual uint32_t GetGlyph(uint32_t unicode,
                             uint32_t variation_selector) override;
   bool ProvidesGlyphWidths() const override { return true; }
   int32_t GetGlyphWidth(uint16_t aGID) override;
+  bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) override;
 
   FontType GetType() const override { return FONT_TYPE_FT2; }
 
   static void SetupVarCoords(FT_MM_Var* aMMVar,
                              const nsTArray<gfxFontVariation>& aVariations,
                              FT_Face aFTFace);
 
   FT_Face LockFTFace();
   void UnlockFTFace();
 
  private:
-  uint32_t GetCharExtents(char aChar, gfxFloat* aWidth, gfxFloat* aHeight);
-  uint32_t GetCharWidth(char aChar, gfxFloat* aWidth);
+  uint32_t GetCharExtents(char aChar, gfxFloat* aWidth,
+                          gfxRect* aBounds = nullptr);
 
-  // Get advance (and optionally height) of a single glyph from FreeType,
+  // Get advance (and optionally bounds) of a single glyph from FreeType,
   // and return true, or return false if we failed.
   bool GetFTGlyphExtents(uint16_t aGID, int32_t* aWidth,
-                         int32_t* aHeight = nullptr);
+                         gfxRect* aBounds = nullptr);
 
  protected:
   void InitMetrics();
   const Metrics& GetHorizontalMetrics() override;
-  FT_Fixed GetEmboldenAdvance(FT_Face aFace, FT_Fixed aAdvance);
+  FT_Vector GetEmboldenStrength(FT_Face aFace);
 
   RefPtr<mozilla::gfx::SharedFTFace> mFTFace;
 
   uint32_t mSpaceGlyph;
   Metrics mMetrics;
   int mFTLoadFlags;
   bool mEmbolden;
   gfxFloat mFTSize;
--- a/gfx/thebes/gfxFT2Fonts.cpp
+++ b/gfx/thebes/gfxFT2Fonts.cpp
@@ -211,18 +211,20 @@ void gfxFT2Font::FillGlyphDataForChar(FT
 
     gd->glyphIndex = 0;
     return;
   }
 
   gd->glyphIndex = gid;
   gd->lsbDelta = face->glyph->lsb_delta;
   gd->rsbDelta = face->glyph->rsb_delta;
-  gd->xAdvance =
-      face->glyph->advance.x + GetEmboldenAdvance(face, face->glyph->advance.x);
+  gd->xAdvance = face->glyph->advance.x;
+  if (gd->xAdvance) {
+    gd->xAdvance += GetEmboldenStrength(face).x;
+  }
 }
 
 void gfxFT2Font::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                         FontCacheSizes* aSizes) const {
   gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   aSizes->mFontInstances +=
       mCharGlyphCache.ShallowSizeOfExcludingThis(aMallocSizeOf);
 }
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -825,20 +825,19 @@ gfxFont::RoundingFlags gfxFont::GetRound
   // Could do something fancy here for ScaleFactors of
   // AxisAlignedTransforms, but we leave things simple.
   // Not much point rounding if a matrix will mess things up anyway.
   // Also check if the font already knows hint metrics is off...
   if (aDrawTarget->GetTransform().HasNonTranslation() || !ShouldHintMetrics()) {
     return RoundingFlags(0);
   }
 
-  cairo_t* cr = nullptr;
-  if (aDrawTarget->GetBackendType() == BackendType::CAIRO) {
-    cr = static_cast<cairo_t*>(
-        aDrawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+  cairo_t* cr = static_cast<cairo_t*>(
+      aDrawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+  if (cr) {
     cairo_surface_t* target = cairo_get_target(cr);
 
     // Check whether the cairo surface's font options hint metrics.
     cairo_font_options_t* fontOptions = cairo_font_options_create();
     cairo_surface_get_font_options(target, fontOptions);
     cairo_hint_metrics_t hintMetrics =
         cairo_font_options_get_hint_metrics(fontOptions);
     cairo_font_options_destroy(fontOptions);
@@ -3370,48 +3369,41 @@ void gfxFont::SetupGlyphExtents(DrawTarg
                                      &svgBounds)) {
     gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
     aExtents->SetTightGlyphExtents(
         aGlyphID, gfxRect(svgBounds.X() * d2a, svgBounds.Y() * d2a,
                           svgBounds.Width() * d2a, svgBounds.Height() * d2a));
     return;
   }
 
-  RefPtr<ScaledFont> sf = GetScaledFont(aDrawTarget);
-  uint16_t glyphIndex = aGlyphID;
-  GlyphMetrics metrics;
-  if (mAntialiasOption == kAntialiasNone) {
-    sf->GetGlyphDesignMetrics(&glyphIndex, 1, &metrics);
-  } else {
-    aDrawTarget->GetGlyphRasterizationMetrics(sf, &glyphIndex, 1, &metrics);
-  }
+  gfxRect bounds;
+  GetGlyphBounds(aGlyphID, &bounds, mAntialiasOption == kAntialiasNone);
 
   const Metrics& fontMetrics = GetMetrics(nsFontMetrics::eHorizontal);
   int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
-  if (!aNeedTight && metrics.mXBearing >= 0.0 &&
-      metrics.mYBearing >= -fontMetrics.maxAscent &&
-      metrics.mHeight + metrics.mYBearing <= fontMetrics.maxDescent) {
-    uint32_t appUnitsWidth = uint32_t(
-        ceil((metrics.mXBearing + metrics.mWidth) * appUnitsPerDevUnit));
+  if (!aNeedTight && bounds.x >= 0.0 && bounds.y >= -fontMetrics.maxAscent &&
+      bounds.height + bounds.y <= fontMetrics.maxDescent) {
+    uint32_t appUnitsWidth =
+        uint32_t(ceil((bounds.x + bounds.width) * appUnitsPerDevUnit));
     if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
       aExtents->SetContainedGlyphWidthAppUnits(aGlyphID,
                                                uint16_t(appUnitsWidth));
       return;
     }
   }
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
   if (!aNeedTight) {
     ++gGlyphExtentsSetupFallBackToTight;
   }
 #endif
 
   gfxFloat d2a = appUnitsPerDevUnit;
-  gfxRect bounds(metrics.mXBearing * d2a, metrics.mYBearing * d2a,
-                 metrics.mWidth * d2a, metrics.mHeight * d2a);
-  aExtents->SetTightGlyphExtents(aGlyphID, bounds);
+  aExtents->SetTightGlyphExtents(
+      aGlyphID, gfxRect(bounds.x * d2a, bounds.y * d2a, bounds.width * d2a,
+                        bounds.height * d2a));
 }
 
 // Try to initialize font metrics by reading sfnt tables directly;
 // set mIsValid=TRUE and return TRUE on success.
 // Return FALSE if the gfxFontEntry subclass does not
 // implement GetFontTable(), or for non-sfnt fonts where tables are
 // not available.
 // If this returns TRUE without setting the mIsValid flag, then we -did-
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1914,16 +1914,21 @@ class gfxFont {
   // if they do not override this, harfbuzz will use unhinted widths
   // derived from the font tables
   virtual bool ProvidesGlyphWidths() const { return false; }
 
   // The return value is interpreted as a horizontal advance in 16.16 fixed
   // point format.
   virtual int32_t GetGlyphWidth(uint16_t aGID) { return -1; }
 
+  virtual bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
+                              bool aTight = false) {
+    return false;
+  }
+
   bool IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget,
                              const gfxTextRun* aTextRun);
 
   void AddGlyphChangeObserver(GlyphChangeObserver* aObserver);
   void RemoveGlyphChangeObserver(GlyphChangeObserver* aObserver);
 
   // whether font contains substitution lookups containing spaces
   bool HasSubstitutionRulesWithSpaceLookups(Script aRunScript);
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -42,16 +42,17 @@ static inline cairo_antialias_t GetCairo
 
 gfxGDIFont::gfxGDIFont(GDIFontEntry* aFontEntry, const gfxFontStyle* aFontStyle,
                        AntialiasOption anAAOption)
     : gfxFont(nullptr, aFontEntry, aFontStyle, anAAOption),
       mFont(nullptr),
       mFontFace(nullptr),
       mMetrics(nullptr),
       mSpaceGlyph(0),
+      mIsBitmap(false),
       mScriptCache(nullptr) {
   mNeedsSyntheticBold = aFontStyle->NeedsSyntheticBold(aFontEntry);
 
   Initialize();
 
   if (mFont) {
     mUnscaledFont = aFontEntry->LookupUnscaledFont(mFont);
   }
@@ -270,16 +271,18 @@ void gfxGDIFont::Initialize() {
     mMetrics->maxAdvance = metrics.tmMaxCharWidth;
     mMetrics->aveCharWidth = std::max<gfxFloat>(1, metrics.tmAveCharWidth);
     // The font is monospace when TMPF_FIXED_PITCH is *not* set!
     // See http://msdn2.microsoft.com/en-us/library/ms534202(VS.85).aspx
     if (!(metrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
       mMetrics->maxAdvance = mMetrics->aveCharWidth;
     }
 
+    mIsBitmap = !(metrics.tmPitchAndFamily & TMPF_VECTOR);
+
     // For fonts with USE_TYPO_METRICS set in the fsSelection field,
     // let the OS/2 sTypo* metrics override the previous values.
     // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
     // Using the equivalent values from oMetrics provides inconsistent
     // results with CFF fonts, so we instead rely on OS2Table.
     gfxFontEntry::AutoTable os2Table(mFontEntry,
                                      TRUETYPE_TAG('O', 'S', '/', '2'));
     if (os2Table) {
@@ -484,16 +487,60 @@ int32_t gfxGDIFont::GetGlyphWidth(uint16
     width = devWidth << 16;
     mGlyphWidths->Put(aGID, width);
     return width;
   }
 
   return -1;
 }
 
+bool gfxGDIFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) {
+  DCForMetrics dc;
+  AutoSelectFont fs(dc, GetHFONT());
+
+  if (mIsBitmap) {
+    int devWidth;
+    if (!GetCharWidthI(dc, aGID, 1, nullptr, &devWidth)) {
+      return false;
+    }
+    devWidth = std::min(std::max(0, devWidth), 0x7fff);
+
+    *aBounds = gfxRect(0, -mMetrics->maxAscent, devWidth,
+                       mMetrics->maxAscent + mMetrics->maxDescent);
+    return true;
+  }
+
+  const MAT2 kIdentityMatrix = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
+  GLYPHMETRICS gm;
+  if (GetGlyphOutlineW(dc, aGID, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr,
+                       &kIdentityMatrix) == GDI_ERROR) {
+    return false;
+  }
+
+  if (gm.gmBlackBoxX == 1 && gm.gmBlackBoxY == 1 &&
+      !GetGlyphOutlineW(dc, aGID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, nullptr,
+                        &kIdentityMatrix)) {
+    // Workaround for GetGlyphOutline returning 1x1 bounding box
+    // for <space> glyph that is in fact empty.
+    gm.gmBlackBoxX = 0;
+    gm.gmBlackBoxY = 0;
+  } else if (gm.gmBlackBoxX > 0 && !aTight) {
+    // The bounding box reported by Windows supposedly contains the glyph's
+    // "black" area; however, antialiasing (especially with ClearType) means
+    // that the actual image that needs to be rendered may "bleed" into the
+    // adjacent pixels, mainly on the right side.
+    gm.gmptGlyphOrigin.x -= 1;
+    gm.gmBlackBoxX += 3;
+  }
+
+  *aBounds = gfxRect(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y,
+                     gm.gmBlackBoxX, gm.gmBlackBoxY);
+  return true;
+}
+
 void gfxGDIFont::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontCacheSizes* aSizes) const {
   gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   aSizes->mFontInstances += aMallocSizeOf(mMetrics);
   if (mGlyphWidths) {
     aSizes->mFontInstances +=
         mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
   }
--- a/gfx/thebes/gfxGDIFont.h
+++ b/gfx/thebes/gfxGDIFont.h
@@ -50,16 +50,18 @@ class gfxGDIFont : public gfxFont {
 
   uint32_t GetGlyph(uint32_t aUnicode, uint32_t aVarSelector) override;
 
   bool ProvidesGlyphWidths() const override { return true; }
 
   // get hinted glyph width in pixels as 16.16 fixed-point value
   int32_t GetGlyphWidth(uint16_t aGID) override;
 
+  bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) override;
+
   void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                               FontCacheSizes* aSizes) const;
   void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                               FontCacheSizes* aSizes) const;
 
   FontType GetType() const override { return FONT_TYPE_GDI; }
 
  protected:
@@ -79,16 +81,17 @@ class gfxGDIFont : public gfxFont {
   // have generic support for this in gfxFont::Draw instead.)
   void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize);
 
   HFONT mFont;
   cairo_font_face_t* mFontFace;
 
   Metrics* mMetrics;
   uint32_t mSpaceGlyph;
+  bool mIsBitmap;
 
   bool mNeedsSyntheticBold;
 
   // cache of glyph IDs (used for non-sfnt fonts only)
   mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey, uint32_t> > mGlyphIDs;
   SCRIPT_CACHE mScriptCache;
 
   // cache of glyph widths in 16.16 fixed-point pixels
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -507,16 +507,37 @@ int32_t gfxMacFont::GetGlyphWidth(uint16
   }
 
   CGSize advance;
   ::CTFontGetAdvancesForGlyphs(mCTFont, kCTFontDefaultOrientation, &aGID,
                                &advance, 1);
   return advance.width * 0x10000;
 }
 
+bool gfxMacFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) {
+  CGRect bb;
+  if (!::CGFontGetGlyphBBoxes(mCGFont, &aGID, 1, &bb)) {
+    return false;
+  }
+
+  // broken fonts can return incorrect bounds for some null characters,
+  // see https://bugzilla.mozilla.org/show_bug.cgi?id=534260
+  if (bb.origin.x == -32767 && bb.origin.y == -32767 &&
+      bb.size.width == 65534 && bb.size.height == 65534) {
+    *aBounds = gfxRect(0, 0, 0, 0);
+    return true;
+  }
+
+  gfxRect bounds(bb.origin.x, -(bb.origin.y + bb.size.height), bb.size.width,
+                 bb.size.height);
+  bounds.Scale(mFUnitsConvFactor);
+  *aBounds = bounds;
+  return true;
+}
+
 // Try to initialize font metrics via platform APIs (CG/CT),
 // and set mIsValid = TRUE on success.
 // We ONLY call this for local (platform) fonts that are not sfnt format;
 // for sfnts, including ALL downloadable fonts, we prefer to use
 // InitMetricsFromSfntTables and avoid platform APIs.
 void gfxMacFont::InitMetricsFromPlatform() {
   CTFontRef ctFont =
       ::CTFontCreateWithGraphicsFont(mCGFont, mAdjustedSize, nullptr, nullptr);
--- a/gfx/thebes/gfxMacFont.h
+++ b/gfx/thebes/gfxMacFont.h
@@ -36,16 +36,18 @@ class gfxMacFont : public gfxFont {
   // with embedded color bitmaps (Apple Color Emoji), as Core Text renders
   // the glyphs with non-linear scaling at small pixel sizes.
   bool ProvidesGlyphWidths() const override {
     return mVariationFont || mFontEntry->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'));
   }
 
   int32_t GetGlyphWidth(uint16_t aGID) override;
 
+  bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) override;
+
   already_AddRefed<mozilla::gfx::ScaledFont> GetScaledFont(
       mozilla::gfx::DrawTarget* aTarget) override;
 
   bool ShouldRoundXOffset(cairo_t* aCairo) const override;
 
   void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                               FontCacheSizes* aSizes) const override;
   void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -603,21 +603,16 @@ class TextDrawTarget : public DrawTarget
 
   already_AddRefed<GradientStops> CreateGradientStops(
       GradientStop* aStops, uint32_t aNumStops,
       ExtendMode aExtendMode) const override {
     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
     return nullptr;
   }
 
-  void* GetNativeSurface(NativeSurfaceType aType) override {
-    MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
-    return nullptr;
-  }
-
   void DetachAllSnapshots() override {
     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
   }
 };
 
 }  // namespace layout
 }  // namespace mozilla