]> granicus.if.org Git - libass/commitdiff
renderer: switch to using two border outlines instead of one
authorDr.Smile <vabnick@gmail.com>
Mon, 31 Jul 2017 03:27:09 +0000 (06:27 +0300)
committerDr.Smile <vabnick@gmail.com>
Mon, 31 Jul 2017 03:27:09 +0000 (06:27 +0300)
libass/ass_bitmap.c
libass/ass_bitmap.h
libass/ass_cache.c
libass/ass_cache.h
libass/ass_outline.c
libass/ass_outline.h
libass/ass_render.c
libass/ass_render.h

index 80e041b5e49c029e9b383b53c18be15de64e5608..f369e25bccc87a940617da521d2a6350a91cf97a 100644 (file)
@@ -187,10 +187,15 @@ Bitmap *copy_bitmap(const BitmapEngine *engine, const Bitmap *src)
 }
 
 Bitmap *outline_to_bitmap(ASS_Renderer *render_priv,
-                          ASS_Outline *outline, int bord)
+                          ASS_Outline *outline1, ASS_Outline *outline2,
+                          int bord)
 {
     RasterizerData *rst = &render_priv->rasterizer;
-    if (!rasterizer_set_outline(rst, outline, false)) {
+    if (outline1 && !rasterizer_set_outline(rst, outline1, false)) {
+        ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
+        return NULL;
+    }
+    if (outline2 && !rasterizer_set_outline(rst, outline2, !!outline1)) {
         ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
         return NULL;
     }
@@ -438,21 +443,27 @@ int be_padding(int be)
     return FFMAX(128 - be, 0);
 }
 
-int outline_to_bitmap2(ASS_Renderer *render_priv,
-                       ASS_Outline *outline, ASS_Outline *border,
+int outline_to_bitmap2(ASS_Renderer *render_priv, ASS_Outline *outline,
+                       ASS_Outline *border1, ASS_Outline *border2,
                        Bitmap **bm_g, Bitmap **bm_o)
 {
     assert(bm_g && bm_o);
-
     *bm_g = *bm_o = NULL;
 
+    if (outline && !outline->n_points)
+        outline = NULL;
+    if (border1 && !border1->n_points)
+        border1 = NULL;
+    if (border2 && !border2->n_points)
+        border2 = NULL;
+
     if (outline)
-        *bm_g = outline_to_bitmap(render_priv, outline, 1);
+        *bm_g = outline_to_bitmap(render_priv, outline, NULL, 1);
     if (!*bm_g)
         return 1;
 
-    if (border) {
-        *bm_o = outline_to_bitmap(render_priv, border, 1);
+    if (border1 || border2) {
+        *bm_o = outline_to_bitmap(render_priv, border1, border2, 1);
         if (!*bm_o) {
             return 1;
         }
index 3323cdae961fbd60ae75f7963226b9ef687bd975..04cb51b4d1a13a449486eb433e21fff844e01be7 100644 (file)
@@ -103,7 +103,8 @@ Bitmap *copy_bitmap(const BitmapEngine *engine, const Bitmap *src);
 void ass_free_bitmap(Bitmap *bm);
 
 Bitmap *outline_to_bitmap(ASS_Renderer *render_priv,
-                          ASS_Outline *outline, int bord);
+                          ASS_Outline *outline1, ASS_Outline *outline2,
+                          int bord);
 
 void ass_synth_blur(const BitmapEngine *engine, int opaque_box, int be,
                     double blur_radius, Bitmap *bm_g, Bitmap *bm_o);
@@ -111,12 +112,13 @@ void ass_synth_blur(const BitmapEngine *engine, int opaque_box, int be,
 /**
  * \brief perform glyph rendering
  * \param outline original glyph
- * \param border "border" glyph, produced from outline by FreeType's glyph stroker
+ * \param border1 inside "border" outline, produced by stroker
+ * \param border2 outside "border" outline, produced by stroker
  * \param bm_g out: pointer to the bitmap of original glyph is returned here
  * \param bm_o out: pointer to the bitmap of border glyph is returned here
  */
-int outline_to_bitmap2(ASS_Renderer *render_priv,
-                       ASS_Outline *outline, ASS_Outline *border,
+int outline_to_bitmap2(ASS_Renderer *render_priv, ASS_Outline *outline,
+                       ASS_Outline *border1, ASS_Outline *border2,
                        Bitmap **bm_g, Bitmap **bm_o);
 
 int be_padding(int be);
index 577f7f3d30f0c045f03ab608ac839557134d659f..2e8d7d7f1c3ca89837c30498ab1b1c17b62ca42c 100644 (file)
@@ -259,10 +259,9 @@ static void outline_destruct(void *key, void *value)
 {
     OutlineHashValue *v = value;
     OutlineHashKey *k = key;
-    outline_free(v->outline);
-    free(v->outline);
-    outline_free(v->border);
-    free(v->border);
+    outline_free(&v->outline);
+    outline_free(&v->border[0]);
+    outline_free(&v->border[1]);
     switch (k->type) {
         case OUTLINE_GLYPH: ass_cache_dec_ref(k->u.glyph.font); break;
         case OUTLINE_DRAWING: free(k->u.drawing.text); break;
index 687c1971f499bc79c6f90c49684f38450f2fe451..64751df915d8168f8dcbde08ad4395fa6d602280 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "ass.h"
 #include "ass_font.h"
+#include "ass_outline.h"
 #include "ass_bitmap.h"
 
 typedef struct cache Cache;
@@ -40,8 +41,8 @@ typedef struct {
 } CompositeHashValue;
 
 typedef struct {
-    ASS_Outline *outline;
-    ASS_Outline *border;
+    ASS_Outline outline;
+    ASS_Outline border[2];
     FT_BBox bbox_scaled;        // bbox after scaling, but before rotation
     FT_Vector advance;          // 26.6, advance distance to the next outline in line
     int asc, desc;              // ascender/descender
index 1facc6d770ae3aee8a6ada1eadd7565ddc5a46eb..60bb7ee096f0266454fcea3a698fa380315b0868 100644 (file)
@@ -29,62 +29,61 @@ bool outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_contours)
     outline->contours = malloc(sizeof(size_t) * n_contours);
     outline->points = malloc(sizeof(FT_Vector) * n_points);
     outline->tags = malloc(n_points);
-    if (!outline->contours || !outline->points || !outline->tags)
+    if (!outline->contours || !outline->points || !outline->tags) {
+        outline_free(outline);
         return false;
+    }
 
     outline->max_contours = n_contours;
     outline->max_points = n_points;
     return true;
 }
 
-ASS_Outline *outline_create(size_t n_points, size_t n_contours)
+static void outline_clear(ASS_Outline *outline)
 {
-    ASS_Outline *ol = calloc(1, sizeof(*ol));
-    if (!ol)
-        return NULL;
-
-    if (!outline_alloc(ol, n_points, n_contours)) {
-        outline_free(ol);
-        free(ol);
-        return NULL;
-    }
+    outline->contours = NULL;
+    outline->points = NULL;
+    outline->tags = NULL;
 
-    return ol;
+    outline->n_contours = outline->max_contours = 0;
+    outline->n_points = outline->max_points = 0;
 }
 
-ASS_Outline *outline_convert(const FT_Outline *source)
+bool outline_convert(ASS_Outline *outline, const FT_Outline *source)
 {
-    if (!source)
-        return NULL;
+    if (!source || !source->n_points) {
+        outline_clear(outline);
+        return true;
+    }
 
-    ASS_Outline *ol = outline_create(source->n_points, source->n_contours);
-    if (!ol)
-        return NULL;
+    if (!outline_alloc(outline, source->n_points, source->n_contours))
+        return false;
 
     for (int i = 0; i < source->n_contours; i++)
-        ol->contours[i] = source->contours[i];
-    memcpy(ol->points, source->points, sizeof(FT_Vector) * source->n_points);
-    memcpy(ol->tags, source->tags, source->n_points);
-    ol->n_contours = source->n_contours;
-    ol->n_points = source->n_points;
-    return ol;
+        outline->contours[i] = source->contours[i];
+    memcpy(outline->points, source->points, sizeof(FT_Vector) * source->n_points);
+    memcpy(outline->tags, source->tags, source->n_points);
+    outline->n_contours = source->n_contours;
+    outline->n_points = source->n_points;
+    return true;
 }
 
-ASS_Outline *outline_copy(const ASS_Outline *source)
+bool outline_copy(ASS_Outline *outline, const ASS_Outline *source)
 {
-    if (!source)
-        return NULL;
-
-    ASS_Outline *ol = outline_create(source->n_points, source->n_contours);
-    if (!ol)
-        return NULL;
-
-    memcpy(ol->contours, source->contours, sizeof(size_t) * source->n_contours);
-    memcpy(ol->points, source->points, sizeof(FT_Vector) * source->n_points);
-    memcpy(ol->tags, source->tags, source->n_points);
-    ol->n_contours = source->n_contours;
-    ol->n_points = source->n_points;
-    return ol;
+    if (!source || !source->n_points) {
+        outline_clear(outline);
+        return true;
+    }
+
+    if (!outline_alloc(outline, source->n_points, source->n_contours))
+        return false;
+
+    memcpy(outline->contours, source->contours, sizeof(size_t) * source->n_contours);
+    memcpy(outline->points, source->points, sizeof(FT_Vector) * source->n_points);
+    memcpy(outline->tags, source->tags, source->n_points);
+    outline->n_contours = source->n_contours;
+    outline->n_points = source->n_points;
+    return true;
 }
 
 void outline_free(ASS_Outline *outline)
@@ -95,6 +94,8 @@ void outline_free(ASS_Outline *outline)
     free(outline->contours);
     free(outline->points);
     free(outline->tags);
+
+    outline_clear(outline);
 }
 
 
@@ -154,6 +155,19 @@ void outline_transform(const ASS_Outline *outline, const FT_Matrix *matrix)
     }
 }
 
+void outline_update_cbox(const ASS_Outline *outline, FT_BBox *cbox)
+{
+    if (!outline)
+        return;
+
+    for (size_t i = 0; i < outline->n_points; i++) {
+        cbox->xMin = FFMIN(cbox->xMin, outline->points[i].x);
+        cbox->xMax = FFMAX(cbox->xMax, outline->points[i].x);
+        cbox->yMin = FFMIN(cbox->yMin, outline->points[i].y);
+        cbox->yMax = FFMAX(cbox->yMax, outline->points[i].y);
+    }
+}
+
 void outline_get_cbox(const ASS_Outline *outline, FT_BBox *cbox)
 {
     if (!outline->n_points) {
@@ -968,24 +982,12 @@ static bool close_contour(StrokerState *str, int dir)
 bool outline_stroke(ASS_Outline *result, ASS_Outline *result1,
                     const ASS_Outline *path, int xbord, int ybord, int eps)
 {
-    if (result1)
-        result1->n_contours = result1->n_points = 0;
-
     int rad = FFMAX(xbord, ybord);
-    if (rad < eps) {
-        assert(result->max_contours >= path->n_contours);
-        assert(result->max_points >= path->n_points);
-        memcpy(result->contours, path->contours, sizeof(size_t) * path->n_contours);
-        memcpy(result->points, path->points, sizeof(FT_Vector) * path->n_points);
-        memcpy(result->tags, path->tags, path->n_points);
-        result->n_contours = path->n_contours;
-        result->n_points = path->n_points;
-        return true;
-    }
+    assert(rad >= eps);
 
     result->n_contours = result->n_points = 0;
+    result1->n_contours = result1->n_points = 0;
 
-    int dir = result1 ? 3 : 1;
     StrokerState str;
     str.result[0] = result;
     str.result[1] = result1;
@@ -1009,6 +1011,7 @@ bool outline_stroke(ASS_Outline *result, ASS_Outline *result1,
         S_ON, S_Q, S_C1, S_C2
     };
 
+    const int dir = 3;
     for (size_t i = 0, j = 0; i < path->n_contours; i++) {
         OutlinePoint start, p[4];
         int process_end = 1;
index 9f3802034809fd9512bcd15ae5e6fbc07acf5cb9..0a1b8eceb9377695915579517f2fae2b43c8cb31 100644 (file)
@@ -33,9 +33,8 @@ typedef struct ass_outline {
 } ASS_Outline;
 
 bool outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_contours);
-ASS_Outline *outline_create(size_t n_points, size_t n_contours);
-ASS_Outline *outline_convert(const FT_Outline *source);
-ASS_Outline *outline_copy(const ASS_Outline *source);
+bool outline_convert(ASS_Outline *outline, const FT_Outline *source);
+bool outline_copy(ASS_Outline *outline, const ASS_Outline *source);
 void outline_free(ASS_Outline *outline);
 
 bool outline_add_point(ASS_Outline *outline, FT_Vector pt, char tag);
@@ -43,6 +42,7 @@ bool outline_close_contour(ASS_Outline *outline);
 
 void outline_translate(const ASS_Outline *outline, FT_Pos dx, FT_Pos dy);
 void outline_transform(const ASS_Outline *outline, const FT_Matrix *matrix);
+void outline_update_cbox(const ASS_Outline *outline, FT_BBox *cbox);
 void outline_get_cbox(const ASS_Outline *outline, FT_BBox *cbox);
 
 bool outline_stroke(ASS_Outline *result, ASS_Outline *result1,
index 88e1a7d310d3482ccda21c14717f3a840e8bde1c..7e8f6348f0642b6401d01e47d55c50fb8c5b1847 100644 (file)
@@ -492,7 +492,7 @@ static void blend_vector_clip(ASS_Renderer *render_priv,
             outline_translate(outline, trans.x, trans.y);
         }
 
-        val->bm = outline_to_bitmap(render_priv, outline, 0);
+        val->bm = outline_to_bitmap(render_priv, outline, NULL, 1);
         ass_cache_commit(val, bitmap_size(val->bm) +
                          sizeof(BitmapHashKey) + sizeof(BitmapHashValue));
     }
@@ -1035,7 +1035,7 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info)
                 ass_cache_dec_ref(val);
                 return;
             }
-            val->outline = outline_copy(&drawing->outline);
+            outline_copy(&val->outline, &drawing->outline);
             val->advance.x = drawing->advance.x;
             val->advance.y = drawing->advance.y;
             val->asc = drawing->asc;
@@ -1050,7 +1050,7 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info)
                         info->symbol, info->face_index, info->glyph_index,
                         priv->settings.hinting, info->flags);
             if (glyph != NULL) {
-                val->outline = outline_convert(&((FT_OutlineGlyph) glyph)->outline);
+                outline_convert(&val->outline, &((FT_OutlineGlyph) glyph)->outline);
                 if (priv->settings.shaper == ASS_SHAPING_SIMPLE) {
                     val->advance.x = d16_to_d6(glyph->advance.x);
                     val->advance.y = d16_to_d6(glyph->advance.y);
@@ -1063,61 +1063,55 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info)
             }
         }
 
-        if (!val->outline) {
+        if (!val->outline.n_points) {
             ass_cache_commit(val, 1);
             ass_cache_dec_ref(val);
             return;
         }
 
-        outline_get_cbox(val->outline, &val->bbox_scaled);
+        outline_get_cbox(&val->outline, &val->bbox_scaled);
 
         if (info->border_style == 3) {
-            val->border = calloc(1, sizeof(ASS_Outline));
-            if (!val->border) {
-                outline_free(val->outline);
-                free(val->outline);
-                val->outline = NULL;
-                ass_cache_commit(val, 1);
-                ass_cache_dec_ref(val);
-                return;
-            }
-
             FT_Vector advance;
             if (priv->settings.shaper == ASS_SHAPING_SIMPLE || info->drawing)
                 advance = val->advance;
             else
                 advance = info->advance;
 
-            draw_opaque_box(priv, info, val->asc, val->desc, val->border, advance,
-                    double_to_d6(info->border_x * priv->border_scale),
-                    double_to_d6(info->border_y * priv->border_scale));
+            draw_opaque_box(priv, info, val->asc, val->desc, &val->border[0], advance,
+                            double_to_d6(info->border_x * priv->border_scale),
+                            double_to_d6(info->border_y * priv->border_scale));
 
         } else if ((info->border_x > 0 || info->border_y > 0)
                 && double_to_d6(info->scale_x) && double_to_d6(info->scale_y)) {
-
-            val->border = outline_create(2 * val->outline->n_points,
-                                         val->outline->n_contours);
-            if (val->border && !outline_stroke(val->border, NULL, val->outline,
-                    double_to_d6(info->border_x * priv->border_scale),
-                    double_to_d6(info->border_y * priv->border_scale), 16)) {
-                ass_msg(priv->library, MSGL_WARN, "Cannot stoke outline");
-                outline_free(val->border);
-                free(val->border);
-                val->border = NULL;
+            const int eps = 16;
+            int xbord = double_to_d6(info->border_x * priv->border_scale);
+            int ybord = double_to_d6(info->border_y * priv->border_scale);
+            if(xbord >= eps || ybord >= eps) {
+                outline_alloc(&val->border[0], 2 * val->outline.n_points, val->outline.n_contours);
+                outline_alloc(&val->border[1], 2 * val->outline.n_points, val->outline.n_contours);
+                if (!val->border[0].max_points || !val->border[1].max_points ||
+                        !outline_stroke(&val->border[0], &val->border[1],
+                                        &val->outline, xbord, ybord, eps)) {
+                    ass_msg(priv->library, MSGL_WARN, "Cannot stoke outline");
+                    outline_free(&val->border[0]);
+                    outline_free(&val->border[1]);
+                }
             }
         }
 
         ass_cache_commit(val, 1);
     }
 
-    if (!val->outline) {
+    if (!val->outline.n_points) {
         ass_cache_dec_ref(val);
         return;
     }
 
     info->hash_key.u.outline.outline = val;
-    info->outline = val->outline;
-    info->border = val->border;
+    info->outline = &val->outline;
+    info->border[0] = &val->border[0];
+    info->border[1] = &val->border[1];
     info->bbox = val->bbox_scaled;
     if (info->drawing || priv->settings.shaper == ASS_SHAPING_SIMPLE) {
         info->cluster_advance.x = info->advance.x = val->advance.x;
@@ -1185,21 +1179,16 @@ transform_3d_points(FT_Vector shift, ASS_Outline *outline, double frx, double fr
  * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it.
  */
 static void
-transform_3d(FT_Vector shift, ASS_Outline *outline, ASS_Outline *border,
+transform_3d(FT_Vector shift, ASS_Outline *outline, int n_outlines,
              double frx, double fry, double frz, double fax, double fay,
              double scale, int yshift)
 {
     frx = -frx;
     frz = -frz;
-    if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.) {
-        if (outline)
-            transform_3d_points(shift, outline, frx, fry, frz,
-                                fax, fay, scale, yshift);
-
-        if (border)
-            transform_3d_points(shift, border, frx, fry, frz,
+    if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.)
+        for (int i = 0; i < n_outlines; i++)
+            transform_3d_points(shift, &outline[i], frx, fry, frz,
                                 fax, fay, scale, yshift);
-    }
 }
 
 /**
@@ -1225,8 +1214,11 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
     if (!val)
         return;
 
-    ASS_Outline *outline = outline_copy(info->outline);
-    ASS_Outline *border  = outline_copy(info->border);
+    const int n_outlines = 3;
+    ASS_Outline outline[n_outlines];
+    outline_copy(&outline[0], info->outline);
+    outline_copy(&outline[1], info->border[0]);
+    outline_copy(&outline[2], info->border[1]);
 
     // calculating rotation shift vector (from rotation origin to the glyph basepoint)
     FT_Vector shift = { key->shift_x, key->shift_y };
@@ -1236,7 +1228,7 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
 
     // apply rotation
     // use blur_scale because, like blurs, VSFilter forgets to scale this
-    transform_3d(shift, outline, border,
+    transform_3d(shift, outline, n_outlines,
                  info->frx, info->fry, info->frz, fax_scaled,
                  fay_scaled, render_priv->blur_scale, info->asc);
 
@@ -1245,19 +1237,15 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
                     0, double_to_d16(1.0) };
 
     // subpixel shift
-    if (outline) {
-        if (scale_x != 1.0)
-            outline_transform(outline, &m);
-        outline_translate(outline, key->advance.x, -key->advance.y);
-    }
-    if (border) {
-        if (scale_x != 1.0)
-            outline_transform(border, &m);
-        outline_translate(border, key->advance.x, -key->advance.y);
-    }
+    if (scale_x != 1.0)
+        for (int i = 0; i < n_outlines; i++)
+            outline_transform(&outline[i], &m);
+    for (int i = 0; i < n_outlines; i++)
+        outline_translate(&outline[i], key->advance.x, -key->advance.y);
 
     // render glyph
-    int error = outline_to_bitmap2(render_priv, outline, border,
+    int error = outline_to_bitmap2(render_priv,
+                                   &outline[0], &outline[1], &outline[2],
                                    &val->bm, &val->bm_o);
     if (error)
         info->symbol = 0;
@@ -1266,10 +1254,8 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
                      sizeof(BitmapHashKey) + sizeof(BitmapHashValue));
     info->image = val;
 
-    outline_free(outline);
-    free(outline);
-    outline_free(border);
-    free(border);
+    for (int i = 0; i < n_outlines; i++)
+        outline_free(&outline[i]);
 }
 
 /**
index e858813a2bedc6d90f72839a45d5d98b801ee254..8731a963404994c4f8fef1088b1ee57db3749552 100644 (file)
@@ -149,7 +149,7 @@ typedef struct glyph_info {
     double font_size;
     ASS_Drawing *drawing;
     ASS_Outline *outline;
-    ASS_Outline *border;
+    ASS_Outline *border[2];
     FT_BBox bbox;
     FT_Vector pos;
     FT_Vector offset;