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/file.h"
34 #include "libavutil/eval.h"
35 #include "libavutil/opt.h"
36 #include "libavutil/mathematics.h"
37 #include "libavutil/random_seed.h"
38 #include "libavutil/parseutils.h"
39 #include "libavutil/pixdesc.h"
40 #include "libavutil/tree.h"
41 #include "libavutil/lfg.h"
43 #include "drawutils.h"
48 #include <freetype/config/ftheader.h>
49 #include FT_FREETYPE_H
52 static const char *var_names
[] = {
56 "main_w", "W", ///< width of the main video
57 "main_h", "H", ///< height of the main video
58 "text_w", "w", ///< width of the overlay text
59 "text_h", "h", ///< height of the overlay text
62 "n", ///< number of processed frames
63 "t", ///< timestamp expressed in seconds
67 static const char *fun2_names
[] = {
71 static double drand(void *opaque
, double min
, double max
)
73 return min
+ (max
-min
) / UINT_MAX
* av_lfg_get(opaque
);
76 typedef double (*eval_func2
)(void *, double a
, double b
);
78 static const eval_func2 fun2
[] = {
100 uint8_t *fontfile
; ///< font to be used
101 uint8_t *text
; ///< text to be drawn
102 uint8_t *expanded_text
; ///< used to contain the strftime()-expanded text
103 size_t expanded_text_size
; ///< size in bytes of the expanded_text buffer
104 int ft_load_flags
; ///< flags used for loading fonts, see FT_LOAD_*
105 FT_Vector
*positions
; ///< positions for each element in the text
106 size_t nb_positions
; ///< number of elements of positions array
107 char *textfile
; ///< file with text to be drawn
108 int x
, y
; ///< position to start drawing text
109 int w
, h
; ///< dimension of the text block
110 int shadowx
, shadowy
;
111 unsigned int fontsize
; ///< font size to use
112 char *fontcolor_string
; ///< font color as string
113 char *boxcolor_string
; ///< box color as string
114 char *shadowcolor_string
; ///< shadow color as string
115 uint8_t fontcolor
[4]; ///< foreground color
116 uint8_t boxcolor
[4]; ///< background color
117 uint8_t shadowcolor
[4]; ///< shadow color
118 uint8_t fontcolor_rgba
[4]; ///< foreground color in RGBA
119 uint8_t boxcolor_rgba
[4]; ///< background color in RGBA
120 uint8_t shadowcolor_rgba
[4]; ///< shadow color in RGBA
122 short int draw_box
; ///< draw box around text - true or false
123 int use_kerning
; ///< font kerning is used - true/false
124 int tabsize
; ///< tab size
125 int fix_bounds
; ///< do we let it go out of frame bounds - t/f
127 FT_Library library
; ///< freetype font library handle
128 FT_Face face
; ///< freetype font face handle
129 struct AVTreeNode
*glyphs
; ///< rendered glyphs, stored using the UTF-32 char code
130 int hsub
, vsub
; ///< chroma subsampling values
132 int pixel_step
[4]; ///< distance in bytes between the component of each pixel
133 uint8_t rgba_map
[4]; ///< map RGBA offsets to the positions in the packed RGBA format
134 uint8_t *box_line
[4]; ///< line used for filling the box background
135 char *x_expr
, *y_expr
;
136 AVExpr
*x_pexpr
, *y_pexpr
; ///< parsed expressions for x and y
137 double var_values
[VAR_VARS_NB
];
140 int draw
; ///< set to zero to prevent drawing
141 AVLFG prng
; ///< random
144 #define OFFSET(x) offsetof(DrawTextContext, x)
146 static const AVOption drawtext_options
[]= {
147 {"fontfile", "set font file", OFFSET(fontfile
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
148 {"text", "set text", OFFSET(text
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
149 {"textfile", "set text file", OFFSET(textfile
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
150 {"fontcolor","set foreground color", OFFSET(fontcolor_string
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
151 {"boxcolor", "set box color", OFFSET(boxcolor_string
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
152 {"shadowcolor", "set shadow color", OFFSET(shadowcolor_string
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, CHAR_MIN
, CHAR_MAX
},
153 {"box", "set box", OFFSET(draw_box
), AV_OPT_TYPE_INT
, {.dbl
=0}, 0, 1 },
154 {"fontsize", "set font size", OFFSET(fontsize
), AV_OPT_TYPE_INT
, {.dbl
=16}, 1, 72 },
155 {"x", "set x", OFFSET(x_expr
), AV_OPT_TYPE_STRING
, {.str
="0"}, CHAR_MIN
, CHAR_MAX
},
156 {"y", "set y", OFFSET(y_expr
), AV_OPT_TYPE_STRING
, {.str
="0"}, CHAR_MIN
, CHAR_MAX
},
157 {"shadowx", "set x", OFFSET(shadowx
), AV_OPT_TYPE_INT
, {.dbl
=0}, INT_MIN
, INT_MAX
},
158 {"shadowy", "set y", OFFSET(shadowy
), AV_OPT_TYPE_INT
, {.dbl
=0}, INT_MIN
, INT_MAX
},
159 {"tabsize", "set tab size", OFFSET(tabsize
), AV_OPT_TYPE_INT
, {.dbl
=4}, 0, INT_MAX
},
160 {"draw", "if false do not draw", OFFSET(d_expr
), AV_OPT_TYPE_STRING
, {.str
="1"}, CHAR_MIN
, CHAR_MAX
},
161 {"fix_bounds", "if true, check and fix text coords to avoid clipping",
162 OFFSET(fix_bounds
), AV_OPT_TYPE_INT
, {.dbl
=1}, 0, 1 },
164 /* FT_LOAD_* flags */
165 {"ft_load_flags", "set font loading flags for libfreetype", OFFSET(ft_load_flags
), AV_OPT_TYPE_FLAGS
, {.dbl
=FT_LOAD_DEFAULT
|FT_LOAD_RENDER
}, 0, INT_MAX
, 0, "ft_load_flags" },
166 {"default", "set default", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_DEFAULT
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
167 {"no_scale", "set no_scale", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_NO_SCALE
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
168 {"no_hinting", "set no_hinting", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_NO_HINTING
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
169 {"render", "set render", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_RENDER
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
170 {"no_bitmap", "set no_bitmap", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_NO_BITMAP
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
171 {"vertical_layout", "set vertical_layout", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_VERTICAL_LAYOUT
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
172 {"force_autohint", "set force_autohint", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_FORCE_AUTOHINT
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
173 {"crop_bitmap", "set crop_bitmap", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_CROP_BITMAP
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
174 {"pedantic", "set pedantic", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_PEDANTIC
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
175 {"ignore_global_advance_width", "set ignore_global_advance_width", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
176 {"no_recurse", "set no_recurse", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_NO_RECURSE
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
177 {"ignore_transform", "set ignore_transform", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_IGNORE_TRANSFORM
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
178 {"monochrome", "set monochrome", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_MONOCHROME
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
179 {"linear_design", "set linear_design", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_LINEAR_DESIGN
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
180 {"no_autohint", "set no_autohint", 0, AV_OPT_TYPE_CONST
, {FT_LOAD_NO_AUTOHINT
}, INT_MIN
, INT_MAX
, 0, "ft_load_flags" },
184 static const char *drawtext_get_name(void *ctx
)
189 static const AVClass drawtext_class
= {
195 #undef __FTERRORS_H__
196 #define FT_ERROR_START_LIST {
197 #define FT_ERRORDEF(e, v, s) { (e), (s) },
198 #define FT_ERROR_END_LIST { 0, NULL } };
204 } static ft_errors
[] =
207 #define FT_ERRMSG(e) ft_errors[e].err_msg
212 FT_Bitmap bitmap
; ///< array holding bitmaps of font
219 static int glyph_cmp(void *key
, const void *b
)
221 const Glyph
*a
= key
, *bb
= b
;
222 int64_t diff
= (int64_t)a
->code
- (int64_t)bb
->code
;
223 return diff
> 0 ?
1 : diff
< 0 ?
-1 : 0;
227 * Load glyphs corresponding to the UTF-32 codepoint code.
229 static int load_glyph(AVFilterContext
*ctx
, Glyph
**glyph_ptr
, uint32_t code
)
231 DrawTextContext
*dtext
= ctx
->priv
;
233 struct AVTreeNode
*node
= NULL
;
236 /* load glyph into dtext->face->glyph */
237 if (FT_Load_Char(dtext
->face
, code
, dtext
->ft_load_flags
))
238 return AVERROR(EINVAL
);
241 if (!(glyph
= av_mallocz(sizeof(*glyph
))) ||
242 !(glyph
->glyph
= av_mallocz(sizeof(*glyph
->glyph
)))) {
243 ret
= AVERROR(ENOMEM
);
248 if (FT_Get_Glyph(dtext
->face
->glyph
, glyph
->glyph
)) {
249 ret
= AVERROR(EINVAL
);
253 glyph
->bitmap
= dtext
->face
->glyph
->bitmap
;
254 glyph
->bitmap_left
= dtext
->face
->glyph
->bitmap_left
;
255 glyph
->bitmap_top
= dtext
->face
->glyph
->bitmap_top
;
256 glyph
->advance
= dtext
->face
->glyph
->advance
.x
>> 6;
258 /* measure text height to calculate text_height (or the maximum text height) */
259 FT_Glyph_Get_CBox(*glyph
->glyph
, ft_glyph_bbox_pixels
, &glyph
->bbox
);
261 /* cache the newly created glyph */
262 if (!(node
= av_mallocz(av_tree_node_size
))) {
263 ret
= AVERROR(ENOMEM
);
266 av_tree_insert(&dtext
->glyphs
, glyph
, glyph_cmp
, &node
);
274 av_freep(&glyph
->glyph
);
280 static av_cold
int init(AVFilterContext
*ctx
, const char *args
, void *opaque
)
283 DrawTextContext
*dtext
= ctx
->priv
;
286 dtext
->class = &drawtext_class
;
287 av_opt_set_defaults(dtext
);
288 dtext
->fontcolor_string
= av_strdup("black");
289 dtext
->boxcolor_string
= av_strdup("white");
290 dtext
->shadowcolor_string
= av_strdup("black");
292 if ((err
= (av_set_options_string(dtext
, args
, "=", ":"))) < 0) {
293 av_log(ctx
, AV_LOG_ERROR
, "Error parsing options string: '%s'\n", args
);
297 if (!dtext
->fontfile
) {
298 av_log(ctx
, AV_LOG_ERROR
, "No font filename provided\n");
299 return AVERROR(EINVAL
);
302 if (dtext
->textfile
) {
307 av_log(ctx
, AV_LOG_ERROR
,
308 "Both text and text file provided. Please provide only one\n");
309 return AVERROR(EINVAL
);
311 if ((err
= av_file_map(dtext
->textfile
, &textbuf
, &textbuf_size
, 0, ctx
)) < 0) {
312 av_log(ctx
, AV_LOG_ERROR
,
313 "The text file '%s' could not be read or is empty\n",
318 if (!(dtext
->text
= av_malloc(textbuf_size
+1)))
319 return AVERROR(ENOMEM
);
320 memcpy(dtext
->text
, textbuf
, textbuf_size
);
321 dtext
->text
[textbuf_size
] = 0;
322 av_file_unmap(textbuf
, textbuf_size
);
326 av_log(ctx
, AV_LOG_ERROR
,
327 "Either text or a valid file must be provided\n");
328 return AVERROR(EINVAL
);
331 if ((err
= av_parse_color(dtext
->fontcolor_rgba
, dtext
->fontcolor_string
, -1, ctx
))) {
332 av_log(ctx
, AV_LOG_ERROR
,
333 "Invalid font color '%s'\n", dtext
->fontcolor_string
);
337 if ((err
= av_parse_color(dtext
->boxcolor_rgba
, dtext
->boxcolor_string
, -1, ctx
))) {
338 av_log(ctx
, AV_LOG_ERROR
,
339 "Invalid box color '%s'\n", dtext
->boxcolor_string
);
343 if ((err
= av_parse_color(dtext
->shadowcolor_rgba
, dtext
->shadowcolor_string
, -1, ctx
))) {
344 av_log(ctx
, AV_LOG_ERROR
,
345 "Invalid shadow color '%s'\n", dtext
->shadowcolor_string
);
349 if ((err
= FT_Init_FreeType(&(dtext
->library
)))) {
350 av_log(ctx
, AV_LOG_ERROR
,
351 "Could not load FreeType: %s\n", FT_ERRMSG(err
));
352 return AVERROR(EINVAL
);
355 /* load the face, and set up the encoding, which is by default UTF-8 */
356 if ((err
= FT_New_Face(dtext
->library
, dtext
->fontfile
, 0, &dtext
->face
))) {
357 av_log(ctx
, AV_LOG_ERROR
, "Could not load fontface from file '%s': %s\n",
358 dtext
->fontfile
, FT_ERRMSG(err
));
359 return AVERROR(EINVAL
);
361 if ((err
= FT_Set_Pixel_Sizes(dtext
->face
, 0, dtext
->fontsize
))) {
362 av_log(ctx
, AV_LOG_ERROR
, "Could not set font size to %d pixels: %s\n",
363 dtext
->fontsize
, FT_ERRMSG(err
));
364 return AVERROR(EINVAL
);
367 dtext
->use_kerning
= FT_HAS_KERNING(dtext
->face
);
369 /* load the fallback glyph with code 0 */
370 load_glyph(ctx
, NULL
, 0);
372 /* set the tabsize in pixels */
373 if ((err
= load_glyph(ctx
, &glyph
, ' ') < 0)) {
374 av_log(ctx
, AV_LOG_ERROR
, "Could not set tabsize.\n");
377 dtext
->tabsize
*= glyph
->advance
;
379 #if !HAVE_LOCALTIME_R
380 av_log(ctx
, AV_LOG_WARNING
, "strftime() expansion unavailable!\n");
386 static int query_formats(AVFilterContext
*ctx
)
388 static const enum PixelFormat pix_fmts
[] = {
389 PIX_FMT_ARGB
, PIX_FMT_RGBA
,
390 PIX_FMT_ABGR
, PIX_FMT_BGRA
,
391 PIX_FMT_RGB24
, PIX_FMT_BGR24
,
392 PIX_FMT_YUV420P
, PIX_FMT_YUV444P
,
393 PIX_FMT_YUV422P
, PIX_FMT_YUV411P
,
394 PIX_FMT_YUV410P
, PIX_FMT_YUV440P
,
398 avfilter_set_common_formats(ctx
, avfilter_make_format_list(pix_fmts
));
402 static int glyph_enu_free(void *opaque
, void *elem
)
408 static av_cold
void uninit(AVFilterContext
*ctx
)
410 DrawTextContext
*dtext
= ctx
->priv
;
413 av_freep(&dtext
->fontfile
);
414 av_freep(&dtext
->text
);
415 av_freep(&dtext
->expanded_text
);
416 av_freep(&dtext
->fontcolor_string
);
417 av_freep(&dtext
->boxcolor_string
);
418 av_freep(&dtext
->positions
);
419 av_freep(&dtext
->shadowcolor_string
);
420 av_tree_enumerate(dtext
->glyphs
, NULL
, NULL
, glyph_enu_free
);
421 av_tree_destroy(dtext
->glyphs
);
423 FT_Done_Face(dtext
->face
);
424 FT_Done_FreeType(dtext
->library
);
426 for (i
= 0; i
< 4; i
++) {
427 av_freep(&dtext
->box_line
[i
]);
428 dtext
->pixel_step
[i
] = 0;
433 static inline int is_newline(uint32_t c
)
435 return c
== '\n' || c
== '\r' || c
== '\f' || c
== '\v';
438 static int dtext_prepare_text(AVFilterContext
*ctx
)
440 DrawTextContext
*dtext
= ctx
->priv
;
441 uint32_t code
= 0, prev_code
= 0;
442 int x
= 0, y
= 0, i
= 0, ret
;
443 int text_height
, baseline
;
444 char *text
= dtext
->text
;
447 int y_min
= 32000, y_max
= -32000;
449 Glyph
*glyph
= NULL
, *prev_glyph
= NULL
;
451 int width
= ctx
->inputs
[0]->w
;
452 int height
= ctx
->inputs
[0]->h
;
455 time_t now
= time(0);
457 uint8_t *buf
= dtext
->expanded_text
;
458 int buf_size
= dtext
->expanded_text_size
;
461 buf_size
= 2*strlen(dtext
->text
)+1;
463 localtime_r(&now
, <ime
);
465 while ((buf
= av_realloc(buf
, buf_size
))) {
467 if (strftime(buf
, buf_size
, dtext
->text
, <ime
) != 0 || *buf
== 0)
473 return AVERROR(ENOMEM
);
474 text
= dtext
->expanded_text
= buf
;
475 dtext
->expanded_text_size
= buf_size
;
478 if ((len
= strlen(text
)) > dtext
->nb_positions
) {
479 FT_Vector
*p
= av_realloc(dtext
->positions
,
480 len
* sizeof(*dtext
->positions
));
482 av_freep(dtext
->positions
);
483 dtext
->nb_positions
= 0;
484 return AVERROR(ENOMEM
);
486 dtext
->positions
= p
;
487 dtext
->nb_positions
= len
;
491 /* load and cache glyphs */
492 for (i
= 0, p
= text
; *p
; i
++) {
493 GET_UTF8(code
, *p
++, continue;);
497 glyph
= av_tree_find(dtext
->glyphs
, &dummy
, glyph_cmp
, NULL
);
499 ret
= load_glyph(ctx
, &glyph
, code
);
504 y_min
= FFMIN(glyph
->bbox
.yMin
, y_min
);
505 y_max
= FFMAX(glyph
->bbox
.yMax
, y_max
);
507 text_height
= y_max
- y_min
;
510 /* compute and save position for each glyph */
512 for (i
= 0, p
= text
; *p
; i
++) {
513 GET_UTF8(code
, *p
++, continue;);
515 /* skip the \n in the sequence \r\n */
516 if (prev_code
== '\r' && code
== '\n')
520 if (is_newline(code
)) {
521 str_w
= FFMAX(str_w
, x
- dtext
->x
);
530 glyph
= av_tree_find(dtext
->glyphs
, &dummy
, glyph_cmp
, NULL
);
533 if (dtext
->use_kerning
&& prev_glyph
&& glyph
->code
) {
534 FT_Get_Kerning(dtext
->face
, prev_glyph
->code
, glyph
->code
,
535 ft_kerning_default
, &delta
);
539 if (x
+ glyph
->bbox
.xMax
>= width
) {
540 str_w
= FFMAX(str_w
, x
);
546 dtext
->positions
[i
].x
= x
+ glyph
->bitmap_left
;
547 dtext
->positions
[i
].y
= y
- glyph
->bitmap_top
+ baseline
;
548 if (code
== '\t') x
= (x
/ dtext
->tabsize
+ 1)*dtext
->tabsize
;
549 else x
+= glyph
->advance
;
552 str_w
= FFMIN(width
- 1, FFMAX(str_w
, x
));
553 y
= FFMIN(y
+ text_height
, height
- 1);
556 dtext
->var_values
[VAR_TEXT_W
] = dtext
->var_values
[VAR_TW
] = dtext
->w
;
558 dtext
->var_values
[VAR_TEXT_H
] = dtext
->var_values
[VAR_TH
] = dtext
->h
;
564 static int config_input(AVFilterLink
*inlink
)
566 AVFilterContext
*ctx
= inlink
->dst
;
567 DrawTextContext
*dtext
= ctx
->priv
;
568 const AVPixFmtDescriptor
*pix_desc
= &av_pix_fmt_descriptors
[inlink
->format
];
571 dtext
->hsub
= pix_desc
->log2_chroma_w
;
572 dtext
->vsub
= pix_desc
->log2_chroma_h
;
574 dtext
->var_values
[VAR_E
] = M_E
;
575 dtext
->var_values
[VAR_PHI
] = M_PHI
;
576 dtext
->var_values
[VAR_PI
] = M_PI
;
578 dtext
->var_values
[VAR_MAIN_W
] =
579 dtext
->var_values
[VAR_MW
] = ctx
->inputs
[0]->w
;
580 dtext
->var_values
[VAR_MAIN_H
] =
581 dtext
->var_values
[VAR_MH
] = ctx
->inputs
[0]->h
;
583 dtext
->var_values
[VAR_X
] = 0;
584 dtext
->var_values
[VAR_Y
] = 0;
585 dtext
->var_values
[VAR_N
] = 0;
586 dtext
->var_values
[VAR_T
] = NAN
;
588 av_lfg_init(&dtext
->prng
, av_get_random_seed());
590 if ((ret
= av_expr_parse(&dtext
->x_pexpr
, dtext
->x_expr
, var_names
,
591 NULL
, NULL
, fun2_names
, fun2
, 0, ctx
)) < 0 ||
592 (ret
= av_expr_parse(&dtext
->y_pexpr
, dtext
->y_expr
, var_names
,
593 NULL
, NULL
, fun2_names
, fun2
, 0, ctx
)) < 0 ||
594 (ret
= av_expr_parse(&dtext
->d_pexpr
, dtext
->d_expr
, var_names
,
595 NULL
, NULL
, fun2_names
, fun2
, 0, ctx
)) < 0)
596 return AVERROR(EINVAL
);
599 ff_fill_line_with_color(dtext
->box_line
, dtext
->pixel_step
,
600 inlink
->w
, dtext
->boxcolor
,
601 inlink
->format
, dtext
->boxcolor_rgba
,
602 &dtext
->is_packed_rgb
, dtext
->rgba_map
)) < 0)
605 if (!dtext
->is_packed_rgb
) {
606 uint8_t *rgba
= dtext
->fontcolor_rgba
;
607 dtext
->fontcolor
[0] = RGB_TO_Y_CCIR(rgba
[0], rgba
[1], rgba
[2]);
608 dtext
->fontcolor
[1] = RGB_TO_U_CCIR(rgba
[0], rgba
[1], rgba
[2], 0);
609 dtext
->fontcolor
[2] = RGB_TO_V_CCIR(rgba
[0], rgba
[1], rgba
[2], 0);
610 dtext
->fontcolor
[3] = rgba
[3];
611 rgba
= dtext
->shadowcolor_rgba
;
612 dtext
->shadowcolor
[0] = RGB_TO_Y_CCIR(rgba
[0], rgba
[1], rgba
[2]);
613 dtext
->shadowcolor
[1] = RGB_TO_U_CCIR(rgba
[0], rgba
[1], rgba
[2], 0);
614 dtext
->shadowcolor
[2] = RGB_TO_V_CCIR(rgba
[0], rgba
[1], rgba
[2], 0);
615 dtext
->shadowcolor
[3] = rgba
[3];
620 return dtext_prepare_text(ctx
);
623 #define GET_BITMAP_VAL(r, c) \
624 bitmap->pixel_mode == FT_PIXEL_MODE_MONO ? \
625 (bitmap->buffer[(r) * bitmap->pitch + ((c)>>3)] & (0x80 >> ((c)&7))) * 255 : \
626 bitmap->buffer[(r) * bitmap->pitch + (c)]
628 #define SET_PIXEL_YUV(picref, yuva_color, val, x, y, hsub, vsub) { \
629 luma_pos = ((x) ) + ((y) ) * picref->linesize[0]; \
630 alpha = yuva_color[3] * (val) * 129; \
631 picref->data[0][luma_pos] = (alpha * yuva_color[0] + (255*255*129 - alpha) * picref->data[0][luma_pos] ) >> 23; \
632 if (((x) & ((1<<(hsub)) - 1)) == 0 && ((y) & ((1<<(vsub)) - 1)) == 0) {\
633 chroma_pos1 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[1]; \
634 chroma_pos2 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[2]; \
635 picref->data[1][chroma_pos1] = (alpha * yuva_color[1] + (255*255*129 - alpha) * picref->data[1][chroma_pos1]) >> 23; \
636 picref->data[2][chroma_pos2] = (alpha * yuva_color[2] + (255*255*129 - alpha) * picref->data[2][chroma_pos2]) >> 23; \
640 static inline int draw_glyph_yuv(AVFilterBufferRef
*picref
, FT_Bitmap
*bitmap
, unsigned int x
,
641 unsigned int y
, unsigned int width
, unsigned int height
,
642 const uint8_t yuva_color
[4], int hsub
, int vsub
)
645 unsigned int luma_pos
, chroma_pos1
, chroma_pos2
;
648 for (r
= 0; r
< bitmap
->rows
&& r
+y
< height
; r
++) {
649 for (c
= 0; c
< bitmap
->width
&& c
+x
< width
; c
++) {
650 /* get intensity value in the glyph bitmap (source) */
651 src_val
= GET_BITMAP_VAL(r
, c
);
655 SET_PIXEL_YUV(picref
, yuva_color
, src_val
, c
+x
, y
+r
, hsub
, vsub
);
662 #define SET_PIXEL_RGB(picref, rgba_color, val, x, y, pixel_step, r_off, g_off, b_off, a_off) { \
663 p = picref->data[0] + (x) * pixel_step + ((y) * picref->linesize[0]); \
664 alpha = rgba_color[3] * (val) * 129; \
665 *(p+r_off) = (alpha * rgba_color[0] + (255*255*129 - alpha) * *(p+r_off)) >> 23; \
666 *(p+g_off) = (alpha * rgba_color[1] + (255*255*129 - alpha) * *(p+g_off)) >> 23; \
667 *(p+b_off) = (alpha * rgba_color[2] + (255*255*129 - alpha) * *(p+b_off)) >> 23; \
670 static inline int draw_glyph_rgb(AVFilterBufferRef
*picref
, FT_Bitmap
*bitmap
,
671 unsigned int x
, unsigned int y
,
672 unsigned int width
, unsigned int height
, int pixel_step
,
673 const uint8_t rgba_color
[4], const uint8_t rgba_map
[4])
679 for (r
= 0; r
< bitmap
->rows
&& r
+y
< height
; r
++) {
680 for (c
= 0; c
< bitmap
->width
&& c
+x
< width
; c
++) {
681 /* get intensity value in the glyph bitmap (source) */
682 src_val
= GET_BITMAP_VAL(r
, c
);
686 SET_PIXEL_RGB(picref
, rgba_color
, src_val
, c
+x
, y
+r
, pixel_step
,
687 rgba_map
[0], rgba_map
[1], rgba_map
[2], rgba_map
[3]);
694 static inline void drawbox(AVFilterBufferRef
*picref
, unsigned int x
, unsigned int y
,
695 unsigned int width
, unsigned int height
,
696 uint8_t *line
[4], int pixel_step
[4], uint8_t color
[4],
697 int hsub
, int vsub
, int is_rgba_packed
, uint8_t rgba_map
[4])
701 if (color
[3] != 0xFF) {
702 if (is_rgba_packed
) {
704 for (j
= 0; j
< height
; j
++)
705 for (i
= 0; i
< width
; i
++)
706 SET_PIXEL_RGB(picref
, color
, 255, i
+x
, y
+j
, pixel_step
[0],
707 rgba_map
[0], rgba_map
[1], rgba_map
[2], rgba_map
[3]);
709 unsigned int luma_pos
, chroma_pos1
, chroma_pos2
;
710 for (j
= 0; j
< height
; j
++)
711 for (i
= 0; i
< width
; i
++)
712 SET_PIXEL_YUV(picref
, color
, 255, i
+x
, y
+j
, hsub
, vsub
);
715 ff_draw_rectangle(picref
->data
, picref
->linesize
,
716 line
, pixel_step
, hsub
, vsub
,
717 x
, y
, width
, height
);
721 static int draw_glyphs(DrawTextContext
*dtext
, AVFilterBufferRef
*picref
,
722 int width
, int height
, const uint8_t rgbcolor
[4], const uint8_t yuvcolor
[4], int x
, int y
)
724 char *text
= HAVE_LOCALTIME_R ? dtext
->expanded_text
: dtext
->text
;
730 for (i
= 0, p
= text
; *p
; i
++) {
732 GET_UTF8(code
, *p
++, continue;);
734 /* skip new line chars, just go to new line */
735 if (code
== '\n' || code
== '\r' || code
== '\t')
739 glyph
= av_tree_find(dtext
->glyphs
, &dummy
, (void *)glyph_cmp
, NULL
);
741 if (glyph
->bitmap
.pixel_mode
!= FT_PIXEL_MODE_MONO
&&
742 glyph
->bitmap
.pixel_mode
!= FT_PIXEL_MODE_GRAY
)
743 return AVERROR(EINVAL
);
745 if (dtext
->is_packed_rgb
) {
746 draw_glyph_rgb(picref
, &glyph
->bitmap
,
747 dtext
->positions
[i
].x
+x
, dtext
->positions
[i
].y
+y
, width
, height
,
748 dtext
->pixel_step
[0], rgbcolor
, dtext
->rgba_map
);
750 draw_glyph_yuv(picref
, &glyph
->bitmap
,
751 dtext
->positions
[i
].x
+x
, dtext
->positions
[i
].y
+y
, width
, height
,
752 yuvcolor
, dtext
->hsub
, dtext
->vsub
);
759 static int draw_text(AVFilterContext
*ctx
, AVFilterBufferRef
*picref
,
760 int width
, int height
)
762 DrawTextContext
*dtext
= ctx
->priv
;
767 drawbox(picref
, dtext
->x
, dtext
->y
, dtext
->w
, dtext
->h
,
768 dtext
->box_line
, dtext
->pixel_step
, dtext
->boxcolor
,
769 dtext
->hsub
, dtext
->vsub
, dtext
->is_packed_rgb
,
772 if (dtext
->shadowx
|| dtext
->shadowy
) {
773 if ((ret
= draw_glyphs(dtext
, picref
, width
, height
,
774 dtext
->shadowcolor_rgba
,
776 dtext
->x
+ dtext
->shadowx
,
777 dtext
->y
+ dtext
->shadowy
)) < 0)
781 if ((ret
= draw_glyphs(dtext
, picref
, width
, height
,
782 dtext
->fontcolor_rgba
,
791 static void null_draw_slice(AVFilterLink
*link
, int y
, int h
, int slice_dir
) { }
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 void start_frame(AVFilterLink
*inlink
, AVFilterBufferRef
*inpicref
)
810 AVFilterContext
*ctx
= inlink
->dst
;
811 DrawTextContext
*dtext
= ctx
->priv
;
814 if (dtext_prepare_text(ctx
) < 0) {
815 av_log(ctx
, AV_LOG_ERROR
, "Can't draw text\n");
819 dtext
->var_values
[VAR_T
] = inpicref
->pts
== AV_NOPTS_VALUE ?
820 NAN
: inpicref
->pts
* av_q2d(inlink
->time_base
);
821 dtext
->var_values
[VAR_X
] =
822 av_expr_eval(dtext
->x_pexpr
, dtext
->var_values
, &dtext
->prng
);
823 dtext
->var_values
[VAR_Y
] =
824 av_expr_eval(dtext
->y_pexpr
, dtext
->var_values
, &dtext
->prng
);
825 dtext
->var_values
[VAR_X
] =
826 av_expr_eval(dtext
->x_pexpr
, dtext
->var_values
, &dtext
->prng
);
828 dtext
->draw
= fail ?
0 :
829 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
);
850 avfilter_start_frame(inlink
->dst
->outputs
[0], inpicref
);
853 static void end_frame(AVFilterLink
*inlink
)
855 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
856 AVFilterBufferRef
*picref
= inlink
->cur_buf
;
857 DrawTextContext
*dtext
= inlink
->dst
->priv
;
860 draw_text(inlink
->dst
, picref
, picref
->video
->w
, picref
->video
->h
);
862 dtext
->var_values
[VAR_N
] += 1.0;
864 avfilter_draw_slice(outlink
, 0, picref
->video
->h
, 1);
865 avfilter_end_frame(outlink
);
868 AVFilter avfilter_vf_drawtext
= {
870 .description
= NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."),
871 .priv_size
= sizeof(DrawTextContext
),
874 .query_formats
= query_formats
,
876 .inputs
= (AVFilterPad
[]) {{ .name
= "default",
877 .type
= AVMEDIA_TYPE_VIDEO
,
878 .get_video_buffer
= avfilter_null_get_video_buffer
,
879 .start_frame
= start_frame
,
880 .draw_slice
= null_draw_slice
,
881 .end_frame
= end_frame
,
882 .config_props
= config_input
,
883 .min_perms
= AV_PERM_WRITE
|
885 .rej_perms
= AV_PERM_PRESERVE
},
887 .outputs
= (AVFilterPad
[]) {{ .name
= "default",
888 .type
= AVMEDIA_TYPE_VIDEO
, },