From 15df379c97159dbbc8d876a769edac0f8efecd75 Mon Sep 17 00:00:00 2001 From: Grigori Goronzy Date: Wed, 8 Jul 2009 14:43:58 +0200 Subject: [PATCH] Use better method for stroking with \xbord, \ybord Instead of stroking in two passes using the regular stroker use the outline emboldener in case different x and y stroke widths are requested. The regular outline stroker draws circular arcs, adding points to the outline. The number of points can vary according to the width of the stroker. This makes it impossible to reliably stroke in two passes with it. The outline emboldener does not produce outlines looking as nice as the stroker outlines, but it never adds points to the outline, making it suitable for two-pass emboldening with different x and y width. --- libass/ass_render.c | 148 +++++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 86 deletions(-) diff --git a/libass/ass_render.c b/libass/ass_render.c index 0317340..e2eac7b 100644 --- a/libass/ass_render.c +++ b/libass/ass_render.c @@ -147,8 +147,7 @@ typedef struct render_context_s { double font_size; int flags; // decoration flags (underline/strike-through) - FT_Stroker stroker_x; - FT_Stroker stroker_y; + FT_Stroker stroker; int alignment; // alignment overrides go here; if zero, style value will be used double frx, fry, frz; double fax, fay; // text shearing @@ -332,13 +331,9 @@ void ass_renderer_done(ass_renderer_t *render_priv) ass_bitmap_cache_done(render_priv->cache.bitmap_cache); ass_composite_cache_done(render_priv->cache.composite_cache); ass_glyph_cache_done(render_priv->cache.glyph_cache); - if (render_priv->state.stroker_x) { - FT_Stroker_Done(render_priv->state.stroker_x); - render_priv->state.stroker_x = 0; - } - if (render_priv->state.stroker_y) { - FT_Stroker_Done(render_priv->state.stroker_y); - render_priv->state.stroker_y = 0; + if (render_priv->state.stroker) { + FT_Stroker_Done(render_priv->state.stroker); + render_priv->state.stroker = 0; } if (render_priv && render_priv->ftlibrary) FT_Done_FreeType(render_priv->ftlibrary); @@ -916,7 +911,7 @@ static void update_font(ass_renderer_t *render_priv) static void change_border(ass_renderer_t *render_priv, double border_x, double border_y) { - int bx, by; + int bord; if (!render_priv->state.font) return; @@ -930,62 +925,31 @@ static void change_border(ass_renderer_t *render_priv, double border_x, render_priv->state.border_x = border_x; render_priv->state.border_y = border_y; - bx = 64 * border_x * render_priv->border_scale; - by = 64 * border_y * render_priv->border_scale; - // Set border to some miniscule size in case only one direction is set - if (bx > 0 && by <= 0) by = 1; - if (by > 0 && bx <= 0) bx = 1; - if (bx > 0) { - if (!render_priv->state.stroker_x) { - int error; -#if (FREETYPE_MAJOR > 2) || ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 1)) - error = - FT_Stroker_New(render_priv->ftlibrary, - &render_priv->state.stroker_x); -#else // < 2.2 - error = - FT_Stroker_New(render_priv->state.font->faces[0]-> - memory, &render_priv->state.stroker_x); -#endif - if (error) { - ass_msg(MSGL_V, "failed to get stroker\n"); - render_priv->state.stroker_x = 0; - } - } - if (render_priv->state.stroker_x) - FT_Stroker_Set(render_priv->state.stroker_x, bx, - FT_STROKER_LINECAP_ROUND, - FT_STROKER_LINEJOIN_ROUND, 0); - } else { - FT_Stroker_Done(render_priv->state.stroker_x); - render_priv->state.stroker_x = 0; - } - - // FIXME: less code duplication - if (by > 0) { - if (!render_priv->state.stroker_y) { + bord = 64 * border_x * render_priv->border_scale; + if (bord > 0 && border_x == border_y) { + if (!render_priv->state.stroker) { int error; #if (FREETYPE_MAJOR > 2) || ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 1)) error = FT_Stroker_New(render_priv->ftlibrary, - &render_priv->state.stroker_y); + &render_priv->state.stroker); #else // < 2.2 error = FT_Stroker_New(render_priv->state.font->faces[0]-> - memory, &render_priv->state.stroker_y); + memory, &render_priv->state.stroker); #endif if (error) { ass_msg(MSGL_V, "failed to get stroker\n"); - render_priv->state.stroker_y = 0; + render_priv->state.stroker = 0; } } - if (render_priv->state.stroker_y) - FT_Stroker_Set(render_priv->state.stroker_y, by, + if (render_priv->state.stroker) + FT_Stroker_Set(render_priv->state.stroker, bord, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); } else { - FT_Stroker_Done(render_priv->state.stroker_y); - render_priv->state.stroker_y = 0; + FT_Stroker_Done(render_priv->state.stroker); + render_priv->state.stroker = 0; } } @@ -1883,6 +1847,48 @@ static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, free(valid_cont); } +/* + * Stroke an outline glyph in x/y direction. Applies various fixups to get + * around limitations of the FreeType stroker. + */ +static void stroke_outline_glyph(ass_renderer_t *render_priv, + FT_OutlineGlyph *glyph, int sx, int sy) +{ + if (sx <= 0 || sy <= 0) + return; + + fix_freetype_stroker(*glyph, sx, sy); + + // Borders are equal; use the regular stroker + if (sx == sy && render_priv->state.stroker) { + int error; + error = FT_Glyph_StrokeBorder((FT_Glyph *) glyph, + render_priv->state.stroker, 0, 1); + if (error) + ass_msg(MSGL_WARN, MSGTR_LIBASS_FT_Glyph_Stroke_Error, error); + + // "Stroke" with the outline emboldener in two passes. + // The outlines look uglier, but the emboldening never adds any points + } else { + int i; + FT_Outline *ol = &(*glyph)->outline; + FT_Outline nol; + FT_Outline_New(render_priv->ftlibrary, ol->n_points, + ol->n_contours, &nol); + FT_Outline_Copy(ol, &nol); + + FT_Outline_Embolden(ol, sx * 2); + FT_Outline_Translate(ol, -sx, -sx); + FT_Outline_Embolden(&nol, sy * 2); + FT_Outline_Translate(&nol, -sy, -sy); + + for (i = 0; i < ol->n_points; i++) + ol->points[i].y = nol.points[i].y; + + FT_Outline_Done(render_priv->ftlibrary, &nol); + } +} + /** * \brief Get normal and outline (border) glyphs * \param symbol ucs4 char @@ -1898,7 +1904,6 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol, glyph_info_t *info, FT_Vector *advance, ass_drawing_t *drawing) { - int error; glyph_hash_val_t *val; glyph_hash_key_t key; memset(&key, 0, sizeof(key)); @@ -1955,45 +1960,16 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol, info->advance.y = d16_to_d6(info->glyph->advance.y); FT_Glyph_Get_CBox(info->glyph, FT_GLYPH_BBOX_SUBPIXELS, &info->bbox); - if (render_priv->state.stroker_x && render_priv->state.stroker_y) { - FT_Glyph_Copy(info->glyph, &info->outline_glyph); + if (render_priv->state.border_x > 0 || + render_priv->state.border_y > 0) { - fix_freetype_stroker((FT_OutlineGlyph) info->outline_glyph, + FT_Glyph_Copy(info->glyph, &info->outline_glyph); + stroke_outline_glyph(render_priv, + (FT_OutlineGlyph *) &info->outline_glyph, double_to_d6(render_priv->state.border_x * render_priv->border_scale), double_to_d6(render_priv->state.border_y * render_priv->border_scale)); - - error = - FT_Glyph_StrokeBorder(&(info->outline_glyph), - render_priv->state.stroker_y, 0, 1); - - if (error) - ass_msg(MSGL_WARN, - MSGTR_LIBASS_FT_Glyph_Stroke_Error, error); - - // 2nd pass if x/y borders are different - if (render_priv->state.border_x != render_priv->state.border_y) { - int i, m; - FT_Glyph g; - FT_OutlineGlyph go, gi; - - FT_Glyph_Copy(info->glyph, &g); - error = FT_Glyph_StrokeBorder(&g, - render_priv->state.stroker_x, - 0, 1); - if (error) - ass_msg(MSGL_WARN, - MSGTR_LIBASS_FT_Glyph_Stroke_Error, error); - - // Replace x coordinates - go = (FT_OutlineGlyph) info->outline_glyph; - gi = (FT_OutlineGlyph) g; - m = FFMIN(go->outline.n_points, gi->outline.n_points); - for (i = 0; i < m; i++) - go->outline.points[i].x = gi->outline.points[i].x; - FT_Done_Glyph(g); - } } memset(&v, 0, sizeof(v)); -- 2.40.0