Bug 1600470 - Reduce the emboldening strength used for synthetic-bold faces with FreeType. r=lsalzman
authorJonathan Kew <jkew@mozilla.com>
Tue, 17 Dec 2019 15:51:57 +0000
changeset 507408 f2d54eefdb444abf8a5dc66a8eafec16ba8c59fc
parent 507407 ecccb4f29be8ae8b92c19aff9941c9346e2c2c22
child 507409 b4cc65a4d11e4569ea64670d24e38ef6c88a075e
push id36927
push useraiakab@mozilla.com
push dateWed, 18 Dec 2019 00:51:03 +0000
treeherdermozilla-central@35af0b925215 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsalzman
bugs1600470
milestone73.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 1600470 - Reduce the emboldening strength used for synthetic-bold faces with FreeType. r=lsalzman Differential Revision: https://phabricator.services.mozilla.com/D57246
gfx/skia/generate_mozbuild.py
gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
gfx/thebes/gfxFT2FontBase.cpp
gfx/wr/webrender/src/platform/unix/font.rs
layout/reftests/css-break/reftest.list
layout/reftests/text/synthetic-bold-metrics-01-notref.html
layout/reftests/text/synthetic-bold-metrics-01.html
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -67,16 +67,20 @@ elif CONFIG['CPU_ARCH'] == 'aarch64' and
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 
 if CONFIG['MOZ_ENABLE_SKIA_PDF_SFNTLY']:
     DEFINES['SK_PDF_USE_SFNTLY'] = 1
 
 if CONFIG['MOZ_TREE_FREETYPE']:
     DEFINES['SK_CAN_USE_DLOPEN'] = 0
 
+# Reduce strength of synthetic-emboldening used in the freetype backend
+# (see bug 1600470).
+DEFINES['SK_OUTLINE_EMBOLDEN_DIVISOR'] = 48
+
 # Suppress warnings in third-party code.
 CXXFLAGS += [
     '-Wno-deprecated-declarations',
     '-Wno-overloaded-virtual',
     '-Wno-shadow',
     '-Wno-sign-compare',
     '-Wno-unreachable-code',
     '-Wno-unused-function',
--- a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
+++ b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
@@ -53,39 +53,38 @@ typedef enum FT_LcdFilter_
 #endif
 
 #ifndef SK_FONTHOST_CAIRO_STANDALONE
 #define SK_FONTHOST_CAIRO_STANDALONE 1
 #endif
 
 static bool gFontHintingEnabled = true;
 static FT_Error (*gSetLcdFilter)(FT_Library, FT_LcdFilter) = nullptr;
-static void (*gGlyphSlotEmbolden)(FT_GlyphSlot) = nullptr;
 
 extern "C"
 {
     void mozilla_LockFTLibrary(FT_Library aLibrary);
     void mozilla_UnlockFTLibrary(FT_Library aLibrary);
     void mozilla_AddRefSharedFTFace(void* aContext);
     void mozilla_ReleaseSharedFTFace(void* aContext, void* aOwner);
     void mozilla_ForgetSharedFTFaceLockOwner(void* aContext, void* aOwner);
     int mozilla_LockSharedFTFace(void* aContext, void* aOwner);
     void mozilla_UnlockSharedFTFace(void* aContext);
     FT_Error mozilla_LoadFTGlyph(FT_Face aFace, uint32_t aGlyphIndex, int32_t aFlags);
+    // Implemented in webrender:
+    void mozilla_glyphslot_embolden_less(FT_GlyphSlot slot);
 }
 
 void SkInitCairoFT(bool fontHintingEnabled)
 {
     gFontHintingEnabled = fontHintingEnabled;
 #if SK_CAN_USE_DLOPEN
     gSetLcdFilter = (FT_Error (*)(FT_Library, FT_LcdFilter))dlsym(RTLD_DEFAULT, "FT_Library_SetLcdFilter");
-    gGlyphSlotEmbolden = (void (*)(FT_GlyphSlot))dlsym(RTLD_DEFAULT, "FT_GlyphSlot_Embolden");
 #else
     gSetLcdFilter = &FT_Library_SetLcdFilter;
-    gGlyphSlotEmbolden = &FT_GlyphSlot_Embolden;
 #endif
     // FT_Library_SetLcdFilter may be provided but have no effect if FreeType
     // is built without FT_CONFIG_OPTION_SUBPIXEL_RENDERING.
     if (gSetLcdFilter &&
         gSetLcdFilter(nullptr, FT_LCD_FILTER_NONE) == FT_Err_Unimplemented_Feature) {
         gSetLcdFilter = nullptr;
     }
 }
@@ -481,19 +480,19 @@ unsigned SkScalerContext_CairoFT::genera
 bool SkScalerContext_CairoFT::generateAdvance(SkGlyph* glyph)
 {
     generateMetrics(glyph);
     return !glyph->isEmpty();
 }
 
 void SkScalerContext_CairoFT::prepareGlyph(FT_GlyphSlot glyph)
 {
-    if (fRec.fFlags & SkScalerContext::kEmbolden_Flag &&
-        gGlyphSlotEmbolden) {
-        gGlyphSlotEmbolden(glyph);
+    if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) {
+        // Not FT_GlyphSlot_Embolden because we want a less extreme effect.
+        mozilla_glyphslot_embolden_less(glyph);
     }
 }
 
 void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph)
 {
     glyph->fMaskFormat = fRec.fMaskFormat;
 
     glyph->zeroMetrics();
--- a/gfx/thebes/gfxFT2FontBase.cpp
+++ b/gfx/thebes/gfxFT2FontBase.cpp
@@ -495,16 +495,27 @@ bool gfxFT2FontBase::ShouldRoundXOffset(
                    gfx_text_subpixel_position_force_enabled_AtStartup()));
 }
 
 FT_Vector gfxFT2FontBase::GetEmboldenStrength(FT_Face aFace) {
   FT_Vector strength = {0, 0};
   if (!mEmbolden) {
     return strength;
   }
+
+  // If it's an outline glyph, we'll be using mozilla_glyphslot_embolden_less
+  // (see gfx/wr/webrender/src/platform/unix/font.rs), so we need to match its
+  // emboldening strength here.
+  if (aFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
+    strength.x =
+        FT_MulFix(aFace->units_per_EM, aFace->size->metrics.y_scale) / 48;
+    strength.y = strength.x;
+    return 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.x &= -64;
     if (!strength.x) {
       strength.x = 64;
--- a/gfx/wr/webrender/src/platform/unix/font.rs
+++ b/gfx/wr/webrender/src/platform/unix/font.rs
@@ -8,17 +8,17 @@ use api::{FontInstanceFlags, FontVariati
 use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
 use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32};
 use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos};
 use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt};
 use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face};
 use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
 use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size};
 use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform, FT_String, FT_ULong};
-use freetype::freetype::{FT_Err_Unimplemented_Feature};
+use freetype::freetype::{FT_Err_Unimplemented_Feature, FT_MulFix, FT_Outline_Embolden};
 use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT};
 use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT};
 use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING};
 use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES};
 use freetype::freetype::{FT_FACE_FLAG_MULTIPLE_MASTERS};
 use freetype::succeeded;
 use crate::glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey};
 use crate::glyph_rasterizer::{GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
@@ -104,16 +104,56 @@ macro_rules! ft_dyn_fn {
 ft_dyn_fn!(FT_Get_MM_Var(face: FT_Face, desc: *mut *mut FT_MM_Var) -> FT_Error);
 ft_dyn_fn!(FT_Done_MM_Var(library: FT_Library, desc: *mut FT_MM_Var) -> FT_Error);
 ft_dyn_fn!(FT_Set_Var_Design_Coordinates(face: FT_Face, num_vals: FT_UInt, vals: *mut FT_Fixed) -> FT_Error);
 
 extern "C" {
     fn FT_GlyphSlot_Embolden(slot: FT_GlyphSlot);
 }
 
+// Custom version of FT_GlyphSlot_Embolden to be less aggressive with outline
+// fonts than the default implementation in FreeType.
+#[no_mangle]
+pub extern "C" fn mozilla_glyphslot_embolden_less(slot: FT_GlyphSlot) {
+    if slot.is_null() {
+        return;
+    }
+
+    let slot_ = unsafe { &mut *slot };
+    let format = slot_.format;
+    if format != FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE {
+        // For non-outline glyphs, just fall back to FreeType's function.
+        unsafe { FT_GlyphSlot_Embolden(slot) };
+        return;
+    }
+
+    let face_ = unsafe { *slot_.face };
+
+    // FT_GlyphSlot_Embolden uses a divisor of 24 here; we'll be only half as
+    // bold.
+    let size_ = unsafe { *face_.size };
+    let strength =
+        unsafe { FT_MulFix(face_.units_per_EM as FT_Long,
+                           size_.metrics.y_scale) / 48 };
+    unsafe { FT_Outline_Embolden(&mut slot_.outline, strength) };
+
+    // Adjust metrics to suit the fattened glyph.
+    if slot_.advance.x != 0 {
+        slot_.advance.x += strength;
+    }
+    if slot_.advance.y != 0 {
+        slot_.advance.y += strength;
+    }
+    slot_.metrics.width += strength;
+    slot_.metrics.height += strength;
+    slot_.metrics.horiAdvance += strength;
+    slot_.metrics.vertAdvance += strength;
+    slot_.metrics.horiBearingY += strength;
+}
+
 enum FontFile {
     Pathname(CString),
     Data(Arc<Vec<u8>>),
 }
 
 struct FontFace {
     // Raw byte data has to live until the font is deleted, according to
     // https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_New_Memory_Face
@@ -490,17 +530,17 @@ impl FontContext {
             );
             return None;
         }
 
         let slot = unsafe { (*face).glyph };
         assert!(slot != ptr::null_mut());
 
         if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
-            unsafe { FT_GlyphSlot_Embolden(slot) };
+            mozilla_glyphslot_embolden_less(slot);
         }
 
         let format = unsafe { (*slot).format };
         match format {
             FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
                 let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
                 Some((slot, req_size as f32 / y_size as f32))
             }
--- a/layout/reftests/css-break/reftest.list
+++ b/layout/reftests/css-break/reftest.list
@@ -1,11 +1,11 @@
 == box-decoration-break-1.html box-decoration-break-1-ref.html
 fuzzy(0-1,0-20) fuzzy-if(skiaContent,0-1,0-700) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1-ref.html
-skip-if(verify) fuzzy(0-45,0-460) fuzzy-if(skiaContent,0-57,0-439) fuzzy-if(Android,0-57,0-1330) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1-ref.html # Bug 1386543, bug 1392106
+skip-if(verify) fuzzy(0-45,0-460) fuzzy-if(skiaContent,0-57,0-439) fuzzy-if(Android,0-70,0-1330) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1-ref.html # Bug 1386543, bug 1392106
 random-if(!gtkWidget||webrender) == box-decoration-break-border-image.html box-decoration-break-border-image-ref.html
 == box-decoration-break-block-border-padding.html box-decoration-break-block-border-padding-ref.html
 == box-decoration-break-block-margin.html box-decoration-break-block-margin-ref.html
 fuzzy-if(!Android,0-1,0-62) fuzzy-if(Android,0-8,0-6627) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-first-letter.html box-decoration-break-first-letter-ref.html #Bug 1313773 # Bug 1392106
 == box-decoration-break-with-bidi.html box-decoration-break-with-bidi-ref.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-bug-1235152.html box-decoration-break-bug-1235152-ref.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-bug-1249913.html box-decoration-break-bug-1249913-ref.html # Bug 1392106
 == vertical-wm-001.html vertical-wm-001-ref.html
--- a/layout/reftests/text/synthetic-bold-metrics-01-notref.html
+++ b/layout/reftests/text/synthetic-bold-metrics-01-notref.html
@@ -3,17 +3,17 @@
 <head>
 <style type="text/css">
 @font-face {
   font-family: dejavu;
   src: url(../fonts/dejavu-sans/DejaVuSans.ttf);
 }
 
 p {
-  font-family: dejavu; /* family with only a single weight */
+  font: 32px dejavu; /* family with only a single weight */
 }
 
 .test {
   color: white; /* hide the text, we're only comparing metrics */
 }
 </style>
 </head>
 <body>
--- a/layout/reftests/text/synthetic-bold-metrics-01.html
+++ b/layout/reftests/text/synthetic-bold-metrics-01.html
@@ -3,17 +3,17 @@
 <head>
 <style type="text/css">
 @font-face {
   font-family: dejavu;
   src: url(../fonts/dejavu-sans/DejaVuSans.ttf);
 }
 
 p {
-  font-family: dejavu; /* family with only a single weight */
+  font: 32px dejavu; /* family with only a single weight */
 }
 
 .test {
   color: white; /* hide the text, we're only comparing metrics */
   font-weight: bold; /* synthetic bold will be used */
 }
 </style>
 </head>