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