00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00029 #include <sys/time.h>
00030 #include <time.h>
00031
00032 #include "libavutil/colorspace.h"
00033 #include "libavutil/file.h"
00034 #include "libavutil/eval.h"
00035 #include "libavutil/opt.h"
00036 #include "libavutil/mathematics.h"
00037 #include "libavutil/random_seed.h"
00038 #include "libavutil/parseutils.h"
00039 #include "libavutil/pixdesc.h"
00040 #include "libavutil/tree.h"
00041 #include "libavutil/lfg.h"
00042 #include "avfilter.h"
00043 #include "drawutils.h"
00044
00045 #undef time
00046
00047 #include <ft2build.h>
00048 #include FT_FREETYPE_H
00049 #include FT_GLYPH_H
00050
00051 static const char *var_names[] = {
00052 "E",
00053 "PHI",
00054 "PI",
00055 "main_w", "W",
00056 "main_h", "H",
00057 "text_w", "w",
00058 "text_h", "h",
00059 "x",
00060 "y",
00061 "n",
00062 "t",
00063 NULL
00064 };
00065
00066 static const char *fun2_names[] = {
00067 "rand",
00068 };
00069
00070 static double drand(void *opaque, double min, double max)
00071 {
00072 return min + (max-min) / UINT_MAX * av_lfg_get(opaque);
00073 }
00074
00075 typedef double (*eval_func2)(void *, double a, double b);
00076
00077 static const eval_func2 fun2[] = {
00078 drand,
00079 NULL
00080 };
00081
00082 enum var_name {
00083 VAR_E,
00084 VAR_PHI,
00085 VAR_PI,
00086 VAR_MAIN_W, VAR_MW,
00087 VAR_MAIN_H, VAR_MH,
00088 VAR_TEXT_W, VAR_TW,
00089 VAR_TEXT_H, VAR_TH,
00090 VAR_X,
00091 VAR_Y,
00092 VAR_N,
00093 VAR_T,
00094 VAR_VARS_NB
00095 };
00096
00097 typedef struct {
00098 const AVClass *class;
00099 uint8_t *fontfile;
00100 uint8_t *text;
00101 uint8_t *expanded_text;
00102 size_t expanded_text_size;
00103 int ft_load_flags;
00104 FT_Vector *positions;
00105 size_t nb_positions;
00106 char *textfile;
00107 int x, y;
00108 int w, h;
00109 int shadowx, shadowy;
00110 unsigned int fontsize;
00111 char *fontcolor_string;
00112 char *boxcolor_string;
00113 char *shadowcolor_string;
00114 uint8_t fontcolor[4];
00115 uint8_t boxcolor[4];
00116 uint8_t shadowcolor[4];
00117 uint8_t fontcolor_rgba[4];
00118 uint8_t boxcolor_rgba[4];
00119 uint8_t shadowcolor_rgba[4];
00120
00121 short int draw_box;
00122 int use_kerning;
00123 int tabsize;
00124
00125 FT_Library library;
00126 FT_Face face;
00127 struct AVTreeNode *glyphs;
00128 int hsub, vsub;
00129 int is_packed_rgb;
00130 int pixel_step[4];
00131 uint8_t rgba_map[4];
00132 uint8_t *box_line[4];
00133 char *x_expr, *y_expr;
00134 AVExpr *x_pexpr, *y_pexpr;
00135 double var_values[VAR_VARS_NB];
00136 char *d_expr;
00137 AVExpr *d_pexpr;
00138 int draw;
00139 AVLFG prng;
00140 } DrawTextContext;
00141
00142 #define OFFSET(x) offsetof(DrawTextContext, x)
00143
00144 static const AVOption drawtext_options[]= {
00145 {"fontfile", "set font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
00146 {"text", "set text", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
00147 {"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
00148 {"fontcolor","set foreground color", OFFSET(fontcolor_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
00149 {"boxcolor", "set box color", OFFSET(boxcolor_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
00150 {"shadowcolor", "set shadow color", OFFSET(shadowcolor_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
00151 {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
00152 {"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.dbl=16}, 1, 72 },
00153 {"x", "set x", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX },
00154 {"y", "set y", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX },
00155 {"shadowx", "set x", OFFSET(shadowx), AV_OPT_TYPE_INT, {.dbl=0}, INT_MIN, INT_MAX },
00156 {"shadowy", "set y", OFFSET(shadowy), AV_OPT_TYPE_INT, {.dbl=0}, INT_MIN, INT_MAX },
00157 {"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.dbl=4}, 0, INT_MAX },
00158 {"draw", "if false do not draw", OFFSET(d_expr), AV_OPT_TYPE_STRING, {.str="1"}, CHAR_MIN, CHAR_MAX },
00159
00160
00161 {"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" },
00162 {"default", "set default", 0, AV_OPT_TYPE_CONST, {FT_LOAD_DEFAULT}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00163 {"no_scale", "set no_scale", 0, AV_OPT_TYPE_CONST, {FT_LOAD_NO_SCALE}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00164 {"no_hinting", "set no_hinting", 0, AV_OPT_TYPE_CONST, {FT_LOAD_NO_HINTING}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00165 {"render", "set render", 0, AV_OPT_TYPE_CONST, {FT_LOAD_RENDER}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00166 {"no_bitmap", "set no_bitmap", 0, AV_OPT_TYPE_CONST, {FT_LOAD_NO_BITMAP}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00167 {"vertical_layout", "set vertical_layout", 0, AV_OPT_TYPE_CONST, {FT_LOAD_VERTICAL_LAYOUT}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00168 {"force_autohint", "set force_autohint", 0, AV_OPT_TYPE_CONST, {FT_LOAD_FORCE_AUTOHINT}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00169 {"crop_bitmap", "set crop_bitmap", 0, AV_OPT_TYPE_CONST, {FT_LOAD_CROP_BITMAP}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00170 {"pedantic", "set pedantic", 0, AV_OPT_TYPE_CONST, {FT_LOAD_PEDANTIC}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00171 {"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" },
00172 {"no_recurse", "set no_recurse", 0, AV_OPT_TYPE_CONST, {FT_LOAD_NO_RECURSE}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00173 {"ignore_transform", "set ignore_transform", 0, AV_OPT_TYPE_CONST, {FT_LOAD_IGNORE_TRANSFORM}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00174 {"monochrome", "set monochrome", 0, AV_OPT_TYPE_CONST, {FT_LOAD_MONOCHROME}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00175 {"linear_design", "set linear_design", 0, AV_OPT_TYPE_CONST, {FT_LOAD_LINEAR_DESIGN}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00176 {"no_autohint", "set no_autohint", 0, AV_OPT_TYPE_CONST, {FT_LOAD_NO_AUTOHINT}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00177 {NULL},
00178 };
00179
00180 static const char *drawtext_get_name(void *ctx)
00181 {
00182 return "drawtext";
00183 }
00184
00185 static const AVClass drawtext_class = {
00186 "DrawTextContext",
00187 drawtext_get_name,
00188 drawtext_options
00189 };
00190
00191 #undef __FTERRORS_H__
00192 #define FT_ERROR_START_LIST {
00193 #define FT_ERRORDEF(e, v, s) { (e), (s) },
00194 #define FT_ERROR_END_LIST { 0, NULL } };
00195
00196 struct ft_error
00197 {
00198 int err;
00199 const char *err_msg;
00200 } static ft_errors[] =
00201 #include FT_ERRORS_H
00202
00203 #define FT_ERRMSG(e) ft_errors[e].err_msg
00204
00205 typedef struct {
00206 FT_Glyph *glyph;
00207 uint32_t code;
00208 FT_Bitmap bitmap;
00209 FT_BBox bbox;
00210 int advance;
00211 int bitmap_left;
00212 int bitmap_top;
00213 } Glyph;
00214
00215 static int glyph_cmp(void *key, const void *b)
00216 {
00217 const Glyph *a = key, *bb = b;
00218 int64_t diff = (int64_t)a->code - (int64_t)bb->code;
00219 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
00220 }
00221
00225 static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
00226 {
00227 DrawTextContext *dtext = ctx->priv;
00228 Glyph *glyph;
00229 struct AVTreeNode *node = NULL;
00230 int ret;
00231
00232
00233 if (FT_Load_Char(dtext->face, code, dtext->ft_load_flags))
00234 return AVERROR(EINVAL);
00235
00236
00237 if (!(glyph = av_mallocz(sizeof(*glyph))) ||
00238 !(glyph->glyph = av_mallocz(sizeof(*glyph->glyph)))) {
00239 ret = AVERROR(ENOMEM);
00240 goto error;
00241 }
00242 glyph->code = code;
00243
00244 if (FT_Get_Glyph(dtext->face->glyph, glyph->glyph)) {
00245 ret = AVERROR(EINVAL);
00246 goto error;
00247 }
00248
00249 glyph->bitmap = dtext->face->glyph->bitmap;
00250 glyph->bitmap_left = dtext->face->glyph->bitmap_left;
00251 glyph->bitmap_top = dtext->face->glyph->bitmap_top;
00252 glyph->advance = dtext->face->glyph->advance.x >> 6;
00253
00254
00255 FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
00256
00257
00258 if (!(node = av_mallocz(av_tree_node_size))) {
00259 ret = AVERROR(ENOMEM);
00260 goto error;
00261 }
00262 av_tree_insert(&dtext->glyphs, glyph, glyph_cmp, &node);
00263
00264 if (glyph_ptr)
00265 *glyph_ptr = glyph;
00266 return 0;
00267
00268 error:
00269 if (glyph)
00270 av_freep(&glyph->glyph);
00271 av_freep(&glyph);
00272 av_freep(&node);
00273 return ret;
00274 }
00275
00276 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
00277 {
00278 int err;
00279 DrawTextContext *dtext = ctx->priv;
00280 Glyph *glyph;
00281
00282 dtext->class = &drawtext_class;
00283 av_opt_set_defaults(dtext);
00284 dtext->fontcolor_string = av_strdup("black");
00285 dtext->boxcolor_string = av_strdup("white");
00286 dtext->shadowcolor_string = av_strdup("black");
00287
00288 if ((err = (av_set_options_string(dtext, args, "=", ":"))) < 0) {
00289 av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
00290 return err;
00291 }
00292
00293 if (!dtext->fontfile) {
00294 av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
00295 return AVERROR(EINVAL);
00296 }
00297
00298 if (dtext->textfile) {
00299 uint8_t *textbuf;
00300 size_t textbuf_size;
00301
00302 if (dtext->text) {
00303 av_log(ctx, AV_LOG_ERROR,
00304 "Both text and text file provided. Please provide only one\n");
00305 return AVERROR(EINVAL);
00306 }
00307 if ((err = av_file_map(dtext->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
00308 av_log(ctx, AV_LOG_ERROR,
00309 "The text file '%s' could not be read or is empty\n",
00310 dtext->textfile);
00311 return err;
00312 }
00313
00314 if (!(dtext->text = av_malloc(textbuf_size+1)))
00315 return AVERROR(ENOMEM);
00316 memcpy(dtext->text, textbuf, textbuf_size);
00317 dtext->text[textbuf_size] = 0;
00318 av_file_unmap(textbuf, textbuf_size);
00319 }
00320
00321 if (!dtext->text) {
00322 av_log(ctx, AV_LOG_ERROR,
00323 "Either text or a valid file must be provided\n");
00324 return AVERROR(EINVAL);
00325 }
00326
00327 if ((err = av_parse_color(dtext->fontcolor_rgba, dtext->fontcolor_string, -1, ctx))) {
00328 av_log(ctx, AV_LOG_ERROR,
00329 "Invalid font color '%s'\n", dtext->fontcolor_string);
00330 return err;
00331 }
00332
00333 if ((err = av_parse_color(dtext->boxcolor_rgba, dtext->boxcolor_string, -1, ctx))) {
00334 av_log(ctx, AV_LOG_ERROR,
00335 "Invalid box color '%s'\n", dtext->boxcolor_string);
00336 return err;
00337 }
00338
00339 if ((err = av_parse_color(dtext->shadowcolor_rgba, dtext->shadowcolor_string, -1, ctx))) {
00340 av_log(ctx, AV_LOG_ERROR,
00341 "Invalid shadow color '%s'\n", dtext->shadowcolor_string);
00342 return err;
00343 }
00344
00345 if ((err = FT_Init_FreeType(&(dtext->library)))) {
00346 av_log(ctx, AV_LOG_ERROR,
00347 "Could not load FreeType: %s\n", FT_ERRMSG(err));
00348 return AVERROR(EINVAL);
00349 }
00350
00351
00352 if ((err = FT_New_Face(dtext->library, dtext->fontfile, 0, &dtext->face))) {
00353 av_log(ctx, AV_LOG_ERROR, "Could not load fontface from file '%s': %s\n",
00354 dtext->fontfile, FT_ERRMSG(err));
00355 return AVERROR(EINVAL);
00356 }
00357 if ((err = FT_Set_Pixel_Sizes(dtext->face, 0, dtext->fontsize))) {
00358 av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n",
00359 dtext->fontsize, FT_ERRMSG(err));
00360 return AVERROR(EINVAL);
00361 }
00362
00363 dtext->use_kerning = FT_HAS_KERNING(dtext->face);
00364
00365
00366 load_glyph(ctx, NULL, 0);
00367
00368
00369 if ((err = load_glyph(ctx, &glyph, ' ') < 0)) {
00370 av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n");
00371 return err;
00372 }
00373 dtext->tabsize *= glyph->advance;
00374
00375 #if !HAVE_LOCALTIME_R
00376 av_log(ctx, AV_LOG_WARNING, "strftime() expansion unavailable!\n");
00377 #endif
00378
00379 return 0;
00380 }
00381
00382 static int query_formats(AVFilterContext *ctx)
00383 {
00384 static const enum PixelFormat pix_fmts[] = {
00385 PIX_FMT_ARGB, PIX_FMT_RGBA,
00386 PIX_FMT_ABGR, PIX_FMT_BGRA,
00387 PIX_FMT_RGB24, PIX_FMT_BGR24,
00388 PIX_FMT_YUV420P, PIX_FMT_YUV444P,
00389 PIX_FMT_YUV422P, PIX_FMT_YUV411P,
00390 PIX_FMT_YUV410P, PIX_FMT_YUV440P,
00391 PIX_FMT_NONE
00392 };
00393
00394 avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
00395 return 0;
00396 }
00397
00398 static int glyph_enu_free(void *opaque, void *elem)
00399 {
00400 av_free(elem);
00401 return 0;
00402 }
00403
00404 static av_cold void uninit(AVFilterContext *ctx)
00405 {
00406 DrawTextContext *dtext = ctx->priv;
00407 int i;
00408
00409 av_freep(&dtext->fontfile);
00410 av_freep(&dtext->text);
00411 av_freep(&dtext->expanded_text);
00412 av_freep(&dtext->fontcolor_string);
00413 av_freep(&dtext->boxcolor_string);
00414 av_freep(&dtext->positions);
00415 av_freep(&dtext->shadowcolor_string);
00416 av_tree_enumerate(dtext->glyphs, NULL, NULL, glyph_enu_free);
00417 av_tree_destroy(dtext->glyphs);
00418 dtext->glyphs = 0;
00419 FT_Done_Face(dtext->face);
00420 FT_Done_FreeType(dtext->library);
00421
00422 for (i = 0; i < 4; i++) {
00423 av_freep(&dtext->box_line[i]);
00424 dtext->pixel_step[i] = 0;
00425 }
00426
00427 }
00428
00429 static inline int is_newline(uint32_t c)
00430 {
00431 return c == '\n' || c == '\r' || c == '\f' || c == '\v';
00432 }
00433
00434 static int dtext_prepare_text(AVFilterContext *ctx)
00435 {
00436 DrawTextContext *dtext = ctx->priv;
00437 uint32_t code = 0, prev_code = 0;
00438 int x = 0, y = 0, i = 0, ret;
00439 int text_height, baseline;
00440 char *text = dtext->text;
00441 uint8_t *p;
00442 int str_w = 0, len;
00443 int y_min = 32000, y_max = -32000;
00444 FT_Vector delta;
00445 Glyph *glyph = NULL, *prev_glyph = NULL;
00446 Glyph dummy = { 0 };
00447 int width = ctx->inputs[0]->w;
00448 int height = ctx->inputs[0]->h;
00449
00450 #if HAVE_LOCALTIME_R
00451 time_t now = time(0);
00452 struct tm ltime;
00453 uint8_t *buf = dtext->expanded_text;
00454 int buf_size = dtext->expanded_text_size;
00455
00456 if (!buf)
00457 buf_size = 2*strlen(dtext->text)+1;
00458
00459 localtime_r(&now, <ime);
00460
00461 while ((buf = av_realloc(buf, buf_size))) {
00462 *buf = 1;
00463 if (strftime(buf, buf_size, dtext->text, <ime) != 0 || *buf == 0)
00464 break;
00465 buf_size *= 2;
00466 }
00467
00468 if (!buf)
00469 return AVERROR(ENOMEM);
00470 text = dtext->expanded_text = buf;
00471 dtext->expanded_text_size = buf_size;
00472 #endif
00473
00474 if ((len = strlen(text)) > dtext->nb_positions) {
00475 FT_Vector *p = av_realloc(dtext->positions,
00476 len * sizeof(*dtext->positions));
00477 if (!p) {
00478 av_freep(dtext->positions);
00479 dtext->nb_positions = 0;
00480 return AVERROR(ENOMEM);
00481 } else {
00482 dtext->positions = p;
00483 dtext->nb_positions = len;
00484 }
00485 }
00486
00487
00488 for (i = 0, p = text; *p; i++) {
00489 GET_UTF8(code, *p++, continue;);
00490
00491
00492 dummy.code = code;
00493 glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
00494 if (!glyph)
00495 ret = load_glyph(ctx, &glyph, code);
00496 if (ret) return ret;
00497
00498 y_min = FFMIN(glyph->bbox.yMin, y_min);
00499 y_max = FFMAX(glyph->bbox.yMax, y_max);
00500 }
00501 text_height = y_max - y_min;
00502 baseline = y_max;
00503
00504
00505 glyph = NULL;
00506 for (i = 0, p = text; *p; i++) {
00507 GET_UTF8(code, *p++, continue;);
00508
00509
00510 if (prev_code == '\r' && code == '\n')
00511 continue;
00512
00513 prev_code = code;
00514 if (is_newline(code)) {
00515 str_w = FFMAX(str_w, x - dtext->x);
00516 y += text_height;
00517 x = 0;
00518 continue;
00519 }
00520
00521
00522 prev_glyph = glyph;
00523 dummy.code = code;
00524 glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
00525
00526
00527 if (dtext->use_kerning && prev_glyph && glyph->code) {
00528 FT_Get_Kerning(dtext->face, prev_glyph->code, glyph->code,
00529 ft_kerning_default, &delta);
00530 x += delta.x >> 6;
00531 }
00532
00533 if (x + glyph->bbox.xMax >= width) {
00534 str_w = FFMAX(str_w, x);
00535 y += text_height;
00536 x = 0;
00537 }
00538
00539
00540 dtext->positions[i].x = x + glyph->bitmap_left;
00541 dtext->positions[i].y = y - glyph->bitmap_top + baseline;
00542 if (code == '\t') x = (x / dtext->tabsize + 1)*dtext->tabsize;
00543 else x += glyph->advance;
00544 }
00545
00546 str_w = FFMIN(width - 1, FFMAX(str_w, x));
00547 y = FFMIN(y + text_height, height - 1);
00548
00549 dtext->w = str_w;
00550 dtext->h = y;
00551
00552 return 0;
00553 }
00554
00555
00556 static int config_input(AVFilterLink *inlink)
00557 {
00558 AVFilterContext *ctx = inlink->dst;
00559 DrawTextContext *dtext = ctx->priv;
00560 const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
00561 int ret;
00562
00563 dtext->hsub = pix_desc->log2_chroma_w;
00564 dtext->vsub = pix_desc->log2_chroma_h;
00565
00566 dtext->var_values[VAR_E ] = M_E;
00567 dtext->var_values[VAR_PHI] = M_PHI;
00568 dtext->var_values[VAR_PI ] = M_PI;
00569
00570 dtext->var_values[VAR_MAIN_W] =
00571 dtext->var_values[VAR_MW] = ctx->inputs[0]->w;
00572 dtext->var_values[VAR_MAIN_H] =
00573 dtext->var_values[VAR_MH] = ctx->inputs[0]->h;
00574
00575 dtext->var_values[VAR_X] = 0;
00576 dtext->var_values[VAR_Y] = 0;
00577 dtext->var_values[VAR_N] = 0;
00578 dtext->var_values[VAR_T] = NAN;
00579
00580 av_lfg_init(&dtext->prng, av_get_random_seed());
00581
00582 if ((ret = av_expr_parse(&dtext->x_pexpr, dtext->x_expr, var_names,
00583 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
00584 (ret = av_expr_parse(&dtext->y_pexpr, dtext->y_expr, var_names,
00585 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
00586 (ret = av_expr_parse(&dtext->d_pexpr, dtext->d_expr, var_names,
00587 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0)
00588 return AVERROR(EINVAL);
00589
00590 if ((ret =
00591 ff_fill_line_with_color(dtext->box_line, dtext->pixel_step,
00592 inlink->w, dtext->boxcolor,
00593 inlink->format, dtext->boxcolor_rgba,
00594 &dtext->is_packed_rgb, dtext->rgba_map)) < 0)
00595 return ret;
00596
00597 if (!dtext->is_packed_rgb) {
00598 uint8_t *rgba = dtext->fontcolor_rgba;
00599 dtext->fontcolor[0] = RGB_TO_Y_CCIR(rgba[0], rgba[1], rgba[2]);
00600 dtext->fontcolor[1] = RGB_TO_U_CCIR(rgba[0], rgba[1], rgba[2], 0);
00601 dtext->fontcolor[2] = RGB_TO_V_CCIR(rgba[0], rgba[1], rgba[2], 0);
00602 dtext->fontcolor[3] = rgba[3];
00603 rgba = dtext->shadowcolor_rgba;
00604 dtext->shadowcolor[0] = RGB_TO_Y_CCIR(rgba[0], rgba[1], rgba[2]);
00605 dtext->shadowcolor[1] = RGB_TO_U_CCIR(rgba[0], rgba[1], rgba[2], 0);
00606 dtext->shadowcolor[2] = RGB_TO_V_CCIR(rgba[0], rgba[1], rgba[2], 0);
00607 dtext->shadowcolor[3] = rgba[3];
00608 }
00609
00610 dtext->draw = 1;
00611
00612 return dtext_prepare_text(ctx);
00613 }
00614
00615 #define GET_BITMAP_VAL(r, c) \
00616 bitmap->pixel_mode == FT_PIXEL_MODE_MONO ? \
00617 (bitmap->buffer[(r) * bitmap->pitch + ((c)>>3)] & (0x80 >> ((c)&7))) * 255 : \
00618 bitmap->buffer[(r) * bitmap->pitch + (c)]
00619
00620 #define SET_PIXEL_YUV(picref, yuva_color, val, x, y, hsub, vsub) { \
00621 luma_pos = ((x) ) + ((y) ) * picref->linesize[0]; \
00622 alpha = yuva_color[3] * (val) * 129; \
00623 picref->data[0][luma_pos] = (alpha * yuva_color[0] + (255*255*129 - alpha) * picref->data[0][luma_pos] ) >> 23; \
00624 if (((x) & ((1<<(hsub)) - 1)) == 0 && ((y) & ((1<<(vsub)) - 1)) == 0) {\
00625 chroma_pos1 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[1]; \
00626 chroma_pos2 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[2]; \
00627 picref->data[1][chroma_pos1] = (alpha * yuva_color[1] + (255*255*129 - alpha) * picref->data[1][chroma_pos1]) >> 23; \
00628 picref->data[2][chroma_pos2] = (alpha * yuva_color[2] + (255*255*129 - alpha) * picref->data[2][chroma_pos2]) >> 23; \
00629 }\
00630 }
00631
00632 static inline int draw_glyph_yuv(AVFilterBufferRef *picref, FT_Bitmap *bitmap, unsigned int x,
00633 unsigned int y, unsigned int width, unsigned int height,
00634 const uint8_t yuva_color[4], int hsub, int vsub)
00635 {
00636 int r, c, alpha;
00637 unsigned int luma_pos, chroma_pos1, chroma_pos2;
00638 uint8_t src_val;
00639
00640 for (r = 0; r < bitmap->rows && r+y < height; r++) {
00641 for (c = 0; c < bitmap->width && c+x < width; c++) {
00642
00643 src_val = GET_BITMAP_VAL(r, c);
00644 if (!src_val)
00645 continue;
00646
00647 SET_PIXEL_YUV(picref, yuva_color, src_val, c+x, y+r, hsub, vsub);
00648 }
00649 }
00650
00651 return 0;
00652 }
00653
00654 #define SET_PIXEL_RGB(picref, rgba_color, val, x, y, pixel_step, r_off, g_off, b_off, a_off) { \
00655 p = picref->data[0] + (x) * pixel_step + ((y) * picref->linesize[0]); \
00656 alpha = rgba_color[3] * (val) * 129; \
00657 *(p+r_off) = (alpha * rgba_color[0] + (255*255*129 - alpha) * *(p+r_off)) >> 23; \
00658 *(p+g_off) = (alpha * rgba_color[1] + (255*255*129 - alpha) * *(p+g_off)) >> 23; \
00659 *(p+b_off) = (alpha * rgba_color[2] + (255*255*129 - alpha) * *(p+b_off)) >> 23; \
00660 }
00661
00662 static inline int draw_glyph_rgb(AVFilterBufferRef *picref, FT_Bitmap *bitmap,
00663 unsigned int x, unsigned int y,
00664 unsigned int width, unsigned int height, int pixel_step,
00665 const uint8_t rgba_color[4], const uint8_t rgba_map[4])
00666 {
00667 int r, c, alpha;
00668 uint8_t *p;
00669 uint8_t src_val;
00670
00671 for (r = 0; r < bitmap->rows && r+y < height; r++) {
00672 for (c = 0; c < bitmap->width && c+x < width; c++) {
00673
00674 src_val = GET_BITMAP_VAL(r, c);
00675 if (!src_val)
00676 continue;
00677
00678 SET_PIXEL_RGB(picref, rgba_color, src_val, c+x, y+r, pixel_step,
00679 rgba_map[0], rgba_map[1], rgba_map[2], rgba_map[3]);
00680 }
00681 }
00682
00683 return 0;
00684 }
00685
00686 static inline void drawbox(AVFilterBufferRef *picref, unsigned int x, unsigned int y,
00687 unsigned int width, unsigned int height,
00688 uint8_t *line[4], int pixel_step[4], uint8_t color[4],
00689 int hsub, int vsub, int is_rgba_packed, uint8_t rgba_map[4])
00690 {
00691 int i, j, alpha;
00692
00693 if (color[3] != 0xFF) {
00694 if (is_rgba_packed) {
00695 uint8_t *p;
00696 for (j = 0; j < height; j++)
00697 for (i = 0; i < width; i++)
00698 SET_PIXEL_RGB(picref, color, 255, i+x, y+j, pixel_step[0],
00699 rgba_map[0], rgba_map[1], rgba_map[2], rgba_map[3]);
00700 } else {
00701 unsigned int luma_pos, chroma_pos1, chroma_pos2;
00702 for (j = 0; j < height; j++)
00703 for (i = 0; i < width; i++)
00704 SET_PIXEL_YUV(picref, color, 255, i+x, y+j, hsub, vsub);
00705 }
00706 } else {
00707 ff_draw_rectangle(picref->data, picref->linesize,
00708 line, pixel_step, hsub, vsub,
00709 x, y, width, height);
00710 }
00711 }
00712
00713 static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
00714 int width, int height, const uint8_t rgbcolor[4], const uint8_t yuvcolor[4], int x, int y)
00715 {
00716 char *text = HAVE_LOCALTIME_R ? dtext->expanded_text : dtext->text;
00717 uint32_t code = 0;
00718 int i;
00719 uint8_t *p;
00720 Glyph *glyph = NULL;
00721
00722 for (i = 0, p = text; *p; i++) {
00723 Glyph dummy = { 0 };
00724 GET_UTF8(code, *p++, continue;);
00725
00726
00727 if (code == '\n' || code == '\r' || code == '\t')
00728 continue;
00729
00730 dummy.code = code;
00731 glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
00732
00733 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
00734 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
00735 return AVERROR(EINVAL);
00736
00737 if (dtext->is_packed_rgb) {
00738 draw_glyph_rgb(picref, &glyph->bitmap,
00739 dtext->positions[i].x+x, dtext->positions[i].y+y, width, height,
00740 dtext->pixel_step[0], rgbcolor, dtext->rgba_map);
00741 } else {
00742 draw_glyph_yuv(picref, &glyph->bitmap,
00743 dtext->positions[i].x+x, dtext->positions[i].y+y, width, height,
00744 yuvcolor, dtext->hsub, dtext->vsub);
00745 }
00746 }
00747
00748 return 0;
00749 }
00750
00751 static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
00752 int width, int height)
00753 {
00754 DrawTextContext *dtext = ctx->priv;
00755 int ret;
00756
00757
00758 if (dtext->draw_box)
00759 drawbox(picref, dtext->x, dtext->y, dtext->w, dtext->h,
00760 dtext->box_line, dtext->pixel_step, dtext->boxcolor,
00761 dtext->hsub, dtext->vsub, dtext->is_packed_rgb,
00762 dtext->rgba_map);
00763
00764 if (dtext->shadowx || dtext->shadowy) {
00765 if ((ret = draw_glyphs(dtext, picref, width, height,
00766 dtext->shadowcolor_rgba,
00767 dtext->shadowcolor,
00768 dtext->x + dtext->shadowx,
00769 dtext->y + dtext->shadowy)) < 0)
00770 return ret;
00771 }
00772
00773 if ((ret = draw_glyphs(dtext, picref, width, height,
00774 dtext->fontcolor_rgba,
00775 dtext->fontcolor,
00776 dtext->x,
00777 dtext->y)) < 0)
00778 return ret;
00779
00780 return 0;
00781 }
00782
00783 static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
00784
00785 static inline int normalize_double(int *n, double d)
00786 {
00787 int ret = 0;
00788
00789 if (isnan(d)) {
00790 ret = AVERROR(EINVAL);
00791 } else if (d > INT_MAX || d < INT_MIN) {
00792 *n = d > INT_MAX ? INT_MAX : INT_MIN;
00793 ret = AVERROR(EINVAL);
00794 } else
00795 *n = round(d);
00796
00797 return ret;
00798 }
00799
00800 static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref)
00801 {
00802 AVFilterContext *ctx = inlink->dst;
00803 DrawTextContext *dtext = ctx->priv;
00804 int fail = 0;
00805
00806 if (dtext_prepare_text(ctx) < 0) {
00807 av_log(ctx, AV_LOG_ERROR, "Can't draw text\n");
00808 fail = 1;
00809 }
00810
00811 dtext->var_values[VAR_T] = inpicref->pts == AV_NOPTS_VALUE ?
00812 NAN : inpicref->pts * av_q2d(inlink->time_base);
00813 dtext->var_values[VAR_X] =
00814 av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng);
00815 dtext->var_values[VAR_Y] =
00816 av_expr_eval(dtext->y_pexpr, dtext->var_values, &dtext->prng);
00817 dtext->var_values[VAR_X] =
00818 av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng);
00819
00820 dtext->draw = fail ? 0 :
00821 av_expr_eval(dtext->d_pexpr, dtext->var_values, &dtext->prng);
00822
00823 normalize_double(&dtext->x, dtext->var_values[VAR_X]);
00824 normalize_double(&dtext->y, dtext->var_values[VAR_Y]);
00825
00826 if (dtext->x < 0) dtext->x = 0;
00827 if (dtext->y < 0) dtext->y = 0;
00828 if ((unsigned)dtext->x + (unsigned)dtext->w > inlink->w)
00829 dtext->x = inlink->w - dtext->w;
00830 if ((unsigned)dtext->y + (unsigned)dtext->h > inlink->h)
00831 dtext->y = inlink->h - dtext->h;
00832
00833 dtext->x &= ~((1 << dtext->hsub) - 1);
00834 dtext->y &= ~((1 << dtext->vsub) - 1);
00835
00836 av_dlog(ctx, "n:%d t:%f x:%d y:%d x+w:%d y+h:%d\n",
00837 (int)dtext->var_values[VAR_N], dtext->var_values[VAR_T],
00838 dtext->x, dtext->y, dtext->x+dtext->w, dtext->y+dtext->h);
00839
00840 avfilter_start_frame(inlink->dst->outputs[0], inpicref);
00841 }
00842
00843 static void end_frame(AVFilterLink *inlink)
00844 {
00845 AVFilterLink *outlink = inlink->dst->outputs[0];
00846 AVFilterBufferRef *picref = inlink->cur_buf;
00847 DrawTextContext *dtext = inlink->dst->priv;
00848
00849 if (dtext->draw)
00850 draw_text(inlink->dst, picref, picref->video->w, picref->video->h);
00851
00852 dtext->var_values[VAR_N] += 1.0;
00853
00854 avfilter_draw_slice(outlink, 0, picref->video->h, 1);
00855 avfilter_end_frame(outlink);
00856 }
00857
00858 AVFilter avfilter_vf_drawtext = {
00859 .name = "drawtext",
00860 .description = NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."),
00861 .priv_size = sizeof(DrawTextContext),
00862 .init = init,
00863 .uninit = uninit,
00864 .query_formats = query_formats,
00865
00866 .inputs = (AVFilterPad[]) {{ .name = "default",
00867 .type = AVMEDIA_TYPE_VIDEO,
00868 .get_video_buffer = avfilter_null_get_video_buffer,
00869 .start_frame = start_frame,
00870 .draw_slice = null_draw_slice,
00871 .end_frame = end_frame,
00872 .config_props = config_input,
00873 .min_perms = AV_PERM_WRITE |
00874 AV_PERM_READ,
00875 .rej_perms = AV_PERM_PRESERVE },
00876 { .name = NULL}},
00877 .outputs = (AVFilterPad[]) {{ .name = "default",
00878 .type = AVMEDIA_TYPE_VIDEO, },
00879 { .name = NULL}},
00880 };