diff options
author | Grigori Goronzy <greg@blackbox> | 2009-06-27 22:06:32 +0200 |
---|---|---|
committer | Grigori Goronzy <greg@blackbox> | 2009-06-27 23:35:02 +0200 |
commit | c6c4b66a0cbee9f2b32289d1f73122530537f00c (patch) | |
tree | 188998d86af4db78f960d34cab619bb5ce486453 | |
parent | 2b4a7365b172bb0b5ee093329a7acdf6bce1cb8d (diff) | |
download | libass-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.c | 4 | ||||
-rw-r--r-- | libass/ass_render.c | 154 |
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 = |