34 #include <sys/types.h>
42 #if CONFIG_LIBFONTCONFIG
43 #include <fontconfig/fontconfig.h>
69 #include FT_FREETYPE_H
79 "max_glyph_a",
"ascent",
80 "max_glyph_d",
"descent",
98 static double drand(
void *opaque,
double min,
double max)
141 #if CONFIG_LIBFONTCONFIG
188 #if CONFIG_LIBFRIBIDI
194 #define OFFSET(x) offsetof(DrawTextContext, x)
195 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
215 #if CONFIG_LIBFONTCONFIG
230 {
"fix_bounds",
"if true, check and fix text coords to avoid clipping",
OFFSET(fix_bounds),
AV_OPT_TYPE_INT, {.i64=1}, 0, 1,
FLAGS},
231 {
"start_number",
"start frame number for n/frame_num variable",
OFFSET(start_number),
AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,
FLAGS},
233 #if CONFIG_LIBFRIBIDI
238 {
"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" },
244 {
"vertical_layout",
NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_VERTICAL_LAYOUT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
248 {
"ignore_global_advance_width",
NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH }, .flags =
FLAGS, .unit =
"ft_load_flags" },
250 {
"ignore_transform",
NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_TRANSFORM }, .flags =
FLAGS, .unit =
"ft_load_flags" },
259 #undef __FTERRORS_H__
260 #define FT_ERROR_START_LIST {
261 #define FT_ERRORDEF(e, v, s) { (e), (s) },
262 #define FT_ERROR_END_LIST { 0, NULL } };
271 #define FT_ERRMSG(e) ft_errors[e].err_msg
273 typedef struct Glyph {
275 FT_Glyph border_glyph;
278 FT_Bitmap border_bitmap;
287 const Glyph *
a = key, *bb =
b;
288 int64_t
diff = (int64_t)a->code - (int64_t)bb->code;
289 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
298 FT_BitmapGlyph bitmapglyph;
314 if (FT_Get_Glyph(s->
face->glyph, &glyph->glyph)) {
319 glyph->border_glyph = glyph->glyph;
320 if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->
stroker, 0, 0) ||
321 FT_Glyph_To_Bitmap(&glyph->border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
325 bitmapglyph = (FT_BitmapGlyph) glyph->border_glyph;
326 glyph->border_bitmap = bitmapglyph->bitmap;
328 if (FT_Glyph_To_Bitmap(&glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
332 bitmapglyph = (FT_BitmapGlyph) glyph->glyph;
334 glyph->bitmap = bitmapglyph->bitmap;
335 glyph->bitmap_left = bitmapglyph->left;
336 glyph->bitmap_top = bitmapglyph->top;
337 glyph->advance = s->
face->glyph->advance.x >> 6;
340 FT_Glyph_Get_CBox(glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
367 err = FT_New_Face(s->
library, path, index, &s->
face);
376 #if CONFIG_LIBFONTCONFIG
380 FcConfig *fontconfig;
381 FcPattern *pat, *best;
382 FcResult result = FcResultMatch;
388 fontconfig = FcInitLoadConfigAndFonts();
394 (
uint8_t *)(intptr_t)
"default");
400 FcPatternAddString(pat, FC_FAMILY, s->font);
402 FcPatternAddDouble(pat, FC_SIZE, (
double)s->
fontsize);
404 FcDefaultSubstitute(pat);
406 if (!FcConfigSubstitute(fontconfig, pat, FcMatchPattern)) {
408 FcPatternDestroy(pat);
412 best = FcFontMatch(fontconfig, pat, &result);
413 FcPatternDestroy(pat);
415 if (!best || result != FcResultMatch) {
417 "Cannot find a valid font for the family %s\n",
423 FcPatternGetInteger(best, FC_INDEX, 0, &index ) != FcResultMatch ||
424 FcPatternGetDouble (best, FC_SIZE, 0, &size ) != FcResultMatch) {
429 if (FcPatternGetString(best, FC_FILE, 0, &filename) != FcResultMatch) {
442 FcConfigDestroy(fontconfig);
444 FcPatternDestroy(best);
458 #if CONFIG_LIBFONTCONFIG
459 err = load_font_fontconfig(ctx);
476 "The text file '%s' could not be read or is empty\n",
481 if (textbuf_size > SIZE_MAX - 1 || !(tmp =
av_realloc(s->
text, textbuf_size + 1))) {
486 memcpy(s->
text, textbuf, textbuf_size);
487 s->
text[textbuf_size] = 0;
495 return c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
498 #if CONFIG_LIBFRIBIDI
504 static const FriBidiFlags
flags = FRIBIDI_FLAGS_DEFAULT |
505 FRIBIDI_FLAGS_ARABIC;
506 FriBidiChar *unicodestr =
NULL;
508 FriBidiParType direction = FRIBIDI_PAR_LTR;
509 FriBidiStrIndex line_start = 0;
510 FriBidiStrIndex line_end = 0;
511 FriBidiLevel *embedding_levels =
NULL;
512 FriBidiArabicProp *ar_props =
NULL;
513 FriBidiCharType *bidi_types =
NULL;
516 len = strlen(s->
text);
520 len = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8,
521 s->
text, len, unicodestr);
528 fribidi_get_bidi_types(unicodestr, len, bidi_types);
531 if (!embedding_levels) {
535 if (!fribidi_get_par_embedding_levels(bidi_types, len, &direction,
545 fribidi_get_joining_types(unicodestr, len, ar_props);
546 fribidi_join_arabic(bidi_types, len, embedding_levels, ar_props);
547 fribidi_shape(flags, embedding_levels, len, ar_props, unicodestr);
549 for (line_end = 0, line_start = 0; line_end <
len; line_end++) {
550 if (
is_newline(unicodestr[line_end]) || line_end == len - 1) {
551 if (!fribidi_reorder_line(flags, bidi_types,
552 line_end - line_start + 1, line_start,
553 direction, embedding_levels, unicodestr,
557 line_start = line_end + 1;
562 for (i = 0, j = 0; i <
len; i++)
563 if (unicodestr[i] != FRIBIDI_CHAR_FILL)
564 unicodestr[j++] = unicodestr[i];
573 len = fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8,
574 unicodestr, len, s->
text);
592 if (!s->
fontfile && !CONFIG_LIBFONTCONFIG) {
600 "Both text and text file provided. Please provide only one\n");
607 #if CONFIG_LIBFRIBIDI
609 if ((err = shape_text(ctx)) < 0)
629 "Either text, a valid file or a timecode must be provided\n");
633 if ((err = FT_Init_FreeType(&(s->
library)))) {
635 "Could not load FreeType: %s\n",
FT_ERRMSG(err));
644 if ((err = FT_Set_Pixel_Sizes(s->
face, 0, s->
fontsize))) {
655 FT_Stroker_Set(s->
stroker, s->
borderw << 6, FT_STROKER_LINECAP_ROUND,
656 FT_STROKER_LINEJOIN_ROUND, 0);
665 if ((err =
load_glyph(ctx, &glyph,
' ')) < 0) {
672 (strchr(s->
text,
'%') || strchr(s->
text,
'\\')))
691 FT_Done_Glyph(glyph->glyph);
692 FT_Done_Glyph(glyph->border_glyph);
712 FT_Done_Face(s->
face);
762 if (!strcmp(cmd,
"reinit")) {
768 if ((ret =
init(ctx)) < 0)
777 char *fct,
unsigned argc,
char **argv,
int tag)
786 char *fct,
unsigned argc,
char **argv,
int tag)
793 fmt = argc >= 1 ? argv[0] :
"flt";
802 if (!strcmp(fmt,
"flt")) {
804 }
else if (!strcmp(fmt,
"hms")) {
808 int64_t ms =
round(pts * 1000);
815 (
int)(ms / (60 * 60 * 1000)),
816 (
int)(ms / (60 * 1000)) % 60,
817 (
int)(ms / 1000) % 60,
828 char *fct,
unsigned argc,
char **argv,
int tag)
837 char *fct,
unsigned argc,
char **argv,
int tag)
848 char *fct,
unsigned argc,
char **argv,
int tag)
850 const char *
fmt = argc ? argv[0] :
"%Y-%m-%d %H:%M:%S";
864 char *fct,
unsigned argc,
char **argv,
int tag)
875 "Expression '%s' for the expr text expansion function is not valid\n",
884 char *fct,
unsigned argc,
char **argv,
int tag)
890 unsigned int positions = 0;
891 char fmt_str[30] =
"%";
904 "Expression '%s' for the expr text expansion function is not valid\n",
909 if (!strchr(
"xXdu", argv[1][0])) {
911 " allowed values: 'x', 'X', 'd', 'u'\n", argv[1][0]);
916 ret = sscanf(argv[2],
"%u", &positions);
919 " to print: '%s'\n", argv[2]);
924 feclearexcept(FE_ALL_EXCEPT);
926 if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) {
927 av_log(ctx,
AV_LOG_ERROR,
"Conversion of floating-point result to int failed. Control register: 0x%08x. Conversion result: %d\n", ret, intval);
932 av_strlcatf(fmt_str,
sizeof(fmt_str),
"0%u", positions);
933 av_strlcatf(fmt_str,
sizeof(fmt_str),
"%c", argv[1][0]);
936 res, argv[0], fmt_str);
963 unsigned argc,
char **argv)
991 const char *text = *rtext;
992 char *argv[16] = {
NULL };
993 unsigned argc = 0, i;
1018 if ((ret =
eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
1021 *rtext = (
char *)text + 1;
1024 for (i = 0; i < argc; i++)
1035 if (*text ==
'\\' && text[1]) {
1038 }
else if (*text ==
'%') {
1060 Glyph *glyph =
NULL;
1062 for (i = 0, p = text; *p; i++) {
1064 Glyph
dummy = { 0 };
1068 if (code ==
'\n' || code ==
'\r' || code ==
'\t')
1074 bitmap = borderw ? glyph->border_bitmap : glyph->bitmap;
1076 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
1077 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
1085 bitmap.buffer, bitmap.pitch,
1086 bitmap.width, bitmap.rows,
1087 bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
1100 uint32_t code = 0, prev_code = 0;
1101 int x = 0,
y = 0, i = 0,
ret;
1102 int max_text_line_w = 0,
len;
1106 int y_min = 32000, y_max = -32000;
1107 int x_min = 32000, x_max = -32000;
1109 Glyph *glyph =
NULL, *prev_glyph =
NULL;
1110 Glyph
dummy = { 0 };
1112 time_t now = time(0);
1170 for (i = 0, p = text; *p; i++) {
1180 y_min =
FFMIN(glyph->bbox.yMin, y_min);
1181 y_max =
FFMAX(glyph->bbox.yMax, y_max);
1182 x_min =
FFMIN(glyph->bbox.xMin, x_min);
1183 x_max =
FFMAX(glyph->bbox.xMax, x_max);
1190 for (i = 0, p = text; *p; i++) {
1194 if (prev_code ==
'\r' && code ==
'\n')
1200 max_text_line_w =
FFMAX(max_text_line_w, x);
1212 if (s->
use_kerning && prev_glyph && glyph->code) {
1213 FT_Get_Kerning(s->
face, prev_glyph->code, glyph->code,
1214 ft_kerning_default, &delta);
1219 s->
positions[i].x = x + glyph->bitmap_left;
1220 s->
positions[i].y =
y - glyph->bitmap_top + y_max;
1222 else x += glyph->advance;
1225 max_text_line_w =
FFMAX(x, max_text_line_w);
1241 box_w =
FFMIN(width - 1 , max_text_line_w);
1248 s->
x, s->
y, box_w, box_h);
1278 #if CONFIG_LIBFRIBIDI
1279 if (s->text_shaping)
1280 if ((ret = shape_text(ctx)) < 0)
1308 .needs_writable = 1,
1323 .description =
NULL_IF_CONFIG_SMALL(
"Draw text on top of video frames using libfreetype library."),
1325 .priv_class = &drawtext_class,
1329 .
inputs = avfilter_vf_drawtext_inputs,
1330 .
outputs = avfilter_vf_drawtext_outputs,