]> granicus.if.org Git - libass/commitdiff
fontselect: replace is_postscript flag with check_postscript function
authorOleg Oshmyan <chortos@inbox.lv>
Wed, 4 Nov 2015 13:25:47 +0000 (15:25 +0200)
committerOleg Oshmyan <chortos@inbox.lv>
Wed, 4 Nov 2015 13:38:50 +0000 (15:38 +0200)
DirectWrite does not provide fast access to the is_postscript flag,
requiring each font to be loaded before its format can be determined.
Eagerly doing this for every installed font can be quite slow,
on the order of seconds.

To improve performance, ask the font provider for this information
only when it is actually needed, i.e. when one of the font's full
names or its PostScript name matches a requested font name and we
need to know whether to accept this match.

The return value of check_postscript is not cached in this commit.
This makes repeated calls slower than accessing is_postscript was.
This should not be a problem, but if it is, the value can be cached
(or precomputed) by font providers in their font private data.

This commit also potentially increases the memory usage of some
font providers by retaining data structures needed to implement
check_postscript in their font private data. This should not be
a problem either, but if it is, the value of check_postscript
can be precomputed by all providers other than DirectWrite.

libass/ass_coretext.c
libass/ass_directwrite.c
libass/ass_fontconfig.c
libass/ass_fontselect.c
libass/ass_fontselect.h

index 2eacd9244acdc60fe4ceeb6004c697a452daf239..1e74ad1434f3ac72d6307097e80e72bb1f9cd3c6 100644 (file)
@@ -49,13 +49,29 @@ static char *cfstr2buf(CFStringRef string)
 
 static void destroy_font(void *priv)
 {
-    CFCharacterSetRef set = priv;
-    SAFE_CFRelease(set);
+    CTFontDescriptorRef fontd = priv;
+    SAFE_CFRelease(fontd);
+}
+
+static bool check_postscript(void *priv)
+{
+    CTFontDescriptorRef fontd = priv;
+    CFNumberRef cfformat =
+        CTFontDescriptorCopyAttribute(fontd, kCTFontFormatAttribute);
+    int format;
+
+    if (!CFNumberGetValue(cfformat, kCFNumberIntType, &format))
+        return false;
+
+    return format == kCTFontFormatOpenTypePostScript ||
+           format == kCTFontFormatPostScript;
 }
 
 static bool check_glyph(void *priv, uint32_t code)
 {
-    CFCharacterSetRef set = priv;
+    CTFontDescriptorRef fontd = priv;
+    CFCharacterSetRef set =
+        CTFontDescriptorCopyAttribute(fontd, kCTFontCharacterSetAttribute);
 
     if (!set)
         return true;
@@ -63,7 +79,9 @@ static bool check_glyph(void *priv, uint32_t code)
     if (code == 0)
         return true;
 
-    return CFCharacterSetIsLongCharacterMember(set, code);
+    bool result = CFCharacterSetIsLongCharacterMember(set, code);
+    SAFE_CFRelease(set);
+    return result;
 }
 
 static char *get_font_file(CTFontDescriptorRef fontd)
@@ -88,19 +106,6 @@ static void get_name(CTFontDescriptorRef fontd, CFStringRef attr,
     }
 }
 
-static bool is_postscript(CTFontDescriptorRef fontd)
-{
-    int format;
-    CFNumberRef cfformat =
-        CTFontDescriptorCopyAttribute(fontd, kCTFontFormatAttribute);
-
-    if (!CFNumberGetValue(cfformat, kCFNumberIntType, &format))
-        return false;
-
-    return format == kCTFontFormatOpenTypePostScript ||
-           format == kCTFontFormatPostScript;
-}
-
 static void get_trait(CFDictionaryRef traits, CFStringRef attribute,
                       float *trait)
 {
@@ -205,11 +210,8 @@ static void process_descriptors(ASS_FontProvider *provider, CFArrayRef fontsd)
         get_name(fontd, kCTFontNameAttribute, identifiers, &zero);
         meta.postscript_name = identifiers[0];
 
-        meta.is_postscript = is_postscript(fontd);
-
-        CFCharacterSetRef chset =
-            CTFontDescriptorCopyAttribute(fontd, kCTFontCharacterSetAttribute);
-        ass_font_provider_add_font(provider, &meta, path, index, (void*)chset);
+        CFRetain(fontd);
+        ass_font_provider_add_font(provider, &meta, path, index, (void*)fontd);
 
         for (int j = 0; j < meta.n_family; j++)
             free(meta.families[j]);
@@ -297,6 +299,7 @@ static void get_substitutions(void *priv, const char *name,
 }
 
 static ASS_FontProviderFuncs coretext_callbacks = {
+    .check_postscript   = check_postscript,
     .check_glyph        = check_glyph,
     .destroy_font       = destroy_font,
     .match_fonts        = match_fonts,
index cb21dd4d770bfdf111e395b42dc00e15b278c380..b0ee057c699b4e5624a3e0fb9355dbe77fc9ff13 100644 (file)
@@ -43,6 +43,7 @@ static const ASS_FontMapping font_substitutions[] = {
  */
 typedef struct {
     IDWriteFont *font;
+    IDWriteFontFace *face;
     IDWriteFontFileStream *stream;
 } FontPrivate;
 
@@ -223,16 +224,35 @@ static void init_FallbackLogTextRenderer(FallbackLogTextRenderer *r,
     };
 }
 
+/*
+ * This function is called whenever a font is accessed for the
+ * first time. It will create a FontFace for metadata access and
+ * memory reading, which will be stored within the private data.
+ */
+static bool init_font_private_face(FontPrivate *priv)
+{
+    HRESULT hr;
+    IDWriteFontFace *face;
+
+    if (priv->face != NULL)
+        return true;
+
+    hr = IDWriteFont_CreateFontFace(priv->font, &face);
+    if (FAILED(hr) || !face)
+        return false;
+
+    priv->face = face;
+    return true;
+}
+
 /*
  * 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)
+static bool init_font_private_stream(FontPrivate *priv)
 {
     HRESULT hr = S_OK;
-    IDWriteFont *font = priv->font;
-    IDWriteFontFace *face = NULL;
     IDWriteFontFile *file = NULL;
     IDWriteFontFileStream *stream = NULL;
     IDWriteFontFileLoader *loader = NULL;
@@ -243,41 +263,34 @@ static bool init_font_private(FontPrivate *priv)
     if (priv->stream != NULL)
         return true;
 
-    hr = IDWriteFont_CreateFontFace(font, &face);
-    if (FAILED(hr) || !face)
+    if (!init_font_private_face(priv))
         return false;
 
     /* DirectWrite only supports one file per face */
-    hr = IDWriteFontFace_GetFiles(face, &n_files, &file);
-    if (FAILED(hr) || !file) {
-        IDWriteFontFace_Release(face);
+    hr = IDWriteFontFace_GetFiles(priv->face, &n_files, &file);
+    if (FAILED(hr) || !file)
         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;
 }
@@ -297,7 +310,7 @@ static size_t get_data(void *data, unsigned char *buf, size_t offset,
     const void *fileBuf = NULL;
     void *fragContext = NULL;
 
-    if (!init_font_private(priv))
+    if (!init_font_private_stream(priv))
         return 0;
 
     if (buf == NULL) {
@@ -322,6 +335,22 @@ static size_t get_data(void *data, unsigned char *buf, size_t offset,
     return length;
 }
 
+/*
+ * Check whether the font contains PostScript outlines.
+ */
+static bool check_postscript(void *data)
+{
+    FontPrivate *priv = (FontPrivate *) data;
+
+    if (!init_font_private_face(priv))
+        return false;
+
+    DWRITE_FONT_FACE_TYPE type = IDWriteFontFace_GetType(priv->face);
+    return type == DWRITE_FONT_FACE_TYPE_CFF ||
+           type == DWRITE_FONT_FACE_TYPE_RAW_CFF ||
+           type == DWRITE_FONT_FACE_TYPE_TYPE1;
+}
+
 /*
  * Check if the passed font has a specific unicode character.
  */
@@ -362,6 +391,8 @@ static void destroy_font(void *data)
     FontPrivate *priv = (FontPrivate *) data;
 
     IDWriteFont_Release(priv->font);
+    if (priv->face != NULL)
+        IDWriteFontFace_Release(priv->face);
     if (priv->stream != NULL)
         IDWriteFontFileStream_Release(priv->stream);
 
@@ -482,24 +513,6 @@ static int map_width(enum DWRITE_FONT_STRETCH stretch)
     }
 }
 
-static bool is_postscript(IDWriteFont *font)
-{
-    HRESULT hr = S_OK;
-    IDWriteFontFace *face = NULL;
-    DWRITE_FONT_FACE_TYPE type;
-
-    hr = IDWriteFont_CreateFontFace(font, &face);
-    if (FAILED(hr) || !face)
-        return false;
-
-    type = IDWriteFontFace_GetType(face);
-    IDWriteFontFace_Release(face);
-
-    return type == DWRITE_FONT_FACE_TYPE_CFF ||
-           type == DWRITE_FONT_FACE_TYPE_RAW_CFF ||
-           type == DWRITE_FONT_FACE_TYPE_TYPE1;
-}
-
 static void add_font(IDWriteFont *font, IDWriteFontFamily *fontFamily,
                      ASS_FontProvider *provider)
 {
@@ -613,8 +626,6 @@ static void add_font(IDWriteFont *font, IDWriteFontFamily *fontFamily,
     }
     IDWriteLocalizedStrings_Release(familyNames);
 
-    meta.is_postscript = is_postscript(font);
-
     FontPrivate *font_priv = (FontPrivate *) calloc(1, sizeof(*font_priv));
     if (!font_priv)
         goto cleanup;
@@ -698,6 +709,7 @@ static void get_substitutions(void *priv, const char *name,
  */
 static ASS_FontProviderFuncs directwrite_callbacks = {
     .get_data           = get_data,
+    .check_postscript   = check_postscript,
     .check_glyph        = check_glyph,
     .destroy_font       = destroy_font,
     .destroy_provider   = destroy_provider,
index b3b7100163fd11e13fbaa6348a85293def408f24..304598e470808c76514d484999faad619eabb87a 100644 (file)
@@ -40,6 +40,20 @@ typedef struct fc_private {
     FcCharSet *fallback_chars;
 } ProviderPrivate;
 
+static bool check_postscript(void *priv)
+{
+    FcPattern *pat = (FcPattern *)priv;
+    char *format;
+
+    FcResult result =
+        FcPatternGetString(pat, FC_FONTFORMAT, 0, (FcChar8 **)&format);
+    if (result != FcResultMatch)
+        return false;
+
+    return !strcmp(format, "Type 1") || !strcmp(format, "Type 42") ||
+           !strcmp(format, "CID Type 1") || !strcmp(format, "CFF");
+}
+
 static bool check_glyph(void *priv, uint32_t code)
 {
     FcPattern *pat = (FcPattern *)priv;
@@ -86,7 +100,6 @@ static void scan_fonts(FcConfig *config, ASS_FontProvider *provider)
         FcBool outline;
         int index, weight;
         char *path;
-        char *format;
         char *fullnames[MAX_NAME];
         char *families[MAX_NAME];
 
@@ -139,13 +152,6 @@ static void scan_fonts(FcConfig *config, ASS_FontProvider *provider)
         FcPatternGetString(pat, FC_POSTSCRIPT_NAME, 0,
                            (FcChar8 **)&meta.postscript_name);
 
-        meta.is_postscript = false;
-        if (FcPatternGetString(pat, FC_FONTFORMAT, 0,
-                               (FcChar8 **)&format) == FcResultMatch)
-            meta.is_postscript =
-                !strcmp(format, "Type 1") || !strcmp(format, "Type 42") ||
-                !strcmp(format, "CID Type 1") || !strcmp(format, "CFF");
-
         ass_font_provider_add_font(provider, &meta, path, index, (void *)pat);
     }
 }
@@ -268,6 +274,7 @@ cleanup:
 }
 
 static ASS_FontProviderFuncs fontconfig_callbacks = {
+    .check_postscript   = check_postscript,
     .check_glyph        = check_glyph,
     .destroy_provider   = destroy,
     .get_substitutions  = get_substitutions,
index e4be338dc1b9443625b0ad1282256949e611f08c..7f735291edcf484ec0d8489d5ba1235fe84510f6 100644 (file)
@@ -70,7 +70,6 @@ struct font_info {
 
     char *postscript_name; // can be used as an alternative to index to
                            // identify a font inside a collection
-    bool is_postscript;
 
     // font source
     ASS_FontProvider *provider;
@@ -110,6 +109,13 @@ struct font_data_ft {
     int idx;
 };
 
+static bool check_postscript_ft(void *data)
+{
+    FontDataFT *fd = (FontDataFT *)data;
+    PS_FontInfoRec postscript_info;
+    return !FT_Get_PS_Font_Info(fd->face, &postscript_info);
+}
+
 static bool check_glyph_ft(void *data, uint32_t codepoint)
 {
     FontDataFT *fd = (FontDataFT *)data;
@@ -149,13 +155,10 @@ get_data_embedded(void *data, unsigned char *buf, size_t offset, size_t len)
 }
 
 static ASS_FontProviderFuncs ft_funcs = {
-    get_data_embedded,
-    check_glyph_ft,
-    destroy_font_ft,
-    NULL,
-    NULL,
-    NULL,
-    NULL
+    .get_data          = get_data_embedded,
+    .check_postscript  = check_postscript_ft,
+    .check_glyph       = check_glyph_ft,
+    .destroy_font      = destroy_font_ft,
 };
 
 static void load_fonts_from_dir(ASS_Library *library, const char *dir)
@@ -302,7 +305,6 @@ ass_font_provider_add_font(ASS_FontProvider *provider,
     info->width         = width;
     info->n_fullname    = meta->n_fullname;
     info->n_family      = meta->n_family;
-    info->is_postscript = meta->is_postscript;
 
     info->families = calloc(meta->n_family, sizeof(char *));
     if (info->families == NULL)
@@ -406,6 +408,14 @@ void ass_font_provider_free(ASS_FontProvider *provider)
     free(provider);
 }
 
+static bool check_postscript(ASS_FontInfo *fi)
+{
+    ASS_FontProvider *provider = fi->provider;
+    assert(provider && provider->funcs.check_postscript);
+
+    return provider->funcs.check_postscript(fi->priv);
+}
+
 /**
  * \brief Return whether the given font is in the given family.
  */
@@ -425,17 +435,27 @@ static bool matches_family_name(ASS_FontInfo *f, const char *family)
 static bool matches_full_or_postscript_name(ASS_FontInfo *f,
                                             const char *fullname)
 {
-    if (f->is_postscript) {
-        if (f->postscript_name != NULL &&
-            ass_strcasecmp(f->postscript_name, fullname) == 0)
-            return true;
-    } else {
-        for (int i = 0; i < f->n_fullname; i++) {
-            if (ass_strcasecmp(f->fullnames[i], fullname) == 0)
-                return true;
+    bool matches_fullname = false;
+    bool matches_postscript_name = false;
+
+    for (int i = 0; i < f->n_fullname; i++) {
+        if (ass_strcasecmp(f->fullnames[i], fullname) == 0) {
+            matches_fullname = true;
+            break;
         }
     }
-    return false;
+
+    if (f->postscript_name != NULL &&
+        ass_strcasecmp(f->postscript_name, fullname) == 0)
+        matches_postscript_name = true;
+
+    if (matches_fullname == matches_postscript_name)
+        return matches_fullname;
+
+    if (check_postscript(f))
+        return matches_postscript_name;
+    else
+        return matches_fullname;
 }
 
 /**
@@ -730,7 +750,6 @@ get_font_info(FT_Library lib, FT_Face face, ASS_FontProviderMetaData *info)
     int slant, weight;
     char *fullnames[MAX_FULLNAME];
     char *families[MAX_FULLNAME];
-    PS_FontInfoRec postscript_info;
 
     // we're only interested in outlines
     if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
@@ -788,7 +807,6 @@ get_font_info(FT_Library lib, FT_Face face, ASS_FontProviderMetaData *info)
     info->width  = 100;     // FIXME, should probably query the OS/2 table
 
     info->postscript_name = (char *)FT_Get_Postscript_Name(face);
-    info->is_postscript = !FT_Get_PS_Font_Info(face, &postscript_info);
 
     info->families = calloc(sizeof(char *), num_family);
     if (info->families == NULL)
index eceb2f08fd4ed587462ffe69ebca742e280a5f3c..73d526580e9986a485cd104bc15b542c429ece68 100644 (file)
@@ -53,6 +53,14 @@ typedef struct ass_font_provider_meta_data ASS_FontProviderMetaData;
 typedef size_t  (*GetDataFunc)(void *font_priv, unsigned char *data,
                                size_t offset, size_t len);
 
+/**
+ * Check whether the font contains PostScript outlines.
+ *
+ * \param font_priv font private data
+ * \return true if the font contains PostScript outlines
+ */
+typedef bool    (*CheckPostscriptFunc)(void *font_priv);
+
 /**
  * Check if a glyph is supported by a font.
  *
@@ -135,6 +143,7 @@ typedef char   *(*GetFallbackFunc)(void *priv,
 
 typedef struct font_provider_funcs {
     GetDataFunc         get_data;               /* optional/mandatory */
+    CheckPostscriptFunc check_postscript;       /* mandatory */
     CheckGlyphFunc      check_glyph;            /* mandatory */
     DestroyFontFunc     destroy_font;           /* optional */
     DestroyProviderFunc destroy_provider;       /* optional */
@@ -172,8 +181,6 @@ struct ass_font_provider_meta_data {
                         // See FONT_WEIGHT_*
     int width;          // Font weight in percent, normally 100
                         // See FONT_WIDTH_*
-
-    bool is_postscript; // Whether the font contains PostScript outlines
 };
 
 typedef struct ass_font_stream ASS_FontStream;