[poppler] change font cache policy to improve performance.
Sam Liao
phyomh at gmail.com
Sat Aug 27 22:31:10 PDT 2011
Not quite sure part of this fix which remove a bunch of
xMin/yMin/xMax/yMax related code would
lead any side effect I do not know, but they make the code more clean.
-Sam
On Sun, Aug 28, 2011 at 1:26 PM, Sam Liao <phyomh at gmail.com> wrote:
> 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