diff options
author | Grigori Goronzy <greg@dell> | 2009-07-08 14:43:58 +0200 |
---|---|---|
committer | greg <greg@dell> | 2009-07-08 14:49:20 +0200 |
commit | 15df379c97159dbbc8d876a769edac0f8efecd75 (patch) | |
tree | 8fc7230ad78e214adb4f15fe83dda0c326e52994 | |
parent | 55e9f91ec4868e8cd9098b367a0d935d703290d4 (diff) | |
download | libass-15df379c97159dbbc8d876a769edac0f8efecd75.tar.bz2 libass-15df379c97159dbbc8d876a769edac0f8efecd75.tar.xz |
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.
-rw-r--r-- | libass/ass_render.c | 148 |
1 files changed, 62 insertions, 86 deletions
diff --git a/libass/ass_render.c b/libass/ass_render.c index 03173400..e2eac7b4 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)); |