fonts: keep some unused fonts in the cache

Huw D M Davies h.davies1 at physics.ox.ac.uk
Thu Aug 5 07:50:03 CDT 2004


        Huw Davies <huw at codeweavers.com>
        Maintain a cache (currently of up to 10) of unused fonts.  This
        speeds up apps that repeatedly recreate the same font.
-- 
Huw Davies
huw at codeweavers.com
Index: dlls/gdi/freetype.c
===================================================================
RCS file: /home/wine/wine/dlls/gdi/freetype.c,v
retrieving revision 1.64
diff -u -r1.64 freetype.c
--- dlls/gdi/freetype.c	4 Aug 2004 18:16:13 -0000	1.64
+++ dlls/gdi/freetype.c	5 Aug 2004 12:45:17 -0000
@@ -189,10 +189,25 @@
     BOOL init;
 } GM;
 
+typedef struct {
+    FLOAT eM11, eM12;
+    FLOAT eM21, eM22;
+} FMAT2;
+
+typedef struct {
+    DWORD hash;
+    LOGFONTW lf;
+    FMAT2 matrix;
+} FONT_DESC;
+
+typedef struct tagHFONTLIST {
+    struct list entry;
+    HFONT hfont;
+} HFONTLIST;
+
 struct tagGdiFont {
     struct list entry;
     FT_Face ft_face;
-    XFORM xform;
     LPWSTR name;
     int charset;
     BOOL fake_italic;
@@ -202,7 +217,8 @@
     INT orientation;
     GM *gm;
     DWORD gmsize;
-    HFONT hfont;
+    struct list hfontlist;
+    FONT_DESC font_desc;
     LONG aveWidth;
     SHORT yMax;
     SHORT yMin;
@@ -213,6 +229,8 @@
 #define INIT_GM_SIZE 128
 
 static struct list gdi_font_list = LIST_INIT(gdi_font_list);
+static struct list unused_gdi_font_list = LIST_INIT(unused_gdi_font_list);
+#define UNUSED_CACHE_SIZE 10
 
 static Family *FontList = NULL;
 
@@ -1331,7 +1349,8 @@
     ret->gm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
 			ret->gmsize * sizeof(*ret->gm));
     ret->potm = NULL;
-    ret->xform.eM11 = ret->xform.eM22 = 1.0;
+    ret->font_desc.matrix.eM11 = ret->font_desc.matrix.eM22 = 1.0;
+    list_init(&ret->hfontlist);
     return ret;
 }
 
@@ -1492,6 +1511,85 @@
     return ppem;
 }
 
+static BOOL fontcmp(GdiFont font, FONT_DESC *fd)
+{
+    if(font->font_desc.hash != fd->hash) return TRUE;
+    if(memcmp(&font->font_desc.matrix, &fd->matrix, sizeof(fd->matrix))) return TRUE;
+    if(memcmp(&font->font_desc.lf, &fd->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
+    return strcmpiW(font->font_desc.lf.lfFaceName, fd->lf.lfFaceName);
+}
+
+static void calc_hash(FONT_DESC *pfd)
+{
+    DWORD hash = 0, *ptr, two_chars;
+    WORD *pwc;
+    int i;
+
+    for(i = 0, ptr = (DWORD*)&pfd->matrix; i < sizeof(FMAT2)/sizeof(DWORD); i++, ptr++)
+        hash ^= *ptr;
+    for(i = 0, ptr = (DWORD*)&pfd->lf; i < 7; i++, ptr++)
+        hash ^= *ptr;
+    for(i = 0, ptr = (DWORD*)&pfd->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
+        two_chars = *ptr;
+        pwc = (WCHAR *)&two_chars;
+        if(!*pwc) break;
+        *pwc = toupperW(*pwc);
+        pwc++;
+        *pwc = toupperW(*pwc);
+        hash ^= two_chars;
+        if(!*pwc) break;
+    }
+    pfd->hash = hash;
+    return;
+}
+
+static GdiFont find_in_cache(HFONT hfont, LOGFONTW *plf, XFORM *pxf, BOOL can_use_bitmap)
+{
+    GdiFont ret;
+    FONT_DESC fd;
+    HFONTLIST *hflist;
+    struct list *font_elem_ptr, *hfontlist_elem_ptr;
+
+    memcpy(&fd.lf, plf, sizeof(LOGFONTW));
+    memcpy(&fd.matrix, pxf, sizeof(FMAT2));
+    calc_hash(&fd);
+
+    /* try the in-use list */
+    LIST_FOR_EACH(font_elem_ptr, &gdi_font_list) {
+        ret = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
+        if(!fontcmp(ret, &fd)) {
+            if(!can_use_bitmap && !FT_IS_SCALABLE(ret->ft_face)) continue;
+            LIST_FOR_EACH(hfontlist_elem_ptr, &ret->hfontlist) {
+                hflist = LIST_ENTRY(hfontlist_elem_ptr, struct tagHFONTLIST, entry);
+                if(hflist->hfont == hfont)
+                    return ret;
+            }
+            hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
+            hflist->hfont = hfont;
+            list_add_head(&ret->hfontlist, &hflist->entry);
+            return ret;
+        }
+    }
+ 
+    /* then the unused list */
+    font_elem_ptr = list_head(&unused_gdi_font_list);
+    while(font_elem_ptr) {
+        ret = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
+        font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
+        if(!fontcmp(ret, &fd)) {
+            if(!can_use_bitmap && !FT_IS_SCALABLE(ret->ft_face)) continue;
+            assert(list_empty(&ret->hfontlist));
+            TRACE("Found %p in unused list\n", ret);
+            list_remove(&ret->entry);
+            list_add_head(&gdi_font_list, &ret->entry);
+            hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
+            hflist->hfont = hfont;
+            list_add_head(&ret->hfontlist, &hflist->entry);
+            return ret;
+        }
+    }
+    return NULL;
+}
 
 /*************************************************************
  * WineEngCreateFontInstance
@@ -1507,7 +1605,7 @@
     BOOL bd, it, can_use_bitmap;
     LOGFONTW lf;
     CHARSETINFO csi;
-    struct list *elem_ptr;
+    HFONTLIST *hflist;
 
     if (!GetObjectW( hfont, sizeof(lf), &lf )) return NULL;
     can_use_bitmap = GetDeviceCaps(dc->hSelf, TEXTCAPS) & TC_RA_ABLE;
@@ -1518,16 +1616,12 @@
 	  lf.lfEscapement);
 
     /* check the cache first */
-    LIST_FOR_EACH(elem_ptr, &gdi_font_list) {
-        ret = LIST_ENTRY(elem_ptr, struct tagGdiFont, entry);
-        if(ret->hfont == hfont && !memcmp(&ret->xform, &dc->xformWorld2Vport, offsetof(XFORM, eDx)) &&
-           (can_use_bitmap || FT_IS_SCALABLE(ret->ft_face))) {
-
-	    TRACE("returning cached gdiFont(%p) for hFont %p\n", ret, hfont);
-	    return ret;
-	}
+    if((ret = find_in_cache(hfont, &lf, &dc->xformWorld2Vport, can_use_bitmap)) != NULL) {
+        TRACE("returning cached gdiFont(%p) for hFont %p\n", ret, hfont);
+        return ret;
     }
 
+    TRACE("not in cache\n");
     if(!FontList || !have_installed_roman_font) /* No fonts installed */
     {
 	TRACE("No fonts installed\n");
@@ -1535,7 +1629,14 @@
     }
 
     ret = alloc_font();
-    memcpy(&ret->xform, &dc->xformWorld2Vport, sizeof(XFORM));
+
+     memcpy(&ret->font_desc.matrix, &dc->xformWorld2Vport, sizeof(FMAT2));
+     memcpy(&ret->font_desc.lf, &lf, sizeof(LOGFONTW));
+     calc_hash(&ret->font_desc);
+     hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
+     hflist->hfont = hfont;
+     list_add_head(&ret->hfontlist, &hflist->entry);
+
 
     /* If lfFaceName is "Symbol" then Windows fixes up lfCharSet to
        SYMBOL_CHARSET so that Symbol gets picked irrespective of the
@@ -1702,7 +1803,6 @@
     ret->strikeout = lf.lfStrikeOut ? 0xff : 0;
 
     TRACE("caching: gdiFont=%p  hfont=%p\n", ret, hfont);
-    ret->hfont = hfont;
     ret->aveWidth= lf.lfWidth;
     list_add_head(&gdi_font_list, &ret->entry);
     return ret;
@@ -1711,15 +1811,20 @@
 static void dump_gdi_font_list(void)
 {
     GdiFont gdiFont;
-    LOGFONTW lf;
     struct list *elem_ptr;
 
     TRACE("---------- gdiFont Cache ----------\n");
     LIST_FOR_EACH(elem_ptr, &gdi_font_list) {
         gdiFont = LIST_ENTRY(elem_ptr, struct tagGdiFont, entry);
-        GetObjectW( gdiFont->hfont, sizeof(lf), &lf );
-	TRACE("gdiFont=%p  hfont=%p (%s)\n",
-	       gdiFont, gdiFont->hfont, debugstr_w(lf.lfFaceName));
+        TRACE("gdiFont=%p %s %ld\n",
+              gdiFont, debugstr_w(gdiFont->font_desc.lf.lfFaceName), gdiFont->font_desc.lf.lfHeight);
+    }
+
+    TRACE("---------- Unused gdiFont Cache ----------\n");
+    LIST_FOR_EACH(elem_ptr, &unused_gdi_font_list) {
+        gdiFont = LIST_ENTRY(elem_ptr, struct tagGdiFont, entry);
+        TRACE("gdiFont=%p %s %ld\n",
+              gdiFont, debugstr_w(gdiFont->font_desc.lf.lfFaceName), gdiFont->font_desc.lf.lfHeight);
     }
 }
 
@@ -1732,23 +1837,48 @@
 BOOL WineEngDestroyFontInstance(HFONT handle)
 {
     GdiFont gdiFont;
+    HFONTLIST *hflist;
     BOOL ret = FALSE;
-    struct list *elem_ptr;
+    struct list *font_elem_ptr, *hfontlist_elem_ptr;
+    int i = 0;
 
     TRACE("destroying hfont=%p\n", handle);
     if(TRACE_ON(font))
 	dump_gdi_font_list();
 
-    elem_ptr = list_head(&gdi_font_list);
-    while(elem_ptr) {
-        gdiFont = LIST_ENTRY(elem_ptr, struct tagGdiFont, entry);
-        elem_ptr = list_next(&gdi_font_list, elem_ptr);
-        if(gdiFont->hfont == handle) {
+    font_elem_ptr = list_head(&gdi_font_list);
+    while(font_elem_ptr) {
+        gdiFont = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
+        font_elem_ptr = list_next(&gdi_font_list, font_elem_ptr);
+
+        hfontlist_elem_ptr = list_head(&gdiFont->hfontlist);
+        while(hfontlist_elem_ptr) {
+            hflist = LIST_ENTRY(hfontlist_elem_ptr, struct tagHFONTLIST, entry);
+            hfontlist_elem_ptr = list_next(&gdiFont->hfontlist, hfontlist_elem_ptr);
+            if(hflist->hfont == handle) {
+                list_remove(&hflist->entry);
+                HeapFree(GetProcessHeap(), 0, hflist);
+                ret = TRUE;
+            }
+        }
+        if(list_empty(&gdiFont->hfontlist)) {
+            TRACE("Moving to Unused list\n");
             list_remove(&gdiFont->entry);
-            free_font(gdiFont);
-            ret = TRUE;
+            list_add_head(&unused_gdi_font_list, &gdiFont->entry);
         }
     }
+
+
+    font_elem_ptr = list_head(&unused_gdi_font_list);
+    while(font_elem_ptr && i++ < UNUSED_CACHE_SIZE)
+        font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
+    while(font_elem_ptr) {
+        gdiFont = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
+        font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
+        TRACE("freeing %p\n", gdiFont);
+        list_remove(&gdiFont->entry);
+        free_font(gdiFont);
+    }
     return ret;
 }
 
@@ -2056,7 +2186,7 @@
 	
     /* Scaling factor */
     if (font->aveWidth && font->potm) {
-	     widthRatio = (float)font->aveWidth * font->xform.eM11 / (float) font->potm->otmTextMetrics.tmAveCharWidth;
+        widthRatio = (float)font->aveWidth * font->font_desc.matrix.eM11 / (float) font->potm->otmTextMetrics.tmAveCharWidth;
     }
 
     left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
@@ -2530,7 +2660,7 @@
     memcpy(ptm, &font->potm->otmTextMetrics, sizeof(*ptm));
 
     if (font->aveWidth) {
-	     ptm->tmAveCharWidth = font->aveWidth * font->xform.eM11;
+        ptm->tmAveCharWidth = font->aveWidth * font->font_desc.matrix.eM11;
     }
     return TRUE;
 }



More information about the wine-patches mailing list