Bug 1430820 - Cache any required feature-modified Core Text font instances in gfxCoreTextShaper, rather than re-creating them for each shaping call. r=jrmuizel
authorJonathan Kew <jkew@mozilla.com>
Thu, 29 Mar 2018 21:53:55 +0100
changeset 410751 619174f7144fc328a46c1acce2e4f248eae52d65
parent 410750 483a612a7c5a0c541958552568e913d2c3dbf92a
child 410752 8eec06866ec979e059f8e95a0f9d6fae46ad9159
push id33736
push usershindli@mozilla.com
push dateFri, 30 Mar 2018 09:56:41 +0000
treeherdermozilla-central@b7fa9d95150e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1430820
milestone61.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 1430820 - Cache any required feature-modified Core Text font instances in gfxCoreTextShaper, rather than re-creating them for each shaping call. r=jrmuizel
gfx/thebes/gfxCoreTextShaper.cpp
gfx/thebes/gfxCoreTextShaper.h
--- a/gfx/thebes/gfxCoreTextShaper.cpp
+++ b/gfx/thebes/gfxCoreTextShaper.cpp
@@ -13,22 +13,17 @@
 
 #include <algorithm>
 
 #include <dlfcn.h>
 
 using namespace mozilla;
 
 // standard font descriptors that we construct the first time they're needed
-CTFontDescriptorRef gfxCoreTextShaper::sDefaultFeaturesDescriptor = nullptr;
-CTFontDescriptorRef gfxCoreTextShaper::sSmallCapsDescriptor = nullptr;
-CTFontDescriptorRef gfxCoreTextShaper::sDisableLigaturesDescriptor = nullptr;
-CTFontDescriptorRef gfxCoreTextShaper::sSmallCapDisableLigDescriptor = nullptr;
-CTFontDescriptorRef gfxCoreTextShaper::sIndicFeaturesDescriptor = nullptr;
-CTFontDescriptorRef gfxCoreTextShaper::sIndicDisableLigaturesDescriptor = nullptr;
+CTFontDescriptorRef gfxCoreTextShaper::sFeaturesDescriptor[kMaxFontInstances];
 
 // Helper to create a CFDictionary with the right attributes for shaping our
 // text, including imposing the given directionality.
 CFDictionaryRef
 gfxCoreTextShaper::CreateAttrDict(bool aRightToLeft)
 {
     // Because we always shape unidirectional runs, and may have applied
     // directional overrides, we want to force a direction rather than
@@ -40,46 +35,52 @@ gfxCoreTextShaper::CreateAttrDict(bool a
         ::CFNumberCreate(kCFAllocatorDefault,
                          kCFNumberSInt16Type, &dirOverride);
     CFArrayRef dirArray =
         ::CFArrayCreate(kCFAllocatorDefault,
                         (const void **) &dirNumber, 1,
                         &kCFTypeArrayCallBacks);
     ::CFRelease(dirNumber);
     CFTypeRef attrs[] = { kCTFontAttributeName, kCTWritingDirectionAttributeName };
-    CFTypeRef values[] = { mCTFont, dirArray };
+    CFTypeRef values[] = { mCTFont[0], dirArray };
     CFDictionaryRef attrDict =
         ::CFDictionaryCreate(kCFAllocatorDefault,
                              attrs, values, ArrayLength(attrs),
                              &kCFTypeDictionaryKeyCallBacks,
                              &kCFTypeDictionaryValueCallBacks);
     ::CFRelease(dirArray);
     return attrDict;
 }
 
 gfxCoreTextShaper::gfxCoreTextShaper(gfxMacFont *aFont)
     : gfxFontShaper(aFont)
     , mAttributesDictLTR(nullptr)
     , mAttributesDictRTL(nullptr)
 {
-    // Create our CTFontRef
-    mCTFont = CreateCTFontWithFeatures(aFont->GetAdjustedSize(),
-                                       GetDefaultFeaturesDescriptor());
+    for (size_t i = 0; i < kMaxFontInstances; i++) {
+        mCTFont[i] = nullptr;
+    }
+    // Create our default CTFontRef
+    mCTFont[0] =
+        CreateCTFontWithFeatures(aFont->GetAdjustedSize(),
+                                 GetFeaturesDescriptor(kDefaultFeatures));
 }
 
 gfxCoreTextShaper::~gfxCoreTextShaper()
 {
     if (mAttributesDictLTR) {
         ::CFRelease(mAttributesDictLTR);
     }
     if (mAttributesDictRTL) {
         ::CFRelease(mAttributesDictRTL);
     }
-    if (mCTFont) {
-        ::CFRelease(mCTFont);
+    for (size_t i = 0; i < kMaxFontInstances; i++) {
+        if (mCTFont[i]) {
+            ::CFRelease(mCTFont[i]);
+        }
     }
 }
 
 static bool
 IsBuggyIndicScript(unicode::Script aScript)
 {
     return aScript == unicode::Script::BENGALI ||
            aScript == unicode::Script::KANNADA ||
@@ -130,54 +131,48 @@ gfxCoreTextShaper::ShapeText(DrawTarget 
     // current direction, creating it if necessary.
     CFDictionaryRef attrObj =
         isRightToLeft ? mAttributesDictRTL : mAttributesDictLTR;
     if (!attrObj) {
         attrObj = CreateAttrDict(isRightToLeft);
         (isRightToLeft ? mAttributesDictRTL : mAttributesDictLTR) = attrObj;
     }
 
-    CTFontRef tempCTFont = nullptr;
+    FeatureFlags featureFlags = kDefaultFeatures;
     if (IsBuggyIndicScript(aScript)) {
         // To work around buggy Indic AAT fonts shipped with OS X,
         // we re-enable the Line Initial Smart Swashes feature that is needed
         // for "split vowels" to work in at least Bengali and Kannada fonts.
         // Affected fonts include Bangla MN, Bangla Sangam MN, Kannada MN,
         // Kannada Sangam MN. See bugs 686225, 728557, 953231, 1145515.
         // Also applies to Oriya and Khmer, see bug 1370927 and bug 1403166.
-        tempCTFont =
-            CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
-                                     aShapedText->DisableLigatures()
-                                         ? GetIndicDisableLigaturesDescriptor()
-                                         : GetIndicFeaturesDescriptor());
-    } else if (aShapedText->DisableLigatures()) {
+        featureFlags |= kIndicFeatures;
+    }
+    if (aShapedText->DisableLigatures()) {
         // For letterspacing (or maybe other situations) we need to make
         // a copy of the CTFont with the ligature feature disabled.
-        tempCTFont =
-            CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
-                                     addSmallCaps
-                                         ? GetSmallCapDisableLigDescriptor()
-                                         : GetDisableLigaturesDescriptor());
-    } else if (addSmallCaps) {
-        tempCTFont =
-            CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
-                                     GetSmallCapsDescriptor());
+        featureFlags |= kDisableLigatures;
+    }
+    if (addSmallCaps) {
+        featureFlags |= kAddSmallCaps;
     }
 
     // For the disabled-ligature, buggy-indic-font or small-caps case, replace
-    // the standard CTFont in the attribute dictionary with a tweaked version.
+    // the default CTFont in the attribute dictionary with a tweaked version.
     CFMutableDictionaryRef mutableAttr = nullptr;
-    if (tempCTFont) {
-        mutableAttr = ::CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 2,
-                                                      attrObj);
+    if (featureFlags != 0) {
+        if (!mCTFont[featureFlags]) {
+            mCTFont[featureFlags] =
+                CreateCTFontWithFeatures(mFont->GetAdjustedSize(),
+                                         GetFeaturesDescriptor(featureFlags));
+        }
+        mutableAttr =
+            ::CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 2, attrObj);
         ::CFDictionaryReplaceValue(mutableAttr,
-                                   kCTFontAttributeName, tempCTFont);
-        // Having created the dict, we're finished with our temporary
-        // Indic and/or ligature-disabled or small-caps CTFontRef.
-        ::CFRelease(tempCTFont);
+                                   kCTFontAttributeName, mCTFont[featureFlags]);
         attrObj = mutableAttr;
     }
 
     // Now we can create an attributed string
     CFAttributedStringRef attrStringObj =
         ::CFAttributedStringCreate(kCFAllocatorDefault, stringObj, attrObj);
     ::CFRelease(stringObj);
 
@@ -572,17 +567,17 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxS
 
 // We also cache feature descriptors for shaping with disabled ligatures, and
 // for buggy Indic AAT font workarounds, created on an as-needed basis.
 
 #define MAX_FEATURES  5 // max used by any of our Get*Descriptor functions
 
 CTFontDescriptorRef
 gfxCoreTextShaper::CreateFontFeaturesDescriptor(
-    const std::pair<SInt16,SInt16> aFeatures[],
+    const std::pair<SInt16,SInt16>* aFeatures,
     size_t aCount)
 {
     MOZ_ASSERT(aCount <= MAX_FEATURES);
 
     CFDictionaryRef featureSettings[MAX_FEATURES];
 
     for (size_t i = 0; i < aCount; i++) {
         CFNumberRef type = ::CFNumberCreate(kCFAllocatorDefault,
@@ -632,143 +627,57 @@ gfxCoreTextShaper::CreateFontFeaturesDes
     CTFontDescriptorRef descriptor =
         ::CTFontDescriptorCreateWithAttributes(attributesDict);
     ::CFRelease(attributesDict);
 
     return descriptor;
 }
 
 CTFontDescriptorRef
-gfxCoreTextShaper::GetDefaultFeaturesDescriptor()
-{
-    if (sDefaultFeaturesDescriptor == nullptr) {
-        const std::pair<SInt16,SInt16> kDefaultFeatures[] = {
-            { kSmartSwashType, kLineInitialSwashesOffSelector },
-            { kSmartSwashType, kLineFinalSwashesOffSelector }
-        };
-        static_assert(ArrayLength(kDefaultFeatures) <= MAX_FEATURES,
-                      "need to increase MAX_FEATURES");
-        sDefaultFeaturesDescriptor =
-            CreateFontFeaturesDescriptor(kDefaultFeatures,
-                                         ArrayLength(kDefaultFeatures));
-    }
-    return sDefaultFeaturesDescriptor;
-}
-
-CTFontDescriptorRef
-gfxCoreTextShaper::GetSmallCapsDescriptor()
+gfxCoreTextShaper::GetFeaturesDescriptor(FeatureFlags aFeatureFlags)
 {
-    if (sSmallCapsDescriptor == nullptr) {
-        const std::pair<SInt16,SInt16> kSmallCaps[] = {
-            { kSmartSwashType, kLineInitialSwashesOffSelector },
-            { kSmartSwashType, kLineFinalSwashesOffSelector },
-            { kLetterCaseType, kSmallCapsSelector },
-            { kLowerCaseType, kLowerCaseSmallCapsSelector }
-        };
-        static_assert(ArrayLength(kSmallCaps) <= MAX_FEATURES,
-                      "need to increase MAX_FEATURES");
-        sSmallCapsDescriptor =
-            CreateFontFeaturesDescriptor(kSmallCaps,
-                                         ArrayLength(kSmallCaps));
-    }
-    return sSmallCapsDescriptor;
-}
-
-CTFontDescriptorRef
-gfxCoreTextShaper::GetDisableLigaturesDescriptor()
-{
-    if (sDisableLigaturesDescriptor == nullptr) {
-        const std::pair<SInt16,SInt16> kDisableLigatures[] = {
-            { kSmartSwashType, kLineInitialSwashesOffSelector },
-            { kSmartSwashType, kLineFinalSwashesOffSelector },
-            { kLigaturesType, kCommonLigaturesOffSelector }
-        };
-        static_assert(ArrayLength(kDisableLigatures) <= MAX_FEATURES,
-                      "need to increase MAX_FEATURES");
-        sDisableLigaturesDescriptor =
-            CreateFontFeaturesDescriptor(kDisableLigatures,
-                                         ArrayLength(kDisableLigatures));
+    MOZ_ASSERT(aFeatureFlags < kMaxFontInstances);
+    if (!sFeaturesDescriptor[aFeatureFlags]) {
+        typedef std::pair<SInt16,SInt16> FeatT;
+        AutoTArray<FeatT,MAX_FEATURES> features;
+        features.AppendElement(FeatT(kSmartSwashType,
+                                     kLineFinalSwashesOffSelector));
+        if ((aFeatureFlags & kIndicFeatures) == 0) {
+            features.AppendElement(FeatT(kSmartSwashType,
+                                         kLineInitialSwashesOffSelector));
+        }
+        if (aFeatureFlags & kAddSmallCaps) {
+            features.AppendElement(FeatT(kLetterCaseType,
+                                         kSmallCapsSelector));
+            features.AppendElement(FeatT(kLowerCaseType,
+                                         kLowerCaseSmallCapsSelector));
+        }
+        if (aFeatureFlags & kDisableLigatures) {
+            features.AppendElement(FeatT(kLigaturesType,
+                                         kCommonLigaturesOffSelector));
+        }
+        MOZ_ASSERT(features.Length() <= MAX_FEATURES);
+        sFeaturesDescriptor[aFeatureFlags] =
+            CreateFontFeaturesDescriptor(features.Elements(),
+                                         features.Length());
     }
-    return sDisableLigaturesDescriptor;
-}
-
-CTFontDescriptorRef
-gfxCoreTextShaper::GetSmallCapDisableLigDescriptor()
-{
-    if (sSmallCapDisableLigDescriptor == nullptr) {
-        const std::pair<SInt16,SInt16> kFeatures[] = {
-            { kSmartSwashType, kLineInitialSwashesOffSelector },
-            { kSmartSwashType, kLineFinalSwashesOffSelector },
-            { kLigaturesType, kCommonLigaturesOffSelector },
-            { kLetterCaseType, kSmallCapsSelector },
-            { kLowerCaseType, kLowerCaseSmallCapsSelector }
-        };
-        static_assert(ArrayLength(kFeatures) <= MAX_FEATURES,
-                      "need to increase MAX_FEATURES");
-        sSmallCapDisableLigDescriptor =
-            CreateFontFeaturesDescriptor(kFeatures,
-                                         ArrayLength(kFeatures));
-    }
-    return sSmallCapDisableLigDescriptor;
-}
-
-CTFontDescriptorRef
-gfxCoreTextShaper::GetIndicFeaturesDescriptor()
-{
-    if (sIndicFeaturesDescriptor == nullptr) {
-        const std::pair<SInt16,SInt16> kIndicFeatures[] = {
-            { kSmartSwashType, kLineFinalSwashesOffSelector }
-        };
-        static_assert(ArrayLength(kIndicFeatures) <= MAX_FEATURES,
-                      "need to increase MAX_FEATURES");
-        sIndicFeaturesDescriptor =
-            CreateFontFeaturesDescriptor(kIndicFeatures,
-                                         ArrayLength(kIndicFeatures));
-    }
-    return sIndicFeaturesDescriptor;
-}
-
-CTFontDescriptorRef
-gfxCoreTextShaper::GetIndicDisableLigaturesDescriptor()
-{
-    if (sIndicDisableLigaturesDescriptor == nullptr) {
-        const std::pair<SInt16,SInt16> kIndicDisableLigatures[] = {
-            { kSmartSwashType, kLineFinalSwashesOffSelector },
-            { kLigaturesType, kCommonLigaturesOffSelector }
-        };
-        static_assert(ArrayLength(kIndicDisableLigatures) <= MAX_FEATURES,
-                      "need to increase MAX_FEATURES");
-        sIndicDisableLigaturesDescriptor =
-            CreateFontFeaturesDescriptor(kIndicDisableLigatures,
-                                         ArrayLength(kIndicDisableLigatures));
-    }
-    return sIndicDisableLigaturesDescriptor;
+    return sFeaturesDescriptor[aFeatureFlags];
 }
 
 CTFontRef
 gfxCoreTextShaper::CreateCTFontWithFeatures(CGFloat aSize,
                                             CTFontDescriptorRef aDescriptor)
 {
     CGFontRef cgFont = static_cast<gfxMacFont*>(mFont)->GetCGFontRef();
     return gfxMacFont::CreateCTFontFromCGFontWithVariations(cgFont, aSize,
                                                             aDescriptor);
 }
 
 void
 gfxCoreTextShaper::Shutdown() // [static]
 {
-    if (sIndicDisableLigaturesDescriptor != nullptr) {
-        ::CFRelease(sIndicDisableLigaturesDescriptor);
-        sIndicDisableLigaturesDescriptor = nullptr;
-    }
-    if (sIndicFeaturesDescriptor != nullptr) {
-        ::CFRelease(sIndicFeaturesDescriptor);
-        sIndicFeaturesDescriptor = nullptr;
-    }
-    if (sDisableLigaturesDescriptor != nullptr) {
-        ::CFRelease(sDisableLigaturesDescriptor);
-        sDisableLigaturesDescriptor = nullptr;
-    }
-    if (sDefaultFeaturesDescriptor != nullptr) {
-        ::CFRelease(sDefaultFeaturesDescriptor);
-        sDefaultFeaturesDescriptor = nullptr;
+    for (size_t i = 0; i < kMaxFontInstances; i++) {
+        if (sFeaturesDescriptor[i] != nullptr) {
+            ::CFRelease(sFeaturesDescriptor[i]);
+            sFeaturesDescriptor[i] = nullptr;
+        }
     }
 }
--- a/gfx/thebes/gfxCoreTextShaper.h
+++ b/gfx/thebes/gfxCoreTextShaper.h
@@ -25,18 +25,33 @@ public:
                    Script           aScript,
                    bool             aVertical,
                    RoundingFlags    aRounding,
                    gfxShapedText   *aShapedText) override;
 
     // clean up static objects that may have been cached
     static void Shutdown();
 
+    // Flags used to track what AAT features should be enabled on the Core Text
+    // font instance. (Internal; only public so that we can use the
+    // MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS macro below.)
+    enum FeatureFlags : uint8_t {
+        kDefaultFeatures  = 0x00,
+        // bit flags for non-default feature settings we might need
+        // to use, which will require separate font instances
+        kDisableLigatures = 0x01,
+        kAddSmallCaps     = 0x02,
+        kIndicFeatures    = 0x04,
+
+        // number of font instances, indexed by OR-ing the flags above
+        kMaxFontInstances = 8
+    };
+
 protected:
-    CTFontRef mCTFont;
+    CTFontRef mCTFont[kMaxFontInstances];
 
     // attributes for shaping text with LTR or RTL directionality
     CFDictionaryRef mAttributesDictLTR;
     CFDictionaryRef mAttributesDictRTL;
 
     nsresult SetGlyphsFromRun(gfxShapedText *aShapedText,
                               uint32_t       aOffset,
                               uint32_t       aLength,
@@ -44,32 +59,20 @@ protected:
 
     CTFontRef CreateCTFontWithFeatures(CGFloat aSize,
                                        CTFontDescriptorRef aDescriptor);
 
     CFDictionaryRef CreateAttrDict(bool aRightToLeft);
     CFDictionaryRef CreateAttrDictWithoutDirection();
 
     static CTFontDescriptorRef
-    CreateFontFeaturesDescriptor(const std::pair<SInt16,SInt16> aFeatures[],
+    CreateFontFeaturesDescriptor(const std::pair<SInt16,SInt16>* aFeatures,
                                  size_t aCount);
 
-    static CTFontDescriptorRef GetDefaultFeaturesDescriptor();
-    static CTFontDescriptorRef GetSmallCapsDescriptor();
-    static CTFontDescriptorRef GetDisableLigaturesDescriptor();
-    static CTFontDescriptorRef GetSmallCapDisableLigDescriptor();
-    static CTFontDescriptorRef GetIndicFeaturesDescriptor();
-    static CTFontDescriptorRef GetIndicDisableLigaturesDescriptor();
+    static CTFontDescriptorRef GetFeaturesDescriptor(FeatureFlags aFeatureFlags);
 
-    // cached font descriptor, created the first time it's needed
-    static CTFontDescriptorRef    sDefaultFeaturesDescriptor;
-    static CTFontDescriptorRef    sSmallCapsDescriptor;
-
-    // cached descriptor for adding disable-ligatures setting to a font
-    static CTFontDescriptorRef    sDisableLigaturesDescriptor;
-    static CTFontDescriptorRef    sSmallCapDisableLigDescriptor;
-
-    // feature descriptors for buggy Indic AAT font workaround
-    static CTFontDescriptorRef    sIndicFeaturesDescriptor;
-    static CTFontDescriptorRef    sIndicDisableLigaturesDescriptor;
+    // cached font descriptors, created the first time they're needed
+    static CTFontDescriptorRef sFeaturesDescriptor[kMaxFontInstances];
 };
 
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(gfxCoreTextShaper::FeatureFlags)
+
 #endif /* GFX_CORETEXTSHAPER_H */