bug 594889 - improve glyph spacing with DirectWrite fonts when ClearType is disabled by using GDI-compatible instead of 'ideal' metrics. r=bas a=blocking2.0
authorJonathan Kew <jfkthame@gmail.com>
Tue, 04 Jan 2011 16:58:31 +0000
changeset 59850 d0141c1310ff5127baf8bd236448e4000749a838
parent 59849 59606b331b0e9c45c7955a3cb93cc946725bf56c
child 59851 e1a695a101388811e12cf456d3b0cbcb6420cfc2
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersbas, blocking2
bugs594889
milestone2.0b9pre
bug 594889 - improve glyph spacing with DirectWrite fonts when ClearType is disabled by using GDI-compatible instead of 'ideal' metrics. r=bas a=blocking2.0
gfx/thebes/gfxDWriteFonts.cpp
gfx/thebes/gfxDWriteFonts.h
gfx/thebes/gfxDWriteShaper.cpp
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -63,27 +63,73 @@ GetCairoAntialiasOption(gfxFont::Antiali
         return CAIRO_ANTIALIAS_NONE;
     case gfxFont::kAntialiasGrayscale:
         return CAIRO_ANTIALIAS_GRAY;
     case gfxFont::kAntialiasSubpixel:
         return CAIRO_ANTIALIAS_SUBPIXEL;
     }
 }
 
+// Code to determine whether Windows is set to use ClearType font smoothing;
+// based on private functions in cairo-win32-font.c
+
+#ifndef SPI_GETFONTSMOOTHINGTYPE
+#define SPI_GETFONTSMOOTHINGTYPE 0x200a
+#endif
+#ifndef FE_FONTSMOOTHINGCLEARTYPE
+#define FE_FONTSMOOTHINGCLEARTYPE 2
+#endif
+
+static bool
+HasClearType()
+{
+    OSVERSIONINFO versionInfo;
+    versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+    return (GetVersionEx(&versionInfo) &&
+            (versionInfo.dwMajorVersion > 5 ||
+             (versionInfo.dwMajorVersion == 5 &&
+              versionInfo.dwMinorVersion >= 1))); // XP or newer
+}
+
+static bool
+UsingClearType()
+{
+    BOOL fontSmoothing;
+    if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothing, 0) ||
+        !fontSmoothing)
+    {
+        return false;    
+    }
+
+    if (!HasClearType()) {
+        return false;
+    }
+
+    UINT type;
+    if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &type, 0) &&
+        type == FE_FONTSMOOTHINGCLEARTYPE)
+    {
+        return true;
+    }
+    return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // gfxDWriteFont
 gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
                              const gfxFontStyle *aFontStyle,
                              PRBool aNeedsBold,
                              AntialiasOption anAAOption)
     : gfxFont(aFontEntry, aFontStyle, anAAOption)
     , mCairoFontFace(nsnull)
     , mCairoScaledFont(nsnull)
     , mNeedsOblique(PR_FALSE)
     , mNeedsBold(aNeedsBold)
+    , mUsingClearType(PR_FALSE)
 {
     gfxDWriteFontEntry *fe =
         static_cast<gfxDWriteFontEntry*>(aFontEntry);
     nsresult rv;
     DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
     if ((GetStyle()->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) &&
         !fe->IsItalic()) {
             // For this we always use the font_matrix for uniformity. Not the
@@ -96,16 +142,22 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntr
 
     rv = fe->CreateFontFace(getter_AddRefs(mFontFace), sims);
 
     if (NS_FAILED(rv)) {
         mIsValid = PR_FALSE;
         return;
     }
 
+    if ((anAAOption == gfxFont::kAntialiasDefault && UsingClearType()) ||
+        anAAOption == gfxFont::kAntialiasSubpixel)
+    {
+        mUsingClearType = PR_TRUE;
+    }
+
     ComputeMetrics();
 
     if (FontCanSupportHarfBuzz()) {
         mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
     }
 }
 
 gfxDWriteFont::~gfxDWriteFont()
@@ -422,8 +474,34 @@ gfxDWriteFont::GetFontTable(PRUint32 aTa
         hb_blob_t *blob;
         if (mFontEntry->GetExistingFontTable(aTag, &blob)) {
             return blob;
         }
     }
 
     return nsnull;
 }
+
+PRInt32
+gfxDWriteFont::GetHintedGlyphWidth(gfxContext *aCtx, PRUint16 aGID)
+{
+    if (!mGlyphWidths.IsInitialized()) {
+        mGlyphWidths.Init(200);
+    }
+
+    PRInt32 width;
+    if (mGlyphWidths.Get(aGID, &width)) {
+        return width;
+    }
+
+    DWRITE_GLYPH_METRICS glyphMetrics;
+    HRESULT hr = mFontFace->GetGdiCompatibleGlyphMetrics(
+                  GetAdjustedSize(), 1.0f, nsnull, TRUE,
+                  &aGID, 1, &glyphMetrics, FALSE);
+
+    if (NS_SUCCEEDED(hr)) {
+        width = NS_lround(glyphMetrics.advanceWidth * mFUnitsConvFactor) << 16;
+        mGlyphWidths.Put(aGID, width);
+        return width;
+    }
+
+    return -1;
+}
--- a/gfx/thebes/gfxDWriteFonts.h
+++ b/gfx/thebes/gfxDWriteFonts.h
@@ -74,29 +74,40 @@ public:
     gfxFloat GetAdjustedSize() const { return mAdjustedSize; }
 
     IDWriteFontFace *GetFontFace() { return mFontFace.get(); }
 
     // override gfxFont table access function to bypass gfxFontEntry cache,
     // use DWrite API to get direct access to system font data
     virtual hb_blob_t *GetFontTable(PRUint32 aTag);
 
+    virtual PRBool ProvidesHintedWidths() const {
+        return !mUsingClearType;
+    }
+
+    virtual PRInt32 GetHintedGlyphWidth(gfxContext *aCtx, PRUint16 aGID);
+
 protected:
     virtual void CreatePlatformShaper();
 
     void ComputeMetrics();
 
     cairo_font_face_t *CairoFontFace();
 
     cairo_scaled_font_t *CairoScaledFont();
 
     static void DestroyBlobFunc(void* userArg);
 
     nsRefPtr<IDWriteFontFace> mFontFace;
     cairo_font_face_t *mCairoFontFace;
     cairo_scaled_font_t *mCairoScaledFont;
 
     gfxFont::Metrics mMetrics;
-    PRBool mNeedsOblique;
-    PRBool mNeedsBold;
+
+    // cache of glyph widths in 16.16 fixed-point pixels
+    nsDataHashtable<nsUint32HashKey,PRInt32>    mGlyphWidths;
+
+    PRPackedBool mNeedsOblique;
+    PRPackedBool mNeedsBold;
+    PRPackedBool mUsingClearType;
 };
 
 #endif
--- a/gfx/thebes/gfxDWriteShaper.cpp
+++ b/gfx/thebes/gfxDWriteShaper.cpp
@@ -170,34 +170,59 @@ trymoreglyphs:
         WORD gID = indices[0];
         nsAutoTArray<FLOAT, 400> advances;
         nsAutoTArray<DWRITE_GLYPH_OFFSET, 400> glyphOffsets;
         if (!advances.SetLength(actualGlyphs) || 
             !glyphOffsets.SetLength(actualGlyphs)) {
             continue;
         }
 
-        hr = analyzer->GetGlyphPlacements(aString + range.start, 
-                                          clusters.Elements(),
-                                          textProperties.Elements(),
-                                          range.Length(),
-                                          indices.Elements(),
-                                          glyphProperties.Elements(),
-                                          actualGlyphs,
-                                          font->GetFontFace(),
-                                          font->GetAdjustedSize(),
-                                          FALSE,
-                                          FALSE,
-                                          &runHead->mScript,
-                                          NULL,
-                                          NULL,
-                                          NULL,
-                                          0,
-                                          advances.Elements(),
-                                          glyphOffsets.Elements());
+        if (mFont->ProvidesHintedWidths()) {
+            hr = analyzer->GetGdiCompatibleGlyphPlacements(
+                                              aString + range.start,
+                                              clusters.Elements(),
+                                              textProperties.Elements(),
+                                              range.Length(),
+                                              indices.Elements(),
+                                              glyphProperties.Elements(),
+                                              actualGlyphs,
+                                              font->GetFontFace(),
+                                              font->GetAdjustedSize(),
+                                              1.0,
+                                              nsnull,
+                                              TRUE,
+                                              FALSE,
+                                              FALSE,
+                                              &runHead->mScript,
+                                              NULL,
+                                              NULL,
+                                              NULL,
+                                              0,
+                                              advances.Elements(),
+                                              glyphOffsets.Elements());
+        } else {
+            hr = analyzer->GetGlyphPlacements(aString + range.start,
+                                              clusters.Elements(),
+                                              textProperties.Elements(),
+                                              range.Length(),
+                                              indices.Elements(),
+                                              glyphProperties.Elements(),
+                                              actualGlyphs,
+                                              font->GetFontFace(),
+                                              font->GetAdjustedSize(),
+                                              FALSE,
+                                              FALSE,
+                                              &runHead->mScript,
+                                              NULL,
+                                              NULL,
+                                              NULL,
+                                              0,
+                                              advances.Elements(),
+                                              glyphOffsets.Elements());
+        }
         if (FAILED(hr)) {
             NS_WARNING("Analyzer failed to get glyph placements.");
             result = PR_FALSE;
             break;
         }
 
         nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;