34 #include <sys/types.h>
42 #if CONFIG_LIBFONTCONFIG
43 #include <fontconfig/fontconfig.h>
68 #include FT_FREETYPE_H
78 "max_glyph_a",
"ascent",
79 "max_glyph_d",
"descent",
97 static double drand(
void *opaque,
double min,
double max)
140 #if CONFIG_LIBFONTCONFIG
180 #if FF_API_DRAWTEXT_OLD_TIMELINE
192 #if CONFIG_LIBFRIBIDI
198 #define OFFSET(x) offsetof(DrawTextContext, x)
199 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
219 #if FF_API_DRAWTEXT_OLD_TIMELINE
222 #if CONFIG_LIBFONTCONFIG
237 {
"fix_bounds",
"if true, check and fix text coords to avoid clipping",
OFFSET(fix_bounds),
AV_OPT_TYPE_INT, {.i64=1}, 0, 1,
FLAGS},
238 {
"start_number",
"start frame number for n/frame_num variable",
OFFSET(start_number),
AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,
FLAGS},
240 #if CONFIG_LIBFRIBIDI
245 {
"ft_load_flags",
"set font loading flags for libfreetype",
OFFSET(ft_load_flags),
AV_OPT_TYPE_FLAGS, { .i64 = FT_LOAD_DEFAULT }, 0, INT_MAX,
FLAGS,
"ft_load_flags" },
246 {
"default", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_DEFAULT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
247 {
"no_scale", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_SCALE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
248 {
"no_hinting", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_HINTING }, .flags =
FLAGS, .unit =
"ft_load_flags" },
249 {
"render", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_RENDER }, .flags =
FLAGS, .unit =
"ft_load_flags" },
250 {
"no_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
251 {
"vertical_layout", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_VERTICAL_LAYOUT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
252 {
"force_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_FORCE_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
253 {
"crop_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_CROP_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
254 {
"pedantic", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_PEDANTIC }, .flags =
FLAGS, .unit =
"ft_load_flags" },
255 {
"ignore_global_advance_width", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH }, .flags =
FLAGS, .unit =
"ft_load_flags" },
256 {
"no_recurse", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_RECURSE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
257 {
"ignore_transform", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_TRANSFORM }, .flags =
FLAGS, .unit =
"ft_load_flags" },
258 {
"monochrome", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_MONOCHROME }, .flags =
FLAGS, .unit =
"ft_load_flags" },
259 {
"linear_design", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_LINEAR_DESIGN }, .flags =
FLAGS, .unit =
"ft_load_flags" },
260 {
"no_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
266 #undef __FTERRORS_H__
267 #define FT_ERROR_START_LIST {
268 #define FT_ERRORDEF(e, v, s) { (e), (s) },
269 #define FT_ERROR_END_LIST { 0, NULL } };
278 #define FT_ERRMSG(e) ft_errors[e].err_msg
280 typedef struct Glyph {
282 FT_Glyph border_glyph;
285 FT_Bitmap border_bitmap;
294 const Glyph *
a = key, *bb =
b;
295 int64_t diff = (int64_t)a->code - (int64_t)bb->code;
296 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
305 FT_BitmapGlyph bitmapglyph;
321 if (FT_Get_Glyph(s->
face->glyph, &glyph->glyph)) {
326 glyph->border_glyph = glyph->glyph;
327 if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->
stroker, 0, 0) ||
328 FT_Glyph_To_Bitmap(&glyph->border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
332 bitmapglyph = (FT_BitmapGlyph) glyph->border_glyph;
333 glyph->border_bitmap = bitmapglyph->bitmap;
335 if (FT_Glyph_To_Bitmap(&glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
339 bitmapglyph = (FT_BitmapGlyph) glyph->glyph;
341 glyph->bitmap = bitmapglyph->bitmap;
342 glyph->bitmap_left = bitmapglyph->left;
343 glyph->bitmap_top = bitmapglyph->top;
344 glyph->advance = s->
face->glyph->advance.x >> 6;
347 FT_Glyph_Get_CBox(glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
374 err = FT_New_Face(s->
library, path, index, &s->
face);
383 #if CONFIG_LIBFONTCONFIG
387 FcConfig *fontconfig;
388 FcPattern *pat, *best;
389 FcResult result = FcResultMatch;
395 fontconfig = FcInitLoadConfigAndFonts();
401 (
uint8_t *)(intptr_t)
"default");
407 FcPatternAddString(pat, FC_FAMILY, s->font);
409 FcPatternAddDouble(pat, FC_SIZE, (
double)s->
fontsize);
411 FcDefaultSubstitute(pat);
413 if (!FcConfigSubstitute(fontconfig, pat, FcMatchPattern)) {
415 FcPatternDestroy(pat);
419 best = FcFontMatch(fontconfig, pat, &result);
420 FcPatternDestroy(pat);
422 if (!best || result != FcResultMatch) {
424 "Cannot find a valid font for the family %s\n",
430 FcPatternGetInteger(best, FC_INDEX, 0, &index ) != FcResultMatch ||
431 FcPatternGetDouble (best, FC_SIZE, 0, &size ) != FcResultMatch) {
436 if (FcPatternGetString(best, FC_FILE, 0, &filename) != FcResultMatch) {
449 FcConfigDestroy(fontconfig);
451 FcPatternDestroy(best);
465 #if CONFIG_LIBFONTCONFIG
466 err = load_font_fontconfig(ctx);
483 "The text file '%s' could not be read or is empty\n",
493 memcpy(s->
text, textbuf, textbuf_size);
494 s->
text[textbuf_size] = 0;
502 return c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
505 #if CONFIG_LIBFRIBIDI
511 static const FriBidiFlags
flags = FRIBIDI_FLAGS_DEFAULT |
512 FRIBIDI_FLAGS_ARABIC;
513 FriBidiChar *unicodestr = NULL;
515 FriBidiParType direction = FRIBIDI_PAR_LTR;
516 FriBidiStrIndex line_start = 0;
517 FriBidiStrIndex line_end = 0;
518 FriBidiLevel *embedding_levels = NULL;
519 FriBidiArabicProp *ar_props = NULL;
520 FriBidiCharType *bidi_types = NULL;
523 len = strlen(s->
text);
527 len = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8,
528 s->
text, len, unicodestr);
535 fribidi_get_bidi_types(unicodestr, len, bidi_types);
538 if (!embedding_levels) {
542 if (!fribidi_get_par_embedding_levels(bidi_types, len, &direction,
552 fribidi_get_joining_types(unicodestr, len, ar_props);
553 fribidi_join_arabic(bidi_types, len, embedding_levels, ar_props);
554 fribidi_shape(flags, embedding_levels, len, ar_props, unicodestr);
556 for (line_end = 0, line_start = 0; line_end <
len; line_end++) {
557 if (
is_newline(unicodestr[line_end]) || line_end == len - 1) {
558 if (!fribidi_reorder_line(flags, bidi_types,
559 line_end - line_start + 1, line_start,
560 direction, embedding_levels, unicodestr,
564 line_start = line_end + 1;
569 for (i = 0, j = 0; i <
len; i++)
570 if (unicodestr[i] != FRIBIDI_CHAR_FILL)
571 unicodestr[j++] = unicodestr[i];
580 len = fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8,
581 unicodestr, len, s->
text);
599 #if FF_API_DRAWTEXT_OLD_TIMELINE
602 "you are encouraged to use the generic timeline support through the 'enable' option\n");
605 if (!s->
fontfile && !CONFIG_LIBFONTCONFIG) {
613 "Both text and text file provided. Please provide only one\n");
620 #if CONFIG_LIBFRIBIDI
622 if ((err = shape_text(ctx)) < 0)
642 "Either text, a valid file or a timecode must be provided\n");
646 if ((err = FT_Init_FreeType(&(s->
library)))) {
648 "Could not load FreeType: %s\n",
FT_ERRMSG(err));
657 if ((err = FT_Set_Pixel_Sizes(s->
face, 0, s->
fontsize))) {
668 FT_Stroker_Set(s->
stroker, s->
borderw << 6, FT_STROKER_LINECAP_ROUND,
669 FT_STROKER_LINEJOIN_ROUND, 0);
678 if ((err =
load_glyph(ctx, &glyph,
' ')) < 0) {
685 (strchr(s->
text,
'%') || strchr(s->
text,
'\\')))
704 FT_Done_Glyph(glyph->glyph);
705 FT_Done_Glyph(glyph->border_glyph);
716 #if FF_API_DRAWTEXT_OLD_TIMELINE
728 FT_Done_Face(s->
face);
762 #if FF_API_DRAWTEXT_OLD_TIMELINE
775 #if FF_API_DRAWTEXT_OLD_TIMELINE
789 if (!strcmp(cmd,
"reinit")) {
795 if ((ret =
init(ctx)) < 0)
804 char *fct,
unsigned argc,
char **argv,
int tag)
813 char *fct,
unsigned argc,
char **argv,
int tag)
820 fmt = argc >= 1 ? argv[0] :
"flt";
829 if (!strcmp(fmt,
"flt")) {
831 }
else if (!strcmp(fmt,
"hms")) {
835 int64_t ms =
round(pts * 1000);
842 (
int)(ms / (60 * 60 * 1000)),
843 (
int)(ms / (60 * 1000)) % 60,
844 (
int)(ms / 1000) % 60,
855 char *fct,
unsigned argc,
char **argv,
int tag)
864 char *fct,
unsigned argc,
char **argv,
int tag)
874 #if !HAVE_LOCALTIME_R
882 char *fct,
unsigned argc,
char **argv,
int tag)
884 const char *
fmt = argc ? argv[0] :
"%Y-%m-%d %H:%M:%S";
898 char *fct,
unsigned argc,
char **argv,
int tag)
909 "Expression '%s' for the expr text expansion function is not valid\n",
918 char *fct,
unsigned argc,
char **argv,
int tag)
924 unsigned int positions = 0;
925 char fmt_str[30] =
"%";
938 "Expression '%s' for the expr text expansion function is not valid\n",
943 if (!strchr(
"xXdu", argv[1][0])) {
945 " allowed values: 'x', 'X', 'd', 'u'\n", argv[1][0]);
950 ret = sscanf(argv[2],
"%u", &positions);
953 " to print: '%s'\n", argv[2]);
958 feclearexcept(FE_ALL_EXCEPT);
960 if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) {
961 av_log(ctx,
AV_LOG_ERROR,
"Conversion of floating-point result to int failed. Control register: 0x%08x. Conversion result: %d\n", ret, intval);
966 av_strlcatf(fmt_str,
sizeof(fmt_str),
"0%u", positions);
967 av_strlcatf(fmt_str,
sizeof(fmt_str),
"%c", argv[1][0]);
970 res, argv[0], fmt_str);
997 unsigned argc,
char **argv)
1025 const char *text = *rtext;
1026 char *argv[16] = { NULL };
1027 unsigned argc = 0, i;
1052 if ((ret =
eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
1055 *rtext = (
char *)text + 1;
1058 for (i = 0; i < argc; i++)
1069 if (*text ==
'\\' && text[1]) {
1072 }
else if (*text ==
'%') {
1094 Glyph *glyph = NULL;
1096 for (i = 0, p = text; *p; i++) {
1098 Glyph
dummy = { 0 };
1102 if (code ==
'\n' || code ==
'\r' || code ==
'\t')
1108 bitmap = borderw ? glyph->border_bitmap : glyph->bitmap;
1110 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
1111 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
1119 bitmap.buffer, bitmap.pitch,
1120 bitmap.width, bitmap.rows,
1121 bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
1134 uint32_t code = 0, prev_code = 0;
1135 int x = 0,
y = 0, i = 0,
ret;
1136 int max_text_line_w = 0,
len;
1140 int y_min = 32000, y_max = -32000;
1141 int x_min = 32000, x_max = -32000;
1143 Glyph *glyph = NULL, *prev_glyph = NULL;
1144 Glyph
dummy = { 0 };
1146 time_t now = time(0);
1204 for (i = 0, p = text; *p; i++) {
1214 y_min =
FFMIN(glyph->bbox.yMin, y_min);
1215 y_max =
FFMAX(glyph->bbox.yMax, y_max);
1216 x_min =
FFMIN(glyph->bbox.xMin, x_min);
1217 x_max =
FFMAX(glyph->bbox.xMax, x_max);
1224 for (i = 0, p = text; *p; i++) {
1228 if (prev_code ==
'\r' && code ==
'\n')
1234 max_text_line_w =
FFMAX(max_text_line_w, x);
1246 if (s->
use_kerning && prev_glyph && glyph->code) {
1247 FT_Get_Kerning(s->
face, prev_glyph->code, glyph->code,
1248 ft_kerning_default, &delta);
1253 s->
positions[i].x = x + glyph->bitmap_left;
1254 s->
positions[i].y =
y - glyph->bitmap_top + y_max;
1256 else x += glyph->advance;
1259 max_text_line_w =
FFMAX(x, max_text_line_w);
1274 #if FF_API_DRAWTEXT_OLD_TIMELINE
1285 box_w =
FFMIN(width - 1 , max_text_line_w);
1292 s->
x, s->
y, box_w, box_h);
1322 #if CONFIG_LIBFRIBIDI
1323 if (s->text_shaping)
1324 if ((ret = shape_text(ctx)) < 0)
1352 .needs_writable = 1,
1367 .description =
NULL_IF_CONFIG_SMALL(
"Draw text on top of video frames using libfreetype library."),
1369 .priv_class = &drawtext_class,
1373 .
inputs = avfilter_vf_drawtext_inputs,
1374 .
outputs = avfilter_vf_drawtext_outputs,