avfilter/vf_drawtext: added expr evaluation to drawtext fontsize
authorBrett Harrison <brett.harrison@musicmastermind.com>
Wed, 19 Apr 2017 01:15:39 +0000 (18:15 -0700)
committerMichael Niedermayer <michael@niedermayer.cc>
Wed, 19 Apr 2017 10:39:05 +0000 (12:39 +0200)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
libavfilter/vf_drawtext.c

index 8b24f50..cba6cc8 100644 (file)
@@ -156,7 +156,10 @@ typedef struct DrawTextContext {
     int max_glyph_h;                ///< max glyph height
     int shadowx, shadowy;
     int borderw;                    ///< border width
+    char *fontsize_expr;            ///< expression for fontsize
+    AVExpr *fontsize_pexpr;         ///< parsed expressions for fontsize
     unsigned int fontsize;          ///< font size to use
+    unsigned int default_fontsize;  ///< default font size to use
 
     int line_spacing;               ///< lines spacing in pixels
     short int draw_box;             ///< draw box around text - true or false
@@ -211,7 +214,7 @@ static const AVOption drawtext_options[]= {
     {"box",         "set box",              OFFSET(draw_box),           AV_OPT_TYPE_BOOL,   {.i64=0},     0,        1       , FLAGS},
     {"boxborderw",  "set box border width", OFFSET(boxborderw),         AV_OPT_TYPE_INT,    {.i64=0},     INT_MIN,  INT_MAX , FLAGS},
     {"line_spacing",  "set line spacing in pixels", OFFSET(line_spacing),   AV_OPT_TYPE_INT,    {.i64=0},     INT_MIN,  INT_MAX,FLAGS},
-    {"fontsize",    "set font size",        OFFSET(fontsize),           AV_OPT_TYPE_INT,    {.i64=0},     0,        INT_MAX , FLAGS},
+    {"fontsize",    "set font size",        OFFSET(fontsize_expr),      AV_OPT_TYPE_STRING, {.str=NULL},  CHAR_MIN, CHAR_MAX , FLAGS},
     {"x",           "set x expression",     OFFSET(x_expr),             AV_OPT_TYPE_STRING, {.str="0"},   CHAR_MIN, CHAR_MAX, FLAGS},
     {"y",           "set y expression",     OFFSET(y_expr),             AV_OPT_TYPE_STRING, {.str="0"},   CHAR_MIN, CHAR_MAX, FLAGS},
     {"shadowx",     "set shadow x offset",  OFFSET(shadowx),            AV_OPT_TYPE_INT,    {.i64=0},     INT_MIN,  INT_MAX , FLAGS},
@@ -281,6 +284,7 @@ typedef struct Glyph {
     FT_Glyph glyph;
     FT_Glyph border_glyph;
     uint32_t code;
+    unsigned int fontsize;
     FT_Bitmap bitmap; ///< array holding bitmaps of font
     FT_Bitmap border_bitmap; ///< array holding bitmaps of font border
     FT_BBox bbox;
@@ -293,7 +297,11 @@ static int glyph_cmp(const void *key, const void *b)
 {
     const Glyph *a = key, *bb = b;
     int64_t diff = (int64_t)a->code - (int64_t)bb->code;
-    return diff > 0 ? 1 : diff < 0 ? -1 : 0;
+
+    if (diff != 0)
+         return diff > 0 ? 1 : -1;
+    else
+         return FFDIFFSIGN((int64_t)a->fontsize, (int64_t)bb->fontsize);
 }
 
 /**
@@ -317,6 +325,7 @@ static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
         goto error;
     }
     glyph->code  = code;
+    glyph->fontsize = s->fontsize;
 
     if (FT_Get_Glyph(s->face->glyph, &glyph->glyph)) {
         ret = AVERROR(EINVAL);
@@ -366,6 +375,76 @@ error:
     return ret;
 }
 
+static av_cold int set_fontsize(AVFilterContext *ctx, unsigned int fontsize)
+{
+    int err;
+    DrawTextContext *s = ctx->priv;
+
+    if ((err = FT_Set_Pixel_Sizes(s->face, 0, fontsize))) {
+        av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n",
+               fontsize, FT_ERRMSG(err));
+        return AVERROR(EINVAL);
+    }
+
+    s->fontsize = fontsize;
+
+    return 0;
+}
+
+static av_cold int parse_fontsize(AVFilterContext *ctx)
+{
+    DrawTextContext *s = ctx->priv;
+    int err;
+
+    if (s->fontsize_pexpr)
+        return 0;
+
+    if (s->fontsize_expr == NULL)
+        return AVERROR(EINVAL);
+
+    if ((err = av_expr_parse(&s->fontsize_pexpr, s->fontsize_expr, var_names,
+                             NULL, NULL, fun2_names, fun2, 0, ctx)) < 0)
+        return err;
+
+    return 0;
+}
+
+static av_cold int update_fontsize(AVFilterContext *ctx)
+{
+    DrawTextContext *s = ctx->priv;
+    unsigned int fontsize = s->default_fontsize;
+    int err;
+    double size, roundedsize;
+
+    // if no fontsize specified use the default
+    if (s->fontsize_expr != NULL) {
+        if ((err = parse_fontsize(ctx)) < 0)
+           return err;
+
+        size = av_expr_eval(s->fontsize_pexpr, s->var_values, &s->prng);
+
+        if (!isnan(size)) {
+            roundedsize = round(size);
+            // test for overflow before cast
+            if (!(roundedsize > INT_MIN && roundedsize < INT_MAX)) {
+                av_log(ctx, AV_LOG_ERROR, "fontsize overflow\n");
+                return AVERROR(EINVAL);
+            }
+
+            fontsize = roundedsize;
+        }
+    }
+
+    if (fontsize == 0)
+        fontsize = 1;
+
+    // no change
+    if (fontsize == s->fontsize)
+        return 0;
+
+    return set_fontsize(ctx, fontsize);
+}
+
 static int load_font_file(AVFilterContext *ctx, const char *path, int index)
 {
     DrawTextContext *s = ctx->priv;
@@ -393,6 +472,7 @@ static int load_font_fontconfig(AVFilterContext *ctx)
     int index;
     double size;
     int err = AVERROR(ENOENT);
+    int parse_err;
 
     fontconfig = FcInitLoadConfigAndFonts();
     if (!fontconfig) {
@@ -407,8 +487,18 @@ static int load_font_fontconfig(AVFilterContext *ctx)
     }
 
     FcPatternAddString(pat, FC_FAMILY, s->font);
-    if (s->fontsize)
-        FcPatternAddDouble(pat, FC_SIZE, (double)s->fontsize);
+
+    parse_err = parse_fontsize(ctx);
+    if (!parse_err) {
+        double size = av_expr_eval(s->fontsize_pexpr, s->var_values, &s->prng);
+
+        if (isnan(size)) {
+            av_log(ctx, AV_LOG_ERROR, "impossible to find font information");
+            return AVERROR(EINVAL);
+        }
+
+        FcPatternAddDouble(pat, FC_SIZE, size);
+    }
 
     FcDefaultSubstitute(pat);
 
@@ -442,8 +532,8 @@ static int load_font_fontconfig(AVFilterContext *ctx)
     }
 
     av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename);
-    if (!s->fontsize)
-        s->fontsize = size + 0.5;
+    if (parse_err)
+        s->default_fontsize = size + 0.5;
 
     err = load_font_file(ctx, filename, index);
     if (err)
@@ -598,6 +688,12 @@ static av_cold int init(AVFilterContext *ctx)
     DrawTextContext *s = ctx->priv;
     Glyph *glyph;
 
+    av_expr_free(s->fontsize_pexpr);
+    s->fontsize_pexpr = NULL;
+
+    s->fontsize = 0;
+    s->default_fontsize = 16;
+
     if (!s->fontfile && !CONFIG_LIBFONTCONFIG) {
         av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
         return AVERROR(EINVAL);
@@ -645,16 +741,11 @@ static av_cold int init(AVFilterContext *ctx)
         return AVERROR(EINVAL);
     }
 
-    err = load_font(ctx);
-    if (err)
+    if ((err = load_font(ctx)) < 0)
+        return err;
+
+    if ((err = update_fontsize(ctx)) < 0)
         return err;
-    if (!s->fontsize)
-        s->fontsize = 16;
-    if ((err = FT_Set_Pixel_Sizes(s->face, 0, s->fontsize))) {
-        av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n",
-               s->fontsize, FT_ERRMSG(err));
-        return AVERROR(EINVAL);
-    }
 
     if (s->borderw) {
         if (FT_Stroker_New(s->library, &s->stroker)) {
@@ -709,11 +800,13 @@ static av_cold void uninit(AVFilterContext *ctx)
     av_expr_free(s->x_pexpr);
     av_expr_free(s->y_pexpr);
     av_expr_free(s->a_pexpr);
-    s->x_pexpr = s->y_pexpr = s->a_pexpr = NULL;
+    av_expr_free(s->fontsize_pexpr);
+
+    s->x_pexpr = s->y_pexpr = s->a_pexpr = s->fontsize_pexpr = NULL;
+
     av_freep(&s->positions);
     s->nb_positions = 0;
 
-
     av_tree_enumerate(s->glyphs, NULL, NULL, glyph_enu_free);
     av_tree_destroy(s->glyphs);
     s->glyphs = NULL;
@@ -1094,6 +1187,7 @@ static int draw_glyphs(DrawTextContext *s, AVFrame *frame,
             continue;
 
         dummy.code = code;
+        dummy.fontsize = s->fontsize;
         glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
 
         bitmap = borderw ? glyph->border_bitmap : glyph->bitmap;
@@ -1219,12 +1313,16 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame,
     x = 0;
     y = 0;
 
+    if ((ret = update_fontsize(ctx)) < 0)
+        return ret;
+
     /* load and cache glyphs */
     for (i = 0, p = text; *p; i++) {
         GET_UTF8(code, *p++, continue;);
 
         /* get glyph */
         dummy.code = code;
+        dummy.fontsize = s->fontsize;
         glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
         if (!glyph) {
             ret = load_glyph(ctx, &glyph, code);
@@ -1261,6 +1359,7 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame,
         /* get glyph */
         prev_glyph = glyph;
         dummy.code = code;
+        dummy.fontsize = s->fontsize;
         glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
 
         /* kerning */