Commit fbcf2a884d3fdbff0288e49321a09eff880f54cf

sammy 2008-05-01T18:33:44

* Optimise FTFace::KernAdvance() so that kerning for font indices < 128 is precomputed during FTFace() instantiation to avoid calling FT_Get_Kerning() too often. Patch by Sean Morrison, taken from bzflag commit r14652, reworked for safety and performance by me.

diff --git a/src/FTCharmap.cpp b/src/FTCharmap.cpp
index c4cd1b8..545a0c8 100644
--- a/src/FTCharmap.cpp
+++ b/src/FTCharmap.cpp
@@ -40,7 +40,7 @@ FTCharmap::FTCharmap(FTFace* face)
 
     ftEncoding = ftFace->charmap->encoding;
 
-    for(int i = 0; i < FTCharmap::MAX_PRECOMPUTED; i++)
+    for(unsigned int i = 0; i < FTCharmap::MAX_PRECOMPUTED; i++)
     {
         charIndexCache[i] = FT_Get_Char_Index(ftFace, i);
     }
diff --git a/src/FTCharmap.h b/src/FTCharmap.h
index 5b48213..354c494 100644
--- a/src/FTCharmap.h
+++ b/src/FTCharmap.h
@@ -152,8 +152,8 @@ class FTCharmap
         /**
          * Precomputed font indices.
          */
-        static const int MAX_PRECOMPUTED = 128;
-        unsigned int characterCodeIndices[MAX_PRECOMPUTED];
+        static const unsigned int MAX_PRECOMPUTED = 128;
+        unsigned int charIndexCache[MAX_PRECOMPUTED];
 
         /**
          * Current error code.
diff --git a/src/FTFace.cpp b/src/FTFace.cpp
index b2a2c13..a87194f 100644
--- a/src/FTFace.cpp
+++ b/src/FTFace.cpp
@@ -30,9 +30,11 @@
 
 #include FT_TRUETYPE_TABLES_H
 
-FTFace::FTFace(const char* fontFilePath)
-:   numGlyphs(0),
+FTFace::FTFace(const char* fontFilePath, bool precomputeKerning)
+:   ftFace(0),
+    numGlyphs(0),
     fontEncodingList(0),
+    kerningCache(0),
     err(0)
 {
     const FT_Long DEFAULT_FACE_INDEX = 0;
@@ -40,21 +42,25 @@ FTFace::FTFace(const char* fontFilePath)
 
     err = FT_New_Face(*FTLibrary::Instance().GetLibrary(), fontFilePath,
                       DEFAULT_FACE_INDEX, ftFace);
-
     if(err)
     {
         delete ftFace;
         ftFace = 0;
+        return;
     }
-    else
+
+    numGlyphs = (*ftFace)->num_glyphs;
+    hasKerningTable = FT_HAS_KERNING((*ftFace));
+
+    if(hasKerningTable && precomputeKerning)
     {
-        numGlyphs = (*ftFace)->num_glyphs;
-        hasKerningTable = FT_HAS_KERNING((*ftFace));
+        BuildKerningCache();
     }
 }
 
 
-FTFace::FTFace(const unsigned char *pBufferBytes, size_t bufferSizeInBytes)
+FTFace::FTFace(const unsigned char *pBufferBytes, size_t bufferSizeInBytes,
+               bool precomputeKerning)
 :   numGlyphs(0),
     err(0)
 {
@@ -68,16 +74,26 @@ FTFace::FTFace(const unsigned char *pBufferBytes, size_t bufferSizeInBytes)
     {
         delete ftFace;
         ftFace = 0;
+        return;
     }
-    else
+
+    numGlyphs = (*ftFace)->num_glyphs;
+    hasKerningTable = FT_HAS_KERNING((*ftFace));
+
+    if(hasKerningTable && precomputeKerning)
     {
-        numGlyphs = (*ftFace)->num_glyphs;
+        BuildKerningCache();
     }
 }
 
 
 FTFace::~FTFace()
 {
+    if(kerningCache)
+    {
+        delete[] kerningCache;
+    }
+
     if(ftFace)
     {
         FT_Done_Face(*ftFace);
@@ -141,23 +157,34 @@ FT_Encoding* FTFace::CharMapList()
 FTPoint FTFace::KernAdvance(unsigned int index1, unsigned int index2)
 {
     float x, y;
-    x = y = 0.0f;
 
-    if(hasKerningTable && index1 && index2)
+    if(!hasKerningTable || !index1 || !index2)
     {
-        FT_Vector kernAdvance;
-        kernAdvance.x = kernAdvance.y = 0;
+        return FTPoint(0.0f, 0.0f);
+    }
 
-        err = FT_Get_Kerning(*ftFace, index1, index2, ft_kerning_unfitted,
-                             &kernAdvance);
-        if(!err)
-        {
-            x = static_cast<float>(kernAdvance.x) / 64.0f;
-            y = static_cast<float>(kernAdvance.y) / 64.0f;
-        }
+    if(kerningCache && index1 < FTFace::MAX_PRECOMPUTED
+        && index2 < FTFace::MAX_PRECOMPUTED)
+    {
+        x = kerningCache[2 * (index2 * FTFace::MAX_PRECOMPUTED + index1)];
+        y = kerningCache[2 * (index2 * FTFace::MAX_PRECOMPUTED + index1) + 1];
+        return FTPoint(x, y);
     }
 
-    return FTPoint(x, y, 0.0);
+    FT_Vector kernAdvance;
+    kernAdvance.x = kernAdvance.y = 0;
+
+    err = FT_Get_Kerning(*ftFace, index1, index2, ft_kerning_unfitted,
+                         &kernAdvance);
+    if(err)
+    {
+        return FTPoint(0.0f, 0.0f);
+    }
+
+    x = static_cast<float>(kernAdvance.x) / 64.0f;
+    y = static_cast<float>(kernAdvance.y) / 64.0f;
+
+    return FTPoint(x, y);
 }
 
 
@@ -172,3 +199,32 @@ FT_GlyphSlot FTFace::Glyph(unsigned int index, FT_Int load_flags)
     return (*ftFace)->glyph;
 }
 
+
+void FTFace::BuildKerningCache()
+{
+    FT_Vector kernAdvance;
+    kernAdvance.x = 0;
+    kernAdvance.y = 0;
+    kerningCache = new float[FTFace::MAX_PRECOMPUTED
+                              * FTFace::MAX_PRECOMPUTED * 2];
+    for(unsigned int j = 0; j < FTFace::MAX_PRECOMPUTED; j++)
+    {
+        for(unsigned int i = 0; i < FTFace::MAX_PRECOMPUTED; i++)
+        {
+            err = FT_Get_Kerning(*ftFace, i, j, ft_kerning_unfitted,
+                                 &kernAdvance);
+            if(err)
+            {
+                delete[] kerningCache;
+                kerningCache = NULL;
+                return;
+            }
+
+            kerningCache[2 * (j * FTFace::MAX_PRECOMPUTED + i)] =
+                                static_cast<float>(kernAdvance.x) / 64.0f;
+            kerningCache[2 * (j * FTFace::MAX_PRECOMPUTED + i) + 1] =
+                                static_cast<float>(kernAdvance.y) / 64.0f;
+        }
+    }
+}
+
diff --git a/src/FTFace.h b/src/FTFace.h
index 36e02c6..899822f 100644
--- a/src/FTFace.h
+++ b/src/FTFace.h
@@ -48,7 +48,7 @@ class FTFace
          *
          * @param fontFilePath  font file path.
          */
-        FTFace(const char* fontFilePath);
+        FTFace(const char* fontFilePath, bool precomputeKerning = true);
 
         /**
          * Read face data from an in-memory buffer. Error is set.
@@ -56,7 +56,8 @@ class FTFace
          * @param pBufferBytes  the in-memory buffer
          * @param bufferSizeInBytes  the length of the buffer in bytes
          */
-        FTFace(const unsigned char *pBufferBytes, size_t bufferSizeInBytes);
+        FTFace(const unsigned char *pBufferBytes, size_t bufferSizeInBytes,
+               bool precomputeKerning = true);
 
         /**
          * Destructor
@@ -161,7 +162,14 @@ class FTFace
         /**
          * This face has kerning tables
          */
-         bool hasKerningTable;
+        bool hasKerningTable;
+
+        /**
+         * If this face has kerning tables, we can cache them.
+         */
+        void BuildKerningCache();
+        static const unsigned int MAX_PRECOMPUTED = 128;
+        float *kerningCache;
 
         /**
          * Current error code. Zero means no error.