[poppler] change font cache policy to improve performance.

Sam Liao phyomh at gmail.com
Sat Aug 27 22:26:13 PDT 2011


Most of the efforts of font rendering is wasted on makeGlyph as
for the cache mechinism, this fix tries to use a unlimited cache
to improve the performance. Testing result of the perf-test program
showes up it reduces about more than 25% running time.

Possible further improvement not added:
- SplashFontEngine cache size/mechanism. This is also a fixed-size
cache.
---
 splash/SplashFTFont.cc     |   65 -------------------
 splash/SplashFTFontFile.cc |    1 -
 splash/SplashFont.cc       |  153 +++++++++----------------------------------
 splash/SplashFont.h        |   46 ++++++++-----
 splash/SplashT1FontFile.cc |    1 -
 5 files changed, 61 insertions(+), 205 deletions(-)

diff --git a/splash/SplashFTFont.cc b/splash/SplashFTFont.cc
index eea3d64..d8edb18 100644
--- a/splash/SplashFTFont.cc
+++ b/splash/SplashFTFont.cc
@@ -62,8 +62,6 @@ SplashFTFont::SplashFTFont(SplashFTFontFile
*fontFileA, SplashCoord *matA,
   enableSlightHinting(fontFileA->engine->enableSlightHinting)
 {
   FT_Face face;
-  double div;
-  int x, y;

   face = fontFileA->face;
   if (FT_New_Size(face, &sizeObj)) {
@@ -81,69 +79,6 @@ SplashFTFont::SplashFTFont(SplashFTFontFile
*fontFileA, SplashCoord *matA,
   // arithmetic doesn't work so well
   textScale = splashSqrt(textMat[2]*textMat[2] + textMat[3]*textMat[3]) / size;

-  div = face->bbox.xMax > 20000 ? 65536 : 1;
-
-  // transform the four corners of the font bounding box -- the min
-  // and max values form the bounding box of the transformed font
-  x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) /
-	    (div * face->units_per_EM));
-  xMin = xMax = x;
-  y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) /
-	    (div * face->units_per_EM));
-  yMin = yMax = y;
-  x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) /
-	    (div * face->units_per_EM));
-  if (x < xMin) {
-    xMin = x;
-  } else if (x > xMax) {
-    xMax = x;
-  }
-  y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) /
-	    (div * face->units_per_EM));
-  if (y < yMin) {
-    yMin = y;
-  } else if (y > yMax) {
-    yMax = y;
-  }
-  x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) /
-	    (div * face->units_per_EM));
-  if (x < xMin) {
-    xMin = x;
-  } else if (x > xMax) {
-    xMax = x;
-  }
-  y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) /
-	    (div * face->units_per_EM));
-  if (y < yMin) {
-    yMin = y;
-  } else if (y > yMax) {
-    yMax = y;
-  }
-  x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) /
-	    (div * face->units_per_EM));
-  if (x < xMin) {
-    xMin = x;
-  } else if (x > xMax) {
-    xMax = x;
-  }
-  y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) /
-	    (div * face->units_per_EM));
-  if (y < yMin) {
-    yMin = y;
-  } else if (y > yMax) {
-    yMax = y;
-  }
-  // This is a kludge: some buggy PDF generators embed fonts with
-  // zero bounding boxes.
-  if (xMax == xMin) {
-    xMin = 0;
-    xMax = (int)size;
-  }
-  if (yMax == yMin) {
-    yMin = 0;
-    yMax = (int)((SplashCoord)1.2 * size);
-  }
-
   // compute the transform matrix
 #if USE_FIXEDPOINT
   matrix.xx = (FT_Fixed)((mat[0] / size).getRaw());
diff --git a/splash/SplashFTFontFile.cc b/splash/SplashFTFontFile.cc
index 160481e..40aac17 100644
--- a/splash/SplashFTFontFile.cc
+++ b/splash/SplashFTFontFile.cc
@@ -132,7 +132,6 @@ SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat,
   SplashFont *font;

   font = new SplashFTFont(this, mat, textMat);
-  font->initCache();
   return font;
 }

diff --git a/splash/SplashFont.cc b/splash/SplashFont.cc
index 2bfcdc8..58ae214 100644
--- a/splash/SplashFont.cc
+++ b/splash/SplashFont.cc
@@ -32,15 +32,6 @@
 #include "SplashFont.h"

 //------------------------------------------------------------------------
-
-struct SplashFontCacheTag {
-  int c;
-  short xFrac, yFrac;		// x and y fractions
-  int mru;			// valid bit (0x80000000) and MRU index
-  int x, y, w, h;		// offset and size of glyph
-};
-
-//------------------------------------------------------------------------
 // SplashFont
 //------------------------------------------------------------------------

@@ -57,106 +48,45 @@ SplashFont::SplashFont(SplashFontFile *fontFileA,
SplashCoord *matA,
   textMat[2] = textMatA[2];
   textMat[3] = textMatA[3];
   aa = aaA;
-
-  cache = NULL;
-  cacheTags = NULL;
-
-  xMin = yMin = xMax = yMax = 0;
-}
-
-void SplashFont::initCache() {
-  int i;
-
-  // this should be (max - min + 1), but we add some padding to
-  // deal with rounding errors
-  glyphW = xMax - xMin + 3;
-  glyphH = yMax - yMin + 3;
-  if (aa) {
-    glyphSize = glyphW * glyphH;
-  } else {
-    glyphSize = ((glyphW + 7) >> 3) * glyphH;
-  }
-
-  // set up the glyph pixmap cache
-  cacheAssoc = 8;
-  if (glyphSize <= 64) {
-    cacheSets = 32;
-  } else if (glyphSize <= 128) {
-    cacheSets = 16;
-  } else if (glyphSize <= 256) {
-    cacheSets = 8;
-  } else if (glyphSize <= 512) {
-    cacheSets = 4;
-  } else if (glyphSize <= 1024) {
-    cacheSets = 2;
-  } else {
-    cacheSets = 1;
-  }
-  cache = (Guchar *)gmallocn_checkoverflow(cacheSets* cacheAssoc, glyphSize);
-  if (cache != NULL) {
-    cacheTags = (SplashFontCacheTag *)gmallocn(cacheSets * cacheAssoc,
-					     sizeof(SplashFontCacheTag));
-    for (i = 0; i < cacheSets * cacheAssoc; ++i) {
-      cacheTags[i].mru = i & (cacheAssoc - 1);
-    }
-  } else {
-    cacheAssoc = 0;
-  }
 }

 SplashFont::~SplashFont() {
   fontFile->decRefCnt();
-  if (cache) {
-    gfree(cache);
-  }
-  if (cacheTags) {
-    gfree(cacheTags);
-  }
 }

 GBool SplashFont::getGlyph(int c, int xFrac, int yFrac,
 			   SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip,
SplashClipResult *clipRes) {
   SplashGlyphBitmap bitmap2;
   int size;
-  Guchar *p;
-  int i, j, k;
+  SplashFontCacheKey key;
+  SplashFontCacheEntity ent;

   // no fractional coordinates for large glyphs or non-anti-aliased
   // glyphs
-  if (!aa || glyphH > 50) {
+  if (!aa) {
     xFrac = yFrac = 0;
   }

-  // check the cache
-  i = (c & (cacheSets - 1)) * cacheAssoc;
-  for (j = 0; j < cacheAssoc; ++j) {
-    if ((cacheTags[i+j].mru & 0x80000000) &&
-	cacheTags[i+j].c == c &&
-	(int)cacheTags[i+j].xFrac == xFrac &&
-	(int)cacheTags[i+j].yFrac == yFrac) {
-      bitmap->x = cacheTags[i+j].x;
-      bitmap->y = cacheTags[i+j].y;
-      bitmap->w = cacheTags[i+j].w;
-      bitmap->h = cacheTags[i+j].h;
-      for (k = 0; k < cacheAssoc; ++k) {
-	if (k != j &&
-	    (cacheTags[i+k].mru & 0x7fffffff) <
-	      (cacheTags[i+j].mru & 0x7fffffff)) {
-	  ++cacheTags[i+k].mru;
-	}
-      }
-      cacheTags[i+j].mru = 0x80000000;
+  key.c = c;
+  key.xFrac = (short) xFrac;
+  key.yFrac = (short) yFrac;
+
+  SplashFontCache::iterator iter = cache.find(key);
+
+  if (iter != cache.end()) {
+      bitmap->x = iter->second.x;
+      bitmap->y = iter->second.y;
+      bitmap->w = iter->second.w;
+      bitmap->h = iter->second.h;
       bitmap->aa = aa;
-      bitmap->data = cache + (i+j) * glyphSize;
+      bitmap->data = iter->second.data.get();
       bitmap->freeData = gFalse;

       *clipRes = clip->testRect(x0 - bitmap->x,
                                 y0 - bitmap->y,
                                 x0 - bitmap->x + bitmap->w - 1,
                                 y0 - bitmap->y + bitmap->h - 1);
-
       return gTrue;
-    }
   }

   // generate the glyph bitmap
@@ -171,49 +101,30 @@ GBool SplashFont::getGlyph(int c, int xFrac, int yFrac,
     return gTrue;
   }

-  // if the glyph doesn't fit in the bounding box, return a temporary
-  // uncached bitmap
-  if (bitmap2.w > glyphW || bitmap2.h > glyphH) {
-    *bitmap = bitmap2;
-    return gTrue;
-  }
-
   // insert glyph pixmap in cache
   if (aa) {
     size = bitmap2.w * bitmap2.h;
   } else {
     size = ((bitmap2.w + 7) >> 3) * bitmap2.h;
   }
-  p = NULL; // make gcc happy
-  if (cacheAssoc == 0)
-  {
-    // we had problems on the malloc of the cache, so ignore it
-    *bitmap = bitmap2;
-  }
-  else
-  {
-    for (j = 0; j < cacheAssoc; ++j) {
-      if ((cacheTags[i+j].mru & 0x7fffffff) == cacheAssoc - 1) {
-        cacheTags[i+j].mru = 0x80000000;
-        cacheTags[i+j].c = c;
-        cacheTags[i+j].xFrac = (short)xFrac;
-        cacheTags[i+j].yFrac = (short)yFrac;
-        cacheTags[i+j].x = bitmap2.x;
-        cacheTags[i+j].y = bitmap2.y;
-        cacheTags[i+j].w = bitmap2.w;
-        cacheTags[i+j].h = bitmap2.h;
-        p = cache + (i+j) * glyphSize;
-        memcpy(p, bitmap2.data, size);
-      } else {
-        ++cacheTags[i+j].mru;
+
+  ent.x = bitmap2.x;
+  ent.y = bitmap2.y;
+  ent.w = bitmap2.w;
+  ent.h = bitmap2.h;
+  ent.data = GucharPtr((Guchar *)gmallocn_checkoverflow (1, size), gfree);
+  if (ent.data.get() != NULL) {
+      memcpy(ent.data.get(), bitmap2.data, size);
+      cache[key] = ent;
+
+      *bitmap = bitmap2;
+      bitmap->data = ent.data.get();
+      bitmap->freeData = gFalse;
+      if (bitmap2.freeData) {
+	  gfree(bitmap2.data);
       }
-    }
-    *bitmap = bitmap2;
-    bitmap->data = p;
-    bitmap->freeData = gFalse;
-    if (bitmap2.freeData) {
-      gfree(bitmap2.data);
-    }
+  } else {
+      *bitmap = bitmap2;
   }
   return gTrue;
 }
diff --git a/splash/SplashFont.h b/splash/SplashFont.h
index 78b00d2..1f78dbc 100644
--- a/splash/SplashFont.h
+++ b/splash/SplashFont.h
@@ -26,11 +26,13 @@
 #endif

 #include "goo/gtypes.h"
+#include "goo/GooString.h"
 #include "SplashTypes.h"
 #include "SplashClip.h"
+#include <tr1/memory>
+#include <map>

 struct SplashGlyphBitmap;
-struct SplashFontCacheTag;
 class SplashFontFile;
 class SplashPath;

@@ -44,6 +46,31 @@ class SplashPath;
                        ((SplashCoord)1 / (SplashCoord)splashFontFraction)

 //------------------------------------------------------------------------
+
+struct SplashFontCacheKey {
+  int c;
+  short xFrac, yFrac;		// x and y fractions
+
+  bool operator <(const SplashFontCacheKey& b) const {
+      if (c != b.c)
+	  return c < b.c;
+      if (xFrac != b.xFrac)
+	  return xFrac < b.xFrac;
+      if (yFrac != b.yFrac)
+	  return yFrac < b.yFrac;
+      return false;
+  }
+};
+
+typedef std::tr1::shared_ptr<Guchar> GucharPtr;
+struct SplashFontCacheEntity {
+  int x, y, w, h;		// offset and size of glyph
+  GucharPtr data;
+};
+
+typedef std::map<struct SplashFontCacheKey, struct
SplashFontCacheEntity> SplashFontCache;
+
+//------------------------------------------------------------------------
 // SplashFont
 //------------------------------------------------------------------------

@@ -53,10 +80,6 @@ public:
   SplashFont(SplashFontFile *fontFileA, SplashCoord *matA,
 	     SplashCoord *textMatA, GBool aaA);

-  // This must be called after the constructor, so that the subclass
-  // constructor has a chance to compute the bbox.
-  void initCache();
-
   virtual ~SplashFont();

   SplashFontFile *getFontFile() { return fontFile; }
@@ -96,10 +119,6 @@ public:
   // Return the font transform matrix.
   SplashCoord *getMatrix() { return mat; }

-  // Return the glyph bounding box.
-  void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA)
-    { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
-
 protected:

   SplashFontFile *fontFile;
@@ -108,14 +127,7 @@ protected:
   SplashCoord textMat[4];	// text transform matrix
 				//   (text space -> user space)
   GBool aa;			// anti-aliasing
-  int xMin, yMin, xMax, yMax;	// glyph bounding box
-  Guchar *cache;		// glyph bitmap cache
-  SplashFontCacheTag *		// cache tags
-    cacheTags;
-  int glyphW, glyphH;		// size of glyph bitmaps
-  int glyphSize;		// size of glyph bitmaps, in bytes
-  int cacheSets;		// number of sets in cache
-  int cacheAssoc;		// cache associativity (glyphs per set)
+  SplashFontCache cache;
 };

 #endif
diff --git a/splash/SplashT1FontFile.cc b/splash/SplashT1FontFile.cc
index 3f46ba6..45059a1 100644
--- a/splash/SplashT1FontFile.cc
+++ b/splash/SplashT1FontFile.cc
@@ -127,7 +127,6 @@ SplashFont *SplashT1FontFile::makeFont(SplashCoord *mat,
   SplashFont *font;

   font = new SplashT1Font(this, mat, textMat);
-  font->initCache();
   return font;
 }

-- 
1.7.4.1


More information about the poppler mailing list