bug 668813 pt 2 - maintain a character coverage map for gfxFontFamily. r=jdaggett
authorJonathan Kew <jfkthame@gmail.com>
Tue, 09 Aug 2011 09:06:01 +0100
changeset 86814 80fa4ccd0b2b0e3909280a591aae831b3303879c
parent 86813 fa0c78177fb9493188e871b217b3eee09b1d47e1
child 86815 e19de454a479e89f4a01f0cdccafc54da8bb1489
push id22058
push usermak77@bonardo.net
push dateWed, 15 Feb 2012 16:38:33 +0000
treeherdermozilla-central@46e22ce549b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs668813
milestone13.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 668813 pt 2 - maintain a character coverage map for gfxFontFamily. r=jdaggett
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontUtils.cpp
gfx/thebes/gfxFontUtils.h
gfx/thebes/gfxUserFontSet.h
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -687,22 +687,25 @@ void gfxFontFamily::LocalizedName(nsAStr
     // just return the primary name; subclasses should override
     aLocalizedName = mName;
 }
 
 
 void
 gfxFontFamily::FindFontForChar(FontSearch *aMatchData)
 {
-    if (!mHasStyles)
+    if (!mHasStyles) {
         FindStyleVariations();
-
-    // xxx - optimization point - keep a bit vector with the union of supported unicode ranges
-    // by all fonts for this family and bail immediately if the character is not in any of
-    // this family's cmaps
+    }
+
+    if (!TestCharacterMap(aMatchData->mCh)) {
+        // none of the faces in the family support the required char,
+        // so bail out immediately
+        return;
+    }
 
     // iterate over fonts
     PRUint32 numFonts = mAvailableFonts.Length();
     for (PRUint32 i = 0; i < numFonts; i++) {
         gfxFontEntry *fe = mAvailableFonts[i];
 
         // skip certain fonts during system fallback
         if (!fe || fe->SkipDuringSystemFallback())
@@ -3361,21 +3364,18 @@ gfxFontGroup::FindFontForChar(PRUint32 a
     // 1. check fonts in the font group
     for (PRUint32 i = 0; i < FontListLength(); i++) {
         nsRefPtr<gfxFont> font = GetFontAt(i);
         if (font->HasCharacter(aCh)) {
             *aMatchType = gfxTextRange::kFontGroup;
             return font.forget();
         }
         // check other faces of the family
-        // XXX optimization point: give the family a charmap that is the union
-        // of the char maps of all its faces, so we can quickly test whether
-        // it's worth doing this search
         gfxFontFamily *family = font->GetFontEntry()->Family();
-        if (family) {
+        if (family && family->TestCharacterMap(aCh)) {
             FontSearch matchData(aCh, font);
             family->FindFontForChar(&matchData);
             gfxFontEntry *fe = matchData.mBestMatch;
             if (fe) {
                 bool needsBold =
                     font->GetStyle()->weight >= 600 && !fe->IsBold();
                 selectedFont =
                     fe->FindOrMakeFont(font->GetStyle(), needsBold);
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -505,17 +505,18 @@ public:
 
     gfxFontFamily(const nsAString& aName) :
         mName(aName),
         mOtherFamilyNamesInitialized(false),
         mHasOtherFamilyNames(false),
         mFaceNamesInitialized(false),
         mHasStyles(false),
         mIsSimpleFamily(false),
-        mIsBadUnderlineFamily(false)
+        mIsBadUnderlineFamily(false),
+        mCharacterMapInitialized(false)
         { }
 
     virtual ~gfxFontFamily() {
         // clear Family pointers in our faces; the font entries might stay
         // alive due to cached font objects, but they can no longer refer
         // to their families.
         PRUint32 i = mAvailableFonts.Length();
         while (i) {
@@ -578,20 +579,38 @@ public:
     virtual void FindStyleVariations() { }
 
     // search for a specific face using the Postscript name
     gfxFontEntry* FindFont(const nsAString& aPostscriptName);
 
     // read in cmaps for all the faces
     void ReadCMAP() {
         PRUint32 i, numFonts = mAvailableFonts.Length();
-        // called from RunLoader BEFORE CheckForSimpleFamily so that there cannot
-        // be any NULL entries in mAvailableFonts
-        for (i = 0; i < numFonts; i++)
-            mAvailableFonts[i]->ReadCMAP();
+        for (i = 0; i < numFonts; i++) {
+            gfxFontEntry *fe = mAvailableFonts[i];
+            if (!fe) {
+                continue;
+            }
+            fe->ReadCMAP();
+            mCharacterMap.Union(fe->mCharacterMap);
+        }
+        mCharacterMap.Compact();
+        mCharacterMapInitialized = true;
+    }
+
+    bool TestCharacterMap(PRUint32 aCh) {
+        if (!mCharacterMapInitialized) {
+            ReadCMAP();
+        }
+        return mCharacterMap.test(aCh);
+    }
+
+    void ResetCharacterMap() {
+        mCharacterMap.reset();
+        mCharacterMapInitialized = false;
     }
 
     // mark this family as being in the "bad" underline offset blacklist
     void SetBadUnderlineFamily() {
         mIsBadUnderlineFamily = true;
         if (mHasStyles) {
             SetBadUnderlineFonts();
         }
@@ -624,22 +643,24 @@ protected:
             if (mAvailableFonts[i]) {
                 mAvailableFonts[i]->mIsBadUnderlineFont = true;
             }
         }
     }
 
     nsString mName;
     nsTArray<nsRefPtr<gfxFontEntry> >  mAvailableFonts;
+    gfxSparseBitSet mCharacterMap;
     bool mOtherFamilyNamesInitialized;
     bool mHasOtherFamilyNames;
     bool mFaceNamesInitialized;
     bool mHasStyles;
     bool mIsSimpleFamily;
     bool mIsBadUnderlineFamily;
+    bool mCharacterMapInitialized;
 
     enum {
         // for "simple" families, the faces are stored in mAvailableFonts
         // with fixed positions:
         kRegularFaceIndex    = 0,
         kBoldFaceIndex       = 1,
         kItalicFaceIndex     = 2,
         kBoldItalicFaceIndex = 3,
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -310,17 +310,17 @@ gfxFontUtils::ReadCMAPTableFormat12(cons
         NS_ENSURE_TRUE((prevEndCharCode < startCharCode || i == 0) &&
                        startCharCode <= endCharCode &&
                        endCharCode <= CMAP_MAX_CODEPOINT, 
                        NS_ERROR_GFX_CMAP_MALFORMED);
         aCharacterMap.SetRange(startCharCode, endCharCode);
         prevEndCharCode = endCharCode;
     }
 
-    aCharacterMap.mBlocks.Compact();
+    aCharacterMap.Compact();
 
     return NS_OK;
 }
 
 nsresult 
 gfxFontUtils::ReadCMAPTableFormat4(const PRUint8 *aBuf, PRUint32 aLength,
                                    gfxSparseBitSet& aCharacterMap)
 {
@@ -390,17 +390,17 @@ gfxFontUtils::ReadCMAPTableFormat4(const
                     // glyph = (ReadShortAt16(idDeltas, i) + *gdata) % 65536;
 
                     aCharacterMap.set(c);
                 }
             }
         }
     }
 
-    aCharacterMap.mBlocks.Compact();
+    aCharacterMap.Compact();
 
     return NS_OK;
 }
 
 nsresult
 gfxFontUtils::ReadCMAPTableFormat14(const PRUint8 *aBuf, PRUint32 aLength,
                                     PRUint8*& aTable)
 {
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -160,18 +160,16 @@ public:
         if (blockIndex >= mBlocks.Length()) {
             nsAutoPtr<Block> *blocks = mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
             if (NS_UNLIKELY(!blocks)) // OOM
                 return;
         }
         Block *block = mBlocks[blockIndex];
         if (!block) {
             block = new Block;
-            if (NS_UNLIKELY(!block)) // OOM
-                return;
             mBlocks[blockIndex] = block;
         }
         block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
     }
 
     void set(PRUint32 aIndex, bool aValue) {
         if (aValue)
             set(aIndex);
@@ -196,19 +194,16 @@ public:
 
             Block *block = mBlocks[i];
             if (!block) {
                 bool fullBlock = false;
                 if (aStart <= blockFirstBit && aEnd >= blockLastBit)
                     fullBlock = true;
 
                 block = new Block(fullBlock ? 0xFF : 0);
-
-                if (NS_UNLIKELY(!block)) // OOM
-                    return;
                 mBlocks[i] = block;
 
                 if (fullBlock)
                     continue;
             }
 
             const PRUint32 start = aStart > blockFirstBit ? aStart - blockFirstBit : 0;
             const PRUint32 end = NS_MIN<PRUint32>(aEnd - blockFirstBit, BLOCK_SIZE_BITS - 1);
@@ -274,17 +269,54 @@ public:
     }
 
     // clear out all blocks in the array
     void reset() {
         PRUint32 i;
         for (i = 0; i < mBlocks.Length(); i++)
             mBlocks[i] = nsnull;    
     }
-    
+
+    // set this bitset to the union of its current contents and another
+    void Union(const gfxSparseBitSet& aBitset) {
+        // ensure mBlocks is large enough
+        PRUint32 blockCount = aBitset.mBlocks.Length();
+        if (blockCount > mBlocks.Length()) {
+            PRUint32 needed = blockCount - mBlocks.Length();
+            nsAutoPtr<Block> *blocks = mBlocks.AppendElements(needed);
+            if (NS_UNLIKELY(!blocks)) { // OOM
+                return;
+            }
+        }
+        // for each block that may be present in aBitset...
+        for (PRUint32 i = 0; i < blockCount; ++i) {
+            // if it is missing (implicitly empty), just skip
+            if (!aBitset.mBlocks[i]) {
+                continue;
+            }
+            // if the block is missing in this set, just copy the other
+            if (!mBlocks[i]) {
+                mBlocks[i] = new Block(*aBitset.mBlocks[i]);
+                continue;
+            }
+            // else set existing block to the union of both
+            PRUint32 *dst = reinterpret_cast<PRUint32*>(mBlocks[i]->mBits);
+            const PRUint32 *src =
+                reinterpret_cast<const PRUint32*>(aBitset.mBlocks[i]->mBits);
+            for (PRUint32 j = 0; j < BLOCK_SIZE / 4; ++j) {
+                dst[j] |= src[j];
+            }
+        }
+    }
+
+    void Compact() {
+        mBlocks.Compact();
+    }
+
+private:
     nsTArray< nsAutoPtr<Block> > mBlocks;
 };
 
 #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
 
 namespace mozilla {
 
 // Byte-swapping types and name table structure definitions moved from
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -105,45 +105,47 @@ public:
         : gfxFontFamily(aName) { }
 
     virtual ~gfxMixedFontFamily() { }
 
     void AddFontEntry(gfxFontEntry *aFontEntry) {
         nsRefPtr<gfxFontEntry> fe = aFontEntry;
         mAvailableFonts.AppendElement(fe);
         aFontEntry->SetFamily(this);
+        ResetCharacterMap();
     }
 
-    void ReplaceFontEntry(gfxFontEntry *aOldFontEntry, gfxFontEntry *aNewFontEntry) 
-    {
+    void ReplaceFontEntry(gfxFontEntry *aOldFontEntry,
+                          gfxFontEntry *aNewFontEntry) {
         PRUint32 numFonts = mAvailableFonts.Length();
         for (PRUint32 i = 0; i < numFonts; i++) {
             gfxFontEntry *fe = mAvailableFonts[i];
             if (fe == aOldFontEntry) {
                 aOldFontEntry->SetFamily(nsnull);
                 // note that this may delete aOldFontEntry, if there's no
                 // other reference to it except from its family
                 mAvailableFonts[i] = aNewFontEntry;
                 aNewFontEntry->SetFamily(this);
-                return;
+                break;
             }
         }
+        ResetCharacterMap();
     }
 
-    void RemoveFontEntry(gfxFontEntry *aFontEntry) 
-    {
+    void RemoveFontEntry(gfxFontEntry *aFontEntry) {
         PRUint32 numFonts = mAvailableFonts.Length();
         for (PRUint32 i = 0; i < numFonts; i++) {
             gfxFontEntry *fe = mAvailableFonts[i];
             if (fe == aFontEntry) {
                 aFontEntry->SetFamily(nsnull);
                 mAvailableFonts.RemoveElementAt(i);
-                return;
+                break;
             }
         }
+        ResetCharacterMap();
     }
 
     // clear family pointer for all entries and remove them from the family;
     // we need to do this explicitly before inserting the entries into a new
     // family, in case the old one is not actually deleted until later
     void DetachFontEntries() {
         PRUint32 i = mAvailableFonts.Length();
         while (i--) {