[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