From 0d391f67d3d1c87cd8e8cff907bd7c6b3b1f9cd8 Mon Sep 17 00:00:00 2001 From: Grigori Goronzy Date: Mon, 8 Aug 2011 00:22:32 +0200 Subject: Runtime shaper selection Add an API call, ass_set_shaper, and infrastructure to make shapers selectable at runtime. Currently, this allows to switch between two shapers: a SIMPLE shaper that maps to FriBidi and a COMPLEX shaper that maps to HarfBuzz. --- libass/ass.h | 20 +++++++++++++++++++ libass/ass_render.c | 52 ++++++++++++++++++++++++------------------------- libass/ass_render.h | 1 + libass/ass_render_api.c | 9 +++++++++ libass/ass_shaper.c | 34 +++++++++++++++++++++++++++----- libass/ass_shaper.h | 1 + libass/libass.sym | 1 + 7 files changed, 86 insertions(+), 32 deletions(-) diff --git a/libass/ass.h b/libass/ass.h index 57faf7d..4986437 100644 --- a/libass/ass.h +++ b/libass/ass.h @@ -60,6 +60,19 @@ typedef enum { ASS_HINTING_NATIVE } ASS_Hinting; +/** + * \brief Text shaping levels. + * + * SIMPLE is a fast, font-agnostic shaper that can do only substitutions. + * COMPLEX is a slower shaper using OpenType for substitutions and positioning. + * + * libass uses the best shaper available by default. + */ +typedef enum { + ASS_SHAPING_SIMPLE = 0, + ASS_SHAPING_COMPLEX +} ASS_ShapingLevel; + /** * \brief Initialize the library. * \return library handle or NULL if failed @@ -146,6 +159,13 @@ void ass_renderer_done(ASS_Renderer *priv); */ void ass_set_frame_size(ASS_Renderer *priv, int w, int h); +/** + * \brief Set shaping level. This is merely a hint, the renderer will use + * whatever is available if the request cannot be fulfilled. + * \param level shaping level + */ +void ass_set_shaper(ASS_Renderer *priv, ASS_ShapingLevel level); + /** * \brief Set frame margins. These values may be negative if pan-and-scan * is used. diff --git a/libass/ass_render.c b/libass/ass_render.c index 719b8bd..85d30b4 100644 --- a/libass/ass_render.c +++ b/libass/ass_render.c @@ -77,6 +77,7 @@ ASS_Renderer *ass_renderer_init(ASS_Library *library) priv->shaper = ass_shaper_new(0); ass_shaper_info(library); + priv->settings.shaper = ASS_SHAPING_COMPLEX; ass_init_exit: if (priv) @@ -1104,21 +1105,21 @@ fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key, * The glyphs are returned in info->glyph and info->outline_glyph */ static void -get_outline_glyph(ASS_Renderer *render_priv, GlyphInfo *info) +get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) { OutlineHashValue *val; OutlineHashKey key; memset(&info->hash_key, 0, sizeof(key)); - fill_glyph_hash(render_priv, &key, info); - val = ass_cache_get(render_priv->cache.outline_cache, &key); + fill_glyph_hash(priv, &key, info); + val = ass_cache_get(priv->cache.outline_cache, &key); if (val) { info->hash_key.u.outline.outline = val; info->outline = val->outline; info->border = val->border; info->bbox = val->bbox_scaled; - if (info->drawing) { + if (info->drawing || priv->settings.shaper == ASS_SHAPING_SIMPLE) { info->cluster_advance.x = info->advance.x = val->advance.x; info->cluster_advance.y = info->advance.y = val->advance.y; } @@ -1131,7 +1132,7 @@ get_outline_glyph(ASS_Renderer *render_priv, GlyphInfo *info) ass_drawing_hash(drawing); if(!ass_drawing_parse(drawing, 0)) return; - outline_copy(render_priv->ftlibrary, &drawing->outline, + outline_copy(priv->ftlibrary, &drawing->outline, &info->outline); info->cluster_advance.x = info->advance.x = drawing->advance.x; info->cluster_advance.y = info->advance.y = drawing->advance.y; @@ -1143,19 +1144,17 @@ get_outline_glyph(ASS_Renderer *render_priv, GlyphInfo *info) info->font_size); ass_font_set_transform(info->font, info->scale_x, info->scale_y, NULL); - // symbol might have been changed. re-get it. - //if (info->face_index < 0) - // ass_font_get_index(render_priv->fontconfig_priv, info->font, - // info->symbol, &info->face_index, &info->glyph_index); FT_Glyph glyph = - ass_font_get_glyph(render_priv->fontconfig_priv, info->font, + ass_font_get_glyph(priv->fontconfig_priv, info->font, info->symbol, info->face_index, info->glyph_index, - render_priv->settings.hinting, info->flags); + priv->settings.hinting, info->flags); if (glyph != NULL) { - outline_copy(render_priv->ftlibrary, + outline_copy(priv->ftlibrary, &((FT_OutlineGlyph)glyph)->outline, &info->outline); - //info->advance.x = d16_to_d6(glyph->advance.x); - //info->advance.y = d16_to_d6(glyph->advance.y); + if (priv->settings.shaper == ASS_SHAPING_SIMPLE) { + info->cluster_advance.x = d16_to_d6(glyph->advance.x); + info->cluster_advance.y = d16_to_d6(glyph->advance.y); + } FT_Done_Glyph(glyph); ass_font_get_asc_desc(info->font, info->symbol, &info->asc, &info->desc); @@ -1168,26 +1167,24 @@ get_outline_glyph(ASS_Renderer *render_priv, GlyphInfo *info) FT_Outline_Get_CBox(info->outline, &info->bbox); - if (render_priv->state.style->BorderStyle == 3 && + if (priv->state.style->BorderStyle == 3 && (info->border_x > 0|| info->border_y > 0)) { - outline_copy(render_priv->ftlibrary, info->outline, &info->border); - draw_opaque_box(render_priv, info->symbol, info->border, + outline_copy(priv->ftlibrary, info->outline, &info->border); + draw_opaque_box(priv, info->symbol, info->border, info->advance, - double_to_d6(info->border_x * - render_priv->border_scale), - double_to_d6(info->border_y * - render_priv->border_scale)); + double_to_d6(info->border_x * priv->border_scale), + double_to_d6(info->border_y * priv->border_scale)); } else if ((info->border_x > 0 || info->border_y > 0) && double_to_d6(info->scale_x) && double_to_d6(info->scale_y)) { - outline_copy(render_priv->ftlibrary, info->outline, &info->border); - stroke_outline(render_priv, info->border, - double_to_d6(info->border_x * render_priv->border_scale), - double_to_d6(info->border_y * render_priv->border_scale)); + outline_copy(priv->ftlibrary, info->outline, &info->border); + stroke_outline(priv, info->border, + double_to_d6(info->border_x * priv->border_scale), + double_to_d6(info->border_y * priv->border_scale)); } memset(&v, 0, sizeof(v)); - v.lib = render_priv->ftlibrary; + v.lib = priv->ftlibrary; v.outline = info->outline; v.border = info->border; v.advance = info->cluster_advance; @@ -1195,7 +1192,7 @@ get_outline_glyph(ASS_Renderer *render_priv, GlyphInfo *info) v.asc = info->asc; v.desc = info->desc; info->hash_key.u.outline.outline = - ass_cache_put(render_priv->cache.outline_cache, &key, &v); + ass_cache_put(priv->cache.outline_cache, &key, &v); } } @@ -2269,6 +2266,7 @@ ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track, ass_shaper_set_kerning(render_priv->shaper, track->Kerning); if (track->Language) ass_shaper_set_language(render_priv->shaper, track->Language); + ass_shaper_set_level(render_priv->shaper, render_priv->settings.shaper); // PAR correction render_priv->font_scale_x = render_priv->settings.aspect / diff --git a/libass/ass_render.h b/libass/ass_render.h index 05c4974..d3c1bbb 100644 --- a/libass/ass_render.h +++ b/libass/ass_render.h @@ -76,6 +76,7 @@ typedef struct { double aspect; // frame aspect ratio, d_width / d_height. double storage_aspect; // pixel ratio of the source image ASS_Hinting hinting; + ASS_ShapingLevel shaper; char *default_font; char *default_family; diff --git a/libass/ass_render_api.c b/libass/ass_render_api.c index 5b02b46..1d8cba6 100644 --- a/libass/ass_render_api.c +++ b/libass/ass_render_api.c @@ -58,6 +58,15 @@ void ass_set_frame_size(ASS_Renderer *priv, int w, int h) } } +void ass_set_shaper(ASS_Renderer *priv, ASS_ShapingLevel level) +{ + // select the complex shaper for illegal values + if (level == ASS_SHAPING_SIMPLE || level == ASS_SHAPING_COMPLEX) + priv->settings.shaper = level; + else + priv->settings.shaper = ASS_SHAPING_COMPLEX; +} + void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r) { if (priv->settings.left_margin != l || priv->settings.right_margin != r || diff --git a/libass/ass_shaper.c b/libass/ass_shaper.c index ce6389d..f6724d3 100644 --- a/libass/ass_shaper.c +++ b/libass/ass_shaper.c @@ -35,6 +35,7 @@ enum { #define NUM_FEATURES 3 struct ass_shaper { + ASS_ShapingLevel shaping_level; // FriBidi log2vis int n_glyphs; FriBidiChar *event_text; @@ -487,15 +488,25 @@ static void shape_harfbuzz(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len) * Arabic shaping. * \param len number of clusters */ -static void shape_fribidi(ASS_Shaper *shaper, size_t len) +static void shape_fribidi(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len) { + int i; FriBidiJoiningType *joins = calloc(sizeof(*joins), len); + // shape on codepoint level fribidi_get_joining_types(shaper->event_text, len, joins); fribidi_join_arabic(shaper->ctypes, len, shaper->emblevels, joins); fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC, shaper->emblevels, len, joins, shaper->event_text); + // update indexes + for (i = 0; i < len; i++) { + GlyphInfo *info = glyphs + i; + FT_Face face = info->font->faces[info->face_index]; + info->symbol = shaper->event_text[i]; + info->glyph_index = FT_Get_Char_Index(face, shaper->event_text[i]); + } + free(joins); } @@ -556,6 +567,14 @@ void ass_shaper_set_language(ASS_Shaper *shaper, const char *code) shaper->language = hb_language_from_string(code); } +/** + * Set shaping level. Essentially switches between FriBidi and HarfBuzz. + */ +void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level) +{ + shaper->shaping_level = level; +} + /** * \brief Shape an event's text. Calculates directional runs and shapes them. * \param text_info event's text @@ -588,12 +607,17 @@ void ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info) glyphs[i].shape_run_id += shaper->emblevels[i]; } - //shape_fribidi(shaper, text_info->length); - shape_harfbuzz(shaper, glyphs, text_info->length); + switch (shaper->shaping_level) { + case ASS_SHAPING_SIMPLE: + shape_fribidi(shaper, glyphs, text_info->length); + break; + case ASS_SHAPING_COMPLEX: + shape_harfbuzz(shaper, glyphs, text_info->length); + break; + } - // Update glyphs + // clean up for (i = 0; i < text_info->length; i++) { - glyphs[i].symbol = shaper->event_text[i]; // Skip direction override control characters // NOTE: Behdad said HarfBuzz is supposed to remove these, but this hasn't // been implemented yet diff --git a/libass/ass_shaper.h b/libass/ass_shaper.h index 7e8bcc6..81499dd 100644 --- a/libass/ass_shaper.h +++ b/libass/ass_shaper.h @@ -32,6 +32,7 @@ void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv, GlyphInfo *glyphs, size_t len); void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir); void ass_shaper_set_language(ASS_Shaper *shaper, const char *code); +void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level); void ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info); void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info); FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info); diff --git a/libass/libass.sym b/libass/libass.sym index 0ea8792..f8fa145 100644 --- a/libass/libass.sym +++ b/libass/libass.sym @@ -34,3 +34,4 @@ ass_set_message_cb ass_fonts_update ass_set_cache_limits ass_flush_events +ass_set_shaper -- cgit v1.2.3