summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrigori Goronzy <greg@dell>2009-07-08 14:43:58 +0200
committergreg <greg@dell>2009-07-08 14:49:20 +0200
commit15df379c97159dbbc8d876a769edac0f8efecd75 (patch)
tree8fc7230ad78e214adb4f15fe83dda0c326e52994
parent55e9f91ec4868e8cd9098b367a0d935d703290d4 (diff)
downloadlibass-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.c148
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));