]> granicus.if.org Git - libass/commitdiff
Support \xbord and \ybord override tags
authorGrigori Goronzy <greg@blackbox>
Sat, 27 Jun 2009 20:06:32 +0000 (22:06 +0200)
committerGrigori Goronzy <greg@blackbox>
Sat, 27 Jun 2009 21:35:02 +0000 (23:35 +0200)
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.

libass/ass_cache_template.c
libass/ass_render.c

index 96b47521a6c95ff773a23a3738d94c4f0617020a..6c536fbb6f058255850c5a718a577693b01cb02b 100644 (file)
@@ -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
index 451c1d718c65166c0dd43dbb58e2e9eaa846002d..a486f68d80129c2d0fd84fb81c8c23e11f2df7e6 100644 (file)
@@ -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 =