Revision bd8e7503

View differences:

configure
169 169
                           and libraw1394 [no]
170 170
  --enable-libdirac        enable Dirac support via libdirac [no]
171 171
  --enable-libfaac         enable FAAC support via libfaac [no]
172
  --enable-libfreetype     enable libfreetype [no]
172 173
  --enable-libgsm          enable GSM support via libgsm [no]
173 174
  --enable-libmp3lame      enable MP3 encoding via libmp3lame [no]
174 175
  --enable-libnut          enable NUT (de)muxing via libnut,
......
897 898
    libdc1394
898 899
    libdirac
899 900
    libfaac
901
    libfreetype
900 902
    libgsm
901 903
    libmp3lame
902 904
    libnut
......
1043 1045
    llrintf
1044 1046
    local_aligned_16
1045 1047
    local_aligned_8
1048
    localtime_r
1046 1049
    log2
1047 1050
    log2f
1048 1051
    loongson
......
1425 1428
# filters
1426 1429
blackframe_filter_deps="gpl"
1427 1430
cropdetect_filter_deps="gpl"
1431
drawtext_filter_deps="libfreetype"
1428 1432
frei0r_filter_deps="frei0r dlopen strtok_r"
1429 1433
frei0r_src_filter_deps="frei0r dlopen strtok_r"
1430 1434
hqdn3d_filter_deps="gpl"
......
2740 2744
check_struct "sys/time.h sys/resource.h" "struct rusage" ru_maxrss
2741 2745
check_func  inet_aton $network_extralibs
2742 2746
check_func  isatty
2747
check_func  localtime_r
2743 2748
check_func  ${malloc_prefix}memalign            && enable memalign
2744 2749
check_func  mkstemp
2745 2750
check_func  mmap
......
2826 2831
                      require  libdirac libdirac_decoder/dirac_parser.h dirac_decoder_init $(pkg-config --libs dirac) &&
2827 2832
                      require  libdirac libdirac_encoder/dirac_encoder.h dirac_encoder_init $(pkg-config --libs dirac)
2828 2833
enabled libfaac    && require2 libfaac "stdint.h faac.h" faacEncGetVersion -lfaac
2834
enabled libfreetype && add_cflags $(pkg-config --cflags freetype2) && require libfreetype ft2build.h FT_Init_FreeType -lfreetype
2829 2835
enabled libgsm     && require  libgsm gsm/gsm.h gsm_create -lgsm
2830 2836
enabled libmp3lame && require  "libmp3lame >= 3.98.3" lame/lame.h lame_set_VBR_quality -lmp3lame
2831 2837
enabled libnut     && require  libnut libnut.h nut_demuxer_init -lnut
doc/filters.texi
386 386
fade=in:5:20
387 387
@end example
388 388

  
389
@section drawtext
390

  
391
Draw text string or text from specified file on top of video using the
392
libfreetype library.
393

  
394
To enable compilation of this filter you need to configure FFmpeg with
395
@code{--enable-libfreetype}.
396

  
397
The filter also recognizes strftime() sequences in the provided text
398
and expands them accordingly. Check the documentation of strftime().
399

  
400
The filter accepts parameters as a list of @var{key}=@var{value} pairs,
401
separated by ":".
402

  
403
The description of the accepted parameters follows.
404

  
405
@table @option
406

  
407
@item fontfile
408
The font file to be used for drawing text. Path must be included.
409
This parameter is mandatory.
410

  
411
@item text
412
The text string to be drawn. The text must be a sequence of UTF-8
413
encoded characters.
414
This parameter is mandatory if no file is specified.
415

  
416
@item textfile
417
A text file containing text to be drawn. The text must be a sequence
418
of UTF-8 encoded characters
419

  
420
This parameter is mandatory if no text string is specified.
421

  
422
If both text and textfile are specified, an error is thrown.
423

  
424
@item x, y
425
The offsets where text will be drawn within the video frame.
426
Relative to the top/left border of the output image.
427

  
428
The default value of @var{x} and @var{y} is 0.
429

  
430
@item fontsize
431
The font size to be used for drawing text.
432
The default value of @var{size} is 16.
433

  
434
@item fontcolor
435
The color to be used for drawing fonts.
436
Either a string (e.g. "red") or in 0xRRGGBB[AA] format
437
(e.g. "0xff000033"), possibly followed by an alpha specifier.
438
The default value of @var{fontcolor} is "black".
439

  
440
@item boxcolor
441
The color to be used for drawing box around text.
442
Either a string (e.g. "yellow") or in 0xRRGGBB[AA] format
443
(e.g. "0xff00ff"), possibly followed by an alpha specifier.
444

  
445
The default value of @var{bgcolor} is "white".
446

  
447
@item box
448
Used to draw a box around text using background color.
449
Value should be either 1 (enable) or 0 (disable).
450
The default value of @var{box} is 0.
451

  
452
@item ft_load_flags
453
Flags to be used for loading the fonts.
454

  
455
The flags map the corresponding flags supported by libfreetype, and are
456
a combination of the following values:
457
@table @var
458
@item default
459
@item no_scale
460
@item no_hinting
461
@item render
462
@item no_bitmap
463
@item vertical_layout
464
@item force_autohint
465
@item crop_bitmap
466
@item pedantic
467
@item ignore_global_advance_width
468
@item no_recurse
469
@item ignore_transform
470
@item monochrome
471
@item linear_design
472
@item no_autohint
473
@item end table
474
@end table
475

  
476
Default value is "render".
477

  
478
For more information consult the documentation for the FT_LOAD_*
479
libfreetype flags.
480

  
481
@item tabsize
482
The size in number of spaces to use for rendering the tab.
483
Default value is 4.
484
@end table
485

  
486
For example the command:
487
@example
488
drawtext=fontfile=FreeSerif.ttf: text='Test Text': x=100: y=50: fontsize=24: fontcolor=yellow@@0.2: boxcolor=red@@0.2: box=1"
489
@end example
490

  
491
will draw 'Test Text' with font FreeSerif of size 24 at position
492
(100,50), text color is yellow, and draw a red box around text. Both
493
the text and the box have an opacity of 20%.
494

  
495
Note that the double quotes are not necessary if spaces are not used
496
within the parameter list.
497

  
498
For more information about libfreetype, check:
499
@url{http://www.freetype.org/}
500

  
389 501
@section fifo
390 502

  
391 503
Buffer input images and send them when they are requested.
libavfilter/Makefile
27 27
OBJS-$(CONFIG_CROP_FILTER)                   += vf_crop.o
28 28
OBJS-$(CONFIG_CROPDETECT_FILTER)             += vf_cropdetect.o
29 29
OBJS-$(CONFIG_DRAWBOX_FILTER)                += vf_drawbox.o
30
OBJS-$(CONFIG_DRAWTEXT_FILTER)               += vf_drawtext.o
30 31
OBJS-$(CONFIG_FADE_FILTER)                   += vf_fade.o
31 32
OBJS-$(CONFIG_FIFO_FILTER)                   += vf_fifo.o
32 33
OBJS-$(CONFIG_FORMAT_FILTER)                 += vf_format.o
libavfilter/allfilters.c
45 45
    REGISTER_FILTER (CROP,        crop,        vf);
46 46
    REGISTER_FILTER (CROPDETECT,  cropdetect,  vf);
47 47
    REGISTER_FILTER (DRAWBOX,     drawbox,     vf);
48
    REGISTER_FILTER (DRAWTEXT,    drawtext,    vf);
48 49
    REGISTER_FILTER (FADE,        fade,        vf);
49 50
    REGISTER_FILTER (FIFO,        fifo,        vf);
50 51
    REGISTER_FILTER (FORMAT,      format,      vf);
libavfilter/vf_drawtext.c
1
/*
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>
5
 *
6
 * This file is part of FFmpeg.
7
 *
8
 * FFmpeg 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.
12
 *
13
 * FFmpeg 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.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with FFmpeg; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22

  
23
/**
24
 * @file
25
 * drawtext filter, based on the original FFmpeg vhook/drawtext.c
26
 * filter by Gustavo Sverzut Barbieri
27
 */
28

  
29
#include <sys/time.h>
30
#include <time.h>
31

  
32
#include "libavutil/colorspace.h"
33
#include "libavutil/file.h"
34
#include "libavutil/opt.h"
35
#include "libavutil/parseutils.h"
36
#include "libavutil/pixdesc.h"
37
#include "libavutil/tree.h"
38
#include "avfilter.h"
39
#include "drawutils.h"
40

  
41
#undef time
42

  
43
#include <ft2build.h>
44
#include <freetype/config/ftheader.h>
45
#include FT_FREETYPE_H
46
#include FT_GLYPH_H
47

  
48
#define MAX_EXPANDED_TEXT_SIZE 2048
49

  
50
typedef struct {
51
    const AVClass *class;
52
    char *fontfile;                 ///< font to be used
53
    char *text;                     ///< text to be drawn
54
    int ft_load_flags;              ///< flags used for loading fonts, see FT_LOAD_*
55
    /** buffer containing the text expanded by strftime */
56
    char expanded_text[MAX_EXPANDED_TEXT_SIZE];
57
    /** positions for each element in the text */
58
    FT_Vector positions[MAX_EXPANDED_TEXT_SIZE];
59
    char *textfile;                 ///< file with text to be drawn
60
    unsigned int x;                 ///< x position to start drawing text
61
    unsigned int y;                 ///< y position to start drawing text
62
    unsigned int fontsize;          ///< font size to use
63
    char *fontcolor_string;         ///< font color as string
64
    char *boxcolor_string;          ///< box color as string
65
    uint8_t fontcolor[4];           ///< foreground color
66
    uint8_t boxcolor[4];            ///< background color
67
    uint8_t fontcolor_rgba[4];      ///< foreground color in RGBA
68
    uint8_t boxcolor_rgba[4];       ///< background color in RGBA
69

  
70
    short int draw_box;             ///< draw box around text - true or false
71
    int use_kerning;                ///< font kerning is used - true/false
72
    int tabsize;                    ///< tab size
73

  
74
    FT_Library library;             ///< freetype font library handle
75
    FT_Face face;                   ///< freetype font face handle
76
    struct AVTreeNode *glyphs;      ///< rendered glyphs, stored using the UTF-32 char code
77
    int hsub, vsub;                 ///< chroma subsampling values
78
    int is_packed_rgb;
79
    int pixel_step[4];              ///< distance in bytes between the component of each pixel
80
    uint8_t rgba_map[4];            ///< map RGBA offsets to the positions in the packed RGBA format
81
    uint8_t *box_line[4];           ///< line used for filling the box background
82
} DrawTextContext;
83

  
84
#define OFFSET(x) offsetof(DrawTextContext, x)
85

  
86
static const AVOption drawtext_options[]= {
87
{"fontfile", "set font file",        OFFSET(fontfile),         FF_OPT_TYPE_STRING, 0,  CHAR_MIN, CHAR_MAX },
88
{"text",     "set text",             OFFSET(text),             FF_OPT_TYPE_STRING, 0,  CHAR_MIN, CHAR_MAX },
89
{"textfile", "set text file",        OFFSET(textfile),         FF_OPT_TYPE_STRING, 0,  CHAR_MIN, CHAR_MAX },
90
{"fontcolor","set foreground color", OFFSET(fontcolor_string), FF_OPT_TYPE_STRING, 0,  CHAR_MIN, CHAR_MAX },
91
{"boxcolor", "set box color",        OFFSET(boxcolor_string),  FF_OPT_TYPE_STRING, 0,  CHAR_MIN, CHAR_MAX },
92
{"box",      "set box",              OFFSET(draw_box),         FF_OPT_TYPE_INT,    0,         0,        1 },
93
{"fontsize", "set font size",        OFFSET(fontsize),         FF_OPT_TYPE_INT,   16,         1,       72 },
94
{"x",        "set x",                OFFSET(x),                FF_OPT_TYPE_INT,    0,         0,  INT_MAX },
95
{"y",        "set y",                OFFSET(y),                FF_OPT_TYPE_INT,    0,         0,  INT_MAX },
96
{"tabsize",  "set tab size",         OFFSET(tabsize),          FF_OPT_TYPE_INT,    4,         0,  INT_MAX },
97

  
98
/* FT_LOAD_* flags */
99
{"ft_load_flags", "set font loading flags for libfreetype",   OFFSET(ft_load_flags),  FF_OPT_TYPE_FLAGS,  FT_LOAD_DEFAULT|FT_LOAD_RENDER, 0, INT_MAX, 0, "ft_load_flags" },
100
{"default",                     "set default",                     0, FF_OPT_TYPE_CONST, FT_LOAD_DEFAULT,                     INT_MIN, INT_MAX, 0, "ft_load_flags" },
101
{"no_scale",                    "set no_scale",                    0, FF_OPT_TYPE_CONST, FT_LOAD_NO_SCALE,                    INT_MIN, INT_MAX, 0, "ft_load_flags" },
102
{"no_hinting",                  "set no_hinting",                  0, FF_OPT_TYPE_CONST, FT_LOAD_NO_HINTING,                  INT_MIN, INT_MAX, 0, "ft_load_flags" },
103
{"render",                      "set render",                      0, FF_OPT_TYPE_CONST, FT_LOAD_RENDER,                      INT_MIN, INT_MAX, 0, "ft_load_flags" },
104
{"no_bitmap",                   "set no_bitmap",                   0, FF_OPT_TYPE_CONST, FT_LOAD_NO_BITMAP,                   INT_MIN, INT_MAX, 0, "ft_load_flags" },
105
{"vertical_layout",             "set vertical_layout",             0, FF_OPT_TYPE_CONST, FT_LOAD_VERTICAL_LAYOUT,             INT_MIN, INT_MAX, 0, "ft_load_flags" },
106
{"force_autohint",              "set force_autohint",              0, FF_OPT_TYPE_CONST, FT_LOAD_FORCE_AUTOHINT,              INT_MIN, INT_MAX, 0, "ft_load_flags" },
107
{"crop_bitmap",                 "set crop_bitmap",                 0, FF_OPT_TYPE_CONST, FT_LOAD_CROP_BITMAP,                 INT_MIN, INT_MAX, 0, "ft_load_flags" },
108
{"pedantic",                    "set pedantic",                    0, FF_OPT_TYPE_CONST, FT_LOAD_PEDANTIC,                    INT_MIN, INT_MAX, 0, "ft_load_flags" },
109
{"ignore_global_advance_width", "set ignore_global_advance_width", 0, FF_OPT_TYPE_CONST, FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, INT_MIN, INT_MAX, 0, "ft_load_flags" },
110
{"no_recurse",                  "set no_recurse",                  0, FF_OPT_TYPE_CONST, FT_LOAD_NO_RECURSE,                  INT_MIN, INT_MAX, 0, "ft_load_flags" },
111
{"ignore_transform",            "set ignore_transform",            0, FF_OPT_TYPE_CONST, FT_LOAD_IGNORE_TRANSFORM,            INT_MIN, INT_MAX, 0, "ft_load_flags" },
112
{"monochrome",                  "set monochrome",                  0, FF_OPT_TYPE_CONST, FT_LOAD_MONOCHROME,                  INT_MIN, INT_MAX, 0, "ft_load_flags" },
113
{"linear_design",               "set linear_design",               0, FF_OPT_TYPE_CONST, FT_LOAD_LINEAR_DESIGN,               INT_MIN, INT_MAX, 0, "ft_load_flags" },
114
{"no_autohint",                 "set no_autohint",                 0, FF_OPT_TYPE_CONST, FT_LOAD_NO_AUTOHINT,                 INT_MIN, INT_MAX, 0, "ft_load_flags" },
115
{NULL},
116
};
117

  
118
static const char *drawtext_get_name(void *ctx)
119
{
120
    return "drawtext";
121
}
122

  
123
static const AVClass drawtext_class = {
124
    "DrawTextContext",
125
    drawtext_get_name,
126
    drawtext_options
127
};
128

  
129
#undef __FTERRORS_H__
130
#define FT_ERROR_START_LIST {
131
#define FT_ERRORDEF(e, v, s) { (e), (s) },
132
#define FT_ERROR_END_LIST { 0, NULL } };
133

  
134
struct ft_error
135
{
136
    int err;
137
    const char *err_msg;
138
} static ft_errors[] =
139
#include FT_ERRORS_H
140

  
141
#define FT_ERRMSG(e) ft_errors[e].err_msg
142

  
143
typedef struct {
144
    FT_Glyph *glyph;
145
    uint32_t code;
146
    FT_Bitmap bitmap; ///< array holding bitmaps of font
147
    FT_BBox bbox;
148
    int advance;
149
    int bitmap_left;
150
    int bitmap_top;
151
} Glyph;
152

  
153
static int glyph_cmp(const Glyph *a, const Glyph *b)
154
{
155
    int64_t diff = (int64_t)a->code - (int64_t)b->code;
156
    return diff > 0 ? 1 : diff < 0 ? -1 : 0;
157
}
158

  
159
/**
160
 * Load glyphs corresponding to the UTF-32 codepoint code.
161
 */
162
static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
163
{
164
    DrawTextContext *dtext = ctx->priv;
165
    Glyph *glyph = av_mallocz(sizeof(Glyph));
166
    struct AVTreeNode *node = NULL;
167
    int ret;
168

  
169
    /* load glyph into dtext->face->glyph */
170
    ret = FT_Load_Char(dtext->face, code, dtext->ft_load_flags);
171
    if (ret)
172
        return AVERROR(EINVAL);
173

  
174
    /* save glyph */
175
    glyph->code  = code;
176
    glyph->glyph = av_mallocz(sizeof(FT_Glyph));
177
    ret = FT_Get_Glyph(dtext->face->glyph, glyph->glyph);
178
    if (ret)
179
        return AVERROR(EINVAL);
180

  
181
    glyph->bitmap      = dtext->face->glyph->bitmap;
182
    glyph->bitmap_left = dtext->face->glyph->bitmap_left;
183
    glyph->bitmap_top  = dtext->face->glyph->bitmap_top;
184
    glyph->advance     = dtext->face->glyph->advance.x >> 6;
185

  
186
    /* measure text height to calculate text_height (or the maximum text height) */
187
    FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
188

  
189
    /* cache the newly created glyph */
190
    if (!node)
191
        node = av_mallocz(av_tree_node_size);
192
    av_tree_insert(&dtext->glyphs, glyph, (void *)glyph_cmp, &node);
193

  
194
    if (glyph_ptr)
195
        *glyph_ptr = glyph;
196
    return 0;
197
}
198

  
199
static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
200
{
201
    int err;
202
    DrawTextContext *dtext = ctx->priv;
203

  
204
    dtext->class = &drawtext_class;
205
    av_opt_set_defaults2(dtext, 0, 0);
206
    dtext->fontcolor_string = av_strdup("black");
207
    dtext->boxcolor_string = av_strdup("white");
208

  
209
    if ((err = (av_set_options_string(dtext, args, "=", ":"))) < 0) {
210
        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
211
        return err;
212
    }
213

  
214
    if (!dtext->fontfile) {
215
        av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
216
        return AVERROR(EINVAL);
217
    }
218

  
219
    if (dtext->textfile) {
220
        uint8_t *textbuf;
221
        size_t textbuf_size;
222

  
223
        if (dtext->text) {
224
            av_log(ctx, AV_LOG_ERROR,
225
                   "Both text and text file provided. Please provide only one\n");
226
            return AVERROR(EINVAL);
227
        }
228
        if ((err = av_file_map(dtext->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
229
            av_log(ctx, AV_LOG_ERROR,
230
                   "The text file '%s' could not be read or is empty\n",
231
                   dtext->textfile);
232
            return err;
233
        }
234

  
235
        if (!(dtext->text = av_malloc(textbuf_size+1)))
236
            return AVERROR(ENOMEM);
237
        memcpy(dtext->text, textbuf, textbuf_size);
238
        dtext->text[textbuf_size] = 0;
239
        av_file_unmap(textbuf, textbuf_size);
240
    }
241

  
242
    if (!dtext->text) {
243
        av_log(ctx, AV_LOG_ERROR,
244
               "Either text or a valid file must be provided\n");
245
        return AVERROR(EINVAL);
246
    }
247

  
248
    if ((err = av_parse_color(dtext->fontcolor_rgba, dtext->fontcolor_string, -1, ctx))) {
249
        av_log(ctx, AV_LOG_ERROR,
250
               "Invalid font color '%s'\n", dtext->fontcolor_string);
251
        return err;
252
    }
253

  
254
    if ((err = av_parse_color(dtext->boxcolor_rgba, dtext->boxcolor_string, -1, ctx))) {
255
        av_log(ctx, AV_LOG_ERROR,
256
               "Invalid box color '%s'\n", dtext->boxcolor_string);
257
        return err;
258
    }
259

  
260
    if ((err = FT_Init_FreeType(&(dtext->library)))) {
261
        av_log(ctx, AV_LOG_ERROR,
262
               "Could not load FreeType: %s\n", FT_ERRMSG(err));
263
        return AVERROR(EINVAL);
264
    }
265

  
266
    /* load the face, and set up the encoding, which is by default UTF-8 */
267
    if ((err = FT_New_Face(dtext->library, dtext->fontfile, 0, &dtext->face))) {
268
        av_log(ctx, AV_LOG_ERROR, "Could not load fontface from file '%s': %s\n",
269
               dtext->fontfile, FT_ERRMSG(err));
270
        return AVERROR(EINVAL);
271
    }
272
    if ((err = FT_Set_Pixel_Sizes(dtext->face, 0, dtext->fontsize))) {
273
        av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n",
274
               dtext->fontsize, FT_ERRMSG(err));
275
        return AVERROR(EINVAL);
276
    }
277

  
278
    dtext->use_kerning = FT_HAS_KERNING(dtext->face);
279

  
280
    /* load the fallback glyph with code 0 */
281
    load_glyph(ctx, NULL, 0);
282

  
283
#if !HAVE_LOCALTIME_R
284
    av_log(ctx, AV_LOG_WARNING, "strftime() expansion unavailable!\n");
285
#else
286
    if (strlen(dtext->text) >= MAX_EXPANDED_TEXT_SIZE) {
287
        av_log(ctx, AV_LOG_ERROR,
288
               "Impossible to print text, string is too big\n");
289
        return AVERROR(EINVAL);
290
    }
291
#endif
292

  
293
    return 0;
294
}
295

  
296
static int query_formats(AVFilterContext *ctx)
297
{
298
    static const enum PixelFormat pix_fmts[] = {
299
        PIX_FMT_ARGB,    PIX_FMT_RGBA,
300
        PIX_FMT_ABGR,    PIX_FMT_BGRA,
301
        PIX_FMT_RGB24,   PIX_FMT_BGR24,
302
        PIX_FMT_YUV420P, PIX_FMT_YUV444P,
303
        PIX_FMT_YUV422P, PIX_FMT_YUV411P,
304
        PIX_FMT_YUV410P, PIX_FMT_YUV440P,
305
        PIX_FMT_NONE
306
    };
307

  
308
    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
309
    return 0;
310
}
311

  
312
static int glyph_enu_free(void *opaque, void *elem)
313
{
314
    av_free(elem);
315
    return 0;
316
}
317

  
318
static av_cold void uninit(AVFilterContext *ctx)
319
{
320
    DrawTextContext *dtext = ctx->priv;
321
    int i;
322

  
323
    av_freep(&dtext->fontfile);
324
    av_freep(&dtext->text);
325
    av_freep(&dtext->fontcolor_string);
326
    av_freep(&dtext->boxcolor_string);
327
    av_tree_enumerate(dtext->glyphs, NULL, NULL, glyph_enu_free);
328
    av_tree_destroy(dtext->glyphs);
329
    dtext->glyphs = 0;
330
    FT_Done_Face(dtext->face);
331
    FT_Done_FreeType(dtext->library);
332

  
333
    for (i = 0; i < 4; i++) {
334
        av_freep(&dtext->box_line[i]);
335
        dtext->pixel_step[i] = 0;
336
    }
337

  
338
}
339

  
340
static int config_input(AVFilterLink *inlink)
341
{
342
    DrawTextContext *dtext = inlink->dst->priv;
343
    const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
344
    int ret;
345

  
346
    dtext->hsub = pix_desc->log2_chroma_w;
347
    dtext->vsub = pix_desc->log2_chroma_h;
348

  
349
    if ((ret =
350
         ff_fill_line_with_color(dtext->box_line, dtext->pixel_step,
351
                                 inlink->w, dtext->boxcolor,
352
                                 inlink->format, dtext->boxcolor_rgba,
353
                                 &dtext->is_packed_rgb, dtext->rgba_map)) < 0)
354
        return ret;
355

  
356
    if (!dtext->is_packed_rgb) {
357
        uint8_t *rgba = dtext->fontcolor_rgba;
358
        dtext->fontcolor[0] = RGB_TO_Y_CCIR(rgba[0], rgba[1], rgba[2]);
359
        dtext->fontcolor[1] = RGB_TO_U_CCIR(rgba[0], rgba[1], rgba[2], 0);
360
        dtext->fontcolor[2] = RGB_TO_V_CCIR(rgba[0], rgba[1], rgba[2], 0);
361
        dtext->fontcolor[3] = rgba[3];
362
    }
363

  
364
    return 0;
365
}
366

  
367
#define GET_BITMAP_VAL(r, c)                                            \
368
    bitmap->pixel_mode == FT_PIXEL_MODE_MONO ?                          \
369
        (bitmap->buffer[(r) * bitmap->pitch + ((c)>>3)] & (0x80 >> ((c)&7))) * 255 : \
370
         bitmap->buffer[(r) * bitmap->pitch +  (c)]
371

  
372
#define SET_PIXEL_YUV(picref, yuva_color, val, x, y, hsub, vsub) {           \
373
    luma_pos    = ((x)          ) + ((y)          ) * picref->linesize[0]; \
374
    chroma_pos1 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[1]; \
375
    chroma_pos2 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[2]; \
376
    alpha = (yuva_color[3] * (val)) / 255;                               \
377
    picref->data[0][luma_pos] = (alpha * yuva_color[0] + (255 - alpha) * picref->data[0][luma_pos]) >> 8; \
378
    alpha = (yuva_color[3] * (val)) / 224;                               \
379
    picref->data[1][chroma_pos1] = 16 + (alpha * (yuva_color[1]-16) + (224 - alpha) * (picref->data[1][chroma_pos1]-16)) / 224; \
380
    picref->data[2][chroma_pos2] = 16 + (alpha * (yuva_color[2]-16) + (224 - alpha) * (picref->data[2][chroma_pos2]-16)) / 224; \
381
}
382

  
383
static inline int draw_glyph_yuv(AVFilterBufferRef *picref, FT_Bitmap *bitmap, unsigned int x,
384
                                 unsigned int y, unsigned int width, unsigned int height,
385
                                 unsigned char yuva_color[4], int hsub, int vsub)
386
{
387
    int r, c, alpha;
388
    unsigned int luma_pos, chroma_pos1, chroma_pos2;
389
    uint8_t src_val, dst_pixel[4];
390

  
391
    for (r = 0; r < bitmap->rows && r+y < height; r++) {
392
        for (c = 0; c < bitmap->width && c+x < width; c++) {
393
            /* get pixel in the picref (destination) */
394
            dst_pixel[0] = picref->data[0][  c+x           +  (y+r)          * picref->linesize[0]];
395
            dst_pixel[1] = picref->data[1][((c+x) >> hsub) + ((y+r) >> vsub) * picref->linesize[1]];
396
            dst_pixel[2] = picref->data[2][((c+x) >> hsub) + ((y+r) >> vsub) * picref->linesize[2]];
397

  
398
            /* get intensity value in the glyph bitmap (source) */
399
            src_val = GET_BITMAP_VAL(r, c);
400
            if (!src_val)
401
                continue;
402

  
403
            SET_PIXEL_YUV(picref, yuva_color, src_val, c+x, y+r, hsub, vsub);
404
        }
405
    }
406

  
407
    return 0;
408
}
409

  
410
#define SET_PIXEL_RGB(picref, rgba_color, val, x, y, pixel_step, r_off, g_off, b_off, a_off) { \
411
    p   = picref->data[0] + (x) * pixel_step + ((y) * picref->linesize[0]); \
412
    alpha = (rgba_color[3] * (val)) / 255;                              \
413
    *(p+r_off) = (alpha * rgba_color[0] + (255 - alpha) * *(p+r_off)) >> 8; \
414
    *(p+g_off) = (alpha * rgba_color[1] + (255 - alpha) * *(p+g_off)) >> 8; \
415
    *(p+b_off) = (alpha * rgba_color[2] + (255 - alpha) * *(p+b_off)) >> 8; \
416
}
417

  
418
static inline int draw_glyph_rgb(AVFilterBufferRef *picref, FT_Bitmap *bitmap,
419
                                 unsigned int x, unsigned int y,
420
                                 unsigned int width, unsigned int height, int pixel_step,
421
                                 unsigned char rgba_color[4], uint8_t rgba_map[4])
422
{
423
    int r, c, alpha;
424
    uint8_t *p;
425
    uint8_t src_val, dst_pixel[4];
426

  
427
    for (r = 0; r < bitmap->rows && r+y < height; r++) {
428
        for (c = 0; c < bitmap->width && c+x < width; c++) {
429
            /* get pixel in the picref (destination) */
430
            dst_pixel[0] = picref->data[0][(c+x + rgba_map[0]) * pixel_step +
431
                                           (y+r) * picref->linesize[0]];
432
            dst_pixel[1] = picref->data[0][(c+x + rgba_map[1]) * pixel_step +
433
                                           (y+r) * picref->linesize[0]];
434
            dst_pixel[2] = picref->data[0][(c+x + rgba_map[2]) * pixel_step +
435
                                           (y+r) * picref->linesize[0]];
436

  
437
            /* get intensity value in the glyph bitmap (source) */
438
            src_val = GET_BITMAP_VAL(r, c);
439
            if (!src_val)
440
                continue;
441

  
442
            SET_PIXEL_RGB(picref, rgba_color, src_val, c+x, y+r, pixel_step,
443
                          rgba_map[0], rgba_map[1], rgba_map[2], rgba_map[3]);
444
        }
445
    }
446

  
447
    return 0;
448
}
449

  
450
static inline void drawbox(AVFilterBufferRef *picref, unsigned int x, unsigned int y,
451
                           unsigned int width, unsigned int height,
452
                           uint8_t *line[4], int pixel_step[4], uint8_t color[4],
453
                           int hsub, int vsub, int is_rgba_packed, uint8_t rgba_map[4])
454
{
455
    int i, j, alpha;
456

  
457
    if (color[3] != 0xFF) {
458
        if (is_rgba_packed) {
459
            uint8_t *p;
460
            for (j = 0; j < height; j++)
461
                for (i = 0; i < width; i++)
462
                    SET_PIXEL_RGB(picref, color, 255, i+x, y+j, pixel_step[0],
463
                                  rgba_map[0], rgba_map[1], rgba_map[2], rgba_map[3]);
464
        } else {
465
            unsigned int luma_pos, chroma_pos1, chroma_pos2;
466
            for (j = 0; j < height; j++)
467
                for (i = 0; i < width; i++)
468
                    SET_PIXEL_YUV(picref, color, 255, i+x, y+j, hsub, vsub);
469
        }
470
    } else {
471
        ff_draw_rectangle(picref->data, picref->linesize,
472
                          line, pixel_step, hsub, vsub,
473
                          x, y, width, height);
474
    }
475
}
476

  
477
static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
478
                     int width, int height)
479
{
480
    DrawTextContext *dtext = ctx->priv;
481
    char *text = dtext->text;
482
    uint32_t code = 0, prev_code = 0;
483
    int x = 0, y = 0, i = 0;
484
    int text_height, baseline;
485
    uint8_t *p;
486
    int str_w, str_w_max;
487
    int y_min = 32000, y_max = -32000;
488
    FT_Vector delta;
489
    Glyph *glyph = NULL, *prev_glyph = NULL;
490
    Glyph dummy = { 0 };
491

  
492
#if HAVE_LOCALTIME_R
493
    time_t now = time(0);
494
    struct tm ltime;
495
    size_t expanded_text_len;
496

  
497
    dtext->expanded_text[0] = '\1';
498
    expanded_text_len = strftime(dtext->expanded_text, MAX_EXPANDED_TEXT_SIZE,
499
                                 text, localtime_r(&now, &ltime));
500
    text = dtext->expanded_text;
501
    if (expanded_text_len == 0 && dtext->expanded_text[0] != '\0') {
502
        av_log(ctx, AV_LOG_ERROR,
503
               "Impossible to print text, string is too big\n");
504
        return AVERROR(EINVAL);
505
    }
506
#endif
507

  
508
    str_w = str_w_max = 0;
509
    x = dtext->x;
510
    y = dtext->y;
511

  
512
    /* load and cache glyphs */
513
    for (i = 0, p = text; *p; i++) {
514
        GET_UTF8(code, *p++, continue;);
515

  
516
        /* get glyph */
517
        dummy.code = code;
518
        glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
519
        if (!glyph)
520
            load_glyph(ctx, &glyph, code);
521

  
522
        y_min = FFMIN(glyph->bbox.yMin, y_min);
523
        y_max = FFMAX(glyph->bbox.yMax, y_max);
524
    }
525
    text_height = y_max - y_min;
526
    baseline    = y_max;
527

  
528
    /* compute and save position for each glyph */
529
    glyph = NULL;
530
    for (i = 0, p = text; *p; i++) {
531
        GET_UTF8(code, *p++, continue;);
532

  
533
        /* skip the \n in the sequence \r\n */
534
        if (prev_code == '\r' && code == '\n')
535
            continue;
536

  
537
        /* get glyph */
538
        prev_glyph = glyph;
539
        dummy.code = code;
540
        glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
541

  
542
        /* kerning */
543
        if (dtext->use_kerning && prev_glyph && glyph->code) {
544
            FT_Get_Kerning(dtext->face, prev_glyph->code, glyph->code,
545
                           ft_kerning_default, &delta);
546
            x += delta.x >> 6;
547
        }
548

  
549
        if (x + glyph->advance >= width || code == '\r' || code == '\n') {
550
            if (x + glyph->advance >= width)
551
                str_w_max = width - dtext->x - 1;
552
            y += text_height;
553
            x = dtext->x;
554
        }
555

  
556
        /* save position */
557
        dtext->positions[i].x = x + glyph->bitmap_left;
558
        dtext->positions[i].y = y - glyph->bitmap_top + baseline;
559
        if (code != '\n' && code != '\r') {
560
            int advance = glyph->advance;
561
            if (code == '\t')
562
                advance *= dtext->tabsize;
563
            x     += advance;
564
            str_w += advance;
565
        }
566
        prev_code = code;
567
    }
568

  
569
    y += text_height;
570
    if (str_w_max == 0)
571
        str_w_max = str_w;
572

  
573
    /* draw box */
574
    if (dtext->draw_box) {
575
        /* check if it doesn't pass the limits */
576
        str_w_max = FFMIN(str_w_max, width - dtext->x - 1);
577
        y = FFMIN(y, height - 1);
578

  
579
        /* draw background */
580
        drawbox(picref, dtext->x, dtext->y, str_w_max, y-dtext->y,
581
                dtext->box_line, dtext->pixel_step, dtext->boxcolor,
582
                dtext->hsub, dtext->vsub, dtext->is_packed_rgb, dtext->rgba_map);
583
    }
584

  
585
    /* draw glyphs */
586
    for (i = 0, p = text; *p; i++) {
587
        Glyph dummy = { 0 };
588
        GET_UTF8(code, *p++, continue;);
589

  
590
        /* skip new line chars, just go to new line */
591
        if (code == '\n' || code == '\r' || code == '\t')
592
            continue;
593

  
594
        dummy.code = code;
595
        glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
596

  
597
        if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
598
            glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
599
            return AVERROR(EINVAL);
600

  
601
        if (dtext->is_packed_rgb) {
602
            draw_glyph_rgb(picref, &glyph->bitmap,
603
                           dtext->positions[i].x, dtext->positions[i].y, width, height,
604
                           dtext->pixel_step[0], dtext->fontcolor_rgba, dtext->rgba_map);
605
        } else {
606
            draw_glyph_yuv(picref, &glyph->bitmap,
607
                           dtext->positions[i].x, dtext->positions[i].y, width, height,
608
                           dtext->fontcolor, dtext->hsub, dtext->vsub);
609
        }
610
    }
611

  
612
    return 0;
613
}
614

  
615
static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
616

  
617
static void end_frame(AVFilterLink *inlink)
618
{
619
    AVFilterLink *outlink = inlink->dst->outputs[0];
620
    AVFilterBufferRef *picref = inlink->cur_buf;
621

  
622
    draw_text(inlink->dst, picref, picref->video->w, picref->video->h);
623

  
624
    avfilter_draw_slice(outlink, 0, picref->video->h, 1);
625
    avfilter_end_frame(outlink);
626
}
627

  
628
AVFilter avfilter_vf_drawtext = {
629
    .name          = "drawtext",
630
    .description   = NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."),
631
    .priv_size     = sizeof(DrawTextContext),
632
    .init          = init,
633
    .uninit        = uninit,
634
    .query_formats = query_formats,
635

  
636
    .inputs    = (AVFilterPad[]) {{ .name             = "default",
637
                                    .type             = AVMEDIA_TYPE_VIDEO,
638
                                    .get_video_buffer = avfilter_null_get_video_buffer,
639
                                    .start_frame      = avfilter_null_start_frame,
640
                                    .draw_slice       = null_draw_slice,
641
                                    .end_frame        = end_frame,
642
                                    .config_props     = config_input,
643
                                    .min_perms        = AV_PERM_WRITE |
644
                                                        AV_PERM_READ,
645
                                    .rej_perms        = AV_PERM_PRESERVE },
646
                                  { .name = NULL}},
647
    .outputs   = (AVFilterPad[]) {{ .name             = "default",
648
                                    .type             = AVMEDIA_TYPE_VIDEO, },
649
                                  { .name = NULL}},
650
};

Also available in: Unified diff