]> granicus.if.org Git - libass/commitdiff
fontselect: implement a coretext font provider
authorStefano Pigozzi <stefano.pigozzi@gmail.com>
Fri, 1 Nov 2013 14:44:25 +0000 (15:44 +0100)
committerGrigori Goronzy <greg@chown.ath.cx>
Fri, 10 Jul 2015 08:42:40 +0000 (10:42 +0200)
Fontconfig is known to be very slow on OS X and Windows, this has to do with
the extremely prohibitive cache times (which are getting even longer with
latest versions of Fontconfig).

This commits starts to address the problem by using CoreText on OS X to load
the font data. The commit uses the simplest possible approach to load all of
the data in memory and then use it to match. This causes a somewhat slow
startup time (around ~400ms on my i7) but it is already better than waiting
*minutes* for Fontconfig to cache the fonts data.

A later commit will improve the speed of the match by using a hybrid approach
that lazy loads in the libass database only the necessary fonts.

configure.ac
libass/Makefile.am
libass/ass_coretext.c [new file with mode: 0644]
libass/ass_coretext.h [new file with mode: 0644]
libass/ass_fontselect.c

index fce38ef2a4b0d905d036a5420c347336fa1e55b0..6cfd823ebaa8c701529110d503e6bd8c3de3e700 100644 (file)
@@ -151,6 +151,28 @@ PKG_CHECK_MODULES([FONTCONFIG], fontconfig >= 2.4.2, [
     ])
 fi
 
+if test x$enable_coretext != xno; then
+OLDLIBS="$LIBS"
+# Linking to CoreText directly only works from Mountain Lion and iOS6. In
+# earlier OS releases CoreText was part of the ApplicationServices umbrella
+# framework.
+LIBS="$LIBS -framework CoreText -framework CoreFoundation -framework CoreGraphics"
+AC_MSG_CHECKING([for CORETEXT])
+AC_LINK_IFELSE([
+  AC_LANG_PROGRAM(
+    [[#include <CoreText/CoreText.h>]],
+    [[CTFontCreateWithFontDescriptor(NULL, 0.0, NULL);]],)
+  ], [
+    AC_DEFINE(CONFIG_CORETEXT, 1, [found CoreText in System library])
+    coretext=true
+    AC_MSG_RESULT([yes])
+  ], [
+    LIBS="$OLDLIBS"
+    coretext=false
+    AC_MSG_RESULT([no])
+  ])
+fi
+
 if test x$enable_harfbuzz != xno; then
 PKG_CHECK_MODULES([HARFBUZZ], harfbuzz >= 0.9.5, [
     CFLAGS="$CFLAGS $HARFBUZZ_CFLAGS"
index b8659e6b0565a0522963123caa8b4c8d5d91e364..68547682f91328c5ea80e925221f4323173bddbd 100644 (file)
@@ -26,7 +26,9 @@ libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontselect.c ass_render.c \
                     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_fontconfig.c ass_fontconfig.h
+                    ass_shaper.h ass_strtod.c ass_fontconfig.c ass_fontconfig.h \
+                    ass_coretext.c ass_coretext.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
 
diff --git a/libass/ass_coretext.c b/libass/ass_coretext.c
new file mode 100644 (file)
index 0000000..f970226
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * 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"
+
+#ifdef CONFIG_CORETEXT
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreText/CoreText.h>
+
+#include "ass_coretext.h"
+
+static char *cfstr2buf(CFStringRef string)
+{
+    const char *buf_ptr = CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
+    if (buf_ptr) {
+        return strdup(buf_ptr);
+    } else {
+        size_t buf_len = CFStringGetLength(string) + 1;
+        char *buf = calloc(buf_len, sizeof(char));
+        CFStringGetCString(string, buf, buf_len, kCFStringEncodingUTF8);
+        return buf;
+    }
+}
+
+static void destroy_font(void *priv)
+{
+    CFCharacterSetRef set = priv;
+    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);
+    CFRelease(path);
+    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);
+        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);
+
+    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 scan_fonts(ASS_Library *lib, ASS_FontProvider *provider)
+{
+    ASS_FontProviderMetaData meta;
+    char *families[1];
+    char *fullnames[2];
+
+    CTFontCollectionRef coll = CTFontCollectionCreateFromAvailableFonts(NULL);
+    CFArrayRef fontsd = CTFontCollectionCreateMatchingFontDescriptors(coll);
+
+    for (int i = 0; i < CFArrayGetCount(fontsd); i++) {
+        CTFontDescriptorRef fontd = CFArrayGetValueAtIndex(fontsd, i);
+        int index = 0;
+
+        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;
+
+        get_name(fontd, kCTFontDisplayNameAttribute, fullnames, &meta.n_fullname);
+        get_name(fontd, kCTFontNameAttribute, fullnames, &meta.n_fullname);
+        meta.fullnames = fullnames;
+
+        CFCharacterSetRef chset =
+            CTFontDescriptorCopyAttribute(fontd, kCTFontCharacterSetAttribute);
+        ass_font_provider_add_font(provider, &meta, path, index, (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(path);
+    }
+
+    CFRelease(fontsd);
+    CFRelease(coll);
+}
+
+static ASS_FontProviderFuncs coretext_callbacks = {
+    NULL,
+    check_glyph,
+    destroy_font,
+    NULL
+};
+
+ASS_FontProvider *
+ass_coretext_add_provider(ASS_Library *lib, ASS_FontSelector *selector)
+{
+    ASS_FontProvider *provider =
+        ass_font_provider_new(selector, &coretext_callbacks, NULL);
+
+    scan_fonts(lib, provider);
+
+    return provider;
+}
+
+#endif
diff --git a/libass/ass_coretext.h b/libass/ass_coretext.h
new file mode 100644 (file)
index 0000000..8d96927
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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);
+
+#endif
+
+#endif
index cf1c08885e964bde7aefb3f95600c18a12197fc7..9ef0acef12a88fd28fb5e63e31ef45ca51cc6067 100644 (file)
@@ -37,6 +37,7 @@
 #include "ass_library.h"
 #include "ass_fontselect.h"
 #include "ass_fontconfig.h"
+#include "ass_coretext.h"
 #include "ass_font.h"
 
 #define ABS(x) ((x) < 0 ? -(x) : (x))
@@ -803,6 +804,13 @@ ass_fontselect_init(ASS_Library *library,
     priv->embedded_provider = ass_embedded_fonts_add_provider(library, priv,
             ftlibrary);
 
+#ifdef CONFIG_CORETEXT
+    if (fc != 0) {
+        priv->default_provider = ass_coretext_add_provider(library, priv);
+        return priv;
+    }
+#endif
+
 #ifdef CONFIG_FONTCONFIG
     if (fc != 0)
         priv->default_provider = ass_fontconfig_add_provider(library,