Skip to content

Commit

Permalink
directwrite: request font on demand
Browse files Browse the repository at this point in the history
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 libass#334.

Signed-off-by: Oleg Oshmyan <chortos@inbox.lv>
  • Loading branch information
Apache553 authored and astiob committed May 1, 2021
1 parent d82f999 commit d325c63
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 35 deletions.
80 changes: 46 additions & 34 deletions libass/ass_directwrite.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ typedef struct {
typedef struct {
HMODULE directwrite_lib;
IDWriteFactory *factory;
IDWriteGdiInterop *gdi_interop;
} ProviderPrivate;

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -668,50 +670,47 @@ static void add_font(IDWriteFont *font, IDWriteFontFamily *fontFamily,
}

/*
* 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,
Expand All @@ -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,
Expand All @@ -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;
Expand All @@ -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)
Expand Down
32 changes: 31 additions & 1 deletion libass/dwrite_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <dcommon.h>

Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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__ */

0 comments on commit d325c63

Please sign in to comment.