2 * Copyright (c) 2011 Stefano Sabatini
3 * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram
4 * Copyright (c) 2003 Gustavo Sverzut Barbieri <gsbarbieri@yahoo.com.br>
6 * This file is part of Libav.
8 * Libav is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * Libav is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with Libav; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 * drawtext filter, based on the original vhook/drawtext.c
26 * filter by Gustavo Sverzut Barbieri
32 #include "libavutil/colorspace.h"
33 #include "libavutil/common.h"
34 #include "libavutil/file.h"
35 #include "libavutil/eval.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/mathematics.h"
38 #include "libavutil/random_seed.h"
39 #include "libavutil/parseutils.h"
40 #include "libavutil/pixdesc.h"
41 #include "libavutil/tree.h"
42 #include "libavutil/lfg.h"
44 #include "drawutils.h"
50 #include <freetype/config/ftheader.h>
51 #include FT_FREETYPE_H
54 static const char *const var_names
[] = {
58 "main_w", "W", ///< width of the main video
59 "main_h", "H", ///< height of the main video
60 "text_w", "w", ///< width of the overlay text
61 "text_h", "h", ///< height of the overlay text
64 "n", ///< number of processed frames
65 "t", ///< timestamp expressed in seconds
69 static const char *const fun2_names
[] = {
73 static double drand(void *opaque
, double min
, double max
)
75 return min
+ (max
-min
) / UINT_MAX
* av_lfg_get(opaque
);
78 typedef double (*eval_func2
)(void *, double a
, double b
);
80 static const eval_func2 fun2
[] = {
101 const AVClass
*class;
102 uint8_t *fontfile
; ///< font to be used
103 uint8_t *text
; ///< text to be drawn
104 uint8_t *expanded_text
; ///< used to contain the strftime()-expanded text
105 size_t expanded_text_size
; ///< size in bytes of the expanded_text buffer
106 int ft_load_flags
; ///< flags used for loading fonts, see FT_LOAD_*
107 FT_Vector
*positions
; ///< positions for each element in the text
108 size_t nb_positions
; ///< number of elements of positions array
109 char *textfile
; ///< file with text to be drawn
110 int x
, y
; ///< position to start drawing text
111 int w
, h
; ///< dimension of the text block
112 int shadowx
, shadowy
;
113 unsigned int fontsize
; ///< font size to use
114 char *fontcolor_string
; ///< font color as string
115 char *boxcolor_string
; ///< box color as string
116 char *shadowcolor_string
; ///< shadow color as string
117 uint8_t fontcolor
[4]; ///< foreground color
118 uint8_t boxcolor
[4]; ///< background color
119 uint8_t shadowcolor
[4]; ///< shadow color
120 uint8_t fontcolor_rgba
[4]; ///< foreground color in RGBA
121 uint8_t boxcolor_rgba
[4]; ///< background color in RGBA
122 uint8_t shadowcolor_rgba
[4]; ///< shadow color in RGBA
124 short int draw_box
; ///< draw box around text - true or false
125 int use_kerning
; ///< font kerning is used - true/false
126 int tabsize
; ///< tab size
127 int fix_bounds
; ///< do we let it go out of frame bounds - t/f
129 FT_Library library
; ///< freetype font library handle
130 FT_Face face
; ///< freetype font face handle
131 struct AVTreeNode
*glyphs
; ///< rendered glyphs, stored using the UTF-32 char code
132 int hsub
, vsub
; ///< chroma subsampling values
134 int pixel_step
[4]; ///< distance in bytes between the component of each pixel
135 uint8_t rgba_map
[4]; ///< map RGBA offsets to the positions in the packed RGBA format
136 uint8_t *box_line
[4]; ///< line used for filling the box background
137 char *x_expr
, *y_expr
;
138 AVExpr
*x_pexpr
, *y_pexpr
; ///< parsed expressions for x and y
139 double var_values
[VAR_VARS_NB
];
142 int draw
; ///< set to zero to prevent drawing
143 AVLFG prng
; ///< random
146 #define OFFSET(x) offsetof(DrawTextContext, x)
148 static const AVOption drawtext_options
[]= {
149 {"fontfile", "set font file", OFFSET(fontfile
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
150 {"text", "set text", OFFSET(text
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
151 {"textfile", "set text file", OFFSET(textfile
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
152 {"fontcolor","set foreground color", OFFSET(fontcolor_string
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
153 {"boxcolor", "set box color", OFFSET(boxcolor_string
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
154 {"shadowcolor", "set shadow color", OFFSET(shadowcolor_string
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
155 {"box", "set box", OFFSET(draw_box
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, 1 },
156 {"fontsize", "set font size", OFFSET(fontsize
), AV_OPT_TYPE_INT
, {.i64
=16}, 1, 72 },
157 {"x", "set x", OFFSET(x_expr
), AV_OPT_TYPE_STRING
, {.str
="0"}, CHAR_MIN
, CHAR_MAX
},
158 {"y", "set y", OFFSET(y_expr
), AV_OPT_TYPE_STRING
, {.str
="0"}, CHAR_MIN
, CHAR_MAX
},
159 {"shadowx", "set x", OFFSET(shadowx
), AV_OPT_TYPE_INT
, {.i64
=0}, INT_MIN
, INT_MAX
},
160 {"shadowy", "set y", OFFSET(shadowy
), AV_OPT_TYPE_INT
, {.i64
=0}, INT_MIN
, INT_MAX
},
161 {"tabsize", "set tab size", OFFSET(tabsize
), AV_OPT_TYPE_INT
, {.i64
=4}, 0, INT_MAX
},
162 {"draw", "if false do not draw", OFFSET(d_expr
), AV_OPT_TYPE_STRING
, {.str
="1"}, CHAR_MIN
, CHAR_MAX
},
163 {"fix_bounds", "if true, check and fix text coords to avoid clipping",
164 OFFSET(fix_bounds
), AV_OPT_TYPE_INT
, {.i64
=1}, 0, 1 },
166 /* FT_LOAD_* flags */
167 {"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
, 0, "ft_load_flags" },
168 {"default", "set default", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_DEFAULT
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
169 {"no_scale", "set no_scale", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_NO_SCALE
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
170 {"no_hinting", "set no_hinting", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_NO_HINTING
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
171 {"render", "set render", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_RENDER
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
172 {"no_bitmap", "set no_bitmap", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_NO_BITMAP
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
173 {"vertical_layout", "set vertical_layout", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_VERTICAL_LAYOUT
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
174 {"force_autohint", "set force_autohint", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_FORCE_AUTOHINT
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
175 {"crop_bitmap", "set crop_bitmap", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_CROP_BITMAP
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
176 {"pedantic", "set pedantic", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_PEDANTIC
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
177 {"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
, 0, "ft_load_flags" },
178 {"no_recurse", "set no_recurse", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_NO_RECURSE
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
179 {"ignore_transform", "set ignore_transform", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_IGNORE_TRANSFORM
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
180 {"monochrome", "set monochrome", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_MONOCHROME
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
181 {"linear_design", "set linear_design", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_LINEAR_DESIGN
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
182 {"no_autohint", "set no_autohint", 0, AV_OPT_TYPE_CONST
, {.i64
= FT_LOAD_NO_AUTOHINT
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
186 static const char *drawtext_get_name(void *ctx
)
191 static const AVClass drawtext_class
= {
197 #undef __FTERRORS_H__
198 #define FT_ERROR_START_LIST {
199 #define FT_ERRORDEF(e, v, s) { (e), (s) },
200 #define FT_ERROR_END_LIST { 0, NULL } };
206 } static ft_errors
[] =
209 #define FT_ERRMSG(e) ft_errors[e].err_msg
214 FT_Bitmap bitmap
; ///< array holding bitmaps of font
221 static int glyph_cmp(void *key
, const void *b
)
223 const Glyph
*a
= key
, *bb
= b
;
224 int64_t diff
= (int64_t)a
->code
- (int64_t)bb
->code
;
225 return diff
> 0 ? 1 : diff
< 0 ? -1 : 0;
229 * Load glyphs corresponding to the UTF-32 codepoint code.
231 static int load_glyph(AVFilterContext
*ctx
, Glyph
**glyph_ptr
, uint32_t code
)
233 DrawTextContext
*dtext
= ctx
->priv
;
235 struct AVTreeNode
*node
= NULL
;
238 /* load glyph into dtext->face->glyph */
239 if (FT_Load_Char(dtext
->face
, code
, dtext
->ft_load_flags
))
240 return AVERROR(EINVAL
);
243 if (!(glyph
= av_mallocz(sizeof(*glyph
))) ||
244 !(glyph
->glyph
= av_mallocz(sizeof(*glyph
->glyph
)))) {
245 ret
= AVERROR(ENOMEM
);
250 if (FT_Get_Glyph(dtext
->face
->glyph
, glyph
->glyph
)) {
251 ret
= AVERROR(EINVAL
);
255 glyph
->bitmap
= dtext
->face
->glyph
->bitmap
;
256 glyph
->bitmap_left
= dtext
->face
->glyph
->bitmap_left
;
257 glyph
->bitmap_top
= dtext
->face
->glyph
->bitmap_top
;
258 glyph
->advance
= dtext
->face
->glyph
->advance
.x
>> 6;
260 /* measure text height to calculate text_height (or the maximum text height) */
261 FT_Glyph_Get_CBox(*glyph
->glyph
, ft_glyph_bbox_pixels
, &glyph
->bbox
);
263 /* cache the newly created glyph */
264 if (!(node
= av_tree_node_alloc())) {
265 ret
= AVERROR(ENOMEM
);
268 av_tree_insert(&dtext
->glyphs
, glyph
, glyph_cmp
, &node
);
276 av_freep(&glyph
->glyph
);
282 static av_cold
int init(AVFilterContext
*ctx
, const char *args
)
285 DrawTextContext
*dtext
= ctx
->priv
;
288 dtext
->class = &drawtext_class
;
289 av_opt_set_defaults(dtext
);
290 dtext
->fontcolor_string
= av_strdup("black");
291 dtext
->boxcolor_string
= av_strdup("white");
292 dtext
->shadowcolor_string
= av_strdup("black");
294 if ((err
= (av_set_options_string(dtext
, args
, "=", ":"))) < 0) {
295 av_log(ctx
, AV_LOG_ERROR
, "Error parsing options string: '%s'\n", args
);
299 if (!dtext
->fontfile
) {
300 av_log(ctx
, AV_LOG_ERROR
, "No font filename provided\n");
301 return AVERROR(EINVAL
);
304 if (dtext
->textfile
) {
309 av_log(ctx
, AV_LOG_ERROR
,
310 "Both text and text file provided. Please provide only one\n");
311 return AVERROR(EINVAL
);
313 if ((err
= av_file_map(dtext
->textfile
, &textbuf
, &textbuf_size
, 0, ctx
)) < 0) {
314 av_log(ctx
, AV_LOG_ERROR
,
315 "The text file '%s' could not be read or is empty\n",
320 if (!(dtext
->text
= av_malloc(textbuf_size
+1)))
321 return AVERROR(ENOMEM
);
322 memcpy(dtext
->text
, textbuf
, textbuf_size
);
323 dtext
->text
[textbuf_size
] = 0;
324 av_file_unmap(textbuf
, textbuf_size
);
328 av_log(ctx
, AV_LOG_ERROR
,
329 "Either text or a valid file must be provided\n");
330 return AVERROR(EINVAL
);
333 if ((err
= av_parse_color(dtext
->fontcolor_rgba
, dtext
->fontcolor_string
, -1, ctx
))) {
334 av_log(ctx
, AV_LOG_ERROR
,
335 "Invalid font color '%s'\n", dtext
->fontcolor_string
);
339 if ((err
= av_parse_color(dtext
->boxcolor_rgba
, dtext
->boxcolor_string
, -1, ctx
))) {
340 av_log(ctx
, AV_LOG_ERROR
,
341 "Invalid box color '%s'\n", dtext
->boxcolor_string
);
345 if ((err
= av_parse_color(dtext
->shadowcolor_rgba
, dtext
->shadowcolor_string
, -1, ctx
))) {
346 av_log(ctx
, AV_LOG_ERROR
,
347 "Invalid shadow color '%s'\n", dtext
->shadowcolor_string
);
351 if ((err
= FT_Init_FreeType(&(dtext
->library
)))) {
352 av_log(ctx
, AV_LOG_ERROR
,
353 "Could not load FreeType: %s\n", FT_ERRMSG(err
));
354 return AVERROR(EINVAL
);
357 /* load the face, and set up the encoding, which is by default UTF-8 */
358 if ((err
= FT_New_Face(dtext
->library
, dtext
->fontfile
, 0, &dtext
->face
))) {
359 av_log(ctx
, AV_LOG_ERROR
, "Could not load fontface from file '%s': %s\n",
360 dtext
->fontfile
, FT_ERRMSG(err
));
361 return AVERROR(EINVAL
);
363 if ((err
= FT_Set_Pixel_Sizes(dtext
->face
, 0, dtext
->fontsize
))) {
364 av_log(ctx
, AV_LOG_ERROR
, "Could not set font size to %d pixels: %s\n",
365 dtext
->fontsize
, FT_ERRMSG(err
));
366 return AVERROR(EINVAL
);
369 dtext
->use_kerning
= FT_HAS_KERNING(dtext
->face
);
371 /* load the fallback glyph with code 0 */
372 load_glyph(ctx
, NULL
, 0);
374 /* set the tabsize in pixels */
375 if ((err
= load_glyph(ctx
, &glyph
, ' ') < 0)) {
376 av_log(ctx
, AV_LOG_ERROR
, "Could not set tabsize.\n");
379 dtext
->tabsize
*= glyph
->advance
;
381 #if !HAVE_LOCALTIME_R
382 av_log(ctx
, AV_LOG_WARNING
, "strftime() expansion unavailable!\n");
388 static int query_formats(AVFilterContext
*ctx
)
390 static const enum AVPixelFormat pix_fmts
[] = {
391 AV_PIX_FMT_ARGB
, AV_PIX_FMT_RGBA
,
392 AV_PIX_FMT_ABGR
, AV_PIX_FMT_BGRA
,
393 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
394 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUV444P
,
395 AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV411P
,
396 AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV440P
,
400 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
404 static int glyph_enu_free(void *opaque
, void *elem
)
410 static av_cold
void uninit(AVFilterContext
*ctx
)
412 DrawTextContext
*dtext
= ctx
->priv
;
415 av_freep(&dtext
->fontfile
);
416 av_freep(&dtext
->text
);
417 av_freep(&dtext
->expanded_text
);
418 av_freep(&dtext
->fontcolor_string
);
419 av_freep(&dtext
->boxcolor_string
);
420 av_freep(&dtext
->positions
);
421 av_freep(&dtext
->shadowcolor_string
);
422 av_tree_enumerate(dtext
->glyphs
, NULL
, NULL
, glyph_enu_free
);
423 av_tree_destroy(dtext
->glyphs
);
425 FT_Done_Face(dtext
->face
);
426 FT_Done_FreeType(dtext
->library
);
428 for (i
= 0; i
< 4; i
++) {
429 av_freep(&dtext
->box_line
[i
]);
430 dtext
->pixel_step
[i
] = 0;
435 static inline int is_newline(uint32_t c
)
437 return c
== '\n' || c
== '\r' || c
== '\f' || c
== '\v';
440 static int dtext_prepare_text(AVFilterContext
*ctx
)
442 DrawTextContext
*dtext
= ctx
->priv
;
443 uint32_t code
= 0, prev_code
= 0;
444 int x
= 0, y
= 0, i
= 0, ret
;
445 int text_height
, baseline
;
446 char *text
= dtext
->text
;
449 int y_min
= 32000, y_max
= -32000;
451 Glyph
*glyph
= NULL
, *prev_glyph
= NULL
;
453 int width
= ctx
->inputs
[0]->w
;
454 int height
= ctx
->inputs
[0]->h
;
457 time_t now
= time(0);
459 uint8_t *buf
= dtext
->expanded_text
;
460 int buf_size
= dtext
->expanded_text_size
;
463 buf_size
= 2*strlen(dtext
->text
)+1;
465 localtime_r(&now
, <ime
);
467 while ((buf
= av_realloc(buf
, buf_size
))) {
469 if (strftime(buf
, buf_size
, dtext
->text
, <ime
) != 0 || *buf
== 0)
475 return AVERROR(ENOMEM
);
476 text
= dtext
->expanded_text
= buf
;
477 dtext
->expanded_text_size
= buf_size
;
480 if ((len
= strlen(text
)) > dtext
->nb_positions
) {
481 FT_Vector
*p
= av_realloc(dtext
->positions
,
482 len
* sizeof(*dtext
->positions
));
484 av_freep(dtext
->positions
);
485 dtext
->nb_positions
= 0;
486 return AVERROR(ENOMEM
);
488 dtext
->positions
= p
;
489 dtext
->nb_positions
= len
;
493 /* load and cache glyphs */
494 for (i
= 0, p
= text
; *p
; i
++) {
495 GET_UTF8(code
, *p
++, continue;);
499 glyph
= av_tree_find(dtext
->glyphs
, &dummy
, glyph_cmp
, NULL
);
501 ret
= load_glyph(ctx
, &glyph
, code
);
506 y_min
= FFMIN(glyph
->bbox
.yMin
, y_min
);
507 y_max
= FFMAX(glyph
->bbox
.yMax
, y_max
);
509 text_height
= y_max
- y_min
;
512 /* compute and save position for each glyph */
514 for (i
= 0, p
= text
; *p
; i
++) {
515 GET_UTF8(code
, *p
++, continue;);
517 /* skip the \n in the sequence \r\n */
518 if (prev_code
== '\r' && code
== '\n')
522 if (is_newline(code
)) {
523 str_w
= FFMAX(str_w
, x
- dtext
->x
);
532 glyph
= av_tree_find(dtext
->glyphs
, &dummy
, glyph_cmp
, NULL
);
535 if (dtext
->use_kerning
&& prev_glyph
&& glyph
->code
) {
536 FT_Get_Kerning(dtext
->face
, prev_glyph
->code
, glyph
->code
,
537 ft_kerning_default
, &delta
);
541 if (x
+ glyph
->bbox
.xMax
>= width
) {
542 str_w
= FFMAX(str_w
, x
);
548 dtext
->positions
[i
].x
= x
+ glyph
->bitmap_left
;
549 dtext
->positions
[i
].y
= y
- glyph
->bitmap_top
+ baseline
;
550 if (code
== '\t') x
= (x
/ dtext
->tabsize
+ 1)*dtext
->tabsize
;
551 else x
+= glyph
->advance
;
554 str_w
= FFMIN(width
- 1, FFMAX(str_w
, x
));
555 y
= FFMIN(y
+ text_height
, height
- 1);
558 dtext
->var_values
[VAR_TEXT_W
] = dtext
->var_values
[VAR_TW
] = dtext
->w
;
560 dtext
->var_values
[VAR_TEXT_H
] = dtext
->var_values
[VAR_TH
] = dtext
->h
;
566 static int config_input(AVFilterLink
*inlink
)
568 AVFilterContext
*ctx
= inlink
->dst
;
569 DrawTextContext
*dtext
= ctx
->priv
;
570 const AVPixFmtDescriptor
*pix_desc
= av_pix_fmt_desc_get(inlink
->format
);
573 dtext
->hsub
= pix_desc
->log2_chroma_w
;
574 dtext
->vsub
= pix_desc
->log2_chroma_h
;
576 dtext
->var_values
[VAR_E
] = M_E
;
577 dtext
->var_values
[VAR_PHI
] = M_PHI
;
578 dtext
->var_values
[VAR_PI
] = M_PI
;
580 dtext
->var_values
[VAR_MAIN_W
] =
581 dtext
->var_values
[VAR_MW
] = ctx
->inputs
[0]->w
;
582 dtext
->var_values
[VAR_MAIN_H
] =
583 dtext
->var_values
[VAR_MH
] = ctx
->inputs
[0]->h
;
585 dtext
->var_values
[VAR_X
] = 0;
586 dtext
->var_values
[VAR_Y
] = 0;
587 dtext
->var_values
[VAR_N
] = 0;
588 dtext
->var_values
[VAR_T
] = NAN
;
590 av_lfg_init(&dtext
->prng
, av_get_random_seed());
592 if ((ret
= av_expr_parse(&dtext
->x_pexpr
, dtext
->x_expr
, var_names
,
593 NULL
, NULL
, fun2_names
, fun2
, 0, ctx
)) < 0 ||
594 (ret
= av_expr_parse(&dtext
->y_pexpr
, dtext
->y_expr
, var_names
,
595 NULL
, NULL
, fun2_names
, fun2
, 0, ctx
)) < 0 ||
596 (ret
= av_expr_parse(&dtext
->d_pexpr
, dtext
->d_expr
, var_names
,
597 NULL
, NULL
, fun2_names
, fun2
, 0, ctx
)) < 0)
598 return AVERROR(EINVAL
);
601 ff_fill_line_with_color(dtext
->box_line
, dtext
->pixel_step
,
602 inlink
->w
, dtext
->boxcolor
,
603 inlink
->format
, dtext
->boxcolor_rgba
,
604 &dtext
->is_packed_rgb
, dtext
->rgba_map
)) < 0)
607 if (!dtext
->is_packed_rgb
) {
608 uint8_t *rgba
= dtext
->fontcolor_rgba
;
609 dtext
->fontcolor
[0] = RGB_TO_Y_CCIR(rgba
[0], rgba
[1], rgba
[2]);
610 dtext
->fontcolor
[1] = RGB_TO_U_CCIR(rgba
[0], rgba
[1], rgba
[2], 0);
611 dtext
->fontcolor
[2] = RGB_TO_V_CCIR(rgba
[0], rgba
[1], rgba
[2], 0);
612 dtext
->fontcolor
[3] = rgba
[3];
613 rgba
= dtext
->shadowcolor_rgba
;
614 dtext
->shadowcolor
[0] = RGB_TO_Y_CCIR(rgba
[0], rgba
[1], rgba
[2]);
615 dtext
->shadowcolor
[1] = RGB_TO_U_CCIR(rgba
[0], rgba
[1], rgba
[2], 0);
616 dtext
->shadowcolor
[2] = RGB_TO_V_CCIR(rgba
[0], rgba
[1], rgba
[2], 0);
617 dtext
->shadowcolor
[3] = rgba
[3];
622 return dtext_prepare_text(ctx
);
625 #define GET_BITMAP_VAL(r, c) \
626 bitmap->pixel_mode == FT_PIXEL_MODE_MONO ? \
627 (bitmap->buffer[(r) * bitmap->pitch + ((c)>>3)] & (0x80 >> ((c)&7))) * 255 : \
628 bitmap->buffer[(r) * bitmap->pitch + (c)]
630 #define SET_PIXEL_YUV(frame, yuva_color, val, x, y, hsub, vsub) { \
631 luma_pos = ((x) ) + ((y) ) * frame->linesize[0]; \
632 alpha = yuva_color[3] * (val) * 129; \
633 frame->data[0][luma_pos] = (alpha * yuva_color[0] + (255*255*129 - alpha) * frame->data[0][luma_pos] ) >> 23; \
634 if (((x) & ((1<<(hsub)) - 1)) == 0 && ((y) & ((1<<(vsub)) - 1)) == 0) {\
635 chroma_pos1 = ((x) >> (hsub)) + ((y) >> (vsub)) * frame->linesize[1]; \
636 chroma_pos2 = ((x) >> (hsub)) + ((y) >> (vsub)) * frame->linesize[2]; \
637 frame->data[1][chroma_pos1] = (alpha * yuva_color[1] + (255*255*129 - alpha) * frame->data[1][chroma_pos1]) >> 23; \
638 frame->data[2][chroma_pos2] = (alpha * yuva_color[2] + (255*255*129 - alpha) * frame->data[2][chroma_pos2]) >> 23; \
642 static inline int draw_glyph_yuv(AVFrame
*frame
, FT_Bitmap
*bitmap
, unsigned int x
,
643 unsigned int y
, unsigned int width
, unsigned int height
,
644 const uint8_t yuva_color
[4], int hsub
, int vsub
)
647 unsigned int luma_pos
, chroma_pos1
, chroma_pos2
;
650 for (r
= 0; r
< bitmap
->rows
&& r
+y
< height
; r
++) {
651 for (c
= 0; c
< bitmap
->width
&& c
+x
< width
; c
++) {
652 /* get intensity value in the glyph bitmap (source) */
653 src_val
= GET_BITMAP_VAL(r
, c
);
657 SET_PIXEL_YUV(frame
, yuva_color
, src_val
, c
+x
, y
+r
, hsub
, vsub
);
664 #define SET_PIXEL_RGB(frame, rgba_color, val, x, y, pixel_step, r_off, g_off, b_off, a_off) { \
665 p = frame->data[0] + (x) * pixel_step + ((y) * frame->linesize[0]); \
666 alpha = rgba_color[3] * (val) * 129; \
667 *(p+r_off) = (alpha * rgba_color[0] + (255*255*129 - alpha) * *(p+r_off)) >> 23; \
668 *(p+g_off) = (alpha * rgba_color[1] + (255*255*129 - alpha) * *(p+g_off)) >> 23; \
669 *(p+b_off) = (alpha * rgba_color[2] + (255*255*129 - alpha) * *(p+b_off)) >> 23; \
672 static inline int draw_glyph_rgb(AVFrame
*frame
, FT_Bitmap
*bitmap
,
673 unsigned int x
, unsigned int y
,
674 unsigned int width
, unsigned int height
, int pixel_step
,
675 const uint8_t rgba_color
[4], const uint8_t rgba_map
[4])
681 for (r
= 0; r
< bitmap
->rows
&& r
+y
< height
; r
++) {
682 for (c
= 0; c
< bitmap
->width
&& c
+x
< width
; c
++) {
683 /* get intensity value in the glyph bitmap (source) */
684 src_val
= GET_BITMAP_VAL(r
, c
);
688 SET_PIXEL_RGB(frame
, rgba_color
, src_val
, c
+x
, y
+r
, pixel_step
,
689 rgba_map
[0], rgba_map
[1], rgba_map
[2], rgba_map
[3]);
696 static inline void drawbox(AVFrame
*frame
, unsigned int x
, unsigned int y
,
697 unsigned int width
, unsigned int height
,
698 uint8_t *line
[4], int pixel_step
[4], uint8_t color
[4],
699 int hsub
, int vsub
, int is_rgba_packed
, uint8_t rgba_map
[4])
703 if (color
[3] != 0xFF) {
704 if (is_rgba_packed
) {
706 for (j
= 0; j
< height
; j
++)
707 for (i
= 0; i
< width
; i
++)
708 SET_PIXEL_RGB(frame
, color
, 255, i
+x
, y
+j
, pixel_step
[0],
709 rgba_map
[0], rgba_map
[1], rgba_map
[2], rgba_map
[3]);
711 unsigned int luma_pos
, chroma_pos1
, chroma_pos2
;
712 for (j
= 0; j
< height
; j
++)
713 for (i
= 0; i
< width
; i
++)
714 SET_PIXEL_YUV(frame
, color
, 255, i
+x
, y
+j
, hsub
, vsub
);
717 ff_draw_rectangle(frame
->data
, frame
->linesize
,
718 line
, pixel_step
, hsub
, vsub
,
719 x
, y
, width
, height
);
723 static int draw_glyphs(DrawTextContext
*dtext
, AVFrame
*frame
,
724 int width
, int height
, const uint8_t rgbcolor
[4], const uint8_t yuvcolor
[4], int x
, int y
)
726 char *text
= HAVE_LOCALTIME_R
? dtext
->expanded_text
: dtext
->text
;
732 for (i
= 0, p
= text
; *p
; i
++) {
734 GET_UTF8(code
, *p
++, continue;);
736 /* skip new line chars, just go to new line */
737 if (code
== '\n' || code
== '\r' || code
== '\t')
741 glyph
= av_tree_find(dtext
->glyphs
, &dummy
, (void *)glyph_cmp
, NULL
);
743 if (glyph
->bitmap
.pixel_mode
!= FT_PIXEL_MODE_MONO
&&
744 glyph
->bitmap
.pixel_mode
!= FT_PIXEL_MODE_GRAY
)
745 return AVERROR(EINVAL
);
747 if (dtext
->is_packed_rgb
) {
748 draw_glyph_rgb(frame
, &glyph
->bitmap
,
749 dtext
->positions
[i
].x
+x
, dtext
->positions
[i
].y
+y
, width
, height
,
750 dtext
->pixel_step
[0], rgbcolor
, dtext
->rgba_map
);
752 draw_glyph_yuv(frame
, &glyph
->bitmap
,
753 dtext
->positions
[i
].x
+x
, dtext
->positions
[i
].y
+y
, width
, height
,
754 yuvcolor
, dtext
->hsub
, dtext
->vsub
);
761 static int draw_text(AVFilterContext
*ctx
, AVFrame
*frame
,
762 int width
, int height
)
764 DrawTextContext
*dtext
= ctx
->priv
;
769 drawbox(frame
, dtext
->x
, dtext
->y
, dtext
->w
, dtext
->h
,
770 dtext
->box_line
, dtext
->pixel_step
, dtext
->boxcolor
,
771 dtext
->hsub
, dtext
->vsub
, dtext
->is_packed_rgb
,
774 if (dtext
->shadowx
|| dtext
->shadowy
) {
775 if ((ret
= draw_glyphs(dtext
, frame
, width
, height
,
776 dtext
->shadowcolor_rgba
,
778 dtext
->x
+ dtext
->shadowx
,
779 dtext
->y
+ dtext
->shadowy
)) < 0)
783 if ((ret
= draw_glyphs(dtext
, frame
, width
, height
,
784 dtext
->fontcolor_rgba
,
793 static inline int normalize_double(int *n
, double d
)
798 ret
= AVERROR(EINVAL
);
799 } else if (d
> INT_MAX
|| d
< INT_MIN
) {
800 *n
= d
> INT_MAX
? INT_MAX
: INT_MIN
;
801 ret
= AVERROR(EINVAL
);
808 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*frame
)
810 AVFilterContext
*ctx
= inlink
->dst
;
811 DrawTextContext
*dtext
= ctx
->priv
;
814 if ((ret
= dtext_prepare_text(ctx
)) < 0) {
815 av_log(ctx
, AV_LOG_ERROR
, "Can't draw text\n");
816 av_frame_free(&frame
);
820 dtext
->var_values
[VAR_T
] = frame
->pts
== AV_NOPTS_VALUE
?
821 NAN
: frame
->pts
* av_q2d(inlink
->time_base
);
822 dtext
->var_values
[VAR_X
] =
823 av_expr_eval(dtext
->x_pexpr
, dtext
->var_values
, &dtext
->prng
);
824 dtext
->var_values
[VAR_Y
] =
825 av_expr_eval(dtext
->y_pexpr
, dtext
->var_values
, &dtext
->prng
);
826 dtext
->var_values
[VAR_X
] =
827 av_expr_eval(dtext
->x_pexpr
, dtext
->var_values
, &dtext
->prng
);
829 dtext
->draw
= av_expr_eval(dtext
->d_pexpr
, dtext
->var_values
, &dtext
->prng
);
831 normalize_double(&dtext
->x
, dtext
->var_values
[VAR_X
]);
832 normalize_double(&dtext
->y
, dtext
->var_values
[VAR_Y
]);
834 if (dtext
->fix_bounds
) {
835 if (dtext
->x
< 0) dtext
->x
= 0;
836 if (dtext
->y
< 0) dtext
->y
= 0;
837 if ((unsigned)dtext
->x
+ (unsigned)dtext
->w
> inlink
->w
)
838 dtext
->x
= inlink
->w
- dtext
->w
;
839 if ((unsigned)dtext
->y
+ (unsigned)dtext
->h
> inlink
->h
)
840 dtext
->y
= inlink
->h
- dtext
->h
;
843 dtext
->x
&= ~((1 << dtext
->hsub
) - 1);
844 dtext
->y
&= ~((1 << dtext
->vsub
) - 1);
846 av_dlog(ctx
, "n:%d t:%f x:%d y:%d x+w:%d y+h:%d\n",
847 (int)dtext
->var_values
[VAR_N
], dtext
->var_values
[VAR_T
],
848 dtext
->x
, dtext
->y
, dtext
->x
+dtext
->w
, dtext
->y
+dtext
->h
);
851 draw_text(inlink
->dst
, frame
, frame
->width
, frame
->height
);
853 dtext
->var_values
[VAR_N
] += 1.0;
855 return ff_filter_frame(inlink
->dst
->outputs
[0], frame
);
858 static const AVFilterPad avfilter_vf_drawtext_inputs
[] = {
861 .type
= AVMEDIA_TYPE_VIDEO
,
862 .get_video_buffer
= ff_null_get_video_buffer
,
863 .filter_frame
= filter_frame
,
864 .config_props
= config_input
,
870 static const AVFilterPad avfilter_vf_drawtext_outputs
[] = {
873 .type
= AVMEDIA_TYPE_VIDEO
,
878 AVFilter avfilter_vf_drawtext
= {
880 .description
= NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."),
881 .priv_size
= sizeof(DrawTextContext
),
884 .query_formats
= query_formats
,
886 .inputs
= avfilter_vf_drawtext_inputs
,
887 .outputs
= avfilter_vf_drawtext_outputs
,