diff options
author | Marc Jeffreys <maj160@live.co.uk> | 2014-06-21 05:41:45 +0100 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2014-07-16 00:40:39 +0200 |
commit | a0b71e9f3e95d22b098e4d81c26d3bfe1f53249f (patch) | |
tree | 2ce796b97faaee6fd9e44089a2c624a230ede051 | |
parent | 04980dbee805f372f0505a0afd9ae10c0da1e17e (diff) | |
download | ffmpeg-a0b71e9f3e95d22b098e4d81c26d3bfe1f53249f.tar.gz |
avfilter/drawtext: Add basic text shaping using libfribidi
Fixes ticket #3758
Reviewed-by: Andrey Utkin <andrey.krieger.utkin@gmail.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
-rw-r--r-- | Changelog | 1 | ||||
-rw-r--r-- | RELEASE_NOTES | 1 | ||||
-rwxr-xr-x | configure | 3 | ||||
-rw-r--r-- | doc/filters.texi | 11 | ||||
-rw-r--r-- | libavfilter/version.h | 2 | ||||
-rw-r--r-- | libavfilter/vf_drawtext.c | 123 |
6 files changed, 134 insertions, 7 deletions
@@ -35,6 +35,7 @@ version <next>: - LRC demuxer and muxer - Samba protocol (via libsmbclient) - WebM DASH Manifest muxer +- libfribidi support in drawtext version 2.2: diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 4502d9fd95..145abfaca7 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -164,6 +164,7 @@ • signalstats filter • hqx filter (hq2x, hq3x, hq4x) • flanger filter + • libfribidi support in drawtext ┌────────────────────────────┐ │ ⚠ Behaviour changes │ @@ -209,6 +209,7 @@ External library support: --enable-libfdk-aac enable AAC de/encoding via libfdk-aac [no] --enable-libflite enable flite (voice synthesis) support via libflite [no] --enable-libfreetype enable libfreetype [no] + --enable-libfribidi enable libfribidi [no] --enable-libgme enable Game Music Emu via libgme [no] --enable-libgsm enable GSM de/encoding via libgsm [no] --enable-libiec61883 enable iec61883 via libiec61883 [no] @@ -1333,6 +1334,7 @@ EXTERNAL_LIBRARY_LIST=" libflite libfontconfig libfreetype + libfribidi libgme libgsm libiec61883 @@ -4729,6 +4731,7 @@ enabled libflite && require2 libflite "flite/flite.h" flite_init $flite enabled fontconfig && enable libfontconfig enabled libfontconfig && require_pkg_config fontconfig "fontconfig/fontconfig.h" FcInit enabled libfreetype && require_libfreetype +enabled libfribidi && require_pkg_config fribidi fribidi.h fribidi_version_info enabled libgme && require libgme gme/gme.h gme_new_emu -lgme -lstdc++ enabled libgsm && { for gsm_hdr in "gsm.h" "gsm/gsm.h"; do check_lib "${gsm_hdr}" gsm_create -lgsm && break; diff --git a/doc/filters.texi b/doc/filters.texi index 65106a5c63..0f73314a12 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -3653,6 +3653,8 @@ To enable compilation of this filter, you need to configure FFmpeg with @code{--enable-libfreetype}. To enable default font fallback and the @var{font} option you need to configure FFmpeg with @code{--enable-libfontconfig}. +To enable the @var{text_shaping} option, you need to configure FFmpeg with +@code{--enable-libfribidi}. @subsection Syntax @@ -3707,6 +3709,12 @@ This parameter is mandatory if the fontconfig support is disabled. The font size to be used for drawing text. The default value of @var{fontsize} is 16. +@item text_shaping +If set to 1, attempt to shape the text (for example, reverse the order of +right-to-left text and join Arabic characters) before drawing it. +Otherwise, just draw the text exactly as given. +By default 1 (if supported). + @item ft_load_flags The flags to be used for loading the fonts. @@ -4010,6 +4018,9 @@ For more information about libfreetype, check: For more information about fontconfig, check: @url{http://freedesktop.org/software/fontconfig/fontconfig-user.html}. +For more information about libfribidi, check: +@url{http://fribidi.org/}. + @section edgedetect Detect and draw edges. The filter uses the Canny Edge Detection algorithm. diff --git a/libavfilter/version.h b/libavfilter/version.h index bf9191ecec..227d3203ff 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFILTER_VERSION_MAJOR 4 -#define LIBAVFILTER_VERSION_MINOR 10 +#define LIBAVFILTER_VERSION_MINOR 11 #define LIBAVFILTER_VERSION_MICRO 100 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c index 0d829a6d5e..0b93d14533 100644 --- a/libavfilter/vf_drawtext.c +++ b/libavfilter/vf_drawtext.c @@ -59,6 +59,10 @@ #include "internal.h" #include "video.h" +#if CONFIG_LIBFRIBIDI +#include <fribidi.h> +#endif + #include <ft2build.h> #include FT_FREETYPE_H #include FT_GLYPH_H @@ -182,6 +186,9 @@ typedef struct DrawTextContext { int tc24hmax; ///< 1 if timecode is wrapped to 24 hours, 0 otherwise int reload; ///< reload text file for each frame int start_number; ///< starting frame number for n/frame_num var +#if CONFIG_LIBFRIBIDI + int text_shaping; ///< 1 to shape the text before drawing it +#endif AVDictionary *metadata; } DrawTextContext; @@ -226,6 +233,10 @@ static const AVOption drawtext_options[]= { {"fix_bounds", "if true, check and fix text coords to avoid clipping", OFFSET(fix_bounds), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS}, {"start_number", "start frame number for n/frame_num variable", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, +#if CONFIG_LIBFRIBIDI + {"text_shaping", "attempt to shape text before drawing", OFFSET(text_shaping), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS}, +#endif + /* FT_LOAD_* flags */ { "ft_load_flags", "set font loading flags for libfreetype", OFFSET(ft_load_flags), AV_OPT_TYPE_FLAGS, { .i64 = FT_LOAD_DEFAULT }, 0, INT_MAX, FLAGS, "ft_load_flags" }, { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_DEFAULT }, .flags = FLAGS, .unit = "ft_load_flags" }, @@ -482,6 +493,99 @@ static int load_textfile(AVFilterContext *ctx) return 0; } +static inline int is_newline(uint32_t c) +{ + return c == '\n' || c == '\r' || c == '\f' || c == '\v'; +} + +#if CONFIG_LIBFRIBIDI +static int shape_text(AVFilterContext *ctx) +{ + DrawTextContext *s = ctx->priv; + uint8_t *tmp; + int ret = AVERROR(ENOMEM); + static const FriBidiFlags flags = FRIBIDI_FLAGS_DEFAULT | + FRIBIDI_FLAGS_ARABIC; + FriBidiChar *unicodestr = NULL; + FriBidiStrIndex len; + FriBidiParType direction = FRIBIDI_PAR_LTR; + FriBidiStrIndex line_start = 0; + FriBidiStrIndex line_end = 0; + FriBidiLevel *embedding_levels = NULL; + FriBidiArabicProp *ar_props = NULL; + FriBidiCharType *bidi_types = NULL; + FriBidiStrIndex i,j; + + len = strlen(s->text); + if (!(unicodestr = av_malloc_array(len, sizeof(*unicodestr)))) { + goto out; + } + len = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, + s->text, len, unicodestr); + + bidi_types = av_malloc_array(len, sizeof(*bidi_types)); + if (!bidi_types) { + goto out; + } + + fribidi_get_bidi_types(unicodestr, len, bidi_types); + + embedding_levels = av_malloc_array(len, sizeof(*embedding_levels)); + if (!embedding_levels) { + goto out; + } + + if (!fribidi_get_par_embedding_levels(bidi_types, len, &direction, + embedding_levels)) { + goto out; + } + + ar_props = av_malloc_array(len, sizeof(*ar_props)); + if (!ar_props) { + goto out; + } + + fribidi_get_joining_types(unicodestr, len, ar_props); + fribidi_join_arabic(bidi_types, len, embedding_levels, ar_props); + fribidi_shape(flags, embedding_levels, len, ar_props, unicodestr); + + for (line_end = 0, line_start = 0; line_end < len; line_end++) { + if (is_newline(unicodestr[line_end]) || line_end == len - 1) { + if (!fribidi_reorder_line(flags, bidi_types, + line_end - line_start + 1, line_start, + direction, embedding_levels, unicodestr, + NULL)) { + goto out; + } + line_start = line_end + 1; + } + } + + /* Remove zero-width fill chars put in by libfribidi */ + for (i = 0, j = 0; i < len; i++) + if (unicodestr[i] != FRIBIDI_CHAR_FILL) + unicodestr[j++] = unicodestr[i]; + len = j; + + if (!(tmp = av_realloc(s->text, (len * 4 + 1) * sizeof(*s->text)))) { + /* Use len * 4, as a unicode character can be up to 4 bytes in UTF-8 */ + goto out; + } + + s->text = tmp; + len = fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, + unicodestr, len, s->text); + ret = 0; + +out: + av_free(unicodestr); + av_free(embedding_levels); + av_free(ar_props); + av_free(bidi_types); + return ret; +} +#endif + static av_cold int init(AVFilterContext *ctx) { int err; @@ -509,6 +613,12 @@ static av_cold int init(AVFilterContext *ctx) return err; } +#if CONFIG_LIBFRIBIDI + if (s->text_shaping) + if ((err = shape_text(ctx)) < 0) + return err; +#endif + if (s->reload && !s->textfile) av_log(ctx, AV_LOG_WARNING, "No file to reload\n"); @@ -617,11 +727,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_bprint_finalize(&s->expanded_text, NULL); } -static inline int is_newline(uint32_t c) -{ - return c == '\n' || c == '\r' || c == '\f' || c == '\v'; -} - static int config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; @@ -1132,9 +1237,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) DrawTextContext *s = ctx->priv; int ret; - if (s->reload) + if (s->reload) { if ((ret = load_textfile(ctx)) < 0) return ret; +#if CONFIG_LIBFRIBIDI + if (s->text_shaping) + if ((ret = shape_text(ctx)) < 0) + return ret; +#endif + } s->var_values[VAR_N] = inlink->frame_count+s->start_number; s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ? |