richedit: new font management code

Krzysztof Foltman kfoltman at portal.onet.pl
Mon Mar 7 16:47:38 CST 2005


Comparing to the previous one, this one doesn't suck that much. And it 
still can be improved later (by keeping things like TEXTMETRIC structure 
in the cache item, or maybe by increasing cache size and then speeding 
up font comparison by hashing).

ChangeLog:
  * old font management replaced by the cache-based one, which keeps 
maximum of 10 HFONTs at once, instead of one per a couple of runs

Krzysztof
-------------- next part --------------
Index: editor.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/editor.c,v
retrieving revision 1.3
diff -u -r1.3 editor.c
--- editor.c	7 Mar 2005 17:15:33 -0000	1.3
+++ editor.c	7 Mar 2005 22:35:41 -0000
@@ -334,6 +334,7 @@
 ME_TextEditor *ME_MakeEditor(HWND hWnd) {
   ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
   HDC hDC;
+  int i;
   ed->hWnd = hWnd;
   ed->pBuffer = ME_MakeText();
   hDC = GetDC(hWnd);
@@ -357,6 +358,12 @@
   ed->pUndoStack = ed->pRedoStack = NULL;
   ed->nUndoMode = umAddToUndo;
   ed->nParagraphs = 1;
+  for (i=0; i<HFONT_CACHE_SIZE; i++)
+  {
+    ed->pFontCache[i].nRefs = 0;
+    ed->pFontCache[i].nAge = 0;
+    ed->pFontCache[i].hFont = NULL;
+  }
   ME_CheckCharOffsets(ed);
   return ed;
 }
@@ -365,6 +372,7 @@
 {
   ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
   ME_DisplayItem *p = pFirst, *pNext = NULL;
+  int i;
   
   ME_ClearTempStyle(editor);
   ME_EmptyUndoStack(editor);
@@ -374,6 +382,11 @@
     p = pNext;
   }
   ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
+  for (i=0; i<HFONT_CACHE_SIZE; i++)
+  {
+    if (editor->pFontCache[i].hFont)
+      DeleteObject(editor->pFontCache[i].hFont);
+  }
     
   FREE_OBJ(editor);
 }
@@ -597,6 +610,7 @@
     ME_Style *style;
     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
     size_t len = lstrlenW(wszText);
+    TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
     
     ME_GetSelection(editor, &from, &to);
     ME_CursorFromCharOfs(editor, from, &c);
@@ -624,6 +638,7 @@
   case WM_SETTEXT:
   {
     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
+    TRACE("WM_SETTEXT - %s\n", (char *)(wszText)); /* debugstr_w() */
     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
     /* uses default style! */
     ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
Index: editor.h
===================================================================
RCS file: /home/wine/wine/dlls/riched20/editor.h,v
retrieving revision 1.1
diff -u -r1.1 editor.h
--- editor.h	5 Mar 2005 11:19:14 -0000	1.1
+++ editor.h	7 Mar 2005 22:35:41 -0000
@@ -30,10 +30,8 @@
 void ME_ReleaseStyle(ME_Style *item);
 ME_Style *ME_GetInsertStyle(ME_TextEditor *editor, int nCursor);
 ME_Style *ME_ApplyStyle(ME_Style *sSrc, CHARFORMAT2W *style);
-void ME_PrepareStyle(ME_Context *c, ME_Style *s);
-void ME_PrepareStyleFromDC(ME_Style *s, HDC hDC, int nSequence);
-void ME_UnprepareStyle(ME_Style *s);
-HFONT ME_SelectStyleFont(HDC hDC, ME_Style *s);
+HFONT ME_SelectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s);
+void ME_UnselectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s, HFONT hOldFont);
 void ME_InitCharFormat2W(CHARFORMAT2W *pFmt);
 void ME_SaveTempStyle(ME_TextEditor *editor);
 void ME_ClearTempStyle(ME_TextEditor *editor);
Index: editstr.h
===================================================================
RCS file: /home/wine/wine/dlls/riched20/editstr.h,v
retrieving revision 1.1
diff -u -r1.1 editstr.h
--- editstr.h	5 Mar 2005 11:19:14 -0000	1.1
+++ editstr.h	7 Mar 2005 22:35:42 -0000
@@ -191,6 +191,16 @@
   umAddBackToUndo
 } ME_UndoMode;
 
+typedef struct tagME_FontCacheItem
+{
+  LOGFONTW lfSpecs;
+  HFONT hFont;
+  int nRefs;
+  int nAge;
+} ME_FontCacheItem;
+
+#define HFONT_CACHE_SIZE 10
+
 typedef struct tagME_TextEditor
 {
   HWND hWnd;
@@ -211,6 +221,7 @@
   ME_DisplayItem *pUndoStack, *pRedoStack;
   ME_UndoMode nUndoMode;
   int nParagraphs;
+  ME_FontCacheItem pFontCache[HFONT_CACHE_SIZE];
 } ME_TextEditor;
 
 typedef struct tagME_Context
Index: paint.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/paint.c,v
retrieving revision 1.2
diff -u -r1.2 paint.c
--- paint.c	7 Mar 2005 10:59:53 -0000	1.2
+++ paint.c	7 Mar 2005 22:35:42 -0000
@@ -58,14 +58,6 @@
     FillRect(hDC, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
   }
   ME_DestroyContext(&c);
-
-  item = editor->pBuffer->pFirst->next;
-  while(item != editor->pBuffer->pLast) {
-    if (item->type == diRun)
-      ME_UnprepareStyle(item->member.run.style);
-    item = item->next;
-  }
-
 }
 
 void ME_Repaint(ME_TextEditor *editor)
@@ -101,8 +93,7 @@
   HDC hDC = c->hDC;
   HGDIOBJ hOldFont;
   COLORREF rgbOld, rgbBack;
-  ME_PrepareStyle(c, s);
-  hOldFont = SelectObject(hDC, s->hFont);
+  hOldFont = ME_SelectStyleFont(c->editor, hDC, s);
   rgbBack = ME_GetBackColor(c->editor);
   if ((s->fmt.dwMask & CFM_COLOR) && (s->fmt.dwEffects & CFE_AUTOCOLOR))
     rgbOld = SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
@@ -125,11 +116,7 @@
     PatBlt(hDC, x, ymin, sz.cx, cy, DSTINVERT);
   }
   SetTextColor(hDC, rgbOld);
-  SelectObject(hDC, hOldFont);
-}
-
-void ME_DrawSelection(ME_Context *c)
-{
+  ME_UnselectStyleFont(c->editor, hDC, s, hOldFont);
 }
 
 void ME_DebugWrite(HDC hDC, POINT *pt, WCHAR *szText) {
Index: run.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/run.c,v
retrieving revision 1.1
diff -u -r1.1 run.c
--- run.c	5 Mar 2005 11:19:14 -0000	1.1
+++ run.c	7 Mar 2005 22:35:42 -0000
@@ -378,11 +378,10 @@
     return 1;
   }
   hDC = GetDC(editor->hWnd);
-  hOldFont = ME_SelectStyleFont(hDC, run->style);
+  hOldFont = ME_SelectStyleFont(editor, hDC, run->style);
   GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen, 
     cx, &fit, NULL, &sz);
-  assert(run->style->hFont);
-  SelectObject(hDC, hOldFont);  
+  ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);  
   ReleaseDC(editor->hWnd, hDC);
   return fit;
 }
@@ -406,7 +405,7 @@
   }
 
   hDC = GetDC(editor->hWnd);
-  hOldFont = ME_SelectStyleFont(hDC, run->style);
+  hOldFont = ME_SelectStyleFont(editor, hDC, run->style);
   GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen, 
     cx, &fit, NULL, &sz);
   if (fit != run->strText->nLen)
@@ -419,7 +418,7 @@
     if (cx >= (sz2.cx+sz3.cx)/2)
       fit = fit1;
   }
-  SelectObject(hDC, hOldFont);  
+  ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);  
   ReleaseDC(editor->hWnd, hDC);
   return fit;
 }
@@ -436,9 +435,9 @@
     ME_GetGraphicsSize(editor, pRun, &size);
     return 1;
   }
-  hOldFont = ME_SelectStyleFont(hDC, pRun->style);
+  hOldFont = ME_SelectStyleFont(editor, hDC, pRun->style);
   GetTextExtentPoint32W(hDC, pRun->strText->szData, nOffset, &size);
-  SelectObject(hDC, hOldFont);  
+  ME_UnselectStyleFont(editor, hDC, pRun->style, hOldFont);  
   ReleaseDC(editor->hWnd, hDC);
   return size.cx;
 }
@@ -448,9 +447,9 @@
 {
   HDC hDC = c->hDC;
   HGDIOBJ hOldFont;
-  hOldFont = ME_SelectStyleFont(hDC, s);
+  hOldFont = ME_SelectStyleFont(c->editor, hDC, s);
   GetTextExtentPoint32W(hDC, szText, nChars, size);
-  SelectObject(hDC, hOldFont);  
+  ME_UnselectStyleFont(c->editor, hDC, s, hOldFont);  
 }
 
 SIZE ME_GetRunSize(ME_Context *c, ME_Run *run, int nLen)
Index: style.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/style.c,v
retrieving revision 1.1
diff -u -r1.1 style.c
--- style.c	5 Mar 2005 11:19:14 -0000	1.1
+++ style.c	7 Mar 2005 22:35:42 -0000
@@ -248,68 +248,114 @@
   ME_DumpStyleEffect(&p, "Text protected:", pFmt, CFM_PROTECTED);
 }
 
-void ME_UnprepareStyle(ME_Style *s)
+void ME_LogFontFromStyle(HDC hDC, LOGFONTW *lf, ME_Style *s)
 {
-  if (s->hFont) {
-    DeleteObject(s->hFont);
-    s->hFont = NULL;
-    s->nSequence = -2;
-  }
-}
-
-void ME_PrepareStyleFromDC(ME_Style *s, HDC hDC, int nSequence)
-{
-  HGDIOBJ hOldFont;
-  LOGFONTW lf;
   int rx, ry;
-  
-  if (nSequence == s->nSequence && s->hFont)
-    return;
-/*  assert(s); */
   rx = GetDeviceCaps(hDC, LOGPIXELSX);
   ry = GetDeviceCaps(hDC, LOGPIXELSY);
-  if (s->hFont) {
-    DeleteObject(s->hFont);
-    s->hFont = NULL;
-  }
-  ZeroMemory(&lf, sizeof(lf));
-  lstrcpyW(lf.lfFaceName, s->fmt.szFaceName);
-  lf.lfHeight = -s->fmt.yHeight*ry/1440;
-  lf.lfWeight = 400;
+  ZeroMemory(lf, sizeof(LOGFONTW));
+  lstrcpyW(lf->lfFaceName, s->fmt.szFaceName);
+  lf->lfHeight = -s->fmt.yHeight*ry/1440;
+  lf->lfWeight = 400;
   if (s->fmt.dwEffects & s->fmt.dwMask & CFM_BOLD)
-    lf.lfWeight = 700;
+    lf->lfWeight = 700;
   if (s->fmt.dwEffects & s->fmt.dwMask & CFM_WEIGHT)
-    lf.lfWeight = s->fmt.wWeight;
+    lf->lfWeight = s->fmt.wWeight;
   if (s->fmt.dwEffects & s->fmt.dwMask & CFM_ITALIC)
-    lf.lfItalic = 1;
+    lf->lfItalic = 1;
   if (s->fmt.dwEffects & s->fmt.dwMask & CFM_UNDERLINE)
-    lf.lfUnderline = 1;
+    lf->lfUnderline = 1;
   if (s->fmt.dwEffects & s->fmt.dwMask & CFM_STRIKEOUT)
-    lf.lfStrikeOut = 1;
+    lf->lfStrikeOut = 1;
 /*lf.lfQuality = PROOF_QUALITY; */
-  lf.lfPitchAndFamily = s->fmt.bPitchAndFamily;
-  lf.lfCharSet = s->fmt.bCharSet;
-  s->hFont = CreateFontIndirectW(&lf);
-  assert(s->hFont);
-  GetObjectW(s->hFont, sizeof(LOGFONTW), &lf);
-  hOldFont = SelectObject(hDC, s->hFont);
-  GetTextMetricsW(hDC, &s->tm);
-  SelectObject(hDC, hOldFont);
-  s->nSequence = nSequence;
+  lf->lfPitchAndFamily = s->fmt.bPitchAndFamily;
+  lf->lfCharSet = s->fmt.bCharSet;
+}
+
+
+BOOL ME_IsFontEqual(LOGFONTW *p1, LOGFONTW *p2)
+{
+  if (memcmp(p1, p2, sizeof(LOGFONTW)-sizeof(p1->lfFaceName)))
+    return FALSE;
+  if (lstrcmpW(p1->lfFaceName, p2->lfFaceName))
+    return FALSE;
+  return TRUE;
 }
 
-HFONT ME_SelectStyleFont(HDC hDC, ME_Style *s)
+HFONT ME_SelectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s)
 {
   HFONT hOldFont;
-  ME_PrepareStyleFromDC(s, hDC, -1);
-  assert(s->hFont);
+  LOGFONTW lf;
+  int i, nEmpty, nAge = 0x7FFFFFFF;
+  ME_FontCacheItem *item;
+  assert(hDC);
+  assert(s);
+  
+  ME_LogFontFromStyle(hDC, &lf, s);
+  
+  for (i=0; i<HFONT_CACHE_SIZE; i++)
+    editor->pFontCache[i].nAge++;
+  for (i=0, nEmpty=-1, nAge=0; i<HFONT_CACHE_SIZE; i++)
+  {
+    item = &editor->pFontCache[i];
+    if (!item->nRefs)
+    {
+      if (item->nAge > nAge)
+        nEmpty = i, nAge = item->nAge;
+    }
+    if (ME_IsFontEqual(&item->lfSpecs, &lf))
+      break;
+  }
+  if (i < HFONT_CACHE_SIZE) /* found */
+  {
+    item = &editor->pFontCache[i];
+    TRACE("font reused %d\n", i);
+
+    s->hFont = item->hFont;
+    item->nRefs++;
+  }
+  else
+  {
+    item = &editor->pFontCache[nEmpty]; /* this legal even when nEmpty == -1, as we don't dereference it */
+
+    assert(nEmpty != -1); /* otherwise we leak cache entries or get too many fonts at once*/
+    if (item->hFont) {
+      TRACE("font deleted %d\n", nEmpty);
+      DeleteObject(item->hFont);
+      item->hFont = NULL;
+    }
+    s->hFont = CreateFontIndirectW(&lf);
+    assert(s->hFont);
+    TRACE("font created %d\n", nEmpty);
+    item->hFont = s->hFont;
+    item->nRefs = 1;
+    memcpy(&item->lfSpecs, &lf, sizeof(LOGFONTW));
+  }
   hOldFont = SelectObject(hDC, s->hFont);
+  /* should be cached too, maybe ? */
+  GetTextMetricsW(hDC, &s->tm);
   return hOldFont;
 }
 
-void ME_PrepareStyle(ME_Context *c, ME_Style *s)
+void ME_UnselectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s, HFONT hOldFont)
 {
-  ME_PrepareStyleFromDC(s, c->hDC, c->nSequence);
+  int i;
+  
+  assert(hDC);
+  assert(s);
+  SelectObject(hDC, hOldFont);
+  for (i=0; i<HFONT_CACHE_SIZE; i++)
+  {
+    ME_FontCacheItem *pItem = &editor->pFontCache[i];
+    if (pItem->hFont == s->hFont && pItem->nRefs > 0)
+    {
+      pItem->nRefs--;
+      pItem->nAge = 0;
+      s->hFont = NULL;
+      return;
+    }
+  }
+  assert(0 == "UnselectStyleFont without SelectStyleFont");
 }
 
 void ME_DestroyStyle(ME_Style *s) {
Index: wrap.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/wrap.c,v
retrieving revision 1.1
diff -u -r1.1 wrap.c
--- wrap.c	5 Mar 2005 11:19:14 -0000	1.1
+++ wrap.c	7 Mar 2005 22:35:42 -0000
@@ -367,7 +367,6 @@
   for (p = tp->next; p!=tp->member.para.next_para; ) {
     assert(p->type != diStartRow);
     if (p->type == diRun) {
-      ME_PrepareStyle(c, p->member.run.style);
       p = ME_WrapHandleRun(&wc, p);
       continue;
     }


More information about the wine-patches mailing list