|
|
|
@ -156,7 +156,10 @@ 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
|
|
|
|
|
unsigned int default_fontsize; ///< default font size to use
|
|
|
|
|
|
|
|
|
|
int line_spacing; ///< lines spacing in pixels
|
|
|
|
|
short int draw_box; ///< draw box around text - true or false
|
|
|
|
@ -211,7 +214,7 @@ static const AVOption drawtext_options[]= { |
|
|
|
|
{"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}, |
|
|
|
|
{"line_spacing", "set line spacing in pixels", OFFSET(line_spacing), 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=NULL}, 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}, |
|
|
|
@ -281,6 +284,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; |
|
|
|
@ -293,7 +297,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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -317,6 +325,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); |
|
|
|
@ -366,6 +375,76 @@ error: |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static av_cold int set_fontsize(AVFilterContext *ctx, unsigned int fontsize) |
|
|
|
|
{ |
|
|
|
|
int err; |
|
|
|
|
DrawTextContext *s = ctx->priv; |
|
|
|
|
|
|
|
|
|
if ((err = FT_Set_Pixel_Sizes(s->face, 0, fontsize))) { |
|
|
|
|
av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n", |
|
|
|
|
fontsize, FT_ERRMSG(err)); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
s->fontsize = fontsize; |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static av_cold int parse_fontsize(AVFilterContext *ctx) |
|
|
|
|
{ |
|
|
|
|
DrawTextContext *s = ctx->priv; |
|
|
|
|
int err; |
|
|
|
|
|
|
|
|
|
if (s->fontsize_pexpr) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
if (s->fontsize_expr == NULL) |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
|
|
|
|
|
if ((err = av_expr_parse(&s->fontsize_pexpr, s->fontsize_expr, var_names, |
|
|
|
|
NULL, NULL, fun2_names, fun2, 0, ctx)) < 0) |
|
|
|
|
return err; |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static av_cold int update_fontsize(AVFilterContext *ctx) |
|
|
|
|
{ |
|
|
|
|
DrawTextContext *s = ctx->priv; |
|
|
|
|
unsigned int fontsize = s->default_fontsize; |
|
|
|
|
int err; |
|
|
|
|
double size, roundedsize; |
|
|
|
|
|
|
|
|
|
// if no fontsize specified use the default
|
|
|
|
|
if (s->fontsize_expr != NULL) { |
|
|
|
|
if ((err = parse_fontsize(ctx)) < 0) |
|
|
|
|
return err; |
|
|
|
|
|
|
|
|
|
size = av_expr_eval(s->fontsize_pexpr, s->var_values, &s->prng); |
|
|
|
|
|
|
|
|
|
if (!isnan(size)) { |
|
|
|
|
roundedsize = round(size); |
|
|
|
|
// test for overflow before cast
|
|
|
|
|
if (!(roundedsize > INT_MIN && roundedsize < INT_MAX)) { |
|
|
|
|
av_log(ctx, AV_LOG_ERROR, "fontsize overflow\n"); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fontsize = roundedsize; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (fontsize == 0) |
|
|
|
|
fontsize = 1; |
|
|
|
|
|
|
|
|
|
// no change
|
|
|
|
|
if (fontsize == s->fontsize) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
return set_fontsize(ctx, fontsize); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int load_font_file(AVFilterContext *ctx, const char *path, int index) |
|
|
|
|
{ |
|
|
|
|
DrawTextContext *s = ctx->priv; |
|
|
|
@ -393,6 +472,7 @@ static int load_font_fontconfig(AVFilterContext *ctx) |
|
|
|
|
int index; |
|
|
|
|
double size; |
|
|
|
|
int err = AVERROR(ENOENT); |
|
|
|
|
int parse_err; |
|
|
|
|
|
|
|
|
|
fontconfig = FcInitLoadConfigAndFonts(); |
|
|
|
|
if (!fontconfig) { |
|
|
|
@ -407,8 +487,18 @@ static int load_font_fontconfig(AVFilterContext *ctx) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
FcPatternAddString(pat, FC_FAMILY, s->font); |
|
|
|
|
if (s->fontsize) |
|
|
|
|
FcPatternAddDouble(pat, FC_SIZE, (double)s->fontsize); |
|
|
|
|
|
|
|
|
|
parse_err = parse_fontsize(ctx); |
|
|
|
|
if (!parse_err) { |
|
|
|
|
double size = av_expr_eval(s->fontsize_pexpr, s->var_values, &s->prng); |
|
|
|
|
|
|
|
|
|
if (isnan(size)) { |
|
|
|
|
av_log(ctx, AV_LOG_ERROR, "impossible to find font information"); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
FcPatternAddDouble(pat, FC_SIZE, size); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
FcDefaultSubstitute(pat); |
|
|
|
|
|
|
|
|
@ -442,8 +532,8 @@ static int load_font_fontconfig(AVFilterContext *ctx) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename); |
|
|
|
|
if (!s->fontsize) |
|
|
|
|
s->fontsize = size + 0.5; |
|
|
|
|
if (parse_err) |
|
|
|
|
s->default_fontsize = size + 0.5; |
|
|
|
|
|
|
|
|
|
err = load_font_file(ctx, filename, index); |
|
|
|
|
if (err) |
|
|
|
@ -598,6 +688,12 @@ static av_cold int init(AVFilterContext *ctx) |
|
|
|
|
DrawTextContext *s = ctx->priv; |
|
|
|
|
Glyph *glyph; |
|
|
|
|
|
|
|
|
|
av_expr_free(s->fontsize_pexpr); |
|
|
|
|
s->fontsize_pexpr = NULL; |
|
|
|
|
|
|
|
|
|
s->fontsize = 0; |
|
|
|
|
s->default_fontsize = 16; |
|
|
|
|
|
|
|
|
|
if (!s->fontfile && !CONFIG_LIBFONTCONFIG) { |
|
|
|
|
av_log(ctx, AV_LOG_ERROR, "No font filename provided\n"); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
@ -645,16 +741,11 @@ static av_cold int init(AVFilterContext *ctx) |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
err = load_font(ctx); |
|
|
|
|
if (err) |
|
|
|
|
if ((err = load_font(ctx)) < 0) |
|
|
|
|
return err; |
|
|
|
|
|
|
|
|
|
if ((err = update_fontsize(ctx)) < 0) |
|
|
|
|
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)); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (s->borderw) { |
|
|
|
|
if (FT_Stroker_New(s->library, &s->stroker)) { |
|
|
|
@ -709,11 +800,13 @@ static av_cold void uninit(AVFilterContext *ctx) |
|
|
|
|
av_expr_free(s->x_pexpr); |
|
|
|
|
av_expr_free(s->y_pexpr); |
|
|
|
|
av_expr_free(s->a_pexpr); |
|
|
|
|
s->x_pexpr = s->y_pexpr = s->a_pexpr = NULL; |
|
|
|
|
av_expr_free(s->fontsize_pexpr); |
|
|
|
|
|
|
|
|
|
s->x_pexpr = s->y_pexpr = s->a_pexpr = s->fontsize_pexpr = NULL; |
|
|
|
|
|
|
|
|
|
av_freep(&s->positions); |
|
|
|
|
s->nb_positions = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
av_tree_enumerate(s->glyphs, NULL, NULL, glyph_enu_free); |
|
|
|
|
av_tree_destroy(s->glyphs); |
|
|
|
|
s->glyphs = NULL; |
|
|
|
@ -1094,6 +1187,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; |
|
|
|
@ -1219,12 +1313,16 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, |
|
|
|
|
x = 0; |
|
|
|
|
y = 0; |
|
|
|
|
|
|
|
|
|
if ((ret = update_fontsize(ctx)) < 0) |
|
|
|
|
return ret; |
|
|
|
|
|
|
|
|
|
/* load and cache glyphs */ |
|
|
|
|
for (i = 0, p = text; *p; i++) { |
|
|
|
|
GET_UTF8(code, *p++, continue;); |
|
|
|
|
|
|
|
|
|
/* 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); |
|
|
|
@ -1261,6 +1359,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 */ |
|
|
|
|