Fixes ticket #3758
Reviewed-by: Andrey Utkin <andrey.krieger.utkin@gmail.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
... | ... |
@@ -209,6 +209,7 @@ External library support: |
209 | 209 |
--enable-libfdk-aac enable AAC de/encoding via libfdk-aac [no] |
210 | 210 |
--enable-libflite enable flite (voice synthesis) support via libflite [no] |
211 | 211 |
--enable-libfreetype enable libfreetype [no] |
212 |
+ --enable-libfribidi enable libfribidi [no] |
|
212 | 213 |
--enable-libgme enable Game Music Emu via libgme [no] |
213 | 214 |
--enable-libgsm enable GSM de/encoding via libgsm [no] |
214 | 215 |
--enable-libiec61883 enable iec61883 via libiec61883 [no] |
... | ... |
@@ -1333,6 +1334,7 @@ EXTERNAL_LIBRARY_LIST=" |
1333 | 1333 |
libflite |
1334 | 1334 |
libfontconfig |
1335 | 1335 |
libfreetype |
1336 |
+ libfribidi |
|
1336 | 1337 |
libgme |
1337 | 1338 |
libgsm |
1338 | 1339 |
libiec61883 |
... | ... |
@@ -4729,6 +4731,7 @@ enabled libflite && require2 libflite "flite/flite.h" flite_init $flite |
4729 | 4729 |
enabled fontconfig && enable libfontconfig |
4730 | 4730 |
enabled libfontconfig && require_pkg_config fontconfig "fontconfig/fontconfig.h" FcInit |
4731 | 4731 |
enabled libfreetype && require_libfreetype |
4732 |
+enabled libfribidi && require_pkg_config fribidi fribidi.h fribidi_version_info |
|
4732 | 4733 |
enabled libgme && require libgme gme/gme.h gme_new_emu -lgme -lstdc++ |
4733 | 4734 |
enabled libgsm && { for gsm_hdr in "gsm.h" "gsm/gsm.h"; do |
4734 | 4735 |
check_lib "${gsm_hdr}" gsm_create -lgsm && break; |
... | ... |
@@ -3653,6 +3653,8 @@ To enable compilation of this filter, you need to configure FFmpeg with |
3653 | 3653 |
@code{--enable-libfreetype}. |
3654 | 3654 |
To enable default font fallback and the @var{font} option you need to |
3655 | 3655 |
configure FFmpeg with @code{--enable-libfontconfig}. |
3656 |
+To enable the @var{text_shaping} option, you need to configure FFmpeg with |
|
3657 |
+@code{--enable-libfribidi}. |
|
3656 | 3658 |
|
3657 | 3659 |
@subsection Syntax |
3658 | 3660 |
|
... | ... |
@@ -3707,6 +3709,12 @@ This parameter is mandatory if the fontconfig support is disabled. |
3707 | 3707 |
The font size to be used for drawing text. |
3708 | 3708 |
The default value of @var{fontsize} is 16. |
3709 | 3709 |
|
3710 |
+@item text_shaping |
|
3711 |
+If set to 1, attempt to shape the text (for example, reverse the order of |
|
3712 |
+right-to-left text and join Arabic characters) before drawing it. |
|
3713 |
+Otherwise, just draw the text exactly as given. |
|
3714 |
+By default 1 (if supported). |
|
3715 |
+ |
|
3710 | 3716 |
@item ft_load_flags |
3711 | 3717 |
The flags to be used for loading the fonts. |
3712 | 3718 |
|
... | ... |
@@ -4010,6 +4018,9 @@ For more information about libfreetype, check: |
4010 | 4010 |
For more information about fontconfig, check: |
4011 | 4011 |
@url{http://freedesktop.org/software/fontconfig/fontconfig-user.html}. |
4012 | 4012 |
|
4013 |
+For more information about libfribidi, check: |
|
4014 |
+@url{http://fribidi.org/}. |
|
4015 |
+ |
|
4013 | 4016 |
@section edgedetect |
4014 | 4017 |
|
4015 | 4018 |
Detect and draw edges. The filter uses the Canny Edge Detection algorithm. |
... | ... |
@@ -30,7 +30,7 @@ |
30 | 30 |
#include "libavutil/version.h" |
31 | 31 |
|
32 | 32 |
#define LIBAVFILTER_VERSION_MAJOR 4 |
33 |
-#define LIBAVFILTER_VERSION_MINOR 10 |
|
33 |
+#define LIBAVFILTER_VERSION_MINOR 11 |
|
34 | 34 |
#define LIBAVFILTER_VERSION_MICRO 100 |
35 | 35 |
|
36 | 36 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
... | ... |
@@ -59,6 +59,10 @@ |
59 | 59 |
#include "internal.h" |
60 | 60 |
#include "video.h" |
61 | 61 |
|
62 |
+#if CONFIG_LIBFRIBIDI |
|
63 |
+#include <fribidi.h> |
|
64 |
+#endif |
|
65 |
+ |
|
62 | 66 |
#include <ft2build.h> |
63 | 67 |
#include FT_FREETYPE_H |
64 | 68 |
#include FT_GLYPH_H |
... | ... |
@@ -182,6 +186,9 @@ typedef struct DrawTextContext { |
182 | 182 |
int tc24hmax; ///< 1 if timecode is wrapped to 24 hours, 0 otherwise |
183 | 183 |
int reload; ///< reload text file for each frame |
184 | 184 |
int start_number; ///< starting frame number for n/frame_num var |
185 |
+#if CONFIG_LIBFRIBIDI |
|
186 |
+ int text_shaping; ///< 1 to shape the text before drawing it |
|
187 |
+#endif |
|
185 | 188 |
AVDictionary *metadata; |
186 | 189 |
} DrawTextContext; |
187 | 190 |
|
... | ... |
@@ -226,6 +233,10 @@ static const AVOption drawtext_options[]= { |
226 | 226 |
{"fix_bounds", "if true, check and fix text coords to avoid clipping", OFFSET(fix_bounds), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS}, |
227 | 227 |
{"start_number", "start frame number for n/frame_num variable", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, |
228 | 228 |
|
229 |
+#if CONFIG_LIBFRIBIDI |
|
230 |
+ {"text_shaping", "attempt to shape text before drawing", OFFSET(text_shaping), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS}, |
|
231 |
+#endif |
|
232 |
+ |
|
229 | 233 |
/* FT_LOAD_* flags */ |
230 | 234 |
{ "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" }, |
231 | 235 |
{ "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) |
482 | 482 |
return 0; |
483 | 483 |
} |
484 | 484 |
|
485 |
+static inline int is_newline(uint32_t c) |
|
486 |
+{ |
|
487 |
+ return c == '\n' || c == '\r' || c == '\f' || c == '\v'; |
|
488 |
+} |
|
489 |
+ |
|
490 |
+#if CONFIG_LIBFRIBIDI |
|
491 |
+static int shape_text(AVFilterContext *ctx) |
|
492 |
+{ |
|
493 |
+ DrawTextContext *s = ctx->priv; |
|
494 |
+ uint8_t *tmp; |
|
495 |
+ int ret = AVERROR(ENOMEM); |
|
496 |
+ static const FriBidiFlags flags = FRIBIDI_FLAGS_DEFAULT | |
|
497 |
+ FRIBIDI_FLAGS_ARABIC; |
|
498 |
+ FriBidiChar *unicodestr = NULL; |
|
499 |
+ FriBidiStrIndex len; |
|
500 |
+ FriBidiParType direction = FRIBIDI_PAR_LTR; |
|
501 |
+ FriBidiStrIndex line_start = 0; |
|
502 |
+ FriBidiStrIndex line_end = 0; |
|
503 |
+ FriBidiLevel *embedding_levels = NULL; |
|
504 |
+ FriBidiArabicProp *ar_props = NULL; |
|
505 |
+ FriBidiCharType *bidi_types = NULL; |
|
506 |
+ FriBidiStrIndex i,j; |
|
507 |
+ |
|
508 |
+ len = strlen(s->text); |
|
509 |
+ if (!(unicodestr = av_malloc_array(len, sizeof(*unicodestr)))) { |
|
510 |
+ goto out; |
|
511 |
+ } |
|
512 |
+ len = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, |
|
513 |
+ s->text, len, unicodestr); |
|
514 |
+ |
|
515 |
+ bidi_types = av_malloc_array(len, sizeof(*bidi_types)); |
|
516 |
+ if (!bidi_types) { |
|
517 |
+ goto out; |
|
518 |
+ } |
|
519 |
+ |
|
520 |
+ fribidi_get_bidi_types(unicodestr, len, bidi_types); |
|
521 |
+ |
|
522 |
+ embedding_levels = av_malloc_array(len, sizeof(*embedding_levels)); |
|
523 |
+ if (!embedding_levels) { |
|
524 |
+ goto out; |
|
525 |
+ } |
|
526 |
+ |
|
527 |
+ if (!fribidi_get_par_embedding_levels(bidi_types, len, &direction, |
|
528 |
+ embedding_levels)) { |
|
529 |
+ goto out; |
|
530 |
+ } |
|
531 |
+ |
|
532 |
+ ar_props = av_malloc_array(len, sizeof(*ar_props)); |
|
533 |
+ if (!ar_props) { |
|
534 |
+ goto out; |
|
535 |
+ } |
|
536 |
+ |
|
537 |
+ fribidi_get_joining_types(unicodestr, len, ar_props); |
|
538 |
+ fribidi_join_arabic(bidi_types, len, embedding_levels, ar_props); |
|
539 |
+ fribidi_shape(flags, embedding_levels, len, ar_props, unicodestr); |
|
540 |
+ |
|
541 |
+ for (line_end = 0, line_start = 0; line_end < len; line_end++) { |
|
542 |
+ if (is_newline(unicodestr[line_end]) || line_end == len - 1) { |
|
543 |
+ if (!fribidi_reorder_line(flags, bidi_types, |
|
544 |
+ line_end - line_start + 1, line_start, |
|
545 |
+ direction, embedding_levels, unicodestr, |
|
546 |
+ NULL)) { |
|
547 |
+ goto out; |
|
548 |
+ } |
|
549 |
+ line_start = line_end + 1; |
|
550 |
+ } |
|
551 |
+ } |
|
552 |
+ |
|
553 |
+ /* Remove zero-width fill chars put in by libfribidi */ |
|
554 |
+ for (i = 0, j = 0; i < len; i++) |
|
555 |
+ if (unicodestr[i] != FRIBIDI_CHAR_FILL) |
|
556 |
+ unicodestr[j++] = unicodestr[i]; |
|
557 |
+ len = j; |
|
558 |
+ |
|
559 |
+ if (!(tmp = av_realloc(s->text, (len * 4 + 1) * sizeof(*s->text)))) { |
|
560 |
+ /* Use len * 4, as a unicode character can be up to 4 bytes in UTF-8 */ |
|
561 |
+ goto out; |
|
562 |
+ } |
|
563 |
+ |
|
564 |
+ s->text = tmp; |
|
565 |
+ len = fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, |
|
566 |
+ unicodestr, len, s->text); |
|
567 |
+ ret = 0; |
|
568 |
+ |
|
569 |
+out: |
|
570 |
+ av_free(unicodestr); |
|
571 |
+ av_free(embedding_levels); |
|
572 |
+ av_free(ar_props); |
|
573 |
+ av_free(bidi_types); |
|
574 |
+ return ret; |
|
575 |
+} |
|
576 |
+#endif |
|
577 |
+ |
|
485 | 578 |
static av_cold int init(AVFilterContext *ctx) |
486 | 579 |
{ |
487 | 580 |
int err; |
... | ... |
@@ -509,6 +613,12 @@ static av_cold int init(AVFilterContext *ctx) |
509 | 509 |
return err; |
510 | 510 |
} |
511 | 511 |
|
512 |
+#if CONFIG_LIBFRIBIDI |
|
513 |
+ if (s->text_shaping) |
|
514 |
+ if ((err = shape_text(ctx)) < 0) |
|
515 |
+ return err; |
|
516 |
+#endif |
|
517 |
+ |
|
512 | 518 |
if (s->reload && !s->textfile) |
513 | 519 |
av_log(ctx, AV_LOG_WARNING, "No file to reload\n"); |
514 | 520 |
|
... | ... |
@@ -617,11 +727,6 @@ static av_cold void uninit(AVFilterContext *ctx) |
617 | 617 |
av_bprint_finalize(&s->expanded_text, NULL); |
618 | 618 |
} |
619 | 619 |
|
620 |
-static inline int is_newline(uint32_t c) |
|
621 |
-{ |
|
622 |
- return c == '\n' || c == '\r' || c == '\f' || c == '\v'; |
|
623 |
-} |
|
624 |
- |
|
625 | 620 |
static int config_input(AVFilterLink *inlink) |
626 | 621 |
{ |
627 | 622 |
AVFilterContext *ctx = inlink->dst; |
... | ... |
@@ -1132,9 +1237,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) |
1132 | 1132 |
DrawTextContext *s = ctx->priv; |
1133 | 1133 |
int ret; |
1134 | 1134 |
|
1135 |
- if (s->reload) |
|
1135 |
+ if (s->reload) { |
|
1136 | 1136 |
if ((ret = load_textfile(ctx)) < 0) |
1137 | 1137 |
return ret; |
1138 |
+#if CONFIG_LIBFRIBIDI |
|
1139 |
+ if (s->text_shaping) |
|
1140 |
+ if ((ret = shape_text(ctx)) < 0) |
|
1141 |
+ return ret; |
|
1142 |
+#endif |
|
1143 |
+ } |
|
1138 | 1144 |
|
1139 | 1145 |
s->var_values[VAR_N] = inlink->frame_count+s->start_number; |
1140 | 1146 |
s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ? |