summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Oshmyan <chortos@inbox.lv>2020-10-26 00:50:23 +0200
committerOleg Oshmyan <chortos@inbox.lv>2022-02-14 20:43:08 +0200
commitc629be7d70548aea282ea4890c51094055ec66e2 (patch)
tree241292c76a288d30ffc8a4f36a9265c926affa6d
parentc4e2aadc88db638eed6090fa81a690235a11d3bd (diff)
downloadlibass-c629be7d70548aea282ea4890c51094055ec66e2.tar.bz2
libass-c629be7d70548aea282ea4890c51094055ec66e2.tar.xz
Run bidi on each glyph run in isolation by default, like VSFilter
Fixes https://code.google.com/archive/p/libass/issues/111. Fixes https://github.com/libass/libass/issues/226 if it was not fixed by commit c93cb3dbfb7357179379ffd19ff973cf062e2129 already. The old ("sane") behavior of running bidi on each event as a whole* can be restored by setting the new ASS_FEATURE_WHOLE_TEXT_LAYOUT. Additionally, in a nod to ASS files and on-the-fly format converters that use libass's nonstandard Encoding -1 for bidi base direction autodetection, make Encoding -1 force WHOLE_TEXT_LAYOUT processing for individual events even when the ASS_Feature is disabled. * Note: we treat explicit line breaks \N as paragraph separators, and effectively bidi is run on each "paragraph" separately. This is standard bidi behavior. Of particular relevance to libass, this is exactly what WebVTT mandates.
-rw-r--r--libass/ass.c1
-rw-r--r--libass/ass.h19
-rw-r--r--libass/ass_render.c2
-rw-r--r--libass/ass_shaper.c49
-rw-r--r--libass/ass_shaper.h1
5 files changed, 61 insertions, 11 deletions
diff --git a/libass/ass.c b/libass/ass.c
index 79a3532..ba60eda 100644
--- a/libass/ass.c
+++ b/libass/ass.c
@@ -1543,6 +1543,7 @@ int ass_track_set_feature(ASS_Track *track, ASS_Feature feature, int enable)
#ifdef USE_FRIBIDI_EX_API
FEATURE_MASK(ASS_FEATURE_BIDI_BRACKETS) |
#endif
+ FEATURE_MASK(ASS_FEATURE_WHOLE_TEXT_LAYOUT) |
0;
uint32_t requested = 0;
diff --git a/libass/ass.h b/libass/ass.h
index 10d5595..ef3ff23 100644
--- a/libass/ass.h
+++ b/libass/ass.h
@@ -238,6 +238,25 @@ typedef enum {
*/
ASS_FEATURE_BIDI_BRACKETS,
+ /**
+ * When this feature is disabled, text is split into VSFilter-compatible
+ * segments and text in each segment is processed in isolation.
+ * Notably, this includes running the Unicode Bidirectional
+ * Algorithm within each run separately.
+ * The individual runs are then laid out left-to-right,
+ * even if they contain right-to-left text.
+ *
+ * When this feature is enabled, each event's text is processed as a whole
+ * (as far as possible). In particular, the Unicode Bidirectional
+ * Algorithm is run on the whole text.
+ *
+ * This is incompatible with VSFilter and disabled by default.
+ *
+ * libass extensions to ASS such as Encoding -1 can cause individual
+ * events to be always processed as if this feature is enabled.
+ */
+ ASS_FEATURE_WHOLE_TEXT_LAYOUT,
+
// New enum values can be added here in new ABI-compatible library releases.
} ASS_Feature;
diff --git a/libass/ass_render.c b/libass/ass_render.c
index 5a73c35..6e9aef2 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -2885,6 +2885,8 @@ ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track,
ass_shaper_set_bidi_brackets(render_priv->shaper,
track->parser_priv->feature_flags & FEATURE_MASK(ASS_FEATURE_BIDI_BRACKETS));
#endif
+ ass_shaper_set_whole_text_layout(render_priv->shaper,
+ track->parser_priv->feature_flags & FEATURE_MASK(ASS_FEATURE_WHOLE_TEXT_LAYOUT));
// PAR correction
double par = render_priv->settings.par;
diff --git a/libass/ass_shaper.c b/libass/ass_shaper.c
index f95dd58..b05a051 100644
--- a/libass/ass_shaper.c
+++ b/libass/ass_shaper.c
@@ -39,6 +39,12 @@ enum {
};
#define NUM_FEATURES 5
+enum {
+ WHOLE_TEXT_LAYOUT_OFF,
+ WHOLE_TEXT_LAYOUT_IMPLICIT,
+ WHOLE_TEXT_LAYOUT_EXPLICIT,
+};
+
struct ass_shaper {
ASS_ShapingLevel shaping_level;
@@ -62,6 +68,8 @@ struct ass_shaper {
FriBidiBracketType *btypes;
bool bidi_brackets;
#endif
+
+ char whole_text_layout;
};
struct ass_shaper_metrics_data {
@@ -895,6 +903,10 @@ void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv,
void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir)
{
shaper->base_direction = dir;
+
+ if (shaper->whole_text_layout != WHOLE_TEXT_LAYOUT_EXPLICIT)
+ shaper->whole_text_layout = dir == FRIBIDI_PAR_ON ?
+ WHOLE_TEXT_LAYOUT_IMPLICIT : WHOLE_TEXT_LAYOUT_OFF;
}
/**
@@ -929,6 +941,14 @@ void ass_shaper_set_bidi_brackets(ASS_Shaper *shaper, bool match_brackets)
}
#endif
+void ass_shaper_set_whole_text_layout(ASS_Shaper *shaper, bool enable)
+{
+ shaper->whole_text_layout = enable ?
+ WHOLE_TEXT_LAYOUT_EXPLICIT :
+ shaper->base_direction == FRIBIDI_PAR_ON ?
+ WHOLE_TEXT_LAYOUT_IMPLICIT : WHOLE_TEXT_LAYOUT_OFF;
+}
+
/**
* \brief Shape an event's text. Calculates directional runs and shapes them.
* \param text_info event's text
@@ -948,7 +968,8 @@ bool ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info)
for (i = 0; i < text_info->length; i++) {
shaper->event_text[i] = glyphs[i].symbol;
// embedding levels should be calculated paragraph by paragraph
- if (glyphs[i].symbol == '\n' || i == text_info->length - 1) {
+ if (glyphs[i].symbol == '\n' || i == text_info->length - 1 ||
+ (!shaper->whole_text_layout && glyphs[i + 1].starts_new_run)) {
dir = shaper->base_direction;
fribidi_get_bidi_types(shaper->event_text + last_break,
i - last_break + 1, shaper->ctypes + last_break);
@@ -1041,17 +1062,23 @@ FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info)
for (i = 0; i < text_info->length; i++)
shaper->cmap[i] = i;
- // Create reorder map line-by-line
- for (i = 0; i < text_info->n_lines; i++) {
- LineInfo *line = text_info->lines + i;
- FriBidiParType dir = FRIBIDI_PAR_ON;
+ // Create reorder map line-by-line or run-by-run
+ int last_break = 0;
+ GlyphInfo *glyphs = text_info->glyphs;
+ for (i = 0; i < text_info->length; i++) {
+ if (i == text_info->length - 1 || glyphs[i + 1].linebreak ||
+ (!shaper->whole_text_layout && glyphs[i + 1].starts_new_run)) {
+ FriBidiParType dir = FRIBIDI_PAR_ON;
+
+ ret = fribidi_reorder_line(0,
+ shaper->ctypes, i - last_break + 1, last_break, dir,
+ shaper->emblevels, NULL,
+ shaper->cmap);
+ if (ret == 0)
+ return NULL;
- ret = fribidi_reorder_line(0,
- shaper->ctypes + line->offset, line->len, 0, dir,
- shaper->emblevels + line->offset, NULL,
- shaper->cmap + line->offset);
- if (ret == 0)
- return NULL;
+ last_break = i + 1;
+ }
}
return shaper->cmap;
diff --git a/libass/ass_shaper.h b/libass/ass_shaper.h
index 70bec9a..66bfed0 100644
--- a/libass/ass_shaper.h
+++ b/libass/ass_shaper.h
@@ -42,6 +42,7 @@ void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level);
#ifdef USE_FRIBIDI_EX_API
void ass_shaper_set_bidi_brackets(ASS_Shaper *shaper, bool match_brackets);
#endif
+void ass_shaper_set_whole_text_layout(ASS_Shaper *shaper, bool enable);
bool ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info);
void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info);
FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info);