[FFmpeg-devel] [PATCH 2/2] drawtext: introduce fontcolor argument expansion
Stefano Sabatini
stefasab at gmail.com
Mon Jun 23 16:27:46 CEST 2014
On date Sunday 2014-06-22 17:31:10 +0300, Andrey Utkin encoded:
> ---
> libavfilter/vf_drawtext.c | 68 +++++++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 63 insertions(+), 5 deletions(-)
Missing doc updates.
>
> diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
> index adc587e..4ac7ab9 100644
> --- a/libavfilter/vf_drawtext.c
> +++ b/libavfilter/vf_drawtext.c
> @@ -35,6 +35,7 @@
> #include <sys/stat.h>
> #include <time.h>
> #include <unistd.h>
> +#include <fenv.h>
>
> #if CONFIG_LIBFONTCONFIG
> #include <fontconfig/fontconfig.h>
> @@ -136,6 +137,8 @@ typedef struct DrawTextContext {
> uint8_t *fontfile; ///< font to be used
> uint8_t *text; ///< text to be drawn
> AVBPrint expanded_text; ///< used to contain the expanded text
> + uint8_t *fontcolor_expr; ///< fontcolor expression to evaluate
> + AVBPrint expanded_fontcolor; ///< used to contain the expanded fontcolor spec
> int ft_load_flags; ///< flags used for loading fonts, see FT_LOAD_*
> FT_Vector *positions; ///< positions for each element in the text
> size_t nb_positions; ///< number of elements of positions array
> @@ -191,6 +194,7 @@ static const AVOption drawtext_options[]= {
> {"text", "set text", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
> {"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
> {"fontcolor", "set foreground color", OFFSET(fontcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
> + {"fontcolor_expr", "set foreground color expression to evaluate", OFFSET(fontcolor_expr), AV_OPT_TYPE_STRING, {.str=""}, CHAR_MIN, CHAR_MAX, FLAGS},
> {"boxcolor", "set box color", OFFSET(boxcolor.rgba), AV_OPT_TYPE_COLOR, {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS},
> {"bordercolor", "set border color", OFFSET(bordercolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
> {"shadowcolor", "set shadow color", OFFSET(shadowcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
> @@ -198,6 +202,7 @@ static const AVOption drawtext_options[]= {
> {"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_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},
> + {"fontcolor_expr", "set fontcolor expression", OFFSET(fontcolor_expr), AV_OPT_TYPE_STRING, {.str=""}, CHAR_MIN, CHAR_MAX, FLAGS},
Duplicate?
> {"shadowx", "set x", OFFSET(shadowx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS},
> {"shadowy", "set y", OFFSET(shadowy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS},
> {"borderw", "set border width", OFFSET(borderw), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS},
> @@ -569,6 +574,7 @@ static av_cold int init(AVFilterContext *ctx)
> av_log(ctx, AV_LOG_WARNING, "expansion=strftime is deprecated.\n");
>
> av_bprint_init(&s->expanded_text, 0, AV_BPRINT_SIZE_UNLIMITED);
> + av_bprint_init(&s->expanded_fontcolor, 0, AV_BPRINT_SIZE_UNLIMITED);
>
> return 0;
> }
> @@ -612,6 +618,7 @@ static av_cold void uninit(AVFilterContext *ctx)
> FT_Done_FreeType(s->library);
>
> av_bprint_finalize(&s->expanded_text, NULL);
> + av_bprint_finalize(&s->expanded_fontcolor, NULL);
> }
>
yy> static inline int is_newline(uint32_t c)
> @@ -800,6 +807,44 @@ static int func_eval_expr(AVFilterContext *ctx, AVBPrint *bp,
> return ret;
> }
>
> +static int func_eval_expr_int_format(AVFilterContext *ctx, AVBPrint *bp,
> + char *fct, unsigned argc, char **argv, int tag)
> +{
> + DrawTextContext *s = ctx->priv;
> + double res;
> + int intval;
> + int ret;
> +
> + /* Check that argv[0] (format) contains exactly one output specifier */
> + const char *percent_sign = strchr(argv[0], '%');
> + if (!percent_sign || strchr(percent_sign + 1, '%')) {
> + av_log(ctx, AV_LOG_ERROR, "Format must contain exactly one output "
> + "specifier, but '%s' is supplied\n", argv[0]);
> + return AVERROR(EINVAL);
> + }
> + ret = av_expr_parse_and_eval(&res, argv[1], var_names, s->var_values,
> + NULL, NULL, fun2_names, fun2,
> + &s->prng, 0, ctx);
> + if (ret < 0)
> + av_log(ctx, AV_LOG_ERROR,
> + "Expression '%s' for the expr text expansion function is not valid\n",
> + argv[1]);
> + else
> + {
> + av_log(ctx, AV_LOG_DEBUG, "Formatting value %f (expr '%s') with spec '%s'\n",
> + res, argv[1], argv[0]);
> + feclearexcept(FE_ALL_EXCEPT);
> + intval = res;
> + if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) {
> + av_log(ctx, AV_LOG_ERROR, "Conversion of floating-point result to int failed. Control register: 0x%08x. Conversion result: %d\n", ret, intval);
> + return AVERROR(EINVAL);
> + }
> + av_bprintf(bp, argv[0], intval);
> + }
> +
> + return ret;
> +}
If I understand it correctly this evaluates some expression of the
kind "%d" 42.00 -> "42", right? I'd prefer this in a separate patch,
also I believe this has some security implications (for example an
invalid format sequence may lead to a crash). Also how is this related
to $subject.
> +
> static const struct drawtext_function {
> const char *name;
> unsigned argc_min, argc_max;
> @@ -808,6 +853,7 @@ static const struct drawtext_function {
> } functions[] = {
> { "expr", 1, 1, 0, func_eval_expr },
> { "e", 1, 1, 0, func_eval_expr },
> + { "eif", 2, 2, 0, func_eval_expr_int_format },
> { "pict_type", 0, 0, 0, func_pict_type },
> { "pts", 0, 2, 0, func_pts },
> { "gmtime", 0, 1, 'G', func_strftime },
> @@ -884,11 +930,8 @@ end:
> return ret;
> }
>
> -static int expand_text(AVFilterContext *ctx)
> +static int expand_text(AVFilterContext *ctx, char *text, AVBPrint *bp)
> {
> - DrawTextContext *s = ctx->priv;
> - char *text = s->text;
> - AVBPrint *bp = &s->expanded_text;
> int ret;
>
> av_bprint_clear(bp);
> @@ -984,7 +1027,7 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame,
> av_bprintf(bp, "%s", s->text);
> break;
> case EXP_NORMAL:
> - if ((ret = expand_text(ctx)) < 0)
> + if ((ret = expand_text(ctx, s->text, &s->expanded_text)) < 0)
> return ret;
> break;
> case EXP_STRFTIME:
> @@ -1010,6 +1053,21 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame,
> s->nb_positions = len;
> }
>
> + if (s->fontcolor_expr[0]) {
> + /* If expression is set, evaluate and repace statically set */
repace -> replace?
> + av_bprint_clear(&s->expanded_fontcolor);
> + if ((ret = expand_text(ctx, s->fontcolor_expr, &s->expanded_fontcolor)) < 0)
> + return ret;
> + if (!av_bprint_is_complete(&s->expanded_fontcolor))
> + return AVERROR(ENOMEM);
> + av_log(s, AV_LOG_DEBUG, "Evaluated fontcolor is '%s'\n", s->expanded_fontcolor.str);
> + ret = av_parse_color(s->fontcolor.rgba, s->expanded_fontcolor.str, -1, s);
> + if (ret)
> + return ret;
> + ff_draw_color(&s->dc, &s->fontcolor, s->fontcolor.rgba);
> + }
--
FFmpeg = Fierce and Fascinating Mere Ponderous Extended Guide
More information about the ffmpeg-devel
mailing list