Bug 1430632 - Support OpenType variation fonts in the DirectWrite font back-end (on Win10 Fall Creators Update). r=lsalzman
authorJonathan Kew <jkew@mozilla.com>
Thu, 18 Jan 2018 17:45:24 +0000
changeset 400110 794abd47aa0cbcc2608b7e5c38f14227d9c5dd8e
parent 400109 d76b88de07ca4fff02828d24db2dbce6e4f3d7e4
child 400111 6d97ae42700e4086584d74cde222fa2f870780dd
push id33292
push userbtara@mozilla.com
push dateSun, 21 Jan 2018 09:51:58 +0000
treeherdermozilla-central@9fe69ff0762d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsalzman
bugs1430632
milestone59.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 1430632 - Support OpenType variation fonts in the DirectWrite font back-end (on Win10 Fall Creators Update). r=lsalzman
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxDWriteFontList.h
gfx/thebes/gfxDWriteFonts.cpp
gfx/thebes/gfxUserFontSet.cpp
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -582,43 +582,79 @@ gfxDWriteFontEntry::ReadCMAP(FontInfoDat
         SprintfLiteral(prefix, "(cmapdata) name: %.220s",
                        NS_ConvertUTF16toUTF8(mName).get());
         charmap->Dump(prefix, eGfxLog_cmapdata);
     }
 
     return rv;
 }
 
+bool
+gfxDWriteFontEntry::HasVariations()
+{
+    if (mHasVariationsInitialized) {
+        return mHasVariations;
+    }
+    mHasVariationsInitialized = true;
+    if (!mFontFace) {
+        // CreateFontFace will initialize the mFontFace field, and also
+        // mFontFace5 if available on the current DWrite version.
+        RefPtr<IDWriteFontFace> fontFace;
+        if (NS_FAILED(CreateFontFace(getter_AddRefs(fontFace)))) {
+            return false;
+        }
+    }
+    if (mFontFace5) {
+        mHasVariations = mFontFace5->HasVariations();
+    }
+    return mHasVariations;
+}
+
 gfxFont *
 gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle,
                                        bool aNeedsBold)
 {
+    DWRITE_FONT_SIMULATIONS sims =
+        aNeedsBold ? DWRITE_FONT_SIMULATIONS_BOLD : DWRITE_FONT_SIMULATIONS_NONE;
+    if (HasVariations() && !aFontStyle->variationSettings.IsEmpty()) {
+        // If we need to apply variations, we can't use the cached mUnscaledFont
+        // or mUnscaledFontBold here.
+        // XXX todo: consider caching a small number of variation instances?
+        RefPtr<IDWriteFontFace> fontFace;
+        nsresult rv = CreateFontFace(getter_AddRefs(fontFace),
+                                     &aFontStyle->variationSettings,
+                                     sims);
+        if (NS_FAILED(rv)) {
+            return nullptr;
+        }
+        RefPtr<UnscaledFontDWrite> unscaledFont =
+            new UnscaledFontDWrite(fontFace, mIsSystemFont ? mFont : nullptr, sims);
+        return new gfxDWriteFont(unscaledFont, this, aFontStyle, aNeedsBold);
+    }
+
     ThreadSafeWeakPtr<UnscaledFontDWrite>& unscaledFontPtr =
         aNeedsBold ? mUnscaledFontBold : mUnscaledFont;
     RefPtr<UnscaledFontDWrite> unscaledFont(unscaledFontPtr);
     if (!unscaledFont) {
-        DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
-        if (aNeedsBold) {
-            sims |= DWRITE_FONT_SIMULATIONS_BOLD;
-        }
         RefPtr<IDWriteFontFace> fontFace;
-        nsresult rv = CreateFontFace(getter_AddRefs(fontFace), sims);
+        nsresult rv = CreateFontFace(getter_AddRefs(fontFace), nullptr, sims);
         if (NS_FAILED(rv)) {
             return nullptr;
         }
-
-        unscaledFont = new UnscaledFontDWrite(fontFace, mIsSystemFont ? mFont : nullptr, sims);
+        unscaledFont =
+            new UnscaledFontDWrite(fontFace,
+                                   mIsSystemFont ? mFont : nullptr, sims);
         unscaledFontPtr = unscaledFont;
     }
-
     return new gfxDWriteFont(unscaledFont, this, aFontStyle, aNeedsBold);
 }
 
 nsresult
 gfxDWriteFontEntry::CreateFontFace(IDWriteFontFace **aFontFace,
+                                   const nsTArray<gfxFontVariation>* aVariations,
                                    DWRITE_FONT_SIMULATIONS aSimulations)
 {
     // initialize mFontFace if this hasn't been done before
     if (!mFontFace) {
         HRESULT hr;
         if (mFont) {
             hr = mFont->CreateFontFace(getter_AddRefs(mFontFace));
         } else if (mFontFile) {
@@ -632,23 +668,65 @@ gfxDWriteFontEntry::CreateFontFace(IDWri
                                getter_AddRefs(mFontFace));
         } else {
             NS_NOTREACHED("invalid font entry");
             return NS_ERROR_FAILURE;
         }
         if (FAILED(hr)) {
             return NS_ERROR_FAILURE;
         }
+        // Also get the IDWriteFontFace5 interface if we're running on a
+        // sufficiently new DWrite version where it is available.
+        if (mFontFace) {
+            mFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
+                (void**)getter_AddRefs(mFontFace5));
+        }
     }
 
-    // check whether we need to add a DWrite simulated style
-    if ((aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
-        !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD)) {
+    // Do we need to modify DWrite simulations from what mFontFace has?
+    bool needSimulations =
+        (aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
+        !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
+
+    // If the IDWriteFontFace5 interface is available, we can go via
+    // IDWriteFontResource to create a new modified face.
+    if (mFontFace5 && (aVariations && !aVariations->IsEmpty() ||
+                       needSimulations)) {
+        RefPtr<IDWriteFontResource> resource;
+        HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
+        MOZ_ASSERT(SUCCEEDED(hr));
+        AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
+        if (aVariations) {
+            for (const auto& v : *aVariations) {
+                DWRITE_FONT_AXIS_VALUE axisValue = {
+                    // let dwrite put the tag bytes in the order it wants
+                    DWRITE_MAKE_FONT_AXIS_TAG((v.mTag >> 24) & 0xff,
+                                              (v.mTag >> 16) & 0xff,
+                                              (v.mTag >> 8) & 0xff,
+                                              v.mTag & 0xff),
+                    v.mValue
+                };
+                fontAxisValues.AppendElement(axisValue);
+            }
+        }
+        IDWriteFontFace5* ff5;
+        resource->CreateFontFace(aSimulations,
+                                 fontAxisValues.Elements(),
+                                 fontAxisValues.Length(),
+                                 &ff5);
+        if (ff5) {
+            *aFontFace = ff5;
+        }
+        return FAILED(hr) ? NS_ERROR_FAILURE : NS_OK;
+    }
+
+    // Do we need to add DWrite simulations to the face?
+    if (needSimulations) {
         // if so, we need to return not mFontFace itself but a version that
-        // has the Bold simulation - unfortunately, DWrite doesn't provide
+        // has the Bold simulation - unfortunately, old DWrite doesn't provide
         // a simple API for this
         UINT32 numberOfFiles = 0;
         if (FAILED(mFontFace->GetFiles(&numberOfFiles, nullptr))) {
             return NS_ERROR_FAILURE;
         }
         AutoTArray<IDWriteFontFile*,1> files;
         files.AppendElements(numberOfFiles);
         if (FAILED(mFontFace->GetFiles(&numberOfFiles, files.Elements()))) {
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_DWRITEFONTLIST_H
 #define GFX_DWRITEFONTLIST_H
 
 #include "mozilla/MemoryReporting.h"
 #include "gfxDWriteCommon.h"
+#include "dwrite_3.h"
 
 #include "gfxFont.h"
 #include "gfxUserFontSet.h"
 #include "cairo-win32.h"
 
 #include "gfxPlatformFontList.h"
 #include "gfxPlatform.h"
 #include <algorithm>
@@ -88,17 +89,18 @@ public:
      *
      * \param aFaceName The name of the corresponding font face.
      * \param aFont DirectWrite font object
      */
     gfxDWriteFontEntry(const nsAString& aFaceName,
                        IDWriteFont *aFont,
                        bool aIsSystemFont = false)
       : gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
-        mIsSystemFont(aIsSystemFont), mForceGDIClassic(false)
+        mIsSystemFont(aIsSystemFont), mForceGDIClassic(false),
+        mHasVariations(false), mHasVariationsInitialized(false)
     {
         DWRITE_FONT_STYLE dwriteStyle = aFont->GetStyle();
         mStyle = (dwriteStyle == DWRITE_FONT_STYLE_ITALIC ?
                   NS_FONT_STYLE_ITALIC :
                   (dwriteStyle == DWRITE_FONT_STYLE_OBLIQUE ?
                    NS_FONT_STYLE_OBLIQUE : NS_FONT_STYLE_NORMAL));
         mStretch = FontStretchFromDWriteStretch(aFont->GetStretch());
         uint16_t weight = NS_ROUNDUP(aFont->GetWeight() - 50, 100);
@@ -122,17 +124,18 @@ public:
      * \param aStyle italic or oblique of font
      */
     gfxDWriteFontEntry(const nsAString& aFaceName,
                               IDWriteFont *aFont,
                               uint16_t aWeight,
                               int16_t aStretch,
                               uint8_t aStyle)
       : gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
-        mIsSystemFont(false), mForceGDIClassic(false)
+        mIsSystemFont(false), mForceGDIClassic(false),
+        mHasVariations(false), mHasVariationsInitialized(false)
     {
         mWeight = aWeight;
         mStretch = aStretch;
         mStyle = aStyle;
         mIsLocalUserFont = true;
         mIsCJK = UNINITIALIZED_VALUE;
     }
 
@@ -149,17 +152,18 @@ public:
     gfxDWriteFontEntry(const nsAString& aFaceName,
                               IDWriteFontFile *aFontFile,
                               IDWriteFontFileStream *aFontFileStream,
                               uint16_t aWeight,
                               int16_t aStretch,
                               uint8_t aStyle)
       : gfxFontEntry(aFaceName), mFont(nullptr),
         mFontFile(aFontFile), mFontFileStream(aFontFileStream),
-        mIsSystemFont(false), mForceGDIClassic(false)
+        mIsSystemFont(false), mForceGDIClassic(false),
+        mHasVariations(false), mHasVariationsInitialized(false)
     {
         mWeight = aWeight;
         mStretch = aStretch;
         mStyle = aStyle;
         mIsDataUserFont = true;
         mIsCJK = UNINITIALIZED_VALUE;
     }
 
@@ -168,16 +172,18 @@ public:
     virtual ~gfxDWriteFontEntry();
 
     virtual hb_blob_t* GetFontTable(uint32_t aTableTag) override;
 
     nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
 
     bool IsCJKFont();
 
+    bool HasVariations();
+
     void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
     bool GetForceGDIClassic() { return mForceGDIClassic; }
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
@@ -188,16 +194,17 @@ protected:
     virtual nsresult CopyFontTable(uint32_t aTableTag,
                                    nsTArray<uint8_t>& aBuffer) override;
 
     virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle,
                                         bool aNeedsBold);
     
     nsresult CreateFontFace(
         IDWriteFontFace **aFontFace,
+        const nsTArray<gfxFontVariation>* aVariations = nullptr,
         DWRITE_FONT_SIMULATIONS aSimulations = DWRITE_FONT_SIMULATIONS_NONE);
 
     static bool InitLogFont(IDWriteFont *aFont, LOGFONTW *aLogFont);
 
     /**
      * A fontentry only needs to have either of these. If it has both only
      * the IDWriteFont will be used.
      */
@@ -206,22 +213,26 @@ protected:
 
     // For custom fonts, we hold a reference to the IDWriteFontFileStream for
     // for the IDWriteFontFile, so that the data is available.
     RefPtr<IDWriteFontFileStream> mFontFileStream;
 
     // font face corresponding to the mFont/mFontFile *without* any DWrite
     // style simulations applied
     RefPtr<IDWriteFontFace> mFontFace;
+    // Extended fontface interface if supported, else null
+    RefPtr<IDWriteFontFace5> mFontFace5;
 
     DWRITE_FONT_FACE_TYPE mFaceType;
 
     int8_t mIsCJK;
     bool mIsSystemFont;
     bool mForceGDIClassic;
+    bool mHasVariations;
+    bool mHasVariationsInitialized;
 
     mozilla::ThreadSafeWeakPtr<mozilla::gfx::UnscaledFontDWrite> mUnscaledFont;
     mozilla::ThreadSafeWeakPtr<mozilla::gfx::UnscaledFontDWrite> mUnscaledFontBold;
 };
 
 // custom text renderer used to determine the fallback font for a given char
 class DWriteFontFallbackRenderer final : public IDWriteTextRenderer
 {
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -559,17 +559,19 @@ gfxDWriteFont::Measure(const gfxTextRun*
 
     return metrics;
 }
 
 bool
 gfxDWriteFont::ProvidesGlyphWidths() const
 {
     return !mUseSubpixelPositions ||
-           (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
+           (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD) ||
+           (((gfxDWriteFontEntry*)(GetFontEntry()))->HasVariations() &&
+            !mStyle.variationSettings.IsEmpty());
 }
 
 int32_t
 gfxDWriteFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
 {
     if (!mGlyphWidths) {
         mGlyphWidths = MakeUnique<nsDataHashtable<nsUint32HashKey,int32_t>>(128);
     }
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -193,16 +193,17 @@ public:
               aTag == TRUETYPE_TAG('G', 'S', 'U', 'B'))) ||
             (mKeepVariationTables &&
              (aTag == TRUETYPE_TAG('a', 'v', 'a', 'r') ||
               aTag == TRUETYPE_TAG('c', 'v', 'a', 'r') ||
               aTag == TRUETYPE_TAG('f', 'v', 'a', 'r') ||
               aTag == TRUETYPE_TAG('g', 'v', 'a', 'r') ||
               aTag == TRUETYPE_TAG('H', 'V', 'A', 'R') ||
               aTag == TRUETYPE_TAG('M', 'V', 'A', 'R') ||
+              aTag == TRUETYPE_TAG('S', 'T', 'A', 'T') ||
               aTag == TRUETYPE_TAG('V', 'V', 'A', 'R'))) ||
             aTag == TRUETYPE_TAG('S', 'V', 'G', ' ') ||
             aTag == TRUETYPE_TAG('C', 'O', 'L', 'R') ||
             aTag == TRUETYPE_TAG('C', 'P', 'A', 'L') ||
             (mKeepColorBitmaps &&
              (aTag == TRUETYPE_TAG('C', 'B', 'D', 'T') ||
               aTag == TRUETYPE_TAG('C', 'B', 'L', 'C'))) ||
             false) {