From d325c633f4ac32fcb4c76a3fb5926737a45c38d0 Mon Sep 17 00:00:00 2001 From: Apache553 Date: Mon, 22 Feb 2021 01:25:29 +0800 Subject: directwrite: request font on demand scan_fonts can be very slow when many fonts were installed in the system. Use IDWriteGdiInterop to create a font by its name when needed instead of scanning all installed fonts during the initializing stage to get better performance. In case of a non-existent font, the fallback mechanism should do its work. Fixes https://github.com/libass/libass/issues/334. Signed-off-by: Oleg Oshmyan --- libass/ass_directwrite.c | 80 ++++++++++++++++++++++++++++-------------------- libass/dwrite_c.h | 32 ++++++++++++++++++- 2 files changed, 77 insertions(+), 35 deletions(-) diff --git a/libass/ass_directwrite.c b/libass/ass_directwrite.c index b5cc299..e666bd9 100644 --- a/libass/ass_directwrite.c +++ b/libass/ass_directwrite.c @@ -50,6 +50,7 @@ typedef struct { typedef struct { HMODULE directwrite_lib; IDWriteFactory *factory; + IDWriteGdiInterop *gdi_interop; } ProviderPrivate; /** @@ -389,6 +390,7 @@ static bool check_glyph(void *data, uint32_t code) static void destroy_provider(void *priv) { ProviderPrivate *provider_priv = (ProviderPrivate *)priv; + provider_priv->gdi_interop->lpVtbl->Release(provider_priv->gdi_interop); provider_priv->factory->lpVtbl->Release(provider_priv->factory); FreeLibrary(provider_priv->directwrite_lib); free(provider_priv); @@ -668,50 +670,47 @@ cleanup: } /* - * Scan every system font on the current machine and add it - * to the libass lookup. Stores the FontPrivate as private data - * for later memory reading + * When a new font name is requested, called to load that font from Windows */ -static void scan_fonts(IDWriteFactory *factory, - ASS_FontProvider *provider) +static void match_fonts(void *priv, ASS_Library *lib, + ASS_FontProvider *provider, char *name) { - HRESULT hr = S_OK; - IDWriteFontCollection *fontCollection = NULL; + ProviderPrivate *provider_priv = (ProviderPrivate *)priv; IDWriteFont *font = NULL; - hr = IDWriteFactory_GetSystemFontCollection(factory, &fontCollection, FALSE); + IDWriteFontFamily *fontFamily = NULL; + LOGFONTW lf; + HRESULT hr = S_OK; + + memset(&lf, 0, sizeof(LOGFONTW)); + lf.lfWeight = FW_DONTCARE; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfOutPrecision = OUT_TT_PRECIS; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfQuality = ANTIALIASED_QUALITY; + lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + + // lfFaceName can hold up to LF_FACESIZE wchars; truncate longer names + MultiByteToWideChar(CP_UTF8, 0, name, -1, lf.lfFaceName, LF_FACESIZE-1); - if (FAILED(hr) || !fontCollection) + hr = IDWriteGdiInterop_CreateFontFromLOGFONT(provider_priv->gdi_interop, &lf, &font); + if (FAILED(hr) || !font) return; - UINT32 familyCount = IDWriteFontCollection_GetFontFamilyCount(fontCollection); + hr = IDWriteFont_GetFontFamily(font, &fontFamily); + if (FAILED(hr) || !fontFamily) + goto cleanup; - for (UINT32 i = 0; i < familyCount; ++i) { - IDWriteFontFamily *fontFamily = NULL; + add_font(font, fontFamily, provider); - hr = IDWriteFontCollection_GetFontFamily(fontCollection, i, &fontFamily); - if (FAILED(hr)) - continue; - - UINT32 fontCount = IDWriteFontFamily_GetFontCount(fontFamily); - for (UINT32 j = 0; j < fontCount; ++j) { - hr = IDWriteFontFamily_GetFont(fontFamily, j, &font); - if (FAILED(hr)) - continue; - - // Simulations for bold or oblique are sometimes synthesized by - // DirectWrite. We are only interested in physical fonts. - if (IDWriteFont_GetSimulations(font) != 0) { - IDWriteFont_Release(font); - continue; - } + IDWriteFontFamily_Release(fontFamily); - add_font(font, fontFamily, provider); - } + return; +cleanup: + if (font) + IDWriteFont_Release(font); + if (fontFamily) IDWriteFontFamily_Release(fontFamily); - } - - IDWriteFontCollection_Release(fontCollection); } static void get_substitutions(void *priv, const char *name, @@ -731,6 +730,7 @@ static ASS_FontProviderFuncs directwrite_callbacks = { .check_glyph = check_glyph, .destroy_font = destroy_font, .destroy_provider = destroy_provider, + .match_fonts = match_fonts, .get_substitutions = get_substitutions, .get_fallback = get_fallback, .get_font_index = get_font_index, @@ -755,6 +755,7 @@ ASS_FontProvider *ass_directwrite_add_provider(ASS_Library *lib, { HRESULT hr = S_OK; IDWriteFactory *dwFactory = NULL; + IDWriteGdiInterop *dwGdiInterop = NULL; ASS_FontProvider *provider = NULL; DWriteCreateFactoryFn DWriteCreateFactoryPtr = NULL; ProviderPrivate *priv = NULL; @@ -778,22 +779,33 @@ ASS_FontProvider *ass_directwrite_add_provider(ASS_Library *lib, goto cleanup; } + hr = IDWriteFactory_GetGdiInterop(dwFactory, + &dwGdiInterop); + if (FAILED(hr) || !dwGdiInterop) { + ass_msg(lib, MSGL_WARN, "Failed to get IDWriteGdiInterop."); + dwGdiInterop = NULL; + goto cleanup; + } + priv = (ProviderPrivate *)calloc(sizeof(*priv), 1); if (!priv) goto cleanup; priv->directwrite_lib = directwrite_lib; priv->factory = dwFactory; + priv->gdi_interop = dwGdiInterop; + provider = ass_font_provider_new(selector, &directwrite_callbacks, priv); if (!provider) goto cleanup; - scan_fonts(dwFactory, provider); return provider; cleanup: free(priv); + if (dwGdiInterop) + dwGdiInterop->lpVtbl->Release(dwGdiInterop); if (dwFactory) dwFactory->lpVtbl->Release(dwFactory); if (directwrite_lib) diff --git a/libass/dwrite_c.h b/libass/dwrite_c.h index d20139b..5612e9f 100644 --- a/libass/dwrite_c.h +++ b/libass/dwrite_c.h @@ -29,6 +29,7 @@ typedef struct IDWritePixelSnapping IDWritePixelSnapping; typedef struct IDWriteTextFormat IDWriteTextFormat; typedef struct IDWriteTextLayout IDWriteTextLayout; typedef struct IDWriteTextRenderer IDWriteTextRenderer; +typedef struct IDWriteGdiInterop IDWriteGdiInterop; #include @@ -191,7 +192,8 @@ DECLARE_INTERFACE_(IDWriteFactory,IUnknown) IDWriteTextFormat **textFormat) PURE; STDMETHOD(dummy12)(THIS); - STDMETHOD(dummy13)(THIS); + STDMETHOD(GetGdiInterop)(THIS_ + IDWriteGdiInterop **gdiInterop) PURE; STDMETHOD(CreateTextLayout)(THIS_ WCHAR const *string, @@ -211,6 +213,7 @@ DECLARE_INTERFACE_(IDWriteFactory,IUnknown) #define IDWriteFactory_GetSystemFontCollection(This,fontCollection,checkForUpdates) (This)->lpVtbl->GetSystemFontCollection(This,fontCollection,checkForUpdates) #define IDWriteFactory_CreateTextFormat(This,fontFamilyName,fontCollection,fontWeight,fontStyle,fontStretch,fontSize,localeName,textFormat) (This)->lpVtbl->CreateTextFormat(This,fontFamilyName,fontCollection,fontWeight,fontStyle,fontStretch,fontSize,localeName,textFormat) #define IDWriteFactory_CreateTextLayout(This,string,stringLength,textFormat,maxWidth,maxHeight,textLayout) (This)->lpVtbl->CreateTextLayout(This,string,stringLength,textFormat,maxWidth,maxHeight,textLayout) +#define IDWriteFactory_GetGdiInterop(This,gdiInterop) (This)->lpVtbl->GetGdiInterop(This,gdiInterop) #endif /*COBJMACROS*/ #undef INTERFACE @@ -680,8 +683,35 @@ DECLARE_INTERFACE_(IDWriteTextRenderer,IDWritePixelSnapping) END_INTERFACE }; +#undef INTERFACE +#define INTERFACE IDWriteGdiInterop +DECLARE_INTERFACE_(IDWriteGdiInterop,IUnknown) +{ + BEGIN_INTERFACE + +#ifndef __cplusplus + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; +#endif + + STDMETHOD(CreateFontFromLOGFONT)(THIS_ + LOGFONTW const *logFont, + IDWriteFont **font) PURE; + /* rest dropped */ + END_INTERFACE +}; +#ifdef COBJMACROS +#define IDWriteGdiInterop_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteGdiInterop_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteGdiInterop_Release(This) (This)->lpVtbl->Release(This) +#define IDWriteGdiInterop_CreateFontFromLOGFONT(This,logFont,font) (This)->lpVtbl->CreateFontFromLOGFONT(This,logFont,font) +#endif /*COBJMACROS*/ + DEFINE_GUID(IID_IDWriteFactory, 0xb859ee5a,0xd838,0x4b5b,0xa2,0xe8,0x1a,0xdc,0x7d,0x93,0xdb,0x48); DEFINE_GUID(IID_IDWritePixelSnapping, 0xeaf3a2da,0xecf4,0x4d24,0xb6,0x44,0xb3,0x4f,0x68,0x42,0x02,0x4b); DEFINE_GUID(IID_IDWriteTextRenderer, 0xef8a8135,0x5cc6,0x45fe,0x88,0x25,0xc5,0xa0,0x72,0x4e,0xb8,0x19); +DEFINE_GUID(IID_IDWriteGdiInterop, 0x1edd9491,0x9853,0x4299,0x89,0x8f,0x64,0x32,0x98,0x3b,0x6f,0x3a); #endif /* __INC_DWRITE__ */ -- cgit v1.2.3