[NTGDI][FREETYPE] Introduce font look-up cache (#8641)

Text rendering speed is justice! Font
search is heavy operation. Adding
cache to it is rational.
JIRA issue: CORE-19898
- Define FONT_LOOKUP_CACHE
  structure.
- Add s_FontLookupCacheList
  global variable to save cache.
- Use cache in FontLink_PrepareFontInfo
  and TextIntRealizeFont.
This commit is contained in:
Katayama Hirofumi MZ
2026-02-02 09:45:06 +09:00
committed by GitHub
parent 5fd860b383
commit 9d5c0bff36
+250 -127
View File
@@ -325,6 +325,18 @@ FontLink_CleanupCache(VOID)
g_nFontLinkCacheCount = 0;
}
typedef struct _FONT_LOOKUP_CACHE
{
LIST_ENTRY ListEntry;
LOGFONTW LogFont;
PSHARED_FACE SharedFace;
FONTOBJ *pFontObj;
} FONT_LOOKUP_CACHE, *PFONT_LOOKUP_CACHE;
static RTL_STATIC_LIST_HEAD(s_FontLookupCacheList); // The list of FONT_LOOKUP_CACHE
#define MAX_FONT_LOOKUP_CACHE 64
static ULONG s_FontLookupCacheCount = 0;
/* The ranges of the surrogate pairs */
#define HIGH_SURROGATE_MIN 0xD800U
#define HIGH_SURROGATE_MAX 0xDBFFU
@@ -497,57 +509,27 @@ FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty,
const LOGFONTW *LogFont,
const PLIST_ENTRY Head);
static BOOL
MatchFontName(PSHARED_FACE SharedFace, PUNICODE_STRING Name1, FT_UShort NameID, FT_UShort LangID);
PSHARED_FACE IntRealizeFont(const LOGFONTW *pLogFont, _Inout_opt_ PTEXTOBJ TextObj);
static BOOL
FontLink_PrepareFontInfo(
_Inout_ PFONTLINK pFontLink)
{
FONTOBJ *pFontObj;
ULONG MatchPenalty;
UNICODE_STRING FaceName;
PPROCESSINFO Win32Process;
PFONTGDI pFontGDI;
ASSERT_FREETYPE_LOCK_HELD();
if (pFontLink->bIgnore)
return FALSE;
if (pFontLink->SharedFace)
return TRUE;
MatchPenalty = MAXULONG;
pFontObj = NULL;
// Search private fonts
Win32Process = PsGetCurrentProcessWin32Process();
FindBestFontFromList(&pFontObj, &MatchPenalty, &pFontLink->LogFont,
&Win32Process->PrivateFontListHead);
// Search system fonts
FindBestFontFromList(&pFontObj, &MatchPenalty, &pFontLink->LogFont,
&g_FontListHead);
if (!pFontObj) // Not found?
{
pFontLink->bIgnore = TRUE;
return FALSE;
}
pFontGDI = ObjToGDI(pFontObj, FONT);
pFontLink->SharedFace = pFontGDI->SharedFace;
// FontLink uses family name
RtlInitUnicodeString(&FaceName, pFontLink->LogFont.lfFaceName);
if (!MatchFontName(pFontLink->SharedFace, &FaceName, TT_NAME_ID_FONT_FAMILY, LANG_ENGLISH) &&
!MatchFontName(pFontLink->SharedFace, &FaceName, TT_NAME_ID_FONT_FAMILY, gusLanguageID))
PSHARED_FACE SharedFace = IntRealizeFont(&pFontLink->LogFont, NULL);
if (!SharedFace) // Not found?
{
pFontLink->bIgnore = TRUE;
return FALSE;
}
pFontLink->SharedFace = SharedFace;
return TRUE;
}
@@ -675,9 +657,11 @@ SharedFaceCache_Release(PSHARED_FACE_CACHE Cache)
}
static void
SharedFace_Release(PSHARED_FACE Ptr)
SharedFace_Release(PSHARED_FACE Ptr, BOOL bDoLock)
{
IntLockFreeType();
if (bDoLock)
IntLockFreeType();
ASSERT(Ptr->RefCount > 0);
if (Ptr->RefCount <= 0)
@@ -694,7 +678,9 @@ SharedFace_Release(PSHARED_FACE Ptr)
SharedFaceCache_Release(&Ptr->UserLanguage);
ExFreePoolWithTag(Ptr, TAG_FONT);
}
IntUnLockFreeType();
if (bDoLock)
IntUnLockFreeType();
}
@@ -714,7 +700,7 @@ CleanupFontEntryEx(PFONT_ENTRY FontEntry, PFONTGDI FontGDI)
RtlFreeUnicodeString(&FontEntry->FaceName);
EngFreeMem(FontGDI);
SharedFace_Release(SharedFace);
SharedFace_Release(SharedFace, TRUE);
ExFreePoolWithTag(FontEntry, TAG_FONT);
}
@@ -1017,6 +1003,53 @@ IntLoadFontSubstList(PLIST_ENTRY pHead)
return NT_SUCCESS(Status);
}
static void
FontLookUp_Destroy(PFONT_LOOKUP_CACHE pCache)
{
ASSERT_FREETYPE_LOCK_HELD();
SharedFace_Release(pCache->SharedFace, FALSE);
ExFreePoolWithTag(pCache, TAG_FONT);
}
static void
FontLookUp_Cleanup(
_In_ BOOL bDoLock,
_Inout_opt_ PSHARED_FACE SharedFace)
{
if (bDoLock)
IntLockFreeType();
PLIST_ENTRY pHead = &s_FontLookupCacheList, pEntry;
PFONT_LOOKUP_CACHE pCache;
if (SharedFace)
{
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink)
{
pCache = CONTAINING_RECORD(pEntry, FONT_LOOKUP_CACHE, ListEntry);
if (pCache->SharedFace == SharedFace)
{
RemoveEntryList(&pCache->ListEntry);
FontLookUp_Destroy(pCache);
--s_FontLookupCacheCount;
}
}
}
else
{
while (!IsListEmpty(pHead))
{
pEntry = RemoveHeadList(pHead);
pCache = CONTAINING_RECORD(pEntry, FONT_LOOKUP_CACHE, ListEntry);
FontLookUp_Destroy(pCache);
}
s_FontLookupCacheCount = 0;
}
if (bDoLock)
IntUnLockFreeType();
}
BOOL FASTCALL
InitFontSupport(VOID)
{
@@ -1070,6 +1103,9 @@ FreeFontSupport(VOID)
// Cleanup the FontLink cache
FontLink_CleanupCache();
// Cleanup FONT_LOOKUP_CACHE
FontLookUp_Cleanup(FALSE, NULL);
// Free font cache list
pHead = &g_FontCacheListHead;
while (!IsListEmpty(pHead))
@@ -1750,7 +1786,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
Entry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY), TAG_FONT);
if (!Entry)
{
SharedFace_Release(SharedFace);
SharedFace_Release(SharedFace, TRUE);
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0; /* failure */
}
@@ -1759,7 +1795,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), GDITAG_RFONT);
if (!FontGDI)
{
SharedFace_Release(SharedFace);
SharedFace_Release(SharedFace, TRUE);
ExFreePoolWithTag(Entry, TAG_FONT);
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0; /* failure */
@@ -1774,7 +1810,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
if (FontGDI->Filename == NULL)
{
EngFreeMem(FontGDI);
SharedFace_Release(SharedFace);
SharedFace_Release(SharedFace, TRUE);
ExFreePoolWithTag(Entry, TAG_FONT);
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0; /* failure */
@@ -1793,7 +1829,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
if (FontGDI->Filename)
ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF);
EngFreeMem(FontGDI);
SharedFace_Release(SharedFace);
SharedFace_Release(SharedFace, TRUE);
ExFreePoolWithTag(Entry, TAG_FONT);
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0; /* failure */
@@ -1873,7 +1909,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
if (FontGDI->Filename)
ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF);
EngFreeMem(FontGDI);
SharedFace_Release(SharedFace);
SharedFace_Release(SharedFace, TRUE);
ExFreePoolWithTag(Entry, TAG_FONT);
return 0;
}
@@ -2324,6 +2360,9 @@ IntGdiAddFontResourceEx(
pchFile += cchFile + 1;
}
/* Prepare for better LOGFONT-to-face matching */
FontLookUp_Cleanup(TRUE, NULL);
return ret;
}
@@ -2436,6 +2475,7 @@ IntGdiRemoveFontResourceSingle(
ASSERT(FontGDI);
if (FontGDI->Filename && _wcsicmp(FontGDI->Filename, pszFileTitle) == 0)
{
FontLookUp_Cleanup(TRUE, FontGDI->SharedFace);
RemoveEntryList(&FontEntry->ListEntry);
CleanupFontEntry(FontEntry);
if (dwFlags & AFRX_WRITE_REGISTRY)
@@ -6022,8 +6062,7 @@ IntFontType(PFONTGDI Font)
FT_ULong tmp_size = 0;
FT_Face Face = Font->SharedFace->Face;
ASSERT_FREETYPE_LOCK_NOT_HELD();
IntLockFreeType();
ASSERT_FREETYPE_LOCK_HELD();
if (FT_HAS_MULTIPLE_MASTERS(Face))
Font->FontObj.flFontType |= FO_MULTIPLEMASTER;
@@ -6046,8 +6085,6 @@ IntFontType(PFONTGDI Font)
{
Font->FontObj.flFontType |= (FO_CFF|FO_POSTSCRIPT);
}
IntUnLockFreeType();
}
static BOOL
@@ -6099,16 +6136,172 @@ MatchFontNames(PSHARED_FACE SharedFace, LPCWSTR lfFaceName)
return FALSE;
}
static void
IntPopulateTextObj(
PTEXTOBJ TextObj,
PFONTGDI FontGdi,
FONTOBJ *pFontObj,
const LOGFONTW *pLogFont,
PLOGFONTW SubstitutedLogFont)
{
ASSERT_FREETYPE_LOCK_HELD();
TextObj->Font = pFontObj;
TextObj->TextFace[0] = UNICODE_NULL;
PSHARED_FACE SharedFace = FontGdi->SharedFace;
if (MatchFontNames(SharedFace, SubstitutedLogFont->lfFaceName))
{
RtlStringCchCopyW(TextObj->TextFace, _countof(TextObj->TextFace), pLogFont->lfFaceName);
}
else
{
UNICODE_STRING Name;
RtlInitUnicodeString(&Name, NULL);
NTSTATUS Status = IntGetFontLocalizedName(&Name, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID);
if (NT_SUCCESS(Status))
{
/* truncated copy */
IntUnicodeStringToBuffer(TextObj->TextFace, sizeof(TextObj->TextFace), &Name);
RtlFreeUnicodeString(&Name);
}
}
// Need hdev, when freetype is loaded need to create DEVOBJ for
// Consumer and Producer.
TextObj->Font->iUniq = 1; // Now it can be cached.
TextObj->fl |= TEXTOBJECT_INIT;
}
static void
IntPopulateFontGdi(PFONTGDI FontGdi, const FONTOBJ *pFontObj, const LOGFONTW *pLogFont)
{
ASSERT_FREETYPE_LOCK_HELD();
IntFontType(FontGdi);
FontGdi->flType = pFontObj->flFontType;
FontGdi->RequestUnderline = pLogFont->lfUnderline ? 0xFF : 0;
FontGdi->RequestStrikeOut = pLogFont->lfStrikeOut ? 0xFF : 0;
FontGdi->RequestItalic = pLogFont->lfItalic ? 0xFF : 0;
if (pLogFont->lfWeight != FW_DONTCARE)
FontGdi->RequestWeight = pLogFont->lfWeight;
else
FontGdi->RequestWeight = FW_NORMAL;
}
static PFONT_LOOKUP_CACHE
FontLookUp_LookUp(const LOGFONTW *pLogFont)
{
ASSERT_FREETYPE_LOCK_HELD();
PLIST_ENTRY Entry, pHead = &s_FontLookupCacheList;
for (Entry = pHead->Flink; Entry != pHead; Entry = Entry->Flink)
{
PFONT_LOOKUP_CACHE pEntry = CONTAINING_RECORD(Entry, FONT_LOOKUP_CACHE, ListEntry);
if (RtlEqualMemory(&pEntry->LogFont, pLogFont, sizeof(LOGFONTW)))
{
// Move to head
RemoveEntryList(&pEntry->ListEntry);
InsertHeadList(&s_FontLookupCacheList, &pEntry->ListEntry);
return pEntry;
}
}
return NULL;
}
static void
FontLookUp_Add(const LOGFONTW *LogFont, PSHARED_FACE SharedFace, FONTOBJ *pFontObj)
{
ASSERT_FREETYPE_LOCK_HELD();
if (s_FontLookupCacheCount >= MAX_FONT_LOOKUP_CACHE) // Too many cache?
{
// Remove tail one
PLIST_ENTRY OldestEntry = RemoveTailList(&s_FontLookupCacheList);
PFONT_LOOKUP_CACHE pOldCache = CONTAINING_RECORD(OldestEntry, FONT_LOOKUP_CACHE, ListEntry);
FontLookUp_Destroy(pOldCache);
s_FontLookupCacheCount--;
}
// Add new cache
PFONT_LOOKUP_CACHE pEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_LOOKUP_CACHE), TAG_FONT);
if (pEntry)
{
// Populate
RtlCopyMemory(&pEntry->LogFont, LogFont, sizeof(LOGFONTW));
pEntry->SharedFace = SharedFace;
SharedFace_AddRef(SharedFace);
pEntry->pFontObj = pFontObj;
// Add to head
InsertHeadList(&s_FontLookupCacheList, &pEntry->ListEntry);
s_FontLookupCacheCount++;
}
}
PSHARED_FACE
IntRealizeFont(const LOGFONTW *pLogFont, _Inout_opt_ PTEXTOBJ TextObj)
{
ASSERT_FREETYPE_LOCK_HELD();
ASSERT(pLogFont);
LOGFONTW LogFont = *pLogFont;
RtlZeroMemory(&LogFont.lfFaceName, sizeof(LogFont.lfFaceName));
RtlStringCchCopyW(LogFont.lfFaceName, _countof(LogFont.lfFaceName), pLogFont->lfFaceName);
pLogFont = &LogFont;
/* Substitute */
LOGFONTW SubstitutedLogFont = *pLogFont;
SubstituteFontRecurse(&SubstitutedLogFont);
DPRINT("Font '%S,%u' is substituted by '%S,%u'.\n",
pLogFont->lfFaceName, pLogFont->lfCharSet,
SubstitutedLogFont.lfFaceName, SubstitutedLogFont.lfCharSet);
FONTOBJ *pFontObj;
PFONT_LOOKUP_CACHE pLookUp = FontLookUp_LookUp(pLogFont);
if (pLookUp)
{
pFontObj = pLookUp->pFontObj;
}
else
{
ULONG MatchPenalty = 0xFFFFFFFF;
PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
/* Search private fonts */
IntLockProcessPrivateFonts(Win32Process);
FindBestFontFromList(&pFontObj, &MatchPenalty, &SubstitutedLogFont,
&Win32Process->PrivateFontListHead);
IntUnLockProcessPrivateFonts(Win32Process);
/* Search system fonts */
FindBestFontFromList(&pFontObj, &MatchPenalty, &SubstitutedLogFont, &g_FontListHead);
}
if (!pFontObj)
{
DPRINT1("Request font %S not found, no fonts loaded at all\n", pLogFont->lfFaceName);
return NULL;
}
PFONTGDI pFontGDI = ObjToGDI(pFontObj, FONT);
if (TextObj)
IntPopulateTextObj(TextObj, pFontGDI, pFontObj, pLogFont, &SubstitutedLogFont);
IntPopulateFontGdi(pFontGDI, pFontObj, pLogFont);
if (!pLookUp)
FontLookUp_Add(pLogFont, pFontGDI->SharedFace, pFontObj);
return pFontGDI->SharedFace;
}
NTSTATUS
FASTCALL
TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj)
{
NTSTATUS Status = STATUS_SUCCESS;
PTEXTOBJ TextObj;
PPROCESSINFO Win32Process;
ULONG MatchPenalty;
LOGFONTW *pLogFont;
LOGFONTW SubstitutedLogFont;
PSHARED_FACE SharedFace;
if (!pTextObj)
{
@@ -6129,86 +6322,16 @@ TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj)
TextObj = pTextObj;
}
pLogFont = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
/* substitute */
SubstitutedLogFont = *pLogFont;
SubstituteFontRecurse(&SubstitutedLogFont);
DPRINT("Font '%S,%u' is substituted by '%S,%u'.\n",
pLogFont->lfFaceName, pLogFont->lfCharSet,
SubstitutedLogFont.lfFaceName, SubstitutedLogFont.lfCharSet);
MatchPenalty = 0xFFFFFFFF;
TextObj->Font = NULL;
Win32Process = PsGetCurrentProcessWin32Process();
/* Search private fonts */
IntLockFreeType();
IntLockProcessPrivateFonts(Win32Process);
FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont,
&Win32Process->PrivateFontListHead);
IntUnLockProcessPrivateFonts(Win32Process);
/* Search system fonts */
FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont,
&g_FontListHead);
SharedFace = IntRealizeFont(&TextObj->logfont.elfEnumLogfontEx.elfLogFont, TextObj);
IntUnLockFreeType();
if (NULL == TextObj->Font)
{
DPRINT1("Request font %S not found, no fonts loaded at all\n",
pLogFont->lfFaceName);
Status = STATUS_NOT_FOUND;
}
else
{
UNICODE_STRING Name;
PFONTGDI FontGdi = ObjToGDI(TextObj->Font, FONT);
PSHARED_FACE SharedFace = FontGdi->SharedFace;
if (!pTextObj)
TEXTOBJ_UnlockText(TextObj);
TextObj->TextFace[0] = UNICODE_NULL;
IntLockFreeType();
if (MatchFontNames(SharedFace, SubstitutedLogFont.lfFaceName))
{
IntUnLockFreeType();
RtlStringCchCopyW(TextObj->TextFace, _countof(TextObj->TextFace), pLogFont->lfFaceName);
}
else
{
RtlInitUnicodeString(&Name, NULL);
Status = IntGetFontLocalizedName(&Name, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID);
IntUnLockFreeType();
if (NT_SUCCESS(Status))
{
/* truncated copy */
IntUnicodeStringToBuffer(TextObj->TextFace, sizeof(TextObj->TextFace), &Name);
RtlFreeUnicodeString(&Name);
}
}
ASSERT((!!SharedFace ^ (NULL == TextObj->Font)) != 0);
// Need hdev, when freetype is loaded need to create DEVOBJ for
// Consumer and Producer.
TextObj->Font->iUniq = 1; // Now it can be cached.
IntFontType(FontGdi);
FontGdi->flType = TextObj->Font->flFontType;
FontGdi->RequestUnderline = pLogFont->lfUnderline ? 0xFF : 0;
FontGdi->RequestStrikeOut = pLogFont->lfStrikeOut ? 0xFF : 0;
FontGdi->RequestItalic = pLogFont->lfItalic ? 0xFF : 0;
if (pLogFont->lfWeight != FW_DONTCARE)
FontGdi->RequestWeight = pLogFont->lfWeight;
else
FontGdi->RequestWeight = FW_NORMAL;
TextObj->fl |= TEXTOBJECT_INIT;
Status = STATUS_SUCCESS;
}
if (!pTextObj) TEXTOBJ_UnlockText(TextObj);
ASSERT((NT_SUCCESS(Status) ^ (NULL == TextObj->Font)) != 0);
return Status;
return SharedFace ? STATUS_SUCCESS : STATUS_NOT_FOUND;
}
static