Bug 1514869 - patch 7 - Check font families for "simple" set of faces, and mark the Family record appropriately so we can use simplified style-matching. r=jwatt
authorJonathan Kew <jkew@mozilla.com>
Sat, 27 Apr 2019 15:39:27 +0000
changeset 530576 b3fbab6d325ae88011a9589d6398353f5f64377b
parent 530575 0bad995bae2250682807c86f9d9abc23b0f2313c
child 530577 7de7d6a0be86d400ee23ca1ac806eb358555b28d
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs1514869
milestone68.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 1514869 - patch 7 - Check font families for "simple" set of faces, and mark the Family record appropriately so we can use simplified style-matching. r=jwatt Differential Revision: https://phabricator.services.mozilla.com/D23718
gfx/thebes/SharedFontList.cpp
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxMacPlatformFontList.mm
--- a/gfx/thebes/SharedFontList.cpp
+++ b/gfx/thebes/SharedFontList.cpp
@@ -98,31 +98,80 @@ void Face::SetCharacterMap(FontList* aLi
   auto pfl = gfxPlatformFontList::PlatformFontList();
   mCharacterMap = pfl->GetShmemCharMap(aCharMap);
 }
 
 void Family::AddFaces(FontList* aList, const nsTArray<Face::InitData>& aFaces) {
   MOZ_ASSERT(XRE_IsParentProcess());
   if (mFaceCount > 0) {
     // Already initialized!
-    MOZ_ASSERT(mFaceCount == aFaces.Length());
     return;
   }
+
   uint32_t count = aFaces.Length();
-  // Allocate space for the face records, and initialize them
-  Pointer p = aList->Alloc(aFaces.Length() * sizeof(Pointer));
+  bool isSimple = false;
+  // A family is "simple" (i.e. simplified style selection may be used instead
+  // of the full CSS font-matching algorithm) if there is at maximum one normal,
+  // bold, italic, and bold-italic face; in this case, they are stored at known
+  // positions in the mFaces array.
+  const Face::InitData* slots[4] = { nullptr, nullptr, nullptr, nullptr };
+  if (count >= 2 && count <= 4) {
+    // Check if this can be treated as a "simple" family
+    isSimple = true;
+    for (const auto& f : aFaces) {
+      if (!f.mWeight.IsSingle() || !f.mStretch.IsSingle() ||
+          !f.mStyle.IsSingle()) {
+        isSimple = false;
+        break;
+      }
+      if (!f.mStretch.Min().IsNormal()) {
+        isSimple = false;
+        break;
+      }
+      // Figure out which slot (0-3) this face belongs in
+      size_t slot = 0;
+      static_assert((kBoldMask | kItalicMask) == 0b11, "bad bold/italic bits");
+      if (f.mWeight.Min().IsBold()) {
+        slot |= kBoldMask;
+      }
+      if (f.mStyle.Min().IsItalic() || f.mStyle.Min().IsOblique()) {
+        slot |= kItalicMask;
+      }
+      if (slots[slot]) {
+        // More than one face mapped to the same slot - not a simple family!
+        isSimple = false;
+        break;
+      }
+      slots[slot] = &f;
+    }
+    if (isSimple) {
+      // Ensure all 4 slots will exist, even if some are empty.
+      count = 4;
+    }
+  }
+
+  // Allocate space for the face records, and initialize them.
+  // coverity[suspicious_sizeof]
+  Pointer p = aList->Alloc(count * sizeof(Pointer));
   auto facePtrs = static_cast<Pointer*>(p.ToPtr(aList));
-  for (size_t i = 0; i < aFaces.Length(); i++) {
-    Pointer fp = aList->Alloc(sizeof(Face));
-    auto face = static_cast<Face*>(fp.ToPtr(aList));
-    (void)new (face) Face(aList, aFaces[i]);
-    facePtrs[i] = fp;
+  for (size_t i = 0; i < count; i++) {
+    if (isSimple && !slots[i]) {
+      facePtrs[i] = Pointer::Null();
+    } else {
+      Pointer fp = aList->Alloc(sizeof(Face));
+      auto face = static_cast<Face*>(fp.ToPtr(aList));
+      (void)new (face) Face(aList, isSimple ? *slots[i] : aFaces[i]);
+      facePtrs[i] = fp;
+    }
   }
+
+  mIsSimple = isSimple;
   mFaces = p;
   mFaceCount.store(count);
+
   if (LOG_FONTLIST_ENABLED()) {
     const nsCString& fam = DisplayName().AsString(aList);
     for (unsigned j = 0; j < aFaces.Length(); j++) {
       nsAutoCString weight, style, stretch;
       aFaces[j].mWeight.ToString(weight);
       aFaces[j].mStyle.ToString(style);
       aFaces[j].mStretch.ToString(stretch);
       LOG_FONTLIST(
@@ -167,17 +216,17 @@ void Family::FindAllFacesForStyle(FontLi
     // present.
     bool wantBold = aStyle.weight >= FontWeight(600);
     bool wantItalic = !aStyle.style.IsNormal();
     uint8_t faceIndex =
         (wantItalic ? kItalicMask : 0) | (wantBold ? kBoldMask : 0);
 
     // if the desired style is available, return it directly
     Face* face = static_cast<Face*>(facePtrs[faceIndex].ToPtr(aList));
-    if (face->HasValidDescriptor()) {
+    if (face && face->HasValidDescriptor()) {
       aFaceList.AppendElement(face);
       return;
     }
 
     // order to check fallback faces in a simple family, depending on requested
     // style
     static const uint8_t simpleFallbacks[4][3] = {
         {kBoldFaceIndex, kItalicFaceIndex,
@@ -187,17 +236,17 @@ void Family::FindAllFacesForStyle(FontLi
         {kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex}  // BoldItalic
     };
     const uint8_t* order = simpleFallbacks[faceIndex];
 
     for (uint8_t trial = 0; trial < 3; ++trial) {
       // check remaining faces in order of preference to find the first that
       // actually exists
       face = static_cast<Face*>(facePtrs[order[trial]].ToPtr(aList));
-      if (face->HasValidDescriptor()) {
+      if (face && face->HasValidDescriptor()) {
         aFaceList.AppendElement(face);
         return;
       }
     }
 
     // this can't happen unless we have totally broken the font-list manager!
     MOZ_ASSERT_UNREACHABLE("no face found in simple font family!");
   }
@@ -258,16 +307,19 @@ void Family::SearchAllFontsForChar(FontL
       return;
     }
   }
   uint32_t numFaces = NumFaces();
   uint32_t charMapsLoaded = 0; // number of faces whose charmap is loaded
   Pointer* facePtrs = Faces(aList);
   for (uint32_t i = 0; i < numFaces; i++) {
     Face* face = static_cast<Face*>(facePtrs[i].ToPtr(aList));
+    if (!face) {
+      continue;
+    }
     MOZ_ASSERT(face->HasValidDescriptor());
     // Get the face's character map, if available (may be null!)
     charmap =
         static_cast<const SharedBitSet*>(face->mCharacterMap.ToPtr(aList));
     if (charmap) {
       ++charMapsLoaded;
     }
     // Check style distance if the char is supported, or if charmap not known
@@ -296,21 +348,59 @@ void Family::SearchAllFontsForChar(FontL
     }
   }
   if (mCharacterMap.IsNull() && charMapsLoaded == numFaces) {
     SetupFamilyCharMap(aList);
   }
 }
 
 void Family::SetFacePtrs(FontList* aList, nsTArray<Pointer>& aFaces) {
+  if (aFaces.Length() >= 2 && aFaces.Length() <= 4) {
+    // Check whether the faces meet the criteria for a "simple" family: no more
+    // than one each of Regular, Bold, Italic, BoldItalic styles. If so, store
+    // them at the appropriate slots in mFaces and set the mIsSimple flag to
+    // accelerate font-matching.
+    bool isSimple = true;
+    Pointer slots[4] = {
+      Pointer::Null(), Pointer::Null(), Pointer::Null(), Pointer::Null()
+    };
+    for (const Pointer& fp : aFaces) {
+      const Face* f = static_cast<const Face*>(fp.ToPtr(aList));
+      if (!f->mWeight.IsSingle() || !f->mStyle.IsSingle() ||
+          !f->mStretch.IsSingle()) {
+        isSimple = false;
+        break;
+      }
+      if (!f->mStretch.Min().IsNormal()) {
+        isSimple = false;
+        break;
+      }
+      size_t slot = 0;
+      if (f->mWeight.Min().IsBold()) {
+        slot |= kBoldMask;
+      }
+      if (f->mStyle.Min().IsItalic() || f->mStyle.Min().IsOblique()) {
+        slot |= kItalicMask;
+      }
+      if (!slots[slot].IsNull()) {
+        isSimple = false;
+        break;
+      }
+      slots[slot] = fp;
+    }
+    if (isSimple) {
+      size_t size = 4 * sizeof(Pointer);
+      mFaces = aList->Alloc(size);
+      memcpy(mFaces.ToPtr(aList), slots, size);
+      mFaceCount.store(4);
+      mIsSimple = true;
+      return;
+    }
+  }
   size_t size = aFaces.Length() * sizeof(Pointer);
-  // XXX TODO: [in patch 7]
-  // Check whether the faces meet the criteria for a "simple" family; if so,
-  // store them at the appropriate positions and set the mIsSimple flag to
-  // accelerate font-matching.
   mFaces = aList->Alloc(size);
   memcpy(mFaces.ToPtr(aList), aFaces.Elements(), size);
   mFaceCount.store(aFaces.Length());
 }
 
 void Family::SetupFamilyCharMap(FontList* aList) {
   // Set the character map of the family to the union of all the face cmaps,
   // to allow font fallback searches to more rapidly reject the family.
@@ -326,16 +416,19 @@ void Family::SetupFamilyCharMap(FontList
   SharedBitSet* firstMap = nullptr;
   bool merged = false;
   Pointer* faces = Faces(aList);
   if (!faces) {
     return;
   }
   for (size_t i = 0; i < NumFaces(); i++) {
     auto f = static_cast<Face*>(faces[i].ToPtr(aList));
+    if (!f) {
+      continue;
+    }
     auto faceMap = static_cast<SharedBitSet*>(f->mCharacterMap.ToPtr(aList));
     MOZ_ASSERT(faceMap);
     if (!firstMap) {
       firstMap = faceMap;
       firstMapShmPointer = f->mCharacterMap;
     } else if (faceMap != firstMap) {
       if (!merged) {
         familyMap.Union(*firstMap);
@@ -705,16 +798,19 @@ void FontList::SearchForLocalFace(const 
     if (!family->IsInitialized()) {
       if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(family)) {
         continue;
       }
     }
     Pointer* faces = family->Faces(this);
     for (uint32_t j = 0; j < family->NumFaces(); j++) {
       Face* face = static_cast<Face*>(faces[j].ToPtr(this));
+      if (!face) {
+        continue;
+      }
       nsAutoCString psname, fullname;
       if (gfxPlatformFontList::PlatformFontList()->ReadFaceNames(
               family, face, psname, fullname)) {
         LOG_FONTLIST(("(shared-fontlist) read psname (%s) fullname (%s)",
                       psname.get(), fullname.get()));
         ToLowerCase(psname);
         ToLowerCase(fullname);
         if (aName == psname || aName == fullname) {
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -1115,16 +1115,19 @@ void gfxDWriteFontList::ReadFaceNamesFor
   const fontlist::Pointer* facePtrs = aFamily->Faces(list);
   nsAutoCString familyName(aFamily->DisplayName().AsString(list));
   nsAutoCString key(aFamily->Key().AsString(list));
 
   // Read PS-names and fullnames of the faces, and any alternate family names
   // (either localizations or legacy subfamily names)
   for (unsigned i = 0; i < aFamily->NumFaces(); ++i) {
     auto face = static_cast<fontlist::Face*>(facePtrs[i].ToPtr(list));
+    if (!face) {
+      continue;
+    }
     RefPtr<IDWriteFont> dwFont;
     if (FAILED(family->GetFont(face->mIndex, getter_AddRefs(dwFont)))) {
       continue;
     }
     RefPtr<IDWriteFontFace> dwFontFace;
     if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
       continue;
     }
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -1844,16 +1844,19 @@ void gfxMacPlatformFontList::ReadFaceNam
     }
   }
   const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
   fontlist::FontList* list = SharedFontList();
   nsAutoCString canonicalName(aFamily->DisplayName().AsString(list));
   const fontlist::Pointer* facePtrs = aFamily->Faces(list);
   for (uint32_t i = 0, n = aFamily->NumFaces(); i < n; i++) {
     auto face = static_cast<fontlist::Face*>(facePtrs[i].ToPtr(list));
+    if (!face) {
+      continue;
+    }
     nsAutoCString name(face->mDescriptor.AsString(list));
     // We create a temporary MacOSFontEntry just to read family names from the
     // 'name' table in the font resource. The style attributes here are ignored
     // as this entry is not used for font style matching.
     // The size hint might be used to select which face is accessed in the case
     // of the macOS UI font; see MacOSFontEntry::GetFontRef(). We pass 16.0 in
     // order to get a standard text-size face in this case, although it's
     // unlikely to matter for the purpose of just reading family names.