summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrigori Goronzy <greg@chown.ath.cx>2015-06-11 21:29:35 +0200
committerGrigori Goronzy <greg@chown.ath.cx>2015-07-10 10:42:41 +0200
commita7bf39a5714fd872b6ac1bc9e39792fb79e64891 (patch)
tree8dbcafc6451203255efba310474bdb0620188e20
parentfaa5203fe181e50ce1377f09e451e90906ffe9e3 (diff)
downloadlibass-a7bf39a5714fd872b6ac1bc9e39792fb79e64891.tar.bz2
libass-a7bf39a5714fd872b6ac1bc9e39792fb79e64891.tar.xz
fontselect: improved and simplified matching
Sorting the font list is overkill and not very useful. We are interested in *exact* name matches only; all other font families don't matter and we'll use another fallback mechanism for glyph fallbacks (TBD). Replace the sorting and glyph fallback search with a simple linear scan. Fonts are first matched against family name first (to allow further comparison against style attributes) and if that fails, the fullname is considered.
-rw-r--r--libass/ass_fontselect.c127
1 files changed, 59 insertions, 68 deletions
diff --git a/libass/ass_fontselect.c b/libass/ass_fontselect.c
index 38a1a89..7d00a7b 100644
--- a/libass/ass_fontselect.c
+++ b/libass/ass_fontselect.c
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <stdio.h>
+#include <limits.h>
#include <assert.h>
#include <string.h>
#include <strings.h>
@@ -74,9 +75,6 @@ struct font_info {
char *postscript_name; // can be used as an alternative to index to
// identify a font inside a collection
- // similarity score
- unsigned score;
-
// font source
ASS_FontProvider *provider;
@@ -391,48 +389,39 @@ void ass_font_provider_free(ASS_FontProvider *provider)
*/
static unsigned font_info_similarity(ASS_FontInfo *a, ASS_FontInfo *req)
{
- int i, j;
- unsigned similarity = 0;
-
- // compare fullnames
- // a matching fullname is very nice and instantly drops the score to zero
- similarity = 10000;
- for (i = 0; i < a->n_fullname; i++)
- for (j = 0; j < req->n_fullname; j++) {
- if (strcasecmp(a->fullnames[i], req->fullnames[j]) == 0)
- similarity = 0;
+ int i;
+ int family_match = 0;
+
+ // Compare family name first; sometimes family name equals fullname,
+ // but we want to be able to match against the different variants
+ // in case a family name match occurs.
+ for (i = 0; i < a->n_family; i++) {
+ if (strcasecmp(a->families[i], req->fullnames[0]) == 0) {
+ family_match = 1;
+ break;
}
-
- // if we don't have any match, compare fullnames against family
- // sometimes the family name is used similarly
- if (similarity > 0) {
- for (i = 0; i < a->n_family; i++)
- for (j = 0; j < req->n_fullname; j++) {
- if (strcasecmp(a->families[i], req->fullnames[j]) == 0)
- similarity = 0;
- }
}
- // compare slant
- similarity += ABS(a->slant - req->slant);
-
- // compare weight
- similarity += ABS(a->weight - req->weight);
+ // If there's a family match, compare font attributes
+ // to determine best match in that particular family
+ if (family_match) {
+ unsigned similarity = 0;
+ similarity += ABS(a->weight - req->weight);
+ similarity += ABS(a->slant - req->slant);
+ similarity += ABS(a->width - req->width);
- // compare width
- similarity += ABS(a->width - req->width);
-
- return similarity;
-}
+ return similarity;
+ }
-// calculate scores
-static void font_info_req_similarity(ASS_FontInfo *font_infos, size_t len,
- ASS_FontInfo *req)
-{
- int i;
+ // If we don't have any match, compare fullnames against request
+ // if there is a match now, assign lowest score possible. This means
+ // the font should be chosen instantly, without further search.
+ for (i = 0; i < a->n_fullname; i++) {
+ if (strcasecmp(a->fullnames[i], req->fullnames[0]) == 0)
+ return 0;
+ }
- for (i = 0; i < len; i++)
- font_infos[i].score = font_info_similarity(&font_infos[i], req);
+ return UINT_MAX;
}
#if 0
@@ -462,21 +451,12 @@ static void font_info_dump(ASS_FontInfo *font_infos, size_t len)
}
#endif
-static int font_info_compare(const void *av, const void *bv)
-{
- const ASS_FontInfo *a = av;
- const ASS_FontInfo *b = bv;
-
- return a->score - b->score;
-}
-
static char *select_font(ASS_FontSelector *priv, ASS_Library *library,
const char *family, unsigned bold, unsigned italic,
int *index, char **postscript_name, int *uid,
ASS_FontStream *stream, uint32_t code)
{
- int num_fonts = priv->n_font;
- int idx = 0;
+ int idx = -1;
ASS_FontInfo req;
char *req_fullname;
char *tfamily = trim_space(strdup(family));
@@ -500,36 +480,47 @@ static char *select_font(ASS_FontSelector *priv, ASS_Library *library,
req.fullnames = &req_fullname;
req.fullnames[0] = tfamily;
- // calculate similarities
- font_info_req_similarity(font_infos, num_fonts, &req);
+ // match font
+ unsigned score_min = UINT_MAX;
+ for (int i = 0; i < priv->n_font; i++) {
+ unsigned score = font_info_similarity(&priv->font_infos[i], &req);
- // sort
- qsort(font_infos, num_fonts, sizeof(ASS_FontInfo),
- font_info_compare);
+ // lowest possible score instantly matches
+ if (score == 0) {
+ idx = i;
+ break;
+ }
- // check glyph coverage
- while (idx < priv->n_font) {
- ASS_FontProvider *provider = font_infos[idx].provider;
- if (!provider || !provider->funcs.check_glyph) {
- idx++;
- continue;
+ // update idx if score is better
+ if (score < score_min) {
+ score_min = score;
+ idx = i;
}
- if (provider->funcs.check_glyph(font_infos[idx].priv, code) > 0)
- break;
- idx++;
}
+ // free font request
free(req.fullnames[0]);
+ // found anything?
+ if (idx < 0) {
+ return NULL;
+ }
+
+ // check glyph coverage
+ ASS_FontProvider *provider = font_infos[idx].provider;
+ if (!provider || !provider->funcs.check_glyph) {
+ return NULL;
+ }
+ if (provider->funcs.check_glyph(font_infos[idx].priv, code) == 0) {
+ return NULL;
+ }
+
+ // successfully matched, set up return values
*postscript_name = font_infos[idx].postscript_name;
*index = font_infos[idx].index;
*uid = font_infos[idx].uid;
- // nothing found
- if (idx == priv->n_font)
- return NULL;
-
- // if there is no valid path, this is a memory stream font
+ // set up memory stream if there is no path
if (font_infos[idx].path == NULL) {
ASS_FontProvider *provider = font_infos[idx].provider;
stream->func = provider->funcs.get_data;