From 5c9d7d18a5d2f15f48605021d7f5a7890a318cc4 Mon Sep 17 00:00:00 2001
From: Brett Harrison <brett.harrison@musicmastermind.com>
Date: Fri, 26 Aug 2016 14:29:34 -0700
Subject: [PATCH] added expr evaluation to drawtext - fontsize

---
 libavfilter/vf_drawtext.c | 86 +++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 79 insertions(+), 7 deletions(-)

diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
index 214aef0..5311e29 100644
--- a/libavfilter/vf_drawtext.c
+++ b/libavfilter/vf_drawtext.c
@@ -156,6 +156,8 @@ 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
 
     short int draw_box;             ///< draw box around text - true or false
@@ -209,7 +211,7 @@ static const AVOption drawtext_options[]= {
     {"shadowcolor", "set shadow color",     OFFSET(shadowcolor.rgba),   AV_OPT_TYPE_COLOR,  {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
     {"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},
-    {"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="16"},  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},
@@ -280,6 +282,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;
@@ -292,7 +295,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);
 }
 
 /**
@@ -316,6 +323,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);
@@ -591,12 +599,62 @@ out:
 }
 #endif
 
+static av_cold int set_fontsize(AVFilterContext *ctx)
+{
+    int err;
+    DrawTextContext *s = ctx->priv;
+
+    if (s->face == NULL) {
+        av_log(ctx, AV_LOG_ERROR, "Font not open\n");
+        return AVERROR(EINVAL);
+    }
+
+    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);
+    }
+
+    return 0;
+}
+
+static av_cold int update_fontsize(AVFilterContext *ctx)
+{
+    DrawTextContext *s = ctx->priv;
+    unsigned int fontsize = 16;
+
+    double size = av_expr_eval(s->fontsize_pexpr, s->var_values, &s->prng);
+
+    if (isnan(size))
+        fontsize = 16;
+    else if (round(size) <= 0)
+        fontsize = 1;
+    else
+        fontsize = round(size);
+
+    // no change
+    if (fontsize == s->fontsize) {
+      return 0;
+    }
+
+    s->fontsize = fontsize;
+
+    return set_fontsize(ctx);
+}
+
 static av_cold int init(AVFilterContext *ctx)
 {
     int err;
     DrawTextContext *s = ctx->priv;
     Glyph *glyph;
 
+    av_expr_free(s->fontsize_pexpr);
+    s->fontsize_pexpr = NULL;
+
+    if (av_expr_parse(&s->fontsize_pexpr, s->fontsize_expr, var_names,
+                             NULL, NULL, fun2_names, fun2, 0, ctx) < 0)
+        return AVERROR(EINVAL);
+
     if (!s->fontfile && !CONFIG_LIBFONTCONFIG) {
         av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
         return AVERROR(EINVAL);
@@ -647,11 +705,8 @@ static av_cold int init(AVFilterContext *ctx)
     err = load_font(ctx);
     if (err)
         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));
+
+    if ((err = update_fontsize(ctx))) {
         return AVERROR(EINVAL);
     }
 
@@ -708,6 +763,10 @@ static av_cold void uninit(AVFilterContext *ctx)
     av_expr_free(s->x_pexpr);
     av_expr_free(s->y_pexpr);
     s->x_pexpr = s->y_pexpr = NULL;
+
+    av_expr_free(s->fontsize_pexpr);
+    s->fontsize_pexpr = NULL;
+
     av_freep(&s->positions);
     s->nb_positions = 0;
 
@@ -748,6 +807,9 @@ static int config_input(AVFilterLink *inlink)
 
     av_lfg_init(&s->prng, av_get_random_seed());
 
+    av_expr_free(s->fontsize_pexpr);
+    s->fontsize_pexpr = NULL;
+
     av_expr_free(s->x_pexpr);
     av_expr_free(s->y_pexpr);
     s->x_pexpr = s->y_pexpr = NULL;
@@ -757,6 +819,8 @@ static int config_input(AVFilterLink *inlink)
         (ret = av_expr_parse(&s->y_pexpr, s->y_expr, var_names,
                              NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
         (ret = av_expr_parse(&s->a_pexpr, s->a_expr, var_names,
+                             NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
+        (ret = av_expr_parse(&s->fontsize_pexpr, s->fontsize_expr, var_names,
                              NULL, NULL, fun2_names, fun2, 0, ctx)) < 0)
 
         return AVERROR(EINVAL);
@@ -1084,6 +1148,7 @@ static int draw_glyphs(DrawTextContext *s, AVFrame *frame,
     for (i = 0, p = text; *p; i++) {
         FT_Bitmap bitmap;
         Glyph dummy = { 0 };
+
         GET_UTF8(code, *p++, continue;);
 
         /* skip new line chars, just go to new line */
@@ -1091,6 +1156,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;
@@ -1222,6 +1288,7 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame,
 
         /* 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);
@@ -1258,6 +1325,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 */
@@ -1345,6 +1413,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 #endif
     }
 
+    if ((ret = update_fontsize(ctx))) {
+        return AVERROR(EINVAL);
+    }
+
     s->var_values[VAR_N] = inlink->frame_count+s->start_number;
     s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ?
         NAN : frame->pts * av_q2d(inlink->time_base);
-- 
2.1.3

