53 #include FT_FREETYPE_H
57 #include <fontconfig/fontconfig.h>
66 "max_glyph_a",
"ascent",
67 "max_glyph_d",
"descent",
85 static double drand(
void *opaque,
double min,
double max)
163 #if FF_API_DRAWTEXT_OLD_TIMELINE
178 #define OFFSET(x) offsetof(DrawTextContext, x)
179 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
198 #if FF_API_DRAWTEXT_OLD_TIMELINE
213 {
"fix_bounds",
"if true, check and fix text coords to avoid clipping",
OFFSET(fix_bounds),
AV_OPT_TYPE_INT, {.i64=1}, 0, 1,
FLAGS},
214 {
"start_number",
"start frame number for n/frame_num variable",
OFFSET(start_number),
AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,
FLAGS},
217 {
"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" },
218 {
"default", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_DEFAULT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
219 {
"no_scale", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_SCALE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
220 {
"no_hinting", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_HINTING }, .flags =
FLAGS, .unit =
"ft_load_flags" },
221 {
"render", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_RENDER }, .flags =
FLAGS, .unit =
"ft_load_flags" },
222 {
"no_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
223 {
"vertical_layout", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_VERTICAL_LAYOUT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
224 {
"force_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_FORCE_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
225 {
"crop_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_CROP_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
226 {
"pedantic", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_PEDANTIC }, .flags =
FLAGS, .unit =
"ft_load_flags" },
227 {
"ignore_global_advance_width", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH }, .flags =
FLAGS, .unit =
"ft_load_flags" },
228 {
"no_recurse", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_RECURSE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
229 {
"ignore_transform", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_TRANSFORM }, .flags =
FLAGS, .unit =
"ft_load_flags" },
230 {
"monochrome", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_MONOCHROME }, .flags =
FLAGS, .unit =
"ft_load_flags" },
231 {
"linear_design", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_LINEAR_DESIGN }, .flags =
FLAGS, .unit =
"ft_load_flags" },
232 {
"no_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
238 #undef __FTERRORS_H__
239 #define FT_ERROR_START_LIST {
240 #define FT_ERRORDEF(e, v, s) { (e), (s) },
241 #define FT_ERROR_END_LIST { 0, NULL } };
250 #define FT_ERRMSG(e) ft_errors[e].err_msg
256 FT_Bitmap border_bitmap;
265 const Glyph *
a = key, *bb =
b;
266 int64_t diff = (int64_t)a->code - (int64_t)bb->code;
267 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
276 FT_BitmapGlyph bitmapglyph;
287 !(glyph->glyph =
av_mallocz(
sizeof(*glyph->glyph)))) {
293 if (FT_Get_Glyph(s->
face->glyph, glyph->glyph)) {
298 FT_Glyph border_glyph = *glyph->glyph;
299 if (FT_Glyph_StrokeBorder(&border_glyph, s->
stroker, 0, 0) ||
300 FT_Glyph_To_Bitmap(&border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
304 bitmapglyph = (FT_BitmapGlyph) border_glyph;
305 glyph->border_bitmap = bitmapglyph->bitmap;
307 if (FT_Glyph_To_Bitmap(glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
311 bitmapglyph = (FT_BitmapGlyph) *glyph->glyph;
313 glyph->bitmap = bitmapglyph->bitmap;
314 glyph->bitmap_left = bitmapglyph->left;
315 glyph->bitmap_top = bitmapglyph->top;
316 glyph->advance = s->
face->glyph->advance.x >> 6;
319 FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
346 err = FT_New_Face(s->
library, path, index, &s->
face);
354 #if CONFIG_FONTCONFIG
355 static int load_font_fontconfig(
AVFilterContext *ctx,
const char **error)
358 FcConfig *fontconfig;
359 FcPattern *pattern, *fpat;
360 FcResult result = FcResultMatch;
365 fontconfig = FcInitLoadConfigAndFonts();
367 *error =
"impossible to init fontconfig\n";
371 (
uint8_t *)(intptr_t)
"default");
373 *error =
"could not parse fontconfig pattern";
376 if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
377 *error =
"could not substitue fontconfig options";
380 FcDefaultSubstitute(pattern);
381 fpat = FcFontMatch(fontconfig, pattern, &result);
382 if (!fpat || result != FcResultMatch) {
383 *error =
"impossible to find a matching font";
386 if (FcPatternGetString (fpat, FC_FILE, 0, &filename) != FcResultMatch ||
387 FcPatternGetInteger(fpat, FC_INDEX, 0, &index ) != FcResultMatch ||
388 FcPatternGetDouble (fpat, FC_SIZE, 0, &size ) != FcResultMatch) {
389 *error =
"impossible to find font information";
398 FcPatternDestroy(fpat);
399 FcPatternDestroy(pattern);
400 FcConfigDestroy(fontconfig);
409 const char *error =
"unknown error\n";
415 #if CONFIG_FONTCONFIG
416 err = load_font_fontconfig(ctx, &error);
434 "The text file '%s' could not be read or is empty\n",
441 memcpy(s->
text, textbuf, textbuf_size);
442 s->
text[textbuf_size] = 0;
454 #if FF_API_DRAWTEXT_OLD_TIMELINE
457 "you are encouraged to use the generic timeline support through the 'enable' option\n");
460 if (!s->
fontfile && !CONFIG_FONTCONFIG) {
468 "Both text and text file provided. Please provide only one\n");
491 "Either text, a valid file or a timecode must be provided\n");
495 if ((err = FT_Init_FreeType(&(s->
library)))) {
497 "Could not load FreeType: %s\n",
FT_ERRMSG(err));
506 if ((err = FT_Set_Pixel_Sizes(s->
face, 0, s->
fontsize))) {
517 FT_Stroker_Set(s->
stroker, s->
borderw << 6, FT_STROKER_LINECAP_ROUND,
518 FT_STROKER_LINEJOIN_ROUND, 0);
527 if ((err =
load_glyph(ctx, &glyph,
' ')) < 0) {
534 (strchr(s->
text,
'%') || strchr(s->
text,
'\\')))
552 FT_Done_Glyph(*glyph->glyph);
564 #if FF_API_DRAWTEXT_OLD_TIMELINE
576 FT_Done_Face(s->
face);
585 return c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
614 #if FF_API_DRAWTEXT_OLD_TIMELINE
627 #if FF_API_DRAWTEXT_OLD_TIMELINE
641 if (!strcmp(cmd,
"reinit")) {
647 if ((ret =
init(ctx)) < 0)
656 char *fct,
unsigned argc,
char **argv,
int tag)
665 char *fct,
unsigned argc,
char **argv,
int tag)
674 char *fct,
unsigned argc,
char **argv,
int tag)
683 char *fct,
unsigned argc,
char **argv,
int tag)
693 #if !HAVE_LOCALTIME_R
701 char *fct,
unsigned argc,
char **argv,
int tag)
703 const char *
fmt = argc ? argv[0] :
"%Y-%m-%d %H:%M:%S";
717 char *fct,
unsigned argc,
char **argv,
int tag)
728 "Expression '%s' for the expr text expansion function is not valid\n",
754 unsigned argc,
char **argv)
782 const char *text = *rtext;
783 char *argv[16] = { NULL };
784 unsigned argc = 0, i;
809 if ((ret =
eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
812 *rtext = (
char *)text + 1;
815 for (i = 0; i < argc; i++)
823 char *text = s->
text;
829 if (*text ==
'\\' && text[1]) {
832 }
else if (*text ==
'%') {
856 for (i = 0, p = text; *p; i++) {
862 if (code ==
'\n' || code ==
'\r' || code ==
'\t')
868 bitmap = borderw ? glyph->border_bitmap : glyph->bitmap;
870 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
871 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
879 bitmap.buffer, bitmap.pitch,
880 bitmap.width, bitmap.rows,
881 bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
894 uint32_t code = 0, prev_code = 0;
895 int x = 0,
y = 0, i = 0,
ret;
896 int max_text_line_w = 0,
len;
900 int y_min = 32000, y_max = -32000;
901 int x_min = 32000, x_max = -32000;
903 Glyph *glyph = NULL, *prev_glyph = NULL;
906 time_t now = time(0);
950 for (i = 0, p = text; *p; i++) {
960 y_min =
FFMIN(glyph->bbox.yMin, y_min);
961 y_max =
FFMAX(glyph->bbox.yMax, y_max);
962 x_min =
FFMIN(glyph->bbox.xMin, x_min);
963 x_max =
FFMAX(glyph->bbox.xMax, x_max);
970 for (i = 0, p = text; *p; i++) {
974 if (prev_code ==
'\r' && code ==
'\n')
980 max_text_line_w =
FFMAX(max_text_line_w, x);
993 FT_Get_Kerning(s->
face, prev_glyph->code, glyph->code,
994 ft_kerning_default, &delta);
999 s->
positions[i].x = x + glyph->bitmap_left;
1000 s->
positions[i].y =
y - glyph->bitmap_top + y_max;
1002 else x += glyph->advance;
1005 max_text_line_w =
FFMAX(x, max_text_line_w);
1020 #if FF_API_DRAWTEXT_OLD_TIMELINE
1031 box_w =
FFMIN(width - 1 , max_text_line_w);
1038 s->
x, s->
y, box_w, box_h);
1092 .needs_writable = 1,
1107 .description =
NULL_IF_CONFIG_SMALL(
"Draw text on top of video frames using libfreetype library."),
1109 .priv_class = &drawtext_class,
1113 .
inputs = avfilter_vf_drawtext_inputs,
1114 .
outputs = avfilter_vf_drawtext_outputs,