diff options
author | Grigori Goronzy <greg@chown.ath.cx> | 2015-09-07 11:40:54 +0200 |
---|---|---|
committer | Grigori Goronzy <greg@chown.ath.cx> | 2015-09-07 11:40:54 +0200 |
commit | a2f12d6f79bfdc6bc7c1cb6ddd3a42c97ab2de4f (patch) | |
tree | 2600406db3d71a17d4389f34ba4bf66f7c098247 /libass | |
parent | 23de9995103c16ceceba19cae87e328be12fa8e4 (diff) | |
parent | 0268c64b8ec7bef9287a212759152bf7d15e64d8 (diff) | |
download | libass-a2f12d6f79bfdc6bc7c1cb6ddd3a42c97ab2de4f.tar.bz2 libass-a2f12d6f79bfdc6bc7c1cb6ddd3a42c97ab2de4f.tar.xz |
Merge branch 'fonts'
Diffstat (limited to 'libass')
-rw-r--r-- | libass/Makefile.am | 19 | ||||
-rw-r--r-- | libass/ass.c | 2 | ||||
-rw-r--r-- | libass/ass.h | 47 | ||||
-rw-r--r-- | libass/ass_cache.c | 4 | ||||
-rw-r--r-- | libass/ass_coretext.c | 295 | ||||
-rw-r--r-- | libass/ass_coretext.h | 35 | ||||
-rw-r--r-- | libass/ass_directwrite.c | 715 | ||||
-rw-r--r-- | libass/ass_directwrite.h | 31 | ||||
-rw-r--r-- | libass/ass_font.c | 131 | ||||
-rw-r--r-- | libass/ass_font.h | 28 | ||||
-rw-r--r-- | libass/ass_fontconfig.c | 705 | ||||
-rw-r--r-- | libass/ass_fontconfig.h | 32 | ||||
-rw-r--r-- | libass/ass_fontselect.c | 1043 | ||||
-rw-r--r-- | libass/ass_fontselect.h | 260 | ||||
-rw-r--r-- | libass/ass_library.h | 2 | ||||
-rw-r--r-- | libass/ass_parse.c | 11 | ||||
-rw-r--r-- | libass/ass_render.c | 13 | ||||
-rw-r--r-- | libass/ass_render.h | 4 | ||||
-rw-r--r-- | libass/ass_render_api.c | 25 | ||||
-rw-r--r-- | libass/ass_shaper.c | 2 | ||||
-rw-r--r-- | libass/ass_types.h | 13 | ||||
-rw-r--r-- | libass/ass_utils.c | 26 | ||||
-rw-r--r-- | libass/ass_utils.h | 6 | ||||
-rw-r--r-- | libass/dwrite_c.h | 673 | ||||
-rw-r--r-- | libass/libass.sym | 1 |
25 files changed, 3521 insertions, 602 deletions
diff --git a/libass/Makefile.am b/libass/Makefile.am index 4bf9584..2cf6354 100644 --- a/libass/Makefile.am +++ b/libass/Makefile.am @@ -11,7 +11,7 @@ yasm_verbose_ = $(yasm_verbose_$(AM_DEFAULT_VERBOSITY)) yasm_verbose_0 = @echo " YASM " $@; .asm.lo: - $(yasm_verbose)$(LIBTOOL) $(AM_V_lt) --mode=compile $(AS) $(ASFLAGS) -o $@ $< -prefer-non-pic + $(yasm_verbose)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(AS) $(ASFLAGS) -o $@ $< -prefer-non-pic SRC_INTEL = x86/blend_bitmaps.asm x86/blur.asm x86/cpuid.asm x86/cpuid.h SRC_INTEL64 = x86/be_blur.asm @@ -19,18 +19,29 @@ SRC_INTEL_RASTERIZER = x86/rasterizer.asm SRC_RASTERIZER = ass_rasterizer.h ass_rasterizer.c ass_rasterizer_c.c +SRC_DIRECTWRITE = ass_directwrite.c ass_directwrite.h dwrite_c.h +SRC_CORETEXT = ass_coretext.c ass_coretext.h + lib_LTLIBRARIES = libass.la -libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontconfig.c ass_render.c \ +libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontselect.c ass_render.c \ ass_utils.c ass_bitmap.c ass_blur.c ass_library.c ass_bitmap.h \ - ass_cache.h ass_fontconfig.h ass_font.h ass.h \ + ass_cache.h ass_fontselect.h ass_font.h ass.h \ ass_library.h ass_types.h ass_utils.h ass_drawing.c \ ass_drawing.h ass_cache_template.h ass_render.h \ ass_parse.c ass_parse.h ass_render_api.c ass_shaper.c \ - ass_shaper.h ass_strtod.c ass_func_template.h + ass_shaper.h ass_strtod.c ass_fontconfig.c ass_fontconfig.h libass_la_LDFLAGS = -no-undefined -version-info $(LIBASS_LT_CURRENT):$(LIBASS_LT_REVISION):$(LIBASS_LT_AGE) libass_la_LDFLAGS += -export-symbols $(srcdir)/libass.sym +if DIRECTWRITE +libass_la_SOURCES += $(SRC_DIRECTWRITE) +endif + +if CORETEXT +libass_la_SOURCES += $(SRC_CORETEXT) +endif + if RASTERIZER libass_la_SOURCES += $(SRC_RASTERIZER) endif diff --git a/libass/ass.c b/libass/ass.c index 01dd2eb..b51cf2e 100644 --- a/libass/ass.c +++ b/libass/ass.c @@ -1031,7 +1031,7 @@ out: * \param bufsize out: file size * \return pointer to file contents. Caller is responsible for its deallocation. */ -static char *read_file(ASS_Library *library, char *fname, size_t *bufsize) +char *read_file(ASS_Library *library, char *fname, size_t *bufsize) { int res; long sz; diff --git a/libass/ass.h b/libass/ass.h index 774f344..5fdee10 100644 --- a/libass/ass.h +++ b/libass/ass.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> + * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx> * * This file is part of libass. * @@ -175,6 +176,24 @@ typedef enum { int ass_library_version(void); /** + * \brief Default Font provider to load fonts in libass' database + * + * NONE don't use any default font provider for font lookup + * AUTODETECT use the first available font provider + * CORETEXT force a CoreText based font provider (OS X only) + * FONTCONFIG force a Fontconfig based font provider + * + * libass uses the best shaper available by default. + */ +typedef enum { + ASS_FONTPROVIDER_NONE = 0, + ASS_FONTPROVIDER_AUTODETECT = 1, + ASS_FONTPROVIDER_CORETEXT, + ASS_FONTPROVIDER_FONTCONFIG, + ASS_FONTPROVIDER_DIRECTWRITE, +} ASS_DefaultFontProvider; + +/** * \brief Initialize the library. * \return library handle or NULL if failed */ @@ -383,11 +402,29 @@ void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing); void ass_set_line_position(ASS_Renderer *priv, double line_position); /** + * \brief Get the list of available font providers. The output array + * is allocated with malloc and can be released with free(). If an + * allocation error occurs, size is set to (size_t)-1. + * \param priv library handle + * \param providers output, list of default providers (malloc'ed array) + * \param size output, number of providers + * \return list of available font providers (user owns the returned array) + */ +void ass_get_available_font_providers(ASS_Library *priv, + ASS_DefaultFontProvider **providers, + size_t *size); + +/** * \brief Set font lookup defaults. * \param default_font path to default font to use. Must be supplied if * fontconfig is disabled or unavailable. * \param default_family fallback font family for fontconfig, or NULL - * \param fc whether to use fontconfig + * \param dfp which font provider to use (one of ASS_DefaultFontProvider). In + * older libass version, this could be 0 or 1, where 1 enabled fontconfig. + * Newer relases also accept 0 (ASS_FONTPROVIDER_NONE) and 1 + * (ASS_FONTPROVIDER_AUTODETECT), which is almost backward-compatible. + * If the requested fontprovider does not exist or fails to initialize, the + * behavior is the same as when ASS_FONTPROVIDER_NONE was passed. * \param config path to fontconfig configuration file, or NULL. Only relevant * if fontconfig is used. * \param update whether fontconfig cache should be built/updated now. Only @@ -396,8 +433,8 @@ void ass_set_line_position(ASS_Renderer *priv, double line_position); * NOTE: font lookup must be configured before an ASS_Renderer can be used. */ void ass_set_fonts(ASS_Renderer *priv, const char *default_font, - const char *default_family, int fc, const char *config, - int update); + const char *default_family, int dfp, + const char *config, int update); /** * \brief Set selective style override mode. @@ -426,8 +463,8 @@ void ass_set_selective_style_override_enabled(ASS_Renderer *priv, int bits); void ass_set_selective_style_override(ASS_Renderer *priv, ASS_Style *style); /** - * \brief Update/build font cache. This needs to be called if it was - * disabled when ass_set_fonts was set. + * \brief This is a stub and does nothing. Old documentation: Update/build font + * cache. This needs to be called if it was disabled when ass_set_fonts was set. * * \param priv renderer handle * \return success diff --git a/libass/ass_cache.c b/libass/ass_cache.c index 2381e88..d1aaec0 100644 --- a/libass/ass_cache.c +++ b/libass/ass_cache.c @@ -43,8 +43,6 @@ static unsigned font_hash(void *buf, size_t len) hval = fnv_32a_str(desc->family, FNV1_32A_INIT); hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval); hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval); - hval = fnv_32a_buf(&desc->treat_family_as_pattern, - sizeof(desc->treat_family_as_pattern), hval); hval = fnv_32a_buf(&desc->vertical, sizeof(desc->vertical), hval); return hval; } @@ -59,8 +57,6 @@ static unsigned font_compare(void *key1, void *key2, size_t key_size) return 0; if (a->italic != b->italic) return 0; - if (a->treat_family_as_pattern != b->treat_family_as_pattern) - return 0; if (a->vertical != b->vertical) return 0; return 1; diff --git a/libass/ass_coretext.c b/libass/ass_coretext.c new file mode 100644 index 0000000..877018d --- /dev/null +++ b/libass/ass_coretext.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2013 Stefano Pigozzi <stefano.pigozzi@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. + */ + +#include "config.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <CoreText/CoreText.h> + +#include "ass_coretext.h" + +#define SAFE_CFRelease(x) do { if (x) CFRelease(x); } while(0) + +static const ASS_FontMapping font_substitutions[] = { + {"sans-serif", "Helvetica"}, + {"serif", "Times"}, + {"monospace", "Courier"} +}; + +static char *cfstr2buf(CFStringRef string) +{ + const int encoding = kCFStringEncodingUTF8; + const char *buf_ptr = CFStringGetCStringPtr(string, encoding); + if (buf_ptr) { + return strdup(buf_ptr); + } else { + size_t len = CFStringGetLength(string); + CFIndex buf_len = CFStringGetMaximumSizeForEncoding(len, encoding); + char *buf = malloc(buf_len); + CFStringGetCString(string, buf, buf_len, encoding); + return buf; + } +} + +static void destroy_font(void *priv) +{ + CFCharacterSetRef set = priv; + SAFE_CFRelease(set); +} + +static int check_glyph(void *priv, uint32_t code) +{ + CFCharacterSetRef set = priv; + + if (!set) + return 1; + + if (code == 0) + return 1; + + return CFCharacterSetIsLongCharacterMember(set, code); +} + +static char *get_font_file(CTFontDescriptorRef fontd) +{ + CFURLRef url = CTFontDescriptorCopyAttribute(fontd, kCTFontURLAttribute); + CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + char *buffer = cfstr2buf(path); + SAFE_CFRelease(path); + SAFE_CFRelease(url); + return buffer; +} + +static void get_name(CTFontDescriptorRef fontd, CFStringRef attr, + char **array, int *idx) +{ + + CFStringRef name = CTFontDescriptorCopyAttribute(fontd, attr); + if (name) { + array[*idx] = cfstr2buf(name); + SAFE_CFRelease(name); + *idx += 1; + } +} + +static void get_trait(CFDictionaryRef traits, CFStringRef attribute, + float *trait) +{ + CFNumberRef cftrait = CFDictionaryGetValue(traits, attribute); + if (!CFNumberGetValue(cftrait, kCFNumberFloatType, trait)) + *trait = 0.0; +} + +static void get_font_traits(CTFontDescriptorRef fontd, + ASS_FontProviderMetaData *meta) +{ + float weight, slant, width; + + CFDictionaryRef traits = + CTFontDescriptorCopyAttribute(fontd, kCTFontTraitsAttribute); + + get_trait(traits, kCTFontWeightTrait, &weight); + get_trait(traits, kCTFontSlantTrait, &slant); + get_trait(traits, kCTFontWidthTrait, &width); + + SAFE_CFRelease(traits); + + // Printed all of my system fonts (see if'deffed code below). Here is how + // CoreText 'normalized' weights maps to CSS/libass: + + // opentype: 0 100 200 300 400 500 600 700 800 900 + // css: LIGHT REG MED SBOLD BOLD BLACK EXTRABL + // libass: LIGHT MEDIUM BOLD + // coretext: -0.4 0.0 0.23 0.3 0.4 0.62 + + if (weight >= 0.62) + meta->weight = 800; + else if (weight >= 0.4) + meta->weight = 700; + else if (weight >= 0.3) + meta->weight = 600; + else if (weight >= 0.23) + meta->weight = 500; + else if (weight >= -0.4) + meta->weight = 400; + else + meta->weight = 200; + + if (slant > 0.03) + meta->slant = FONT_SLANT_ITALIC; + else + meta->slant = FONT_SLANT_NONE; + + if (width <= -0.2) + meta->width = FONT_WIDTH_CONDENSED; + else if (width >= 0.2) + meta->width = FONT_WIDTH_EXPANDED; + else + meta->width = FONT_WIDTH_NORMAL; + +#if 0 + char *name[1]; + int idx = 0; + get_name(fontd, kCTFontDisplayNameAttribute, name, &idx); + char *file = get_font_file(fontd); + printf( + "Font traits for: %-40s [%-50s] " + "<slant: %f, %03d>, <weight: (%f, %03d)>, <width: %f, %03d>\n", + name[0], file, + slant, meta->slant, weight, meta->weight, width, meta->width); + free(name[0]); + free(file); +#endif +} + +static void process_descriptors(ASS_FontProvider *provider, CFArrayRef fontsd) +{ + ASS_FontProviderMetaData meta; + char *families[1]; + char *identifiers[1]; + char *fullnames[1]; + + if (!fontsd) + return; + + for (int i = 0; i < CFArrayGetCount(fontsd); i++) { + CTFontDescriptorRef fontd = CFArrayGetValueAtIndex(fontsd, i); + int index = -1; + + char *path = get_font_file(fontd); + if (strcmp("", path) == 0) { + // skip the font if the URL field in the font descriptor is empty + free(path); + continue; + } + + memset(&meta, 0, sizeof(meta)); + get_font_traits(fontd, &meta); + + get_name(fontd, kCTFontFamilyNameAttribute, families, &meta.n_family); + meta.families = families; + + int zero = 0; + get_name(fontd, kCTFontNameAttribute, identifiers, &zero); + get_name(fontd, kCTFontDisplayNameAttribute, fullnames, &meta.n_fullname); + meta.fullnames = fullnames; + + CFCharacterSetRef chset = + CTFontDescriptorCopyAttribute(fontd, kCTFontCharacterSetAttribute); + ass_font_provider_add_font(provider, &meta, path, index, + identifiers[0], (void*)chset); + + for (int j = 0; j < meta.n_family; j++) + free(meta.families[j]); + + for (int j = 0; j < meta.n_fullname; j++) + free(meta.fullnames[j]); + + free(identifiers[0]); + + free(path); + } +} + +static void match_fonts(ASS_Library *lib, ASS_FontProvider *provider, + char *name) +{ + const size_t attributes_n = 3; + CTFontDescriptorRef ctdescrs[attributes_n]; + CFMutableDictionaryRef cfattrs[attributes_n]; + CFStringRef attributes[attributes_n] = { + kCTFontFamilyNameAttribute, + kCTFontDisplayNameAttribute, + kCTFontNameAttribute, + }; + + CFStringRef cfname = + CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); + + for (int i = 0; i < attributes_n; i++) { + cfattrs[i] = CFDictionaryCreateMutable(NULL, 0, 0, 0); + CFDictionaryAddValue(cfattrs[i], attributes[i], cfname); + ctdescrs[i] = CTFontDescriptorCreateWithAttributes(cfattrs[i]); + } + + CFArrayRef descriptors = + CFArrayCreate(NULL, (const void **)&ctdescrs, attributes_n, NULL); + + CTFontCollectionRef ctcoll = + CTFontCollectionCreateWithFontDescriptors(descriptors, 0); + + CFArrayRef fontsd = + CTFontCollectionCreateMatchingFontDescriptors(ctcoll); + + process_descriptors(provider, fontsd); + + SAFE_CFRelease(fontsd); + SAFE_CFRelease(ctcoll); + + for (int i = 0; i < attributes_n; i++) { + SAFE_CFRelease(cfattrs[i]); + SAFE_CFRelease(ctdescrs[i]); + } + + SAFE_CFRelease(descriptors); + SAFE_CFRelease(cfname); +} + +static char *get_fallback(void *priv, const char *family, uint32_t codepoint) +{ + CFStringRef name = CFStringCreateWithBytes( + 0, (UInt8 *)family, strlen(family), kCFStringEncodingUTF8, false); + CTFontRef font = CTFontCreateWithName(name, 0, NULL); + uint32_t codepointle = OSSwapHostToLittleInt32(codepoint); + CFStringRef r = CFStringCreateWithBytes( + 0, (UInt8*)&codepointle, sizeof(codepointle), + kCFStringEncodingUTF32LE, false); + CTFontRef fb = CTFontCreateForString(font, r, CFRangeMake(0, 1)); + CFStringRef cffamily = CTFontCopyFamilyName(fb); + char *res_family = cfstr2buf(cffamily); + + SAFE_CFRelease(name); + SAFE_CFRelease(font); + SAFE_CFRelease(r); + SAFE_CFRelease(fb); + SAFE_CFRelease(cffamily); + + return res_family; +} + +static void get_substitutions(void *priv, const char *name, + ASS_FontProviderMetaData *meta) +{ + const int n = sizeof(font_substitutions) / sizeof(font_substitutions[0]); + ass_map_font(font_substitutions, n, name, meta); +} + +static ASS_FontProviderFuncs coretext_callbacks = { + .check_glyph = check_glyph, + .destroy_font = destroy_font, + .match_fonts = match_fonts, + .get_substitutions = get_substitutions, + .get_fallback = get_fallback, +}; + +ASS_FontProvider * +ass_coretext_add_provider(ASS_Library *lib, ASS_FontSelector *selector, + const char *config) +{ + return ass_font_provider_new(selector, &coretext_callbacks, NULL); +} diff --git a/libass/ass_coretext.h b/libass/ass_coretext.h new file mode 100644 index 0000000..07a49fa --- /dev/null +++ b/libass/ass_coretext.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 Stefano Pigozzi <stefano.pigozzi@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. + */ + +#include "config.h" + +#include "ass_types.h" +#include "ass_fontselect.h" + +#ifndef ASS_CORETEXT_H +#define ASS_CORETEXT_H + +#ifdef CONFIG_CORETEXT + +ASS_FontProvider * +ass_coretext_add_provider(ASS_Library *lib, ASS_FontSelector *selector, + const char *config); + +#endif + +#endif diff --git a/libass/ass_directwrite.c b/libass/ass_directwrite.c new file mode 100644 index 0000000..f11deb6 --- /dev/null +++ b/libass/ass_directwrite.c @@ -0,0 +1,715 @@ +/* + * 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_c.h" + +#include "ass_directwrite.h" +#include "ass_utils.h" + +#define NAME_MAX_LENGTH 256 +#define FALLBACK_DEFAULT_FONT L"Arial" + +static const ASS_FontMapping font_substitutions[] = { + {"sans-serif", "Arial"}, + {"serif", "Times New Roman"}, + {"monospace", "Courier New"} +}; + +/* + * 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 (pr |