From 41c008975860f7b7a9d607d1bf3784985d8974dd Mon Sep 17 00:00:00 2001 From: yethie Date: Fri, 3 Feb 2023 14:36:39 +0100 Subject: [PATCH 5/7] vertical and horizontal text alignment --- doc/filters.texi | 5 +++++ libavfilter/vf_drawtext.c | 47 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/doc/filters.texi b/doc/filters.texi index cb1a3ff983..47e04a3ac3 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -12080,6 +12080,11 @@ The default value of @var{boxcolor} is "white". @item line_spacing Set the line spacing in pixels. The default value of @var{line_spacing} is 0. +@item text_align +Set the vertical and horizontal alignment of the text with respect to the box boundaries. +The value must contain exactly two letters, one for the vertical alignment (T=top, +M=middle, B=bottom) and one for the horizontal alignment (L=left, C=center, R=right). + @item borderw Set the width of the border to be drawn around the text using @var{bordercolor}. The default value of @var{borderw} is 0. diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c index 25348ba7de..0b01a78509 100644 --- a/libavfilter/vf_drawtext.c +++ b/libavfilter/vf_drawtext.c @@ -28,6 +28,8 @@ * - Glyphs position is now accurate to 1/4 pixel in both directions * - The size of the background box can now be forced with the boxw * and boxh parameters + * - Text can be aligned horizontally (top, middle, bottom) and vertically + * (left, center, right) relative to the background box * - The default line height is now the one defined in the font * - The boxborderw parameter can now contain a different value for each border * (e.g. boxborderw=top|right|bottom|left) @@ -315,6 +317,7 @@ typedef struct DrawTextContext { int boxw; ///< the value of the boxw parameter int boxh; ///< the value of the boxh parameter + uint8_t *text_align; ///< the horizontal and vertical text alignment TextLine *lines; ///< computed information about text lines int line_count; ///< the number of text lines @@ -339,6 +342,7 @@ static const AVOption drawtext_options[]= { {"boxborderw", "set box borders width", OFFSET(boxborderw), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, {"line_spacing", "set line spacing in pixels", OFFSET(line_spacing), AV_OPT_TYPE_INT, {.i64=-1}, INT_MIN, INT_MAX, FLAGS}, {"fontsize", "set font size", OFFSET(fontsize_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, + {"text_align", "set text alignment", OFFSET(text_align), AV_OPT_TYPE_STRING, {.str="TL"}, 0, 0, FLAGS}, {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, {"boxw", "set box width", OFFSET(boxw), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, @@ -770,6 +774,19 @@ static int string_to_array(const char* source, int* result, int result_size) return counter; } +static int validate_text_align(char* text_align) +{ + int err = 0; + if (strlen(text_align) != 2 + || strchr("LCRTMB", text_align[0]) == NULL || strchr("LCRTMB", text_align[1]) == NULL + || (strchr("TMB", text_align[0]) != NULL && strchr("LCR", text_align[1]) == NULL) + || (strchr("LCR", text_align[0]) != NULL && strchr("TMB", text_align[1]) == NULL)) { + err = AVERROR(EINVAL); + } + + return err; +} + static av_cold int init(AVFilterContext *ctx) { int err; @@ -835,6 +852,14 @@ static av_cold int init(AVFilterContext *ctx) return AVERROR(EINVAL); } + if ((err = validate_text_align(s->text_align))) { + av_log(ctx, AV_LOG_ERROR, + "The value provided for parameter 'text_align' is not valid,\n"); + av_log(ctx, AV_LOG_ERROR, + "please specify a two characters string containing only one letter for horizontal alignment ('LCR') and one for vertical alignment ('TMB')\n"); + return err; + } + #if CONFIG_LIBFRIBIDI if (s->text_shaping) if ((err = shape_text(ctx)) < 0) @@ -1507,13 +1532,27 @@ static int draw_glyphs(DrawTextContext *s, AVFrame *frame, Glyph dummy = { 0 }, *glyph; FT_Bitmap bitmap; FT_BitmapGlyph b_glyph; + uint8_t j_center = 0, j_right = 0, j_middle = 0, j_bottom = 0; + int line_w, offset_y = 0; int clip_x = 0, clip_y = 0; + j_center = strstr(s->text_align, "C") > 0; + j_right = strstr(s->text_align, "R") > 0; + j_middle = strstr(s->text_align, "M") > 0; + j_bottom = strstr(s->text_align, "B") > 0; + + if (j_middle) { + offset_y = (s->box_height - metrics->height) / 2; + } else if (j_bottom) { + offset_y = s->box_height - metrics->height; + } + clip_x = FFMIN(metrics->rect_x + s->box_width + s->bb_right, frame->width); clip_y = FFMIN(metrics->rect_y + s->box_height + s->bb_bottom, frame->height); for (l = 0; l < s->line_count; ++l) { TextLine *line = &s->lines[l]; + line_w = POS_CEIL(line->width64, 64); for (g = 0; g < line->hb_data.glyph_count; ++g) { info = &line->glyphs[g]; dummy.fontsize = s->fontsize; @@ -1527,10 +1566,16 @@ static int draw_glyphs(DrawTextContext *s, AVFrame *frame, b_glyph = borderw ? glyph->border_bglyph[idx] : glyph->bglyph[idx]; bitmap = b_glyph->bitmap; x1 = x + info->x + b_glyph->left; - y1 = y + info->y - b_glyph->top; + y1 = y + info->y - b_glyph->top + offset_y; w1 = bitmap.width; h1 = bitmap.rows; + if (j_center) { + x1 += (s->box_width - line_w) / 2; + } else if (j_right) { + x1 += s->box_width - line_w; + } + // Offset of the glyph's bitmap in the visible region dx = dy = 0; if (x1 < metrics->rect_x - s->bb_left) { -- 2.30.2