summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrigori Goronzy <greg@blackbox>2009-06-27 22:06:32 +0200
committerGrigori Goronzy <greg@blackbox>2009-06-27 23:35:02 +0200
commitc6c4b66a0cbee9f2b32289d1f73122530537f00c (patch)
tree188998d86af4db78f960d34cab619bb5ce486453
parent2b4a7365b172bb0b5ee093329a7acdf6bce1cb8d (diff)
downloadlibass-c6c4b66a0cbee9f2b32289d1f73122530537f00c.tar.bz2
libass-c6c4b66a0cbee9f2b32289d1f73122530537f00c.tar.xz
Support \xbord and \ybord override tags
Improve get_outline_glyph with a second pass which can be used for stroking with different width in x and y direction. This is done by first stroking in y direction, and if the stroke size for the x direction differs, stroking a copy of the glyph in x direction again. The x coordinates of the first stroker result are afterwards replaced by the coordinates of the second stroking. The cache hash keys for bitmaps and glyphs have been extended to carry the new properties.
-rw-r--r--libass/ass_cache_template.c4
-rw-r--r--libass/ass_render.c154
2 files changed, 117 insertions, 41 deletions
diff --git a/libass/ass_cache_template.c b/libass/ass_cache_template.c
index 96b4752..6c536fb 100644
--- a/libass/ass_cache_template.c
+++ b/libass/ass_cache_template.c
@@ -59,7 +59,7 @@ START(bitmap, bipmap_hash_key_s)
GENERIC(ass_font_t *, font)
GENERIC(double, size) // font size
GENERIC(uint32_t, ch) // character code
- GENERIC(unsigned, outline) // border width, 16.16 fixed point value
+ FTVECTOR(outline) // border width, 16.16 fixed point value
GENERIC(int, bold)
GENERIC(int, italic)
GENERIC(char, be) // blur edges
@@ -89,7 +89,7 @@ START(glyph, glyph_hash_key_s)
GENERIC(unsigned, scale_x) // 16.16
GENERIC(unsigned, scale_y) // 16.16
FTVECTOR(advance) // subpixel shift vector
- GENERIC(unsigned, outline) // border width, 16.16
+ FTVECTOR(outline) // border width, 16.16
END(glyph_hash_key_t)
// Cache for composited bitmaps
diff --git a/libass/ass_render.c b/libass/ass_render.c
index 451c1d7..a486f68 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -143,7 +143,8 @@ typedef struct render_context_s {
char *font_path;
double font_size;
- FT_Stroker stroker;
+ FT_Stroker stroker_x;
+ FT_Stroker stroker_y;
int alignment; // alignment overrides go here; if zero, style value will be used
double frx, fry, frz;
double fax, fay; // text shearing
@@ -157,7 +158,8 @@ typedef struct render_context_s {
char have_origin; // origin is explicitly defined; if 0, get_base_point() is used
double scale_x, scale_y;
double hspacing; // distance between letters, in pixels
- double border; // outline width
+ double border_x; // outline width
+ double border_y;
uint32_t c[4]; // colors(Primary, Secondary, so on) in RGBA
int clip_x0, clip_y0, clip_x1, clip_y1;
char detect_collisions;
@@ -323,9 +325,13 @@ 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) {
- FT_Stroker_Done(render_priv->state.stroker);
- render_priv->state.stroker = 0;
+ 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 && render_priv->ftlibrary)
FT_Done_FreeType(render_priv->ftlibrary);
@@ -786,45 +792,79 @@ static void update_font(ass_renderer_t *render_priv)
* \brief Change border width
* negative value resets border to style value
*/
-static void change_border(ass_renderer_t *render_priv, double border)
+static void change_border(ass_renderer_t *render_priv, double border_x,
+ double border_y)
{
- int b;
+ int bx, by;
if (!render_priv->state.font)
return;
- if (border < 0) {
+ if (border_x < 0 && border_y < 0) {
if (render_priv->state.style->BorderStyle == 1)
- border = render_priv->state.style->Outline;
+ border_x = border_y = render_priv->state.style->Outline;
else
- border = 1.;
+ border_x = border_y = 1.;
+ }
+
+ 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;
}
- render_priv->state.border = border;
- b = 64 * border * render_priv->border_scale;
- if (b > 0) {
- if (!render_priv->state.stroker) {
+ // FIXME: less code duplication
+ if (by > 0) {
+ if (!render_priv->state.stroker_y) {
int error;
#if (FREETYPE_MAJOR > 2) || ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 1))
error =
FT_Stroker_New(render_priv->ftlibrary,
- &render_priv->state.stroker);
+ &render_priv->state.stroker_y);
#else // < 2.2
error =
FT_Stroker_New(render_priv->state.font->faces[0]->
- memory, &render_priv->state.stroker);
+ memory, &render_priv->state.stroker_y);
#endif
if (error) {
ass_msg(MSGL_V, "failed to get stroker\n");
- render_priv->state.stroker = 0;
+ render_priv->state.stroker_y = 0;
}
}
- if (render_priv->state.stroker)
- FT_Stroker_Set(render_priv->state.stroker, b,
+ if (render_priv->state.stroker_y)
+ FT_Stroker_Set(render_priv->state.stroker_y, by,
FT_STROKER_LINECAP_ROUND,
FT_STROKER_LINEJOIN_ROUND, 0);
} else {
- FT_Stroker_Done(render_priv->state.stroker);
- render_priv->state.stroker = 0;
+ FT_Stroker_Done(render_priv->state.stroker_y);
+ render_priv->state.stroker_y = 0;
}
}
@@ -913,11 +953,17 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
if (mystrcmp(&p, "xbord")) {
double val;
if (mystrtod(&p, &val))
- ass_msg(MSGL_V, "stub: \\xbord%.2f\n", val);
+ val = render_priv->state.border_x * (1 - pwr) + val * pwr;
+ else
+ val = -1.;
+ change_border(render_priv, val, render_priv->state.border_y);
} else if (mystrcmp(&p, "ybord")) {
double val;
if (mystrtod(&p, &val))
- ass_msg(MSGL_V, "stub: \\ybord%.2f\n", val);
+ val = render_priv->state.border_y * (1 - pwr) + val * pwr;
+ else
+ val = -1.;
+ change_border(render_priv, render_priv->state.border_x, val);
} else if (mystrcmp(&p, "xshad")) {
int val;
if (mystrtoi(&p, &val))
@@ -1001,11 +1047,12 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
change_font_size(render_priv, val);
} else if (mystrcmp(&p, "bord")) {
double val;
- if (mystrtod(&p, &val))
- val = render_priv->state.border * (1 - pwr) + val * pwr;
- else
+ if (mystrtod(&p, &val)) {
+ if (render_priv->state.border_x == render_priv->state.border_y)
+ val = render_priv->state.border_x * (1 - pwr) + val * pwr;
+ } else
val = -1.; // reset to default
- change_border(render_priv, val);
+ change_border(render_priv, val, val);
} else if (mystrcmp(&p, "move")) {
double x1, x2, y1, y2;
long long t1, t2, delta_t, t;
@@ -1540,7 +1587,7 @@ static void reset_render_context(ass_renderer_t *render_priv)
render_priv->state.italic = render_priv->state.style->Italic;
update_font(render_priv);
- change_border(render_priv, -1.);
+ change_border(render_priv, -1., -1.);
render_priv->state.scale_x = render_priv->state.style->ScaleX;
render_priv->state.scale_y = render_priv->state.style->ScaleY;
render_priv->state.hspacing = render_priv->state.style->Spacing;
@@ -1610,7 +1657,8 @@ get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end)
* \brief Fix-up stroker result for huge borders by removing the contours from
* the outline that are harmful.
*/
-static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border)
+static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x,
+ int border_y)
{
int nc = glyph->outline.n_contours;
int begin, stop;
@@ -1643,7 +1691,7 @@ static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border)
boxes[i].yMax <= boxes[j].yMax) {
int width = boxes[i].xMax - boxes[i].xMin;
int height = boxes[i].yMax - boxes[i].yMin;
- if (width < border * 2 || height < border * 2) {
+ if (width < border_x * 2 || height < border_y * 2) {
valid_cont[i] = 0;
modified = 1;
break;
@@ -1697,7 +1745,8 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol,
key.advance = *advance;
key.bold = render_priv->state.bold;
key.italic = render_priv->state.italic;
- key.outline = render_priv->state.border * 0xFFFF;
+ key.outline.x = render_priv->state.border_x * 0xFFFF;
+ key.outline.y = render_priv->state.border_y * 0xFFFF;
memset(info, 0, sizeof(glyph_info_t));
@@ -1721,18 +1770,43 @@ 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) {
+ if (render_priv->state.stroker_x && render_priv->state.stroker_y) {
FT_Glyph_Copy(info->glyph, &info->outline_glyph);
+
fix_freetype_stroker((FT_OutlineGlyph) info->outline_glyph,
- double_to_d6(render_priv->state.
- border *
+ 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, 0, 1);
- if (error) {
+ render_priv->state.stroker_y, 0, 1);
+
+ if (error)
ass_msg(MSGL_WARN,
- MSGTR_LIBASS_FT_Glyph_Stroke_Error, error);
+ 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;
+ 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;
+ for (i = 0; i < go->outline.n_points; i++)
+ go->outline.points[i].x = gi->outline.points[i].x;
+ FT_Done_Glyph(g);
}
}
@@ -2323,8 +2397,10 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
render_priv->state.font;
text_info->glyphs[text_info->length].hash_key.size =
render_priv->state.font_size;
- text_info->glyphs[text_info->length].hash_key.outline =
- render_priv->state.border * 0xFFFF;
+ text_info->glyphs[text_info->length].hash_key.outline.x =
+ render_priv->state.border_x * 0xFFFF;
+ text_info->glyphs[text_info->length].hash_key.outline.y =
+ render_priv->state.border_y * 0xFFFF;
text_info->glyphs[text_info->length].hash_key.scale_x =
render_priv->state.scale_x * 0xFFFF;
text_info->glyphs[text_info->length].hash_key.scale_y =