Text can now be aligned vertically (top, middle, bottom) and horizontally (left, center, right) relative to the background box.
Unified · Split
| @@ -12384,6 +12384,12 @@ The default value of @var{boxcolor} is "white". · 12383 unmodified lines | |||
| 12384 | @item line_spacing | 12384 | @item line_spacing |
| 12385 | Set the line spacing in pixels. The default value of @var{line_spacing} is 0. | 12385 | Set the line spacing in pixels. The default value of @var{line_spacing} is 0. |
| 12386 | 12386 | ||
| 12387 | @item text_align | ||
| 12388 | Set the vertical and horizontal alignment of the text with respect to the box boundaries. | ||
| 12389 | The value must contain exactly two letters, one for the vertical alignment (T=top, | ||
| 12390 | M=middle, B=bottom) and one for the horizontal alignment (L=left, C=center, R=right). | ||
| 12391 | Please note that tab characters are only supported with the left horizontal alignment. | ||
| 12392 | |||
| 12387 | @item borderw | 12393 | @item borderw |
| 12388 | Set the width of the border to be drawn around the text using @var{bordercolor}. | 12394 | Set the width of the border to be drawn around the text using @var{bordercolor}. |
| 12389 | The default value of @var{borderw} is 0. | 12395 | The default value of @var{borderw} is 0. |
| @@ -309,12 +309,14 @@ typedef struct DrawTextContext { · 308 unmodified lines | |||
| 309 | 309 | ||
| 310 | int boxw; ///< the value of the boxw parameter | 310 | int boxw; ///< the value of the boxw parameter |
| 311 | int boxh; ///< the value of the boxh parameter | 311 | int boxh; ///< the value of the boxh parameter |
| 312 | uint8_t *text_align; ///< the horizontal and vertical text alignment | ||
| 312 | 313 | ||
| 313 | TextLine *lines; ///< computed information about text lines | 314 | TextLine *lines; ///< computed information about text lines |
| 314 | int line_count; ///< the number of text lines | 315 | int line_count; ///< the number of text lines |
| 315 | uint32_t *tab_clusters; ///< the position of tab characters in the text | 316 | uint32_t *tab_clusters; ///< the position of tab characters in the text |
| 316 | int tab_count; ///< the number of tab characters | 317 | int tab_count; ///< the number of tab characters |
| 317 | int blank_advance64; ///< the size of the space character | 318 | int blank_advance64; ///< the size of the space character |
| 319 | int tab_warning_printed; ///< ensure the tab warning to be printed only once | ||
| 318 | } DrawTextContext; | 320 | } DrawTextContext; |
| 319 | 321 | ||
| 320 | #define OFFSET(x) offsetof(DrawTextContext, x) | 322 | #define OFFSET(x) offsetof(DrawTextContext, x) |
| @@ -333,6 +335,7 @@ static const AVOption drawtext_options[]= { · 12 unmodified lines | |||
| 333 | {"boxborderw", "set box borders width", OFFSET(boxborderw), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, | 335 | {"boxborderw", "set box borders width", OFFSET(boxborderw), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, |
| 334 | {"line_spacing", "set line spacing in pixels", OFFSET(line_spacing), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGS}, | 336 | {"line_spacing", "set line spacing in pixels", OFFSET(line_spacing), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGS}, |
| 335 | {"fontsize", "set font size", OFFSET(fontsize_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, | 337 | {"fontsize", "set font size", OFFSET(fontsize_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, |
| 338 | {"text_align", "set text alignment", OFFSET(text_align), AV_OPT_TYPE_STRING, {.str="TL"}, 0, 0, FLAGS}, | ||
| 336 | {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, | 339 | {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, |
| 337 | {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, | 340 | {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, |
| 338 | {"boxw", "set box width", OFFSET(boxw), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, | 341 | {"boxw", "set box width", OFFSET(boxw), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, |
| @@ -851,6 +854,19 @@ static int string_to_array(const char *source, int *result, int result_size) · 512 unmodified lines | |||
| 851 | return counter; | 854 | return counter; |
| 852 | } | 855 | } |
| 853 | 856 | ||
| 857 | static int validate_text_align(char* text_align) | ||
| 858 | { | ||
| 859 | int err = 0; | ||
| 860 | if (strlen(text_align) != 2 | ||
| 861 | || strchr("LCRTMB", text_align[0]) == NULL || strchr("LCRTMB", text_align[1]) == NULL | ||
| 862 | || (strchr("TMB", text_align[0]) != NULL && strchr("LCR", text_align[1]) == NULL) | ||
| 863 | || (strchr("LCR", text_align[0]) != NULL && strchr("TMB", text_align[1]) == NULL)) { | ||
| 864 | err = AVERROR(EINVAL); | ||
| 865 | } | ||
| 866 | |||
| 867 | return err; | ||
| 868 | } | ||
| 869 | |||
| 854 | static av_cold int init(AVFilterContext *ctx) | 870 | static av_cold int init(AVFilterContext *ctx) |
| 855 | { | 871 | { |
| 856 | int err; | 872 | int err; |
| @@ -916,6 +932,14 @@ static av_cold int init(AVFilterContext *ctx) · 59 unmodified lines | |||
| 916 | return AVERROR(EINVAL); | 932 | return AVERROR(EINVAL); |
| 917 | } | 933 | } |
| 918 | 934 | ||
| 935 | if ((err = validate_text_align(s->text_align))) { | ||
| 936 | av_log(ctx, AV_LOG_ERROR, | ||
| 937 | "The value provided for parameter 'text_align' is not valid,\n"); | ||
| 938 | av_log(ctx, AV_LOG_ERROR, | ||
| 939 | "please specify a two characters string containing only one letter for horizontal alignment ('LCR') and one for vertical alignment ('TMB')\n"); | ||
| 940 | return err; | ||
| 941 | } | ||
| 942 | |||
| 919 | #if CONFIG_LIBFRIBIDI | 943 | #if CONFIG_LIBFRIBIDI |
| 920 | if (s->text_shaping) | 944 | if (s->text_shaping) |
| 921 | if ((err = shape_text(ctx)) < 0) | 945 | if ((err = shape_text(ctx)) < 0) |
| @@ -1486,13 +1510,32 @@ static int draw_glyphs(DrawTextContext *s, AVFrame *frame, · 564 unmodified lines | |||
| 1486 | Glyph dummy = { 0 }, *glyph; | 1510 | Glyph dummy = { 0 }, *glyph; |
| 1487 | FT_Bitmap bitmap; | 1511 | FT_Bitmap bitmap; |
| 1488 | FT_BitmapGlyph b_glyph; | 1512 | FT_BitmapGlyph b_glyph; |
| 1513 | uint8_t j_center = 0, j_right = 0, j_middle = 0, j_bottom = 0; | ||
| 1514 | int line_w, offset_y = 0; | ||
| 1489 | int clip_x = 0, clip_y = 0; | 1515 | int clip_x = 0, clip_y = 0; |
| 1490 | 1516 | ||
| 1517 | j_center = strstr(s->text_align, "C") > 0; | ||
| 1518 | j_right = strstr(s->text_align, "R") > 0; | ||
| 1519 | j_middle = strstr(s->text_align, "M") > 0; | ||
| 1520 | j_bottom = strstr(s->text_align, "B") > 0; | ||
| 1521 | |||
| 1522 | if (j_middle) { | ||
| 1523 | offset_y = (s->box_height - metrics->height) / 2; | ||
| 1524 | } else if (j_bottom) { | ||
| 1525 | offset_y = s->box_height - metrics->height; | ||
| 1526 | } | ||
| 1527 | |||
| 1528 | if ((j_right || j_center) && !s->tab_warning_printed && s->tab_count > 0) { | ||
| 1529 | s->tab_warning_printed = 1; | ||
| 1530 | av_log(s, AV_LOG_WARNING, "Tab characters are only supported with left horizontal alignment\n"); | ||
| 1531 | } | ||
| 1532 | |||
| 1491 | clip_x = FFMIN(metrics->rect_x + s->box_width + s->bb_right, frame->width); | 1533 | clip_x = FFMIN(metrics->rect_x + s->box_width + s->bb_right, frame->width); |
| 1492 | clip_y = FFMIN(metrics->rect_y + s->box_height + s->bb_bottom, frame->height); | 1534 | clip_y = FFMIN(metrics->rect_y + s->box_height + s->bb_bottom, frame->height); |
| 1493 | 1535 | ||
| 1494 | for (l = 0; l < s->line_count; ++l) { | 1536 | for (l = 0; l < s->line_count; ++l) { |
| 1495 | TextLine *line = &s->lines[l]; | 1537 | TextLine *line = &s->lines[l]; |
| 1538 | line_w = POS_CEIL(line->width64, 64); | ||
| 1496 | for (g = 0; g < line->hb_data.glyph_count; ++g) { | 1539 | for (g = 0; g < line->hb_data.glyph_count; ++g) { |
| 1497 | info = &line->glyphs[g]; | 1540 | info = &line->glyphs[g]; |
| 1498 | dummy.fontsize = s->fontsize; | 1541 | dummy.fontsize = s->fontsize; |
| @@ -1506,10 +1549,16 @@ static int draw_glyphs(DrawTextContext *s, AVFrame *frame, · 7 unmodified lines | |||
| 1506 | b_glyph = borderw ? glyph->border_bglyph[idx] : glyph->bglyph[idx]; | 1549 | b_glyph = borderw ? glyph->border_bglyph[idx] : glyph->bglyph[idx]; |
| 1507 | bitmap = b_glyph->bitmap; | 1550 | bitmap = b_glyph->bitmap; |
| 1508 | x1 = x + info->x + b_glyph->left; | 1551 | x1 = x + info->x + b_glyph->left; |
| 1509 | y1 = y + info->y - b_glyph->top; | 1552 | y1 = y + info->y - b_glyph->top + offset_y; |
| 1510 | w1 = bitmap.width; | 1553 | w1 = bitmap.width; |
| 1511 | h1 = bitmap.rows; | 1554 | h1 = bitmap.rows; |
| 1512 | 1555 | ||
| 1556 | if (j_center) { | ||
| 1557 | x1 += (s->box_width - line_w) / 2; | ||
| 1558 | } else if (j_right) { | ||
| 1559 | x1 += s->box_width - line_w; | ||
| 1560 | } | ||
| 1561 | |||
| 1513 | // Offset of the glyph's bitmap in the visible region | 1562 | // Offset of the glyph's bitmap in the visible region |
| 1514 | dx = dy = 0; | 1563 | dx = dy = 0; |
| 1515 | if (x1 < metrics->rect_x - s->bb_left) { | 1564 | if (x1 < metrics->rect_x - s->bb_left) { |