31 #include <sys/types.h>
37 #if CONFIG_LIBFONTCONFIG
38 #include <fontconfig/fontconfig.h>
42 #include "libavutil/common.h"
43 #include "libavutil/file.h"
44 #include "libavutil/eval.h"
45 #include "libavutil/opt.h"
46 #include "libavutil/mathematics.h"
47 #include "libavutil/random_seed.h"
48 #include "libavutil/parseutils.h"
49 #include "libavutil/pixdesc.h"
51 #include "libavutil/lfg.h"
59 #include FT_FREETYPE_H
81 static double drand(
void *opaque,
double min,
double max)
110 #if CONFIG_LIBFONTCONFIG
157 #define OFFSET(x) offsetof(DrawTextContext, x)
158 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM
161 #if CONFIG_LIBFONTCONFIG
178 {
"fix_bounds",
"if true, check and fix text coords to avoid clipping",
182 {
"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" },
188 {
"vertical_layout",
NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_VERTICAL_LAYOUT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
192 {
"ignore_global_advance_width",
NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH }, .flags =
FLAGS, .unit =
"ft_load_flags" },
194 {
"ignore_transform",
NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_TRANSFORM }, .flags =
FLAGS, .unit =
"ft_load_flags" },
212 #undef __FTERRORS_H__
213 #define FT_ERROR_START_LIST {
214 #define FT_ERRORDEF(e, v, s) { (e), (s) },
215 #define FT_ERROR_END_LIST { 0, NULL } };
224 #define FT_ERRMSG(e) ft_errors[e].err_msg
226 typedef struct Glyph {
238 const Glyph *
a = key, *bb =
b;
239 int64_t diff = (int64_t)a->code - (int64_t)bb->code;
240 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
259 !(glyph->glyph =
av_mallocz(
sizeof(*glyph->glyph)))) {
265 if (FT_Get_Glyph(s->
face->glyph, glyph->glyph)) {
270 glyph->bitmap = s->
face->glyph->bitmap;
271 glyph->bitmap_left = s->
face->glyph->bitmap_left;
272 glyph->bitmap_top = s->
face->glyph->bitmap_top;
273 glyph->advance = s->
face->glyph->advance.x >> 6;
276 FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
300 #if !CONFIG_LIBFONTCONFIG
308 FcPattern *pat, *best;
309 FcResult result = FcResultMatch;
321 if (!(pat = FcPatternCreate()))
324 FcPatternAddString(pat, FC_FAMILY, s->font);
325 FcPatternAddBool(pat, FC_OUTLINE, FcTrue);
326 FcPatternAddDouble(pat, FC_SIZE, (
double)s->
fontsize);
328 FcDefaultSubstitute(pat);
330 if (!FcConfigSubstitute(
NULL, pat, FcMatchPattern)) {
331 FcPatternDestroy(pat);
335 best = FcFontMatch(
NULL, pat, &result);
336 FcPatternDestroy(pat);
338 if (!best || result == FcResultNoMatch) {
340 "Cannot find a valid font for the family %s\n",
345 if (FcPatternGetBool(best, FC_OUTLINE, 0, &fc_bool) != FcResultMatch ||
352 if (FcPatternGetString(best, FC_FAMILY, 0, &fc_string) != FcResultMatch) {
358 if (FcPatternGetString(best, FC_FILE, 0, &fc_string) != FcResultMatch) {
371 FcPatternDestroy(best);
391 "Both text and text file provided. Please provide only one\n");
396 "The text file '%s' could not be read or is empty\n",
403 memcpy(s->
text, textbuf, textbuf_size);
404 s->
text[textbuf_size] = 0;
410 "Either text or a valid file must be provided\n");
432 if ((err = FT_Init_FreeType(&(s->
library)))) {
434 "Could not load FreeType: %s\n",
FT_ERRMSG(err));
444 if ((err = FT_Set_Pixel_Sizes(s->
face, 0, s->
fontsize))) {
456 if ((err =
load_glyph(ctx, &glyph,
' ') < 0)) {
462 #if !HAVE_LOCALTIME_R
505 FT_Done_Face(s->
face);
508 for (i = 0; i < 4; i++) {
517 return c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
523 uint32_t code = 0, prev_code = 0;
524 int x = 0, y = 0, i = 0, ret;
525 int text_height, baseline;
526 char *text = s->
text;
529 int y_min = 32000, y_max = -32000;
531 Glyph *glyph =
NULL, *prev_glyph =
NULL;
537 time_t now = time(0);
543 buf_size = 2*strlen(s->
text)+1;
545 localtime_r(&now, <ime);
549 if (strftime(buf, buf_size, s->
text, <ime) != 0 || *buf == 0)
574 for (i = 0, p = text; *p; i++) {
586 y_min =
FFMIN(glyph->bbox.yMin, y_min);
587 y_max =
FFMAX(glyph->bbox.yMax, y_max);
589 text_height = y_max - y_min;
594 for (i = 0, p = text; *p; i++) {
598 if (prev_code ==
'\r' && code ==
'\n')
603 str_w =
FFMAX(str_w, x - s->
x);
616 FT_Get_Kerning(s->
face, prev_glyph->code, glyph->code,
617 ft_kerning_default, &delta);
621 if (x + glyph->bbox.xMax >= width) {
622 str_w =
FFMAX(str_w, x);
628 s->
positions[i].x = x + glyph->bitmap_left;
629 s->
positions[i].y = y - glyph->bitmap_top + baseline;
631 else x += glyph->advance;
635 y =
FFMIN(y + text_height, height - 1);
708 #define GET_BITMAP_VAL(r, c) \
709 bitmap->pixel_mode == FT_PIXEL_MODE_MONO ? \
710 (bitmap->buffer[(r) * bitmap->pitch + ((c)>>3)] & (0x80 >> ((c)&7))) * 255 : \
711 bitmap->buffer[(r) * bitmap->pitch + (c)]
713 #define SET_PIXEL_YUV(frame, yuva_color, val, x, y, hsub, vsub) { \
714 luma_pos = ((x) ) + ((y) ) * frame->linesize[0]; \
715 alpha = yuva_color[3] * (val) * 129; \
716 frame->data[0][luma_pos] = (alpha * yuva_color[0] + (255*255*129 - alpha) * frame->data[0][luma_pos] ) >> 23; \
717 if (((x) & ((1<<(hsub)) - 1)) == 0 && ((y) & ((1<<(vsub)) - 1)) == 0) {\
718 chroma_pos1 = ((x) >> (hsub)) + ((y) >> (vsub)) * frame->linesize[1]; \
719 chroma_pos2 = ((x) >> (hsub)) + ((y) >> (vsub)) * frame->linesize[2]; \
720 frame->data[1][chroma_pos1] = (alpha * yuva_color[1] + (255*255*129 - alpha) * frame->data[1][chroma_pos1]) >> 23; \
721 frame->data[2][chroma_pos2] = (alpha * yuva_color[2] + (255*255*129 - alpha) * frame->data[2][chroma_pos2]) >> 23; \
726 unsigned int y,
unsigned int width,
unsigned int height,
727 const uint8_t yuva_color[4],
int hsub,
int vsub)
730 unsigned int luma_pos, chroma_pos1, chroma_pos2;
733 for (r = 0; r < bitmap->rows && r+y <
height; r++) {
734 for (c = 0; c < bitmap->width && c+x <
width; c++) {
740 SET_PIXEL_YUV(frame, yuva_color, src_val, c+x, y+r, hsub, vsub);
747 #define SET_PIXEL_RGB(frame, rgba_color, val, x, y, pixel_step, r_off, g_off, b_off, a_off) { \
748 p = frame->data[0] + (x) * pixel_step + ((y) * frame->linesize[0]); \
749 alpha = rgba_color[3] * (val) * 129; \
750 *(p+r_off) = (alpha * rgba_color[0] + (255*255*129 - alpha) * *(p+r_off)) >> 23; \
751 *(p+g_off) = (alpha * rgba_color[1] + (255*255*129 - alpha) * *(p+g_off)) >> 23; \
752 *(p+b_off) = (alpha * rgba_color[2] + (255*255*129 - alpha) * *(p+b_off)) >> 23; \
756 unsigned int x,
unsigned int y,
757 unsigned int width,
unsigned int height,
int pixel_step,
764 for (r = 0; r < bitmap->rows && r+y <
height; r++) {
765 for (c = 0; c < bitmap->width && c+x <
width; c++) {
771 SET_PIXEL_RGB(frame, rgba_color, src_val, c+x, y+r, pixel_step,
772 rgba_map[0], rgba_map[1], rgba_map[2], rgba_map[3]);
782 int hsub,
int vsub,
int is_rgba_packed,
uint8_t rgba_map[4])
786 if (color[3] != 0xFF) {
787 if (is_rgba_packed) {
789 for (j = 0; j <
height; j++)
790 for (i = 0; i <
width; i++)
792 rgba_map[0], rgba_map[1], rgba_map[2], rgba_map[3]);
794 unsigned int luma_pos, chroma_pos1, chroma_pos2;
795 for (j = 0; j <
height; j++)
796 for (i = 0; i <
width; i++)
801 line, pixel_step, hsub, vsub,
802 x, y, width, height);
815 for (i = 0, p = text; *p; i++) {
820 if (code ==
'\n' || code ==
'\r' || code ==
'\t')
826 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
827 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
882 }
else if (d > INT_MAX || d < INT_MIN) {
883 *n = d > INT_MAX ? INT_MAX : INT_MIN;
918 if (s->
x < 0) s->
x = 0;
919 if (s->
y < 0) s->
y = 0;
920 if ((
unsigned)s->
x + (
unsigned)s->
w > inlink->
w)
921 s->
x = inlink->
w - s->
w;
922 if ((
unsigned)s->
y + (
unsigned)s->
h > inlink->
h)
923 s->
y = inlink->
h - s->
h;
926 s->
x &= ~((1 << s->
hsub) - 1);
927 s->
y &= ~((1 << s->
vsub) - 1);
929 av_dlog(ctx,
"n:%d t:%f x:%d y:%d x+w:%d y+h:%d\n",
931 s->
x, s->
y, s->
x+s->
w, s->
y+s->
h);
963 .description =
NULL_IF_CONFIG_SMALL(
"Draw text on top of video frames using libfreetype library."),
965 .priv_class = &drawtext_class,
int draw
set to zero to prevent drawing
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
This structure describes decoded (raw) audio or video data.
packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
double av_expr_eval(AVExpr *e, const double *const_values, void *opaque)
Evaluate a previously parsed expression.
static double drand(void *opaque, double min, double max)
const char * name
Filter name.
static const AVOption drawtext_options[]
unsigned int fontsize
font size to use
packed RGB 8:8:8, 24bpp, RGBRGB...
static const char * drawtext_get_name(void *ctx)
void * priv
private data for use by the filter
#define AV_LOG_WARNING
Something somehow does not look correct.
static const AVFilterPad outputs[]
packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
char * av_strdup(const char *s) av_malloc_attrib
Duplicate the string s.
uint8_t * fontfile
font to be used
int h
agreed upon image height
static int draw_glyphs(DrawTextContext *s, AVFrame *frame, int width, int height, const uint8_t rgbcolor[4], const uint8_t yuvcolor[4], int x, int y)
Various defines for YUV<->RGB conversion.
void av_log(void *avcl, int level, const char *fmt,...) av_printf_format(3
Send the specified message to the log if the level is less than or equal to the current av_log_level...
AVFrame * ff_null_get_video_buffer(AVFilterLink *link, int w, int h)
static int draw_text(AVFilterContext *ctx, AVFrame *frame, int width, int height)
int av_expr_parse(AVExpr **expr, const char *s, const char *const *const_names, const char *const *func1_names, double(*const *funcs1)(void *, double), const char *const *func2_names, double(*const *funcs2)(void *, double, double), int log_offset, void *log_ctx)
Parse an expression.
uint8_t * text
text to be drawn
uint8_t * box_line[4]
line used for filling the box background
av_dlog(ac->avr,"%d samples - audio_convert: %s to %s (%s)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt), use_generic?ac->func_descr_generic:ac->func_descr)
void av_expr_free(AVExpr *e)
Free a parsed expression previously created with av_expr_parse().
#define SET_PIXEL_RGB(frame, rgba_color, val, x, y, pixel_step, r_off, g_off, b_off, a_off)
uint8_t log2_chroma_w
Amount to shift the luma width right to find the chroma width.
uint8_t shadowcolor[4]
shadow color
uint8_t boxcolor_rgba[4]
background color in RGBA
void av_freep(void *ptr)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc() and set the pointer ...
uint8_t fontcolor_rgba[4]
foreground color in RGBA
struct AVTreeNode * av_tree_node_alloc(void)
Allocate an AVTreeNode.
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
static int glyph_enu_free(void *opaque, void *elem)
void * av_tree_find(const AVTreeNode *t, void *key, int(*cmp)(void *key, const void *b), void *next[2])
static av_always_inline av_const int isnan(float x)
static unsigned int av_lfg_get(AVLFG *c)
Get the next random unsigned 32-bit number using an ALFG.
FT_Face face
freetype font face handle
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
static av_cold int init(AVFilterContext *ctx)
static double av_q2d(AVRational a)
Convert rational to double.
static const AVFilterPad avfilter_vf_drawtext_inputs[]
static av_cold void uninit(AVFilterContext *ctx)
FT_Vector * positions
positions for each element in the text
void av_tree_destroy(AVTreeNode *t)
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
char * fontcolor_string
font color as string
uint8_t fontcolor[4]
foreground color
A link between two filters.
void * av_tree_insert(AVTreeNode **tp, void *key, int(*cmp)(void *key, const void *b), AVTreeNode **next)
Insert or remove an element.
double var_values[VAR_VARS_NB]
int width
width and height of the video frame
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
void av_free(void *ptr)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc(). ...
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
uint8_t log2_chroma_h
Amount to shift the luma height right to find the chroma height.
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
AVExpr * y_pexpr
parsed expressions for x and y
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
int y
position to start drawing text
AVRational time_base
Define the time base used by the PTS of the frames/samples which will pass through this link...
static av_always_inline av_const double round(double x)
static int dtext_prepare_text(AVFilterContext *ctx)
#define GET_BITMAP_VAL(r, c)
AVPixelFormat
Pixel format.
static const char *const fun2_names[]
int w
agreed upon image width
void * av_realloc(void *ptr, size_t size) 1(2)
Allocate or reallocate a block of memory.
void * av_malloc(size_t size) av_malloc_attrib 1(1)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...
packed ABGR 8:8:8:8, 32bpp, ABGRABGR...
static int draw_glyph_yuv(AVFrame *frame, FT_Bitmap *bitmap, unsigned int x, unsigned int y, unsigned int width, unsigned int height, const uint8_t yuva_color[4], int hsub, int vsub)
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
int format
agreed upon media format
Main libavfilter public API header.
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
AVFilterLink ** outputs
array of pointers to output links
char * boxcolor_string
box color as string
void av_lfg_init(AVLFG *c, unsigned int seed)
size_t expanded_text_size
size in bytes of the expanded_text buffer
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
int pixel_step[4]
distance in bytes between the component of each pixel
packed RGB 8:8:8, 24bpp, BGRBGR...
#define GET_UTF8(val, GET_BYTE, ERROR)
Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form.
static int draw_glyph_rgb(AVFrame *frame, FT_Bitmap *bitmap, unsigned int x, unsigned int y, unsigned int width, unsigned int height, int pixel_step, const uint8_t rgba_color[4], const uint8_t rgba_map[4])
Describe the class of an AVClass context structure.
static const AVFilterPad inputs[]
AVFilterLink ** inputs
array of pointers to input links
struct AVTreeNode * glyphs
rendered glyphs, stored using the UTF-32 char code
int h
dimension of the text block
static int is_newline(uint32_t c)
int linesize[AV_NUM_DATA_POINTERS]
For video, size in bytes of each picture line.
AVFilterContext * dst
dest filter
int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen, void *log_ctx)
Put the RGBA values that correspond to color_string in rgba_color.
struct ft_error ft_errors[]
short int draw_box
draw box around text - true or false
static int config_input(AVFilterLink *inlink)
static void drawbox(AVFrame *frame, unsigned int x, unsigned int y, unsigned int width, unsigned int height, uint8_t *line[4], int pixel_step[4], uint8_t color[4], int hsub, int vsub, int is_rgba_packed, uint8_t rgba_map[4])
#define RGB_TO_U_CCIR(r1, g1, b1, shift)
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
#define RGB_TO_V_CCIR(r1, g1, b1, shift)
uint8_t shadowcolor_rgba[4]
shadow color in RGBA
uint8_t rgba_map[4]
map RGBA offsets to the positions in the packed RGBA format
static const AVClass drawtext_class
static const char *const var_names[]
int use_kerning
font kerning is used - true/false
static int parse_font(AVFilterContext *ctx)
int vsub
chroma subsampling values
char * shadowcolor_string
shadow color as string
static int glyph_cmp(void *key, const void *b)
static int query_formats(AVFilterContext *ctx)
FT_Library library
freetype font library handle
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
void av_file_unmap(uint8_t *bufptr, size_t size)
Unmap or free the buffer bufptr created by av_file_map().
#define AVERROR_UNKNOWN
Unknown error, typically from an external library.
static const uint8_t color[]
struct AVFilterPad AVFilterPad
double(* eval_func2)(void *, double a, double b)
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
static const AVFilterPad avfilter_vf_drawtext_outputs[]
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
#define RGB_TO_Y_CCIR(r, g, b)
uint8_t * expanded_text
used to contain the strftime()-expanded text
int ff_fill_line_with_color(uint8_t *line[4], int pixel_step[4], int w, uint8_t dst_color[4], enum AVPixelFormat pix_fmt, uint8_t rgba_color[4], int *is_packed_rgba, uint8_t rgba_map_ptr[4])
uint8_t boxcolor[4]
background color
static const eval_func2 fun2[]
int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, int log_offset, void *log_ctx)
Read the file with name filename, and put its content in a newly allocated buffer or map it with mmap...
int fix_bounds
do we let it go out of frame bounds - t/f
uint32_t av_get_random_seed(void)
Get random data.
char * textfile
file with text to be drawn
static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
Load glyphs corresponding to the UTF-32 codepoint code.
void ff_draw_rectangle(uint8_t *dst[4], int dst_linesize[4], uint8_t *src[4], int pixelstep[4], int hsub, int vsub, int x, int y, int w, int h)
int ft_load_flags
flags used for loading fonts, see FT_LOAD_*
void av_tree_enumerate(AVTreeNode *t, void *opaque, int(*cmp)(void *opaque, void *elem), int(*enu)(void *opaque, void *elem))
Apply enu(opaque, &elem) to all the elements in the tree in a given range.
size_t nb_positions
number of elements of positions array
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
static int normalize_double(int *n, double d)
#define AV_NOPTS_VALUE
Undefined timestamp value.
void * av_mallocz(size_t size) av_malloc_attrib 1(1)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...
#define SET_PIXEL_YUV(frame, yuva_color, val, x, y, hsub, vsub)