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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel