33 #include "libavutil/avstring.h"
34 #include "libavutil/bprint.h"
35 #include "libavutil/common.h"
36 #include "libavutil/file.h"
37 #include "libavutil/eval.h"
38 #include "libavutil/opt.h"
39 #include "libavutil/random_seed.h"
40 #include "libavutil/parseutils.h"
41 #include "libavutil/timecode.h"
43 #include "libavutil/lfg.h"
51 #include FT_FREETYPE_H
54 #include <fontconfig/fontconfig.h>
63 "max_glyph_a",
"ascent",
64 "max_glyph_d",
"descent",
81 static double drand(
void *opaque,
double min,
double max)
170 #define OFFSET(x) offsetof(DrawTextContext, x)
171 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
201 {
"fix_bounds",
"if true, check and fix text coords to avoid clipping",
OFFSET(fix_bounds),
AV_OPT_TYPE_INT, {.i64=1}, 0, 1,
FLAGS},
204 {
"ft_load_flags",
"set font loading flags for libfreetype",
OFFSET(ft_load_flags),
AV_OPT_TYPE_FLAGS, {.i64=FT_LOAD_DEFAULT|FT_LOAD_RENDER}, 0, INT_MAX,
FLAGS,
"ft_load_flags"},
205 {
"default",
"set default", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_DEFAULT}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
206 {
"no_scale",
"set no_scale", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_NO_SCALE}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
207 {
"no_hinting",
"set no_hinting", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_NO_HINTING}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
208 {
"render",
"set render", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_RENDER}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
209 {
"no_bitmap",
"set no_bitmap", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_NO_BITMAP}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
210 {
"vertical_layout",
"set vertical_layout", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_VERTICAL_LAYOUT}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
211 {
"force_autohint",
"set force_autohint", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_FORCE_AUTOHINT}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
212 {
"crop_bitmap",
"set crop_bitmap", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_CROP_BITMAP}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
213 {
"pedantic",
"set pedantic", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_PEDANTIC}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
214 {
"ignore_global_advance_width",
"set ignore_global_advance_width", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
215 {
"no_recurse",
"set no_recurse", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_NO_RECURSE}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
216 {
"ignore_transform",
"set ignore_transform", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_IGNORE_TRANSFORM}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
217 {
"monochrome",
"set monochrome", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_MONOCHROME}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
218 {
"linear_design",
"set linear_design", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_LINEAR_DESIGN}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
219 {
"no_autohint",
"set no_autohint", 0,
AV_OPT_TYPE_CONST, {.i64=FT_LOAD_NO_AUTOHINT}, INT_MIN, INT_MAX,
FLAGS,
"ft_load_flags"},
225 #undef __FTERRORS_H__
226 #define FT_ERROR_START_LIST {
227 #define FT_ERRORDEF(e, v, s) { (e), (s) },
228 #define FT_ERROR_END_LIST { 0, NULL } };
237 #define FT_ERRMSG(e) ft_errors[e].err_msg
251 const Glyph *
a = key, *bb =
b;
252 int64_t
diff = (int64_t)a->code - (int64_t)bb->code;
253 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
272 !(glyph->glyph =
av_mallocz(
sizeof(*glyph->glyph)))) {
278 if (FT_Get_Glyph(dtext->
face->glyph, glyph->glyph)) {
283 glyph->bitmap = dtext->
face->glyph->bitmap;
284 glyph->bitmap_left = dtext->
face->glyph->bitmap_left;
285 glyph->bitmap_top = dtext->
face->glyph->bitmap_top;
286 glyph->advance = dtext->
face->glyph->advance.x >> 6;
289 FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
316 err = FT_New_Face(dtext->
library, path, index, &dtext->
face);
324 #if CONFIG_FONTCONFIG
325 static int load_font_fontconfig(
AVFilterContext *ctx,
const char **error)
328 FcConfig *fontconfig;
329 FcPattern *pattern, *fpat;
330 FcResult result = FcResultMatch;
335 fontconfig = FcInitLoadConfigAndFonts();
337 *error =
"impossible to init fontconfig\n";
341 (
uint8_t *)(intptr_t)
"default");
343 *error =
"could not parse fontconfig pattern";
346 if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
347 *error =
"could not substitue fontconfig options";
350 FcDefaultSubstitute(pattern);
351 fpat = FcFontMatch(fontconfig, pattern, &result);
352 if (!fpat || result != FcResultMatch) {
353 *error =
"impossible to find a matching font";
356 if (FcPatternGetString (fpat, FC_FILE, 0, &filename) != FcResultMatch ||
357 FcPatternGetInteger(fpat, FC_INDEX, 0, &index ) != FcResultMatch ||
358 FcPatternGetDouble (fpat, FC_SIZE, 0, &size ) != FcResultMatch) {
359 *error =
"impossible to find font information";
368 FcPatternDestroy(fpat);
369 FcPatternDestroy(pattern);
370 FcConfigDestroy(fontconfig);
379 const char *error =
"unknown error\n";
385 #if CONFIG_FONTCONFIG
386 err = load_font_fontconfig(ctx, &error);
404 "The text file '%s' could not be read or is empty\n",
411 memcpy(dtext->
text, textbuf, textbuf_size);
412 dtext->
text[textbuf_size] = 0;
424 dtext->
class = &drawtext_class;
438 "Both text and text file provided. Please provide only one\n");
461 "Either text, a valid file or a timecode must be provided\n");
483 if ((err = FT_Init_FreeType(&(dtext->
library)))) {
485 "Could not load FreeType: %s\n",
FT_ERRMSG(err));
494 if ((err = FT_Set_Pixel_Sizes(dtext->
face, 0, dtext->
fontsize))) {
506 if ((err =
load_glyph(ctx, &glyph,
' ')) < 0) {
510 dtext->
tabsize *= glyph->advance;
513 (strchr(dtext->
text,
'%') || strchr(dtext->
text,
'\\')))
531 FT_Done_Glyph(*glyph->glyph);
553 FT_Done_Face(dtext->
face);
554 FT_Done_FreeType(dtext->
library);
561 return c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
605 if (!strcmp(cmd,
"reinit")) {
609 if ((ret =
init(ctx, arg)) < 0)
618 char *fct,
unsigned argc,
char **argv,
int tag)
627 char *fct,
unsigned argc,
char **argv,
int tag)
635 #if !HAVE_LOCALTIME_R
643 char *fct,
unsigned argc,
char **argv,
int tag)
645 const char *
fmt = argc ? argv[0] :
"%Y-%m-%d %H:%M:%S";
659 char *fct,
unsigned argc,
char **argv,
int tag)
667 &dtext->
prng, 0, ctx);
670 "Expression '%s' for the expr text expansion function is not valid\n",
694 unsigned argc,
char **argv)
722 const char *text = *rtext;
723 char *argv[16] = {
NULL };
724 unsigned argc = 0, i;
749 if ((ret =
eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
752 *rtext = (
char *)text + 1;
755 for (i = 0; i < argc; i++)
763 char *text = dtext->
text;
769 if (*text ==
'\\' && text[1]) {
772 }
else if (*text ==
'%') {
795 for (i = 0, p = text; *p; i++) {
800 if (code ==
'\n' || code ==
'\r' || code ==
'\t')
806 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
807 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
815 glyph->bitmap.buffer, glyph->bitmap.pitch,
816 glyph->bitmap.width, glyph->bitmap.rows,
817 glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
828 uint32_t code = 0, prev_code = 0;
829 int x = 0,
y = 0, i = 0, ret;
830 int max_text_line_w = 0,
len;
832 char *text = dtext->
text;
834 int y_min = 32000, y_max = -32000;
835 int x_min = 32000, x_max = -32000;
837 Glyph *glyph =
NULL, *prev_glyph =
NULL;
840 time_t now = time(0);
884 for (i = 0, p = text; *p; i++) {
894 y_min =
FFMIN(glyph->bbox.yMin, y_min);
895 y_max =
FFMAX(glyph->bbox.yMax, y_max);
896 x_min =
FFMIN(glyph->bbox.xMin, x_min);
897 x_max =
FFMAX(glyph->bbox.xMax, x_max);
904 for (i = 0, p = text; *p; i++) {
908 if (prev_code ==
'\r' && code ==
'\n')
913 max_text_line_w =
FFMAX(max_text_line_w, x);
925 if (dtext->
use_kerning && prev_glyph && glyph->code) {
926 FT_Get_Kerning(dtext->
face, prev_glyph->code, glyph->code,
927 ft_kerning_default, &delta);
932 dtext->
positions[i].x = x + glyph->bitmap_left;
933 dtext->
positions[i].y =
y - glyph->bitmap_top + y_max;
935 else x += glyph->advance;
938 max_text_line_w =
FFMAX(x, max_text_line_w);
958 box_w =
FFMIN(width - 1 , max_text_line_w);
965 dtext->
x, dtext->
y, box_w, box_h);
1029 .description =
NULL_IF_CONFIG_SMALL(
"Draw text on top of video frames using libfreetype library."),
1035 .
inputs = avfilter_vf_drawtext_inputs,
1036 .
outputs = avfilter_vf_drawtext_outputs,
1038 .priv_class = &drawtext_class,