bug 754452 - use GDI synthetic italic rather than cairo font matrix when it's safe to do so, for better glyph spacing. r=jdaggett
authorJonathan Kew <jkew@mozilla.com>
Tue, 03 Jul 2012 11:42:07 +0100
changeset 98176 b77545d6a4f53b1fc8c60312925846a515ed0439
parent 98175 e1557cf228913760f94ee686bd484473e2dfcf87
child 98177 9a679266be3377d674f715545a9d91ee1ad88775
push id11394
push userjkew@mozilla.com
push dateTue, 03 Jul 2012 10:45:59 +0000
treeherdermozilla-inbound@b77545d6a4f5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs754452
milestone16.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 754452 - use GDI synthetic italic rather than cairo font matrix when it's safe to do so, for better glyph spacing. r=jdaggett
gfx/thebes/gfxFont.h
gfx/thebes/gfxGDIFont.cpp
gfx/thebes/gfxGDIFont.h
gfx/thebes/gfxGDIFontList.cpp
gfx/thebes/gfxGDIFontList.h
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -638,16 +638,27 @@ public:
     // sort available fonts to put preferred (standard) faces towards the end
     void SortAvailableFonts();
 
     // check whether the family fits into the simple 4-face model,
     // so we can use simplified style-matching;
     // if so set the mIsSimpleFamily flag (defaults to False before we've checked)
     void CheckForSimpleFamily();
 
+    // check whether the family has any faces that are marked as Italic
+    bool HasItalicFace() const {
+        size_t count = mAvailableFonts.Length();
+        for (size_t i = 0; i < count; ++i) {
+            if (mAvailableFonts[i] && mAvailableFonts[i]->IsItalic()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     // For memory reporter
     virtual void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                      FontListSizes*    aSizes) const;
     virtual void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                      FontListSizes*    aSizes) const;
 
 protected:
     // fills in an array with weights of faces that match style,
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -269,21 +269,40 @@ gfxGDIFont::Measure(gfxTextRun *aTextRun
 
 void
 gfxGDIFont::Initialize()
 {
     NS_ASSERTION(!mMetrics, "re-creating metrics? this will leak");
 
     LOGFONTW logFont;
 
+    // Figure out if we want to do synthetic oblique styling.
+    GDIFontEntry* fe = static_cast<GDIFontEntry*>(GetFontEntry());
+    bool wantFakeItalic =
+        (mStyle.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) &&
+        !fe->IsItalic();
+
+    // If the font's family has an actual italic face (but font matching
+    // didn't choose it), we have to use a cairo transform instead of asking
+    // GDI to italicize, because that would use a different face and result
+    // in a possible glyph ID mismatch between shaping and rendering.
+    //
+    // The font entry's mFamilyHasItalicFace flag is needed for user fonts
+    // where the *CSS* family may not know about italic faces that are present
+    // in the *GDI* family, and which GDI would use if we asked it to perform
+    // the "italicization".
+    bool useCairoFakeItalic = wantFakeItalic &&
+        (fe->Family()->HasItalicFace() || fe->mFamilyHasItalicFace);
+
     if (mAdjustedSize == 0.0) {
         mAdjustedSize = mStyle.size;
         if (mStyle.sizeAdjust != 0.0 && mAdjustedSize > 0.0) {
             // to implement font-size-adjust, we first create the "unadjusted" font
-            FillLogFont(logFont, mAdjustedSize);
+            FillLogFont(logFont, mAdjustedSize,
+                        wantFakeItalic && !useCairoFakeItalic);
             mFont = ::CreateFontIndirectW(&logFont);
 
             // initialize its metrics so we can calculate size adjustment
             Initialize();
 
             // calculate the properly adjusted size, and then proceed
             // to recreate mFont and recalculate metrics
             gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight;
@@ -301,17 +320,17 @@ gfxGDIFont::Initialize()
     // as it could lead to a different, incompatible face being used
     // but instead do our own multi-striking
     if (mNeedsBold && GetFontEntry()->IsLocalUserFont()) {
         mApplySyntheticBold = true;
     }
 
     // this may end up being zero
     mAdjustedSize = ROUND(mAdjustedSize);
-    FillLogFont(logFont, mAdjustedSize);
+    FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic);
     mFont = ::CreateFontIndirectW(&logFont);
 
     mMetrics = new gfxFont::Metrics;
     ::memset(mMetrics, 0, sizeof(*mMetrics));
 
     AutoDC dc;
     SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
     AutoSelectFont selectFont(dc.GetDC(), mFont);
@@ -346,17 +365,17 @@ gfxGDIFont::Initialize()
             mMetrics->emAscent = ROUND(mMetrics->emHeight * (double)oMetrics.otmAscent / typEmHeight);
             mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
             if (oMetrics.otmEMSquare > 0) {
                 mFUnitsConvFactor = float(mAdjustedSize / oMetrics.otmEMSquare);
             }
         } else {
             // Make a best-effort guess at extended metrics
             // this is based on general typographic guidelines
-            
+
             // GetTextMetrics can fail if the font file has been removed
             // or corrupted recently.
             BOOL result = GetTextMetrics(dc.GetDC(), &metrics);
             if (!result) {
                 NS_WARNING("Missing or corrupt font data, fasten your seatbelt");
                 mIsValid = false;
                 memset(mMetrics, 0, sizeof(*mMetrics));
                 return;
@@ -423,18 +442,19 @@ gfxGDIFont::Initialize()
 
     mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&logFont,
                                                                 mFont);
 
     cairo_matrix_t sizeMatrix, ctm;
     cairo_matrix_init_identity(&ctm);
     cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
 
-    bool italic = (mStyle.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE));
-    if (italic && !mFontEntry->IsItalic()) {
+    if (useCairoFakeItalic) {
+        // Skew the matrix to do fake italic if it wasn't already applied
+        // via the LOGFONT
         double skewfactor = OBLIQUE_SKEW_FACTOR;
         cairo_matrix_t style;
         cairo_matrix_init(&style,
                           1,                //xx
                           0,                //yx
                           -1 * skewfactor,  //xy
                           1,                //yy
                           0,                //x0
@@ -474,17 +494,18 @@ gfxGDIFont::Initialize()
     printf("    spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics->spaceWidth, mMetrics->aveCharWidth, mMetrics->xHeight);
     printf("    uOff: %f uSize: %f stOff: %f stSize: %f supOff: %f subOff: %f\n",
            mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize,
            mMetrics->superscriptOffset, mMetrics->subscriptOffset);
 #endif
 }
 
 void
-gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize)
+gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize,
+                        bool aUseGDIFakeItalic)
 {
     GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry());
 
     PRUint16 weight;
     if (fe->IsUserFont()) {
         if (fe->IsLocalUserFont()) {
             // for local user fonts, don't change the original weight
             // in the entry's logfont, because that could alter the
@@ -496,16 +517,21 @@ gfxGDIFont::FillLogFont(LOGFONTW& aLogFo
             weight = mNeedsBold ? 700 : 200;
         }
     } else {
         weight = mNeedsBold ? 700 : fe->Weight();
     }
 
     fe->FillLogFont(&aLogFont, weight, aSize, 
                     (mAntialiasOption == kAntialiasSubpixel) ? true : false);
+
+    // If GDI synthetic italic is wanted, force the lfItalic field to true
+    if (aUseGDIFakeItalic) {
+        aLogFont.lfItalic = 1;
+    }
 }
 
 PRInt32
 gfxGDIFont::GetGlyphWidth(gfxContext *aCtx, PRUint16 aGID)
 {
     if (!mGlyphWidths.IsInitialized()) {
         mGlyphWidths.Init(200);
     }
--- a/gfx/thebes/gfxGDIFont.h
+++ b/gfx/thebes/gfxGDIFont.h
@@ -66,17 +66,20 @@ protected:
     /* override to check for uniscribe failure and fall back to GDI */
     virtual bool ShapeWord(gfxContext *aContext,
                            gfxShapedWord *aShapedWord,
                            const PRUnichar *aString,
                            bool aPreferPlatformShaping = false);
 
     void Initialize(); // creates metrics and Cairo fonts
 
-    void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize);
+    // Fill the given LOGFONT record according to our style, but don't adjust
+    // the lfItalic field if we're going to use a cairo transform for fake
+    // italics.
+    void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize, bool aUseGDIFakeItalic);
 
     // mPlatformShaper is used for the GDI shaper, mUniscribeShaper
     // for the Uniscribe version if needed
     nsAutoPtr<gfxFontShaper>   mUniscribeShaper;
 
     HFONT                 mFont;
     cairo_font_face_t    *mFontFace;
 
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -156,21 +156,23 @@ FontTypeToOutPrecision(PRUint8 fontType)
  *
  * GDIFontEntry
  *
  */
 
 GDIFontEntry::GDIFontEntry(const nsAString& aFaceName,
                            gfxWindowsFontType aFontType,
                            bool aItalic, PRUint16 aWeight, PRInt16 aStretch,
-                           gfxUserFontData *aUserFontData)
+                           gfxUserFontData *aUserFontData,
+                           bool aFamilyHasItalicFace)
     : gfxFontEntry(aFaceName),
       mWindowsFamily(0), mWindowsPitch(0),
       mFontType(aFontType),
       mForceGDI(false),
+      mFamilyHasItalicFace(aFamilyHasItalicFace),
       mCharset(), mUnicodeRanges()
 {
     mUserFontData = aUserFontData;
     mItalic = aItalic;
     mWeight = aWeight;
     mStretch = aStretch;
     if (IsType1())
         mForceGDI = true;
@@ -398,17 +400,17 @@ GDIFontEntry::TestCharacterMap(PRUint32 
     return false;
 }
 
 void
 GDIFontEntry::InitLogFont(const nsAString& aName,
                           gfxWindowsFontType aFontType)
 {
 #define CLIP_TURNOFF_FONTASSOCIATION 0x40
-    
+
     mLogFont.lfHeight = -1;
 
     // Fill in logFont structure
     mLogFont.lfWidth          = 0;
     mLogFont.lfEscapement     = 0;
     mLogFont.lfOrientation    = 0;
     mLogFont.lfUnderline      = FALSE;
     mLogFont.lfStrikeOut      = FALSE;
@@ -428,22 +430,24 @@ GDIFontEntry::InitLogFont(const nsAStrin
     memcpy(&mLogFont.lfFaceName, nsPromiseFlatString(aName).get(), len * 2);
     mLogFont.lfFaceName[len] = '\0';
 }
 
 GDIFontEntry* 
 GDIFontEntry::CreateFontEntry(const nsAString& aName,
                               gfxWindowsFontType aFontType, bool aItalic,
                               PRUint16 aWeight, PRInt16 aStretch,
-                              gfxUserFontData* aUserFontData)
+                              gfxUserFontData* aUserFontData,
+                              bool aFamilyHasItalicFace)
 {
     // jtdfix - need to set charset, unicode ranges, pitch/family
 
     GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aItalic,
-                                        aWeight, aStretch, aUserFontData);
+                                        aWeight, aStretch, aUserFontData,
+                                        aFamilyHasItalicFace);
 
     return fe;
 }
 
 void
 GDIFontEntry::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                   FontListSizes*    aSizes) const
 {
@@ -490,20 +494,24 @@ GDIFontFamily::FamilyAddStylesProc(const
         if (fe->mWeight == logFont.lfWeight &&
             fe->mItalic == (logFont.lfItalic == 0xFF)) {
             // update the charset bit here since this could be different
             fe->mCharset.set(metrics.tmCharSet);
             return 1; 
         }
     }
 
+    // We can't set the hasItalicFace flag correctly here,
+    // because we might not have seen the family's italic face(s) yet.
+    // Later code does _not_ rely on this flag for platform fonts;
+    // it is only needed for fonts loaded with src:local
     fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName),
                                        feType, (logFont.lfItalic == 0xFF),
                                        (PRUint16) (logFont.lfWeight), 0,
-                                       nsnull);
+                                       nsnull, false);
     if (!fe)
         return 1;
 
     ff->AddFontEntry(fe);
 
     // mark the charset bit
     fe->mCharset.set(metrics.tmCharSet);
 
@@ -753,17 +761,18 @@ gfxGDIFontList::LookupLocalFont(const gf
     
     // use the face name from the lookup font entry, which will be the localized
     // face name which GDI mapping tables use (e.g. with the system locale set to
     // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name
     // 'Arial Vet' which can be used as a key in GDI font lookups).
     GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(lookup->Name(), 
         gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, 
         lookup->mItalic ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL,
-        lookup->mWeight, aProxyEntry->mStretch, nsnull);
+        lookup->mWeight, aProxyEntry->mStretch, nsnull,
+        lookup->Family()->HasItalicFace());
         
     if (!fe)
         return nsnull;
 
     fe->mIsUserFont = true;
     fe->mIsLocalUserFont = true;
 
     // make the new font entry match the proxy entry style characteristics
@@ -975,17 +984,17 @@ gfxGDIFontList::MakePlatformFont(const g
 
     // make a new font entry using the unique name
     WinUserFontData *winUserFontData = new WinUserFontData(fontRef, isEmbedded);
     PRUint16 w = (aProxyEntry->mWeight == 0 ? 400 : aProxyEntry->mWeight);
 
     GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(uniqueName, 
         gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, 
         PRUint32(aProxyEntry->mItalic ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL), 
-        w, aProxyEntry->mStretch, winUserFontData);
+        w, aProxyEntry->mStretch, winUserFontData, false);
 
     if (!fe)
         return fe;
 
     fe->mIsUserFont = true;
 
     // Uniscribe doesn't place CFF fonts loaded privately 
     // via AddFontMemResourceEx on XP/Vista
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -242,37 +242,44 @@ public:
     virtual void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                      FontListSizes*    aSizes) const;
 
     // create a font entry for a font with a given name
     static GDIFontEntry* CreateFontEntry(const nsAString& aName,
                                          gfxWindowsFontType aFontType,
                                          bool aItalic,
                                          PRUint16 aWeight, PRInt16 aStretch,
-                                         gfxUserFontData* aUserFontData);
+                                         gfxUserFontData* aUserFontData,
+                                         bool aFamilyHasItalicFace);
 
     // create a font entry for a font referenced by its fullname
     static GDIFontEntry* LoadLocalFont(const gfxProxyFontEntry &aProxyEntry,
                                        const nsAString& aFullname);
 
     PRUint8 mWindowsFamily;
     PRUint8 mWindowsPitch;
 
     gfxWindowsFontType mFontType;
     bool mForceGDI    : 1;
 
+    // For src:local user-fonts, we keep track of whether the platform family
+    // contains an italic face, because in this case we can't safely ask GDI
+    // to create synthetic italics (oblique) via the LOGFONT.
+    // (For other types of font, this is just set to false.)
+    bool mFamilyHasItalicFace : 1;
+
     gfxSparseBitSet mCharset;
     gfxSparseBitSet mUnicodeRanges;
 
 protected:
     friend class gfxWindowsFont;
 
     GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType,
                  bool aItalic, PRUint16 aWeight, PRInt16 aStretch,
-                 gfxUserFontData *aUserFontData);
+                 gfxUserFontData *aUserFontData, bool aFamilyHasItalicFace);
 
     void InitLogFont(const nsAString& aName, gfxWindowsFontType aFontType);
 
     virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold);
 
     virtual nsresult GetFontTable(PRUint32 aTableTag,
                                   FallibleTArray<PRUint8>& aBuffer);