diff options
author | Grigori Goronzy <greg@blackbox> | 2011-07-29 04:21:48 +0200 |
---|---|---|
committer | Grigori Goronzy <greg@blackbox> | 2011-07-29 04:44:56 +0200 |
commit | d7544850775344879dd3049046c5b7ce2d52d3b3 (patch) | |
tree | c90a7181ea8ff7be5382e33a1ccdec40f568ae2a /libass/ass_shaper.c | |
parent | 2aff07ec62f1f2ef8462bb2954907ecca71288fa (diff) | |
download | libass-d7544850775344879dd3049046c5b7ce2d52d3b3.tar.bz2 libass-d7544850775344879dd3049046c5b7ce2d52d3b3.tar.xz |
harfbuzz: cache glyph metrics
HarfBuzz' FreeType font functions do not cache anything and call
FT_Load_Glyph all the time, which can be slow. Add a cache for metrics
and use it with custom font functions.
Diffstat (limited to 'libass/ass_shaper.c')
-rw-r--r-- | libass/ass_shaper.c | 225 |
1 files changed, 221 insertions, 4 deletions
diff --git a/libass/ass_shaper.c b/libass/ass_shaper.c index c30eb9d..15dc549 100644 --- a/libass/ass_shaper.c +++ b/libass/ass_shaper.c @@ -23,6 +23,7 @@ #include "ass_render.h" #include "ass_font.h" #include "ass_parse.h" +#include "ass_cache.h" #define MAX_RUNS 50 @@ -44,10 +45,19 @@ struct ass_shaper { // OpenType features int n_features; hb_feature_t *features; + // Glyph metrics cache, to speed up shaping + Cache *metrics_cache; +}; + +struct ass_shaper_metrics_data { + Cache *metrics_cache; + GlyphMetricsHashKey hash_key; }; struct ass_shaper_font_data { hb_font_t *fonts[ASS_FONT_MAX_FACES]; + hb_font_funcs_t *font_funcs[ASS_FONT_MAX_FACES]; + struct ass_shaper_metrics_data *metrics_data[ASS_FONT_MAX_FACES]; }; /** @@ -102,6 +112,8 @@ ASS_Shaper *ass_shaper_new(size_t prealloc) init_features(shaper); check_allocations(shaper, prealloc); + shaper->metrics_cache = ass_glyph_metrics_cache_create(); + return shaper; } @@ -110,6 +122,7 @@ ASS_Shaper *ass_shaper_new(size_t prealloc) */ void ass_shaper_free(ASS_Shaper *shaper) { + ass_cache_done(shaper->metrics_cache); free(shaper->event_text); free(shaper->ctypes); free(shaper->emblevels); @@ -122,8 +135,11 @@ void ass_shaper_font_data_free(ASS_ShaperFontData *priv) { int i; for (i = 0; i < ASS_FONT_MAX_FACES; i++) - if (priv->fonts[i]) + if (priv->fonts[i]) { + free(priv->metrics_data[i]); hb_font_destroy(priv->fonts[i]); + hb_font_funcs_destroy(priv->font_funcs[i]); + } free(priv); } @@ -153,13 +169,174 @@ static void update_hb_size(hb_font_t *hb_font, FT_Face face) face->size->metrics.y_ppem); } + +/* + * Cached glyph metrics getters follow + * + * These functions replace HarfBuzz' standard FreeType font functions + * and provide cached access to essential glyph metrics. This usually + * speeds up shaping a lot. It also allows us to use custom load flags. + * + */ + +GlyphMetricsHashValue * +get_cached_metrics(struct ass_shaper_metrics_data *metrics, FT_Face face, + hb_codepoint_t glyph) +{ + GlyphMetricsHashValue *val; + + metrics->hash_key.glyph_index = glyph; + val = ass_cache_get(metrics->metrics_cache, &metrics->hash_key); + + if (!val) { + int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH + | FT_LOAD_IGNORE_TRANSFORM; + GlyphMetricsHashValue new_val; + + if (FT_Load_Glyph(face, glyph, load_flags)) + return NULL; + + memcpy(&new_val.metrics, &face->glyph->metrics, sizeof(FT_Glyph_Metrics)); + val = ass_cache_put(metrics->metrics_cache, &metrics->hash_key, &new_val); + } + + return val; +} + +static hb_bool_t +get_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode, + hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data) +{ + FT_Face face = font_data; + + if (variation) + *glyph = FT_Face_GetCharVariantIndex(face, unicode, variation); + else + *glyph = FT_Get_Char_Index(face, unicode); + + return *glyph != 0; +} + +static hb_position_t +cached_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, + void *user_data) +{ + FT_Face face = font_data; + struct ass_shaper_metrics_data *metrics_priv = user_data; + GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); + + if (!metrics) + return 0; + + return metrics->metrics.horiAdvance; +} + +static hb_position_t +cached_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, + void *user_data) +{ + FT_Face face = font_data; + struct ass_shaper_metrics_data *metrics_priv = user_data; + GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); + + if (!metrics) + return 0; + + return metrics->metrics.vertAdvance; + +} + +static hb_bool_t +cached_h_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y, void *user_data) +{ + return 1; +} + +static hb_bool_t +cached_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y, void *user_data) +{ + FT_Face face = font_data; + struct ass_shaper_metrics_data *metrics_priv = user_data; + GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); + + if (!metrics) + return 0; + + *x = metrics->metrics.horiBearingX - metrics->metrics.vertBearingX; + *y = metrics->metrics.horiBearingY - (-metrics->metrics.vertBearingY); + + return 1; +} + +static hb_position_t +get_h_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first, + hb_codepoint_t second, void *user_data) +{ + FT_Face face = font_data; + FT_Vector kern; + + if (FT_Get_Kerning (face, first, second, FT_KERNING_DEFAULT, &kern)) + return 0; + + return kern.x; +} + +static hb_position_t +get_v_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first, + hb_codepoint_t second, void *user_data) +{ + return 0; +} + +static hb_bool_t +cached_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph, + hb_glyph_extents_t *extents, void *user_data) +{ + FT_Face face = font_data; + struct ass_shaper_metrics_data *metrics_priv = user_data; + GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); + + if (!metrics) + return 0; + + extents->x_bearing = metrics->metrics.horiBearingX; + extents->y_bearing = metrics->metrics.horiBearingY; + extents->width = metrics->metrics.width; + extents->height = metrics->metrics.height; + + return 1; +} + +static hb_bool_t +get_contour_point(hb_font_t *font, void *font_data, hb_codepoint_t glyph, + unsigned int point_index, hb_position_t *x, + hb_position_t *y, void *user_data) +{ + FT_Face face = font_data; + int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH + | FT_LOAD_IGNORE_TRANSFORM; + + if (FT_Load_Glyph(face, glyph, load_flags)) + return 0; + + if (point_index >= (unsigned)face->glyph->outline.n_points) + return 0; + + *x = face->glyph->outline.points[point_index].x; + *y = face->glyph->outline.points[point_index].y; + + return 1; +} + /** * \brief Retrieve HarfBuzz font from cache. * Create it from FreeType font, if needed. * \param info glyph cluster * \return HarfBuzz font */ -static hb_font_t *get_hb_font(GlyphInfo *info) +static hb_font_t *get_hb_font(ASS_Shaper *shaper, GlyphInfo *info) { ASS_Font *font = info->font; hb_font_t **hb_fonts; @@ -169,13 +346,53 @@ static hb_font_t *get_hb_font(GlyphInfo *info) hb_fonts = font->shaper_priv->fonts; - if (!hb_fonts[info->face_index]) + if (!hb_fonts[info->face_index]) { hb_fonts[info->face_index] = hb_ft_font_create(font->faces[info->face_index], NULL); + // set up cached metrics access + font->shaper_priv->metrics_data[info->face_index] = + calloc(sizeof(struct ass_shaper_metrics_data), 1); + struct ass_shaper_metrics_data *metrics = + font->shaper_priv->metrics_data[info->face_index]; + metrics->metrics_cache = shaper->metrics_cache; + + hb_font_funcs_t *funcs = hb_font_funcs_create(); + font->shaper_priv->font_funcs[info->face_index] = funcs; + hb_font_funcs_set_glyph_func(funcs, get_glyph, + metrics, NULL); + hb_font_funcs_set_glyph_h_advance_func(funcs, cached_h_advance, + metrics, NULL); + hb_font_funcs_set_glyph_v_advance_func(funcs, cached_v_advance, + metrics, NULL); + hb_font_funcs_set_glyph_h_origin_func(funcs, cached_h_origin, + metrics, NULL); + hb_font_funcs_set_glyph_v_origin_func(funcs, cached_v_origin, + metrics, NULL); + hb_font_funcs_set_glyph_h_kerning_func(funcs, get_h_kerning, + metrics, NULL); + hb_font_funcs_set_glyph_v_kerning_func(funcs, get_v_kerning, + metrics, NULL); + hb_font_funcs_set_glyph_extents_func(funcs, cached_extents, + metrics, NULL); + hb_font_funcs_set_glyph_contour_point_func(funcs, get_contour_point, + metrics, NULL); + hb_font_set_funcs(hb_fonts[info->face_index], funcs, + font->faces[info->face_index], NULL); + } + ass_face_set_size(font->faces[info->face_index], info->font_size); update_hb_size(hb_fonts[info->face_index], font->faces[info->face_index]); + // update hash key for cached metrics + struct ass_shaper_metrics_data *metrics = + font->shaper_priv->metrics_data[info->face_index]; + metrics->hash_key.font = info->font; + metrics->hash_key.face_index = info->face_index; + metrics->hash_key.size = info->font_size; + metrics->hash_key.scale_x = double_to_d6(info->scale_x); + metrics->hash_key.scale_y = double_to_d6(info->scale_y); + return hb_fonts[info->face_index]; } @@ -207,7 +424,7 @@ static void shape_harfbuzz(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len) runs[run].offset = k; runs[run].end = i; runs[run].buf = hb_buffer_create(i - k + 1); - runs[run].font = get_hb_font(glyphs + k); + runs[run].font = get_hb_font(shaper, glyphs + k); set_run_features(shaper, glyphs + k); hb_buffer_set_direction(runs[run].buf, direction ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); |