[FFmpeg-devel] [PATCH] added expr evaluation to drawtext - fontsize

Brett Harrison brett.harrison at zyamusic.com
Sat Aug 27 00:37:42 EEST 2016


Allows expr evaluation in the fontsize parameter for drawtext.

examples (fontsize 1/3 of video height):

ffmpeg -i  http://i.giphy.com/kwAi4WrChkSfm.gif -vf
"drawtext=fontfile=/Library/Fonts/Verdana
Bold.ttf:text='HI':fontcolor=yellow:x=(w-tw)/2:y=(h-th)/1.3:fontsize=h/3"
out.gif

ffmpeg -i  http://i.giphy.com/3o6ozzX4mAcwkkgzG8.gif -vf
"drawtext=fontfile=/Library/Fonts/Verdana
Bold.ttf:text='HI':fontcolor=yellow:x=(w-tw)/2:y=(h-th)/1.3:fontsize=h/3"
out.gif

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

diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
index 214aef0..1b9d2ca 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,14 @@ 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 : diff < 0 ? -1 : 0;
+    }
+    else {
+      int64_t diff = (int64_t)a->fontsize - (int64_t)bb->fontsize;
+      return diff > 0 ? 1 : diff < 0 ? -1 : 0;
+    }
 }

 /**
@@ -316,6 +326,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 +602,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 +708,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 +766,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 +810,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 +822,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 +1151,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 +1159,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 +1291,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 +1328,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 +1416,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


More information about the ffmpeg-devel mailing list