]> granicus.if.org Git - libass/commitdiff
Support for underline and strikethrough
authorGrigori Goronzy <greg@hein>
Tue, 7 Jul 2009 18:25:55 +0000 (20:25 +0200)
committerGrigori Goronzy <greg@blackbox>
Tue, 7 Jul 2009 21:07:50 +0000 (23:07 +0200)
Add support for the underline (\u) and strikethrough/strikeout (\s)
properties.  This is a bit tricky, since FreeType doesn't offer
any method of adding the lines, so you have to draw them yourself.
libass uses various information from TrueType tables to get position
and size of the lines, does a few simple consistency checks (some fonts
might be broken) and if everything is alright, adds new contours for
the lines.

Sometimes, rendering errors can occur:
- Currently, kerning isn't taken into account, which means the lines
can overlap a little, leading to small optical glitches.
- Some (broken) fonts use the wrong winding direction.  In this case,
the FreeType stroker will only consider the added lines to be "outside"
and only stroke the line instead of the whole glyph.

libass/ass_cache_template.c
libass/ass_drawing.c
libass/ass_font.c
libass/ass_font.h
libass/ass_render.c

index bccb6db726144fdbe0f7330f4edfb9533a0c941a..eae980747bbfdc6abcb9fb7aa4b0d4991f9e50a8 100644 (file)
@@ -93,6 +93,7 @@ START(glyph, glyph_hash_key_s)
     FTVECTOR(advance) // subpixel shift vector
     FTVECTOR(outline) // border width, 16.16
     GENERIC(unsigned, drawing_hash) // hashcode of a drawing
+    GENERIC(unsigned, flags)    // glyph decoration flags
 END(glyph_hash_key_t)
 
 // Cache for composited bitmaps
index 30d742fc903aa8303092e1c99922663e5b908087..09cf2d004930f5855a554006ad8e6ffe06ad15ab 100644 (file)
@@ -40,7 +40,7 @@ static void drawing_make_glyph(ass_drawing_t *drawing, void *fontconfig_priv,
 
     // This is hacky...
     glyph = (FT_OutlineGlyph) ass_font_get_glyph(fontconfig_priv, font,
-                                                 (uint32_t) ' ', hint);
+                                                 (uint32_t) ' ', hint, 0);
 
     FT_Outline_Done(drawing->ftlibrary, &glyph->outline);
     FT_Outline_New(drawing->ftlibrary, GLYPH_INITIAL_POINTS,
index 52e6d3a91ce9cb1b2eb76bae25360a460bd390ec..0aeeec6c15e8215ecefb316c5b81de1757ac5300 100644 (file)
@@ -276,12 +276,93 @@ void ass_font_get_asc_desc(ass_font_t *font, uint32_t ch, int *asc,
     *asc = *desc = 0;
 }
 
+/*
+ * Strike a glyph with a horizontal line; it's possible to underline it
+ * and/or strike through it.  For the line's position and size, truetype
+ * tables are consulted.  Obviously this relies on the data in the tables
+ * being accurate.
+ *
+ */
+static int ass_strike_outline_glyph(FT_Face face, ass_font_t *font,
+                                    FT_Glyph glyph, int under, int through)
+{
+    TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+    TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post);
+    FT_Outline *ol = &((FT_OutlineGlyph) glyph)->outline;
+    int bear, advance, y_scale, i;
+
+    // Grow outline
+    i = (under ? 4 : 0) + (through ? 4 : 0);
+    ol->points = realloc(ol->points, sizeof(FT_Vector) *
+                         (ol->n_points + i));
+    ol->tags = realloc(ol->tags, ol->n_points + i);
+    i = !!under + !!through;
+    ol->contours = realloc(ol->contours, sizeof(short) *
+                           (ol->n_contours + i));
+
+    // If the bearing is negative, the glyph starts left of the current
+    // pen position
+    bear = FFMIN(face->glyph->metrics.horiBearingX, 0);
+    // We're adding half a pixel to avoid small gaps
+    advance = d16_to_d6(glyph->advance.x) + 32;
+    y_scale = face->size->metrics.y_scale;
+
+    // Add points to the outline
+    if (under) {
+        int pos, size;
+        pos = FT_MulFix(ps->underlinePosition, y_scale * font->scale_y);
+        size = FT_MulFix(ps->underlineThickness,
+                         y_scale * font->scale_y / 2);
+
+        if (pos > 0 || size <= 0)
+            return 0;
+
+        FT_Vector points[4] = {
+            {.x = bear,      .y = pos + size},
+            {.x = advance,   .y = pos + size},
+            {.x = advance,   .y = pos - size},
+            {.x = bear,      .y = pos - size},
+        };
+
+        for (i = 0; i < 4; i++) {
+            ol->points[ol->n_points] = points[i];
+            ol->tags[ol->n_points++] = 1;
+        }
+        ol->contours[ol->n_contours++] = ol->n_points - 1;
+    }
+
+    if (through) {
+        int pos, size;
+        pos = FT_MulFix(os2->yStrikeoutPosition, y_scale * font->scale_y);
+        size = FT_MulFix(os2->yStrikeoutSize, y_scale * font->scale_y / 2);
+
+        if (pos < 0 || size <= 0)
+            return 0;
+
+        FT_Vector points[4] = {
+            {.x = bear,      .y = pos + size},
+            {.x = advance,   .y = pos + size},
+            {.x = advance,   .y = pos - size},
+            {.x = bear,      .y = pos - size},
+        };
+
+        for (i = 0; i < 4; i++) {
+            ol->points[ol->n_points] = points[i];
+            ol->tags[ol->n_points++] = 1;
+        }
+
+        ol->contours[ol->n_contours++] = ol->n_points - 1;
+    }
+
+    return 1;
+}
+
 /**
  * \brief Get a glyph
  * \param ch character code
  **/
 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ass_font_t *font,
-                            uint32_t ch, ass_hinting_t hinting)
+                            uint32_t ch, ass_hinting_t hinting, int deco)
 {
     int error;
     int index = 0;
@@ -356,6 +437,9 @@ FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ass_font_t *font,
         return 0;
     }
 
+    ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE,
+                             deco & DECO_STRIKETHROUGH);
+
     return glyph;
 }
 
index b208064297c07b5dd2b61dfbca3cb580839f0948..8fe4c1e650bd6b18bffa4880653b6a4f41b5a5d3 100644 (file)
 #include "ass.h"
 #include "ass_types.h"
 
+#define ASS_FONT_MAX_FACES 10
+#define DECO_UNDERLINE 1
+#define DECO_STRIKETHROUGH 2
+
 typedef struct ass_font_desc_s {
     char *family;
     unsigned bold;
@@ -34,8 +38,6 @@ typedef struct ass_font_desc_s {
     int treat_family_as_pattern;
 } ass_font_desc_t;
 
-#define ASS_FONT_MAX_FACES 10
-
 typedef struct ass_font_s {
     ass_font_desc_t desc;
     ass_library_t *library;
@@ -57,7 +59,7 @@ void ass_font_set_size(ass_font_t *font, double size);
 void ass_font_get_asc_desc(ass_font_t *font, uint32_t ch, int *asc,
                            int *desc);
 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ass_font_t *font,
-                            uint32_t ch, ass_hinting_t hinting);
+                            uint32_t ch, ass_hinting_t hinting, int flags);
 FT_Vector ass_font_get_kerning(ass_font_t *font, uint32_t c1, uint32_t c2);
 void ass_font_free(ass_font_t *font);
 
index c112399b14185463e5b38f368d60170724717d01..19870a55f6dd80f9084ddf287b67e34e1df2237b 100644 (file)
@@ -144,6 +144,7 @@ typedef struct render_context_s {
     ass_font_t *font;
     char *font_path;
     double font_size;
+    int flags;                  // decoration flags (underline/strike-through)
 
     FT_Stroker stroker_x;
     FT_Stroker stroker_y;
@@ -1561,6 +1562,18 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
         } else
             val = 0.;
         render_priv->state.shadow_x = render_priv->state.shadow_y = val;
+    } else if (mystrcmp(&p, "s")) {
+        int val;
+        if (mystrtoi(&p, &val) && val)
+            render_priv->state.flags |= DECO_STRIKETHROUGH;
+        else
+            render_priv->state.flags &= ~DECO_STRIKETHROUGH;
+    } else if (mystrcmp(&p, "u")) {
+        int val;
+        if (mystrtoi(&p, &val) && val)
+            render_priv->state.flags |= DECO_UNDERLINE;
+        else
+            render_priv->state.flags &= ~DECO_UNDERLINE;
     } else if (mystrcmp(&p, "pbo")) {
         double val = 0;
         if (mystrtod(&p, &val))
@@ -1716,6 +1729,9 @@ static void reset_render_context(ass_renderer_t *render_priv)
     render_priv->state.c[1] = render_priv->state.style->SecondaryColour;
     render_priv->state.c[2] = render_priv->state.style->OutlineColour;
     render_priv->state.c[3] = render_priv->state.style->BackColour;
+    render_priv->state.flags =
+        (render_priv->state.style->Underline ? DECO_UNDERLINE : 0) |
+        (render_priv->state.style->StrikeOut ? DECO_STRIKETHROUGH : 0);
     render_priv->state.font_size = render_priv->state.style->FontSize;
 
     if (render_priv->state.family)
@@ -1904,6 +1920,7 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol,
         key.advance = *advance;
         key.outline.x = render_priv->state.border_x * 0xFFFF;
         key.outline.y = render_priv->state.border_y * 0xFFFF;
+        key.flags = render_priv->state.flags;
     }
     memset(info, 0, sizeof(glyph_info_t));
 
@@ -1928,7 +1945,8 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol,
             info->glyph =
                 ass_font_get_glyph(render_priv->fontconfig_priv,
                                    render_priv->state.font, symbol,
-                                   render_priv->settings.hinting);
+                                   render_priv->settings.hinting,
+                                   render_priv->state.flags);
         }
         if (!info->glyph)
             return;