mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
lavfi/drawtext: add option for drawing border around text
Reviewed-by: Stefano Sabatini <stefasab@gmail.com> Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
parent
222fb8276d
commit
78a9f185eb
@ -3548,6 +3548,16 @@ option, check the "Color" section in the ffmpeg-utils manual.
|
||||
|
||||
The default value of @var{boxcolor} is "white".
|
||||
|
||||
@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.
|
||||
|
||||
@item bordercolor
|
||||
Set the color to be used for drawing border around text. For the syntax of this
|
||||
option, check the "Color" section in the ffmpeg-utils manual.
|
||||
|
||||
The default value of @var{bordercolor} is "black".
|
||||
|
||||
@item expansion
|
||||
Select how the @var{text} is expanded. Can be either @code{none},
|
||||
@code{strftime} (deprecated) or
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
#define LIBAVFILTER_VERSION_MAJOR 4
|
||||
#define LIBAVFILTER_VERSION_MINOR 1
|
||||
#define LIBAVFILTER_VERSION_MICRO 100
|
||||
#define LIBAVFILTER_VERSION_MICRO 101
|
||||
|
||||
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
|
||||
LIBAVFILTER_VERSION_MINOR, \
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_GLYPH_H
|
||||
#include FT_STROKER_H
|
||||
#if CONFIG_FONTCONFIG
|
||||
#include <fontconfig/fontconfig.h>
|
||||
#endif
|
||||
@ -134,6 +135,7 @@ typedef struct {
|
||||
int max_glyph_w; ///< max glyph width
|
||||
int max_glyph_h; ///< max glyph height
|
||||
int shadowx, shadowy;
|
||||
int borderw; ///< border width
|
||||
unsigned int fontsize; ///< font size to use
|
||||
|
||||
short int draw_box; ///< draw box around text - true or false
|
||||
@ -144,10 +146,12 @@ typedef struct {
|
||||
FFDrawContext dc;
|
||||
FFDrawColor fontcolor; ///< foreground color
|
||||
FFDrawColor shadowcolor; ///< shadow color
|
||||
FFDrawColor bordercolor; ///< border color
|
||||
FFDrawColor boxcolor; ///< background color
|
||||
|
||||
FT_Library library; ///< freetype font library handle
|
||||
FT_Face face; ///< freetype font face handle
|
||||
FT_Stroker stroker; ///< freetype stroker handle
|
||||
struct AVTreeNode *glyphs; ///< rendered glyphs, stored using the UTF-32 char code
|
||||
char *x_expr; ///< expression for x position
|
||||
char *y_expr; ///< expression for y position
|
||||
@ -178,6 +182,7 @@ static const AVOption drawtext_options[]= {
|
||||
{"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
|
||||
{"fontcolor", "set foreground color", OFFSET(fontcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
||||
{"boxcolor", "set box color", OFFSET(boxcolor.rgba), AV_OPT_TYPE_COLOR, {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
||||
{"bordercolor", "set border color", OFFSET(bordercolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
||||
{"shadowcolor", "set shadow color", OFFSET(shadowcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
||||
{"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 , FLAGS},
|
||||
{"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX , FLAGS},
|
||||
@ -185,6 +190,7 @@ static const AVOption drawtext_options[]= {
|
||||
{"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
||||
{"shadowx", "set x", OFFSET(shadowx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS},
|
||||
{"shadowy", "set y", OFFSET(shadowy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS},
|
||||
{"borderw", "set border width", OFFSET(borderw), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS},
|
||||
{"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.i64=4}, 0, INT_MAX , FLAGS},
|
||||
{"basetime", "set base time", OFFSET(basetime), AV_OPT_TYPE_INT64, {.i64=AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX , FLAGS},
|
||||
#if FF_API_DRAWTEXT_OLD_TIMELINE
|
||||
@ -245,6 +251,7 @@ typedef struct {
|
||||
FT_Glyph *glyph;
|
||||
uint32_t code;
|
||||
FT_Bitmap bitmap; ///< array holding bitmaps of font
|
||||
FT_Bitmap border_bitmap; ///< array holding bitmaps of font border
|
||||
FT_BBox bbox;
|
||||
int advance;
|
||||
int bitmap_left;
|
||||
@ -285,6 +292,16 @@ static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
|
||||
ret = AVERROR(EINVAL);
|
||||
goto error;
|
||||
}
|
||||
if (s->borderw) {
|
||||
FT_Glyph border_glyph = *glyph->glyph;
|
||||
if (FT_Glyph_StrokeBorder(&border_glyph, s->stroker, 0, 0) ||
|
||||
FT_Glyph_To_Bitmap(&border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
|
||||
ret = AVERROR_EXTERNAL;
|
||||
goto error;
|
||||
}
|
||||
bitmapglyph = (FT_BitmapGlyph) border_glyph;
|
||||
glyph->border_bitmap = bitmapglyph->bitmap;
|
||||
}
|
||||
if (FT_Glyph_To_Bitmap(glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
|
||||
ret = AVERROR_EXTERNAL;
|
||||
goto error;
|
||||
@ -490,6 +507,15 @@ static av_cold int init(AVFilterContext *ctx)
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
if (s->borderw) {
|
||||
if (FT_Stroker_New(s->library, &s->stroker)) {
|
||||
av_log(ctx, AV_LOG_ERROR, "Coult not init FT stroker\n");
|
||||
return AVERROR_EXTERNAL;
|
||||
}
|
||||
FT_Stroker_Set(s->stroker, s->borderw << 6, FT_STROKER_LINECAP_ROUND,
|
||||
FT_STROKER_LINEJOIN_ROUND, 0);
|
||||
}
|
||||
|
||||
s->use_kerning = FT_HAS_KERNING(s->face);
|
||||
|
||||
/* load the fallback glyph with code 0 */
|
||||
@ -546,6 +572,7 @@ static av_cold void uninit(AVFilterContext *ctx)
|
||||
s->glyphs = NULL;
|
||||
|
||||
FT_Done_Face(s->face);
|
||||
FT_Stroker_Done(s->stroker);
|
||||
FT_Done_FreeType(s->library);
|
||||
|
||||
av_bprint_finalize(&s->expanded_text, NULL);
|
||||
@ -565,6 +592,7 @@ static int config_input(AVFilterLink *inlink)
|
||||
ff_draw_init(&s->dc, inlink->format, 0);
|
||||
ff_draw_color(&s->dc, &s->fontcolor, s->fontcolor.rgba);
|
||||
ff_draw_color(&s->dc, &s->shadowcolor, s->shadowcolor.rgba);
|
||||
ff_draw_color(&s->dc, &s->bordercolor, s->bordercolor.rgba);
|
||||
ff_draw_color(&s->dc, &s->boxcolor, s->boxcolor.rgba);
|
||||
|
||||
s->var_values[VAR_w] = s->var_values[VAR_W] = s->var_values[VAR_MAIN_W] = inlink->w;
|
||||
@ -812,7 +840,8 @@ static int expand_text(AVFilterContext *ctx)
|
||||
}
|
||||
|
||||
static int draw_glyphs(DrawTextContext *s, AVFrame *frame,
|
||||
int width, int height, const uint8_t rgbcolor[4], FFDrawColor *color, int x, int y)
|
||||
int width, int height, const uint8_t rgbcolor[4],
|
||||
FFDrawColor *color, int x, int y, int borderw)
|
||||
{
|
||||
char *text = s->expanded_text.str;
|
||||
uint32_t code = 0;
|
||||
@ -821,6 +850,7 @@ static int draw_glyphs(DrawTextContext *s, AVFrame *frame,
|
||||
Glyph *glyph = NULL;
|
||||
|
||||
for (i = 0, p = text; *p; i++) {
|
||||
FT_Bitmap bitmap;
|
||||
Glyph dummy = { 0 };
|
||||
GET_UTF8(code, *p++, continue;);
|
||||
|
||||
@ -831,18 +861,20 @@ static int draw_glyphs(DrawTextContext *s, AVFrame *frame,
|
||||
dummy.code = code;
|
||||
glyph = av_tree_find(s->glyphs, &dummy, (void *)glyph_cmp, NULL);
|
||||
|
||||
bitmap = borderw ? glyph->border_bitmap : glyph->bitmap;
|
||||
|
||||
if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
|
||||
glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
x1 = s->positions[i].x+s->x+x;
|
||||
y1 = s->positions[i].y+s->y+y;
|
||||
x1 = s->positions[i].x+s->x+x - borderw;
|
||||
y1 = s->positions[i].y+s->y+y - borderw;
|
||||
|
||||
ff_blend_mask(&s->dc, color,
|
||||
frame->data, frame->linesize, width, height,
|
||||
glyph->bitmap.buffer, glyph->bitmap.pitch,
|
||||
glyph->bitmap.width, glyph->bitmap.rows,
|
||||
glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
|
||||
bitmap.buffer, bitmap.pitch,
|
||||
bitmap.width, bitmap.rows,
|
||||
bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
|
||||
0, x1, y1);
|
||||
}
|
||||
|
||||
@ -1003,12 +1035,17 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame,
|
||||
|
||||
if (s->shadowx || s->shadowy) {
|
||||
if ((ret = draw_glyphs(s, frame, width, height, s->shadowcolor.rgba,
|
||||
&s->shadowcolor, s->shadowx, s->shadowy)) < 0)
|
||||
&s->shadowcolor, s->shadowx, s->shadowy, 0)) < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (s->borderw) {
|
||||
if ((ret = draw_glyphs(s, frame, width, height, s->bordercolor.rgba,
|
||||
&s->bordercolor, 0, 0, s->borderw)) < 0)
|
||||
return ret;
|
||||
}
|
||||
if ((ret = draw_glyphs(s, frame, width, height, s->fontcolor.rgba,
|
||||
&s->fontcolor, 0, 0)) < 0)
|
||||
&s->fontcolor, 0, 0, 0)) < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user