summaryrefslogtreecommitdiffstats
path: root/libass/ass_directwrite.c
diff options
context:
space:
mode:
Diffstat (limited to 'libass/ass_directwrite.c')
-rw-r--r--libass/ass_directwrite.c706
1 files changed, 706 insertions, 0 deletions
diff --git a/libass/ass_directwrite.c b/libass/ass_directwrite.c
new file mode 100644
index 0000000..483af50
--- /dev/null
+++ b/libass/ass_directwrite.c
@@ -0,0 +1,706 @@
+/*
+ * Copyright (C) 2015 Stephan Vedder <stephan.vedder@gmail.com>
+ *
+ * This file is part of libass.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#define COBJMACROS
+
+#include "config.h"
+
+#include <initguid.h>
+#include <ole2.h>
+#include <shobjidl.h>
+
+#include "dwrite.h"
+
+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);
+
+#include "ass_directwrite.h"
+#include "ass_utils.h"
+
+#define NAME_MAX_LENGTH 256
+#define FALLBACK_DEFAULT_FONT L"Arial"
+
+/*
+ * The private data stored for every font, detected by this backend.
+ */
+typedef struct {
+ IDWriteFont *font;
+ IDWriteFontFileStream *stream;
+} FontPrivate;
+
+typedef struct {
+ HMODULE directwrite_lib;
+ IDWriteFactory *factory;
+} ProviderPrivate;
+
+/**
+ * Custom text renderer class for logging the fonts used. It does not
+ * actually render anything or do anything apart from that.
+ */
+
+typedef struct FallbackLogTextRenderer {
+ IDWriteTextRenderer iface;
+ IDWriteTextRendererVtbl vtbl;
+ IDWriteFactory *dw_factory;
+ LONG ref_count;
+} FallbackLogTextRenderer;
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_IsPixelSnappingDisabled(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ _Out_ BOOL* isDisabled
+ )
+{
+ *isDisabled = true;
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_GetCurrentTransform(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ _Out_ DWRITE_MATRIX* transform
+ )
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_GetPixelsPerDip(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ _Out_ FLOAT* pixelsPerDip
+ )
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawGlyphRun(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ DWRITE_MEASURING_MODE measuringMode,
+ _In_ DWRITE_GLYPH_RUN const* glyphRun,
+ _In_ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
+ IUnknown* clientDrawingEffect
+ )
+{
+ FallbackLogTextRenderer *this = (FallbackLogTextRenderer *)This;
+ HRESULT hr;
+ IDWriteFontCollection *font_coll = NULL;
+ IDWriteFont **font = (IDWriteFont **)clientDrawingContext;
+
+ hr = IDWriteFactory_GetSystemFontCollection(this->dw_factory, &font_coll, FALSE);
+ if (FAILED(hr))
+ return E_FAIL;
+
+ hr = IDWriteFontCollection_GetFontFromFontFace(font_coll, glyphRun->fontFace,
+ font);
+ if (FAILED(hr))
+ return E_FAIL;
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawUnderline(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ _In_ DWRITE_UNDERLINE const* underline,
+ IUnknown* clientDrawingEffect
+ )
+{
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawStrikethrough(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ _In_ DWRITE_STRIKETHROUGH const* strikethrough,
+ IUnknown* clientDrawingEffect
+ )
+{
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawInlineObject(
+ IDWriteTextRenderer *This,
+ void *clientDrawingContext,
+ FLOAT originX,
+ FLOAT originY,
+ IDWriteInlineObject *inlineObject,
+ WINBOOL isSideways,
+ WINBOOL isRightToLeft,
+ IUnknown *clientDrawingEffect
+ )
+{
+ return S_OK;
+}
+
+// IUnknown methods
+
+static ULONG STDMETHODCALLTYPE FallbackLogTextRenderer_AddRef(
+ IDWriteTextRenderer *This
+ )
+{
+ FallbackLogTextRenderer *this = (FallbackLogTextRenderer *)This;
+ return InterlockedIncrement(&this->ref_count);
+}
+
+static ULONG STDMETHODCALLTYPE FallbackLogTextRenderer_Release(
+ IDWriteTextRenderer *This
+ )
+{
+ FallbackLogTextRenderer *this = (FallbackLogTextRenderer *)This;
+ unsigned long new_count = InterlockedDecrement(&this->ref_count);
+ if (new_count == 0) {
+ free(this);
+ return 0;
+ }
+
+ return new_count;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_QueryInterface(
+ IDWriteTextRenderer *This,
+ REFIID riid,
+ void **ppvObject
+ )
+{
+ if (IsEqualGUID(riid, &IID_IDWriteTextRenderer)
+ || IsEqualGUID(riid, &IID_IDWritePixelSnapping)
+ || IsEqualGUID(riid, &IID_IUnknown)) {
+ *ppvObject = This;
+ } else {
+ *ppvObject = NULL;
+ return E_FAIL;
+ }
+
+ This->lpVtbl->AddRef(This);
+ return S_OK;
+}
+
+static void init_FallbackLogTextRenderer(FallbackLogTextRenderer *r,
+ IDWriteFactory *factory)
+{
+ *r = (FallbackLogTextRenderer){
+ .iface = {
+ .lpVtbl = &r->vtbl,
+ },
+ .vtbl = {
+ FallbackLogTextRenderer_QueryInterface,
+ FallbackLogTextRenderer_AddRef,
+ FallbackLogTextRenderer_Release,
+ FallbackLogTextRenderer_IsPixelSnappingDisabled,
+ FallbackLogTextRenderer_GetCurrentTransform,
+ FallbackLogTextRenderer_GetPixelsPerDip,
+ FallbackLogTextRenderer_DrawGlyphRun,
+ FallbackLogTextRenderer_DrawUnderline,
+ FallbackLogTextRenderer_DrawStrikethrough,
+ FallbackLogTextRenderer_DrawInlineObject,
+ },
+ .dw_factory = factory,
+ };
+}
+
+/*
+ * This function is called whenever a font is used for the first
+ * time. It will create a FontStream for memory reading, which
+ * will be stored within the private data.
+ */
+static bool init_font_private(FontPrivate *priv)
+{
+ HRESULT hr = S_OK;
+ IDWriteFont *font = priv->font;
+ IDWriteFontFace *face = NULL;
+ IDWriteFontFile *file = NULL;
+ IDWriteFontFileStream *stream = NULL;
+ IDWriteFontFileLoader *loader = NULL;
+ UINT32 n_files = 1;
+ const void *refKey = NULL;
+ UINT32 keySize = 0;
+
+ if (priv->stream != NULL)
+ return true;
+
+ hr = IDWriteFont_CreateFontFace(font, &face);
+ if (FAILED(hr) || !face)
+ return false;
+
+ /* DirectWrite only supports one file per face */
+ hr = IDWriteFontFace_GetFiles(face, &n_files, &file);
+ if (FAILED(hr) || !file) {
+ IDWriteFontFace_Release(face);
+ return false;
+ }
+
+ hr = IDWriteFontFile_GetReferenceKey(file, &refKey, &keySize);
+ if (FAILED(hr)) {
+ IDWriteFontFile_Release(file);
+ IDWriteFontFace_Release(face);
+ return false;
+ }
+
+ hr = IDWriteFontFile_GetLoader(file, &loader);
+ if (FAILED(hr) || !loader) {
+ IDWriteFontFile_Release(file);
+ IDWriteFontFace_Release(face);
+ return false;
+ }
+
+ hr = IDWriteFontFileLoader_CreateStreamFromKey(loader, refKey, keySize, &stream);
+ if (FAILED(hr) || !stream) {
+ IDWriteFontFile_Release(file);
+ IDWriteFontFace_Release(face);
+ return false;
+ }
+
+ priv->stream = stream;
+ IDWriteFontFile_Release(file);
+ IDWriteFontFace_Release(face);
+
+ return true;
+}
+
+/*
+ * Read a specified part of a fontfile into memory.
+ * If the font wasn't used before first creates a
+ * FontStream and save it into the private data for later usage.
+ * If the parameter "buf" is NULL libass wants to know the
+ * size of the Fontfile
+ */
+static size_t get_data(void *data, unsigned char *buf, size_t offset,
+ size_t length)
+{
+ HRESULT hr = S_OK;
+ FontPrivate *priv = (FontPrivate *) data;
+ const void *fileBuf = NULL;
+ void *fragContext = NULL;
+
+ if (!init_font_private(priv))
+ return 0;
+
+ if (buf == NULL) {
+ UINT64 fileSize;
+ hr = IDWriteFontFileStream_GetFileSize(priv->stream, &fileSize);
+ if (FAILED(hr))
+ return 0;
+
+ return fileSize;
+ }
+
+ hr = IDWriteFontFileStream_ReadFileFragment(priv->stream, &fileBuf, offset,
+ length, &fragContext);
+
+ if (FAILED(hr) || !fileBuf)
+ return 0;
+
+ memcpy(buf, fileBuf, length);
+
+ IDWriteFontFileStream_ReleaseFileFragment(priv->stream, fragContext);
+
+ return length;
+}
+
+/*
+ * Checks if the passed font has a specific unicode
+ * character. Returns 0 for failure and 1 for success
+ */
+static int check_glyph(void *data, uint32_t code)
+{
+ HRESULT hr = S_OK;
+ FontPrivate *priv = (FontPrivate *) data;
+ BOOL exists = FALSE;
+
+ if (code == 0)
+ return 1;
+
+ IDWriteFont_HasCharacter(priv->font, code, &exists);
+ if (FAILED(hr))
+ return 0;
+
+ return exists;
+}
+
+/*
+ * This will release the directwrite backend
+ */
+static void destroy_provider(void *priv)
+{
+ ProviderPrivate *provider_priv = (ProviderPrivate *)priv;
+ provider_priv->factory->lpVtbl->Release(provider_priv->factory);
+ FreeLibrary(provider_priv->directwrite_lib);
+ free(provider_priv);
+}
+
+/*
+ * This will destroy a specific font and it's
+ * Fontstream (in case it does exist)
+ */
+
+static void destroy_font(void *data)
+{
+ FontPrivate *priv = (FontPrivate *) data;
+
+ IDWriteFont_Release(priv->font);
+ if (priv->stream != NULL)
+ IDWriteFontFileStream_Release(priv->stream);
+
+ free(priv);
+}
+
+static int encode_utf16(wchar_t *chars, uint32_t codepoint)
+{
+ if (codepoint < 0x10000) {
+ chars[0] = codepoint;
+ return 1;
+ } else {
+ chars[0] = (codepoint >> 10) + 0xD7C0;
+ chars[1] = (codepoint & 0x3FF) + 0xDC00;
+ return 2;
+ }
+}
+
+static char *get_fallback(void *priv, ASS_FontProviderMetaData *meta,
+ uint32_t codepoint)
+{
+ HRESULT hr;
+ ProviderPrivate *provider_priv = (ProviderPrivate *)priv;
+ IDWriteFactory *dw_factory = provider_priv->factory;
+ IDWriteTextFormat *text_format = NULL;
+ IDWriteTextLayout *text_layout = NULL;
+ FallbackLogTextRenderer renderer;
+
+ init_FallbackLogTextRenderer(&renderer, dw_factory);
+
+ hr = IDWriteFactory_CreateTextFormat(dw_factory, FALLBACK_DEFAULT_FONT, NULL,
+ DWRITE_FONT_WEIGHT_MEDIUM, DWRITE_FONT_STYLE_NORMAL,
+ DWRITE_FONT_STRETCH_NORMAL, 1.0f, L"", &text_format);
+ if (FAILED(hr)) {
+ return NULL;
+ }
+
+ // Encode codepoint as UTF-16
+ wchar_t char_string[2];
+ int char_len = encode_utf16(char_string, codepoint);
+
+ // Create a text_layout, a high-level text rendering facility, using
+ // the given codepoint and dummy format.
+ hr = IDWriteFactory_CreateTextLayout(dw_factory, char_string, char_len, text_format,
+ 0.0f, 0.0f, &text_layout);
+ if (FAILED(hr)) {
+ IDWriteTextFormat_Release(text_format);
+ return NULL;
+ }
+
+ // Draw the layout with a dummy renderer, which logs the
+ // font used and stores it.
+ IDWriteFont *font = NULL;
+ hr = IDWriteTextLayout_Draw(text_layout, &font, &renderer.iface, 0.0f, 0.0f);
+ if (FAILED(hr) || font == NULL) {
+ IDWriteTextLayout_Release(text_layout);
+ IDWriteTextFormat_Release(text_format);
+ return NULL;
+ }
+
+ // We're done with these now
+ IDWriteTextLayout_Release(text_layout);
+ IDWriteTextFormat_Release(text_format);
+
+ // Now, just extract the first family name
+ BOOL exists = FALSE;
+ IDWriteLocalizedStrings *familyNames = NULL;
+ hr = IDWriteFont_GetInformationalStrings(font,
+ DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES,
+ &familyNames, &exists);
+ if (FAILED(hr) || !exists) {
+ IDWriteFont_Release(font);
+ return NULL;
+ }
+
+ wchar_t temp_name[NAME_MAX_LENGTH];
+ hr = IDWriteLocalizedStrings_GetString(familyNames, 0, temp_name, NAME_MAX_LENGTH);
+ if (FAILED(hr)) {
+ IDWriteLocalizedStrings_Release(familyNames);
+ IDWriteFont_Release(font);
+ return NULL;
+ }
+ temp_name[NAME_MAX_LENGTH-1] = 0;
+
+ // DirectWrite may not have found a valid fallback, so check that
+ // the selected font actually has the requested glyph.
+ hr = IDWriteFont_HasCharacter(font, codepoint, &exists);
+ if (FAILED(hr) || !exists) {
+ IDWriteLocalizedStrings_Release(familyNames);
+ IDWriteFont_Release(font);
+ return NULL;
+ }
+
+ int size_needed = WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, NULL, 0,NULL, NULL);
+ char *family = (char *) malloc(size_needed);
+ WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, family, size_needed, NULL, NULL);
+
+ IDWriteLocalizedStrings_Release(familyNames);
+ IDWriteFont_Release(font);
+ return family;
+}
+
+static int map_width(enum DWRITE_FONT_STRETCH stretch)
+{
+ switch (stretch) {
+ case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: return 50;
+ case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: return 63;
+ case DWRITE_FONT_STRETCH_CONDENSED: return FONT_WIDTH_CONDENSED;
+ case DWRITE_FONT_STRETCH_SEMI_CONDENSED: return 88;
+ case DWRITE_FONT_STRETCH_MEDIUM: return FONT_WIDTH_NORMAL;
+ case DWRITE_FONT_STRETCH_SEMI_EXPANDED: return 113;
+ case DWRITE_FONT_STRETCH_EXPANDED: return FONT_WIDTH_EXPANDED;
+ case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: return 150;
+ case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: return 200;
+ default:
+ assert(0);
+ }
+}
+
+/*
+ * 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
+ */
+static void scan_fonts(IDWriteFactory *factory,
+ ASS_FontProvider *provider)
+{
+ HRESULT hr = S_OK;
+ IDWriteFontCollection *fontCollection = NULL;
+ IDWriteFont *font = NULL;
+ DWRITE_FONT_METRICS metrics;
+ DWRITE_FONT_STYLE style;
+ ASS_FontProviderMetaData meta = {0};
+ hr = IDWriteFactory_GetSystemFontCollection(factory, &fontCollection, FALSE);
+ wchar_t temp_name[NAME_MAX_LENGTH];
+ int size_needed = 0;
+
+ if (FAILED(hr) || !fontCollection)
+ return;
+
+ UINT32 familyCount = IDWriteFontCollection_GetFontFamilyCount(fontCollection);
+
+ for (UINT32 i = 0; i < familyCount; ++i) {
+ IDWriteFontFamily *fontFamily = NULL;
+ IDWriteLocalizedStrings *familyNames = NULL;
+ IDWriteLocalizedStrings *fontNames = NULL;
+ IDWriteLocalizedStrings *psNames = NULL;
+ BOOL exists = FALSE;
+ char *psName = NULL;
+
+ 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;
+ }
+
+ meta.weight = IDWriteFont_GetWeight(font);
+ meta.width = map_width(IDWriteFont_GetStretch(font));
+ IDWriteFont_GetMetrics(font, &metrics);
+ style = IDWriteFont_GetStyle(font);
+ meta.slant = (style == DWRITE_FONT_STYLE_NORMAL) ? FONT_SLANT_NONE :
+ (style == DWRITE_FONT_STYLE_OBLIQUE)? FONT_SLANT_OBLIQUE :
+ (style == DWRITE_FONT_STYLE_ITALIC) ? FONT_SLANT_ITALIC : FONT_SLANT_NONE;
+
+ hr = IDWriteFont_GetInformationalStrings(font,
+ DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, &psNames,&exists);
+ if (FAILED(hr)) {
+ IDWriteFont_Release(font);
+ continue;
+ }
+
+ if (exists) {
+ hr = IDWriteLocalizedStrings_GetString(psNames, 0, temp_name, NAME_MAX_LENGTH);
+ if (FAILED(hr)) {
+ IDWriteLocalizedStrings_Release(psNames);
+ IDWriteFont_Release(font);
+ continue;
+ }
+
+ temp_name[NAME_MAX_LENGTH-1] = 0;
+ size_needed = WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, NULL, 0,NULL, NULL);
+ psName = (char *) malloc(size_needed);
+ WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, psName, size_needed, NULL, NULL);
+ IDWriteLocalizedStrings_Release(psNames);
+ }
+
+ hr = IDWriteFont_GetInformationalStrings(font,
+ DWRITE_INFORMATIONAL_STRING_FULL_NAME, &fontNames,&exists);
+ if (FAILED(hr)) {
+ IDWriteFont_Release(font);
+ continue;
+ }
+
+ if (exists) {
+ meta.n_fullname = IDWriteLocalizedStrings_GetCount(fontNames);
+ meta.fullnames = (char **) calloc(meta.n_fullname, sizeof(char *));
+ for (UINT32 k = 0; k < meta.n_fullname; ++k) {
+ hr = IDWriteLocalizedStrings_GetString(fontNames, k,
+ temp_name,
+ NAME_MAX_LENGTH);
+ if (FAILED(hr)) {
+ continue;
+ }
+
+ temp_name[NAME_MAX_LENGTH-1] = 0;
+ size_needed = WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, NULL, 0, NULL, NULL);
+ char *mbName = (char *) malloc(size_needed);
+ WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, mbName, size_needed, NULL, NULL);
+ meta.fullnames[k] = mbName;
+ }
+ IDWriteLocalizedStrings_Release(fontNames);
+ }
+
+ hr = IDWriteFont_GetInformationalStrings(font,
+ DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &familyNames, &exists);
+ if (!exists)
+ hr = IDWriteFontFamily_GetFamilyNames(fontFamily, &familyNames);
+ if (FAILED(hr)) {
+ IDWriteFont_Release(font);
+ continue;
+ }
+
+ meta.n_family = IDWriteLocalizedStrings_GetCount(familyNames);
+ meta.families = (char **) calloc(meta.n_family, sizeof(char *));
+ for (UINT32 k = 0; k < meta.n_family; ++k) {
+ hr = IDWriteLocalizedStrings_GetString(familyNames, k,
+ temp_name,
+ NAME_MAX_LENGTH);
+ if (FAILED(hr)) {
+ continue;
+ }
+
+ temp_name[NAME_MAX_LENGTH-1] = 0;
+ size_needed = WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, NULL, 0,NULL, NULL);
+ char *mbName = (char *) malloc(size_needed);
+ WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, mbName, size_needed, NULL, NULL);
+ meta.families[k] = mbName;
+ }
+ IDWriteLocalizedStrings_Release(familyNames);
+
+ FontPrivate *font_priv = (FontPrivate *) calloc(1, sizeof(*font_priv));
+ font_priv->font = font;
+
+ ass_font_provider_add_font(provider, &meta, NULL, 0, psName, font_priv);
+
+ for (UINT32 k = 0; k < meta.n_family; ++k)
+ free(meta.families[k]);
+ for (UINT32 k = 0; k < meta.n_fullname; ++k)
+ free(meta.fullnames[k]);
+ free(meta.fullnames);
+ free(meta.families);
+ free(psName);
+ }
+ }
+}
+
+/*
+ * Called by libass when the provider should perform the
+ * specified task
+ */
+static ASS_FontProviderFuncs directwrite_callbacks = {
+ get_data,
+ check_glyph,
+ destroy_font,
+ destroy_provider,
+ NULL,
+ NULL,
+ get_fallback
+};
+
+typedef HRESULT WINAPI (*DWriteCreateFactoryFn)(
+ _In_ DWRITE_FACTORY_TYPE factoryType,
+ _In_ REFIID iid,
+ _Out_ IUnknown **factory
+);
+
+/*
+ * Register the directwrite provider. Upon registering
+ * scans all system fonts. The private data for this
+ * provider is IDWriteFactory
+ * On failure returns NULL
+ */
+ASS_FontProvider *ass_directwrite_add_provider(ASS_Library *lib,
+ ASS_FontSelector *selector,
+ const char *config)
+{
+ HRESULT hr = S_OK;
+ IDWriteFactory *dwFactory = NULL;
+ ASS_FontProvider *provider = NULL;
+ DWriteCreateFactoryFn DWriteCreateFactoryPtr = NULL;
+ ProviderPrivate *priv = NULL;
+
+ HMODULE directwrite_lib = LoadLibraryW(L"Dwrite.dll");
+ if (!directwrite_lib)
+ goto cleanup;
+
+ DWriteCreateFactoryPtr = (DWriteCreateFactoryFn)GetProcAddress(directwrite_lib,
+ "DWriteCreateFactory");
+ if (!DWriteCreateFactoryPtr)
+ goto cleanup;
+
+ hr = DWriteCreateFactoryPtr(DWRITE_FACTORY_TYPE_SHARED,
+ &IID_IDWriteFactory,
+ (IUnknown **) (&dwFactory));
+ if (FAILED(hr) || !dwFactory) {
+ ass_msg(lib, MSGL_WARN, "Failed to initialize directwrite.");
+ dwFactory = NULL;
+ goto cleanup;
+ }
+
+ priv = (ProviderPrivate *)calloc(sizeof(*priv), 1);
+ if (!priv)
+ goto cleanup;
+
+ priv->directwrite_lib = directwrite_lib;
+ priv->factory = dwFactory;
+ provider = ass_font_provider_new(selector, &directwrite_callbacks, priv);
+ if (!provider)
+ goto cleanup;
+
+ scan_fonts(dwFactory, provider);
+ return provider;
+
+cleanup:
+
+ free(priv);
+ if (dwFactory)
+ dwFactory->lpVtbl->Release(dwFactory);
+ if (directwrite_lib)
+ FreeLibrary(directwrite_lib);
+
+ return NULL;
+}