Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Nikse.SubtitleEdit.Controls.VideoPlayer;
using Nikse.SubtitleEdit.Core.Common;
using Nikse.SubtitleEdit.Core.SubtitleFormats;
using Nikse.SubtitleEdit.Features.Assa.AssaApplyAdvancedEffect.Effects;
using Nikse.SubtitleEdit.Features.Main;
using Nikse.SubtitleEdit.Features.Sync.VisualSync;
using Nikse.SubtitleEdit.Logic;
Expand Down Expand Up @@ -98,6 +99,23 @@ public void Initialize(
? Paragraphs.IndexOf(Paragraphs.First(p => p.Subtitle.Id == _selectedSubtitleLines[0].Id))
: 0;

// Default the karaoke effects to right-to-left when the subtitle text is a right-to-left
// language (Hebrew, Arabic, ...) so the highlight progresses in reading order. (#12271)
if (paragraphs.Any(p => LanguageAutoDetect.ContainsRightToLeftLetter(p.Text)))
{
foreach (var effect in OverrideTags)
{
if (effect is AdvancedEffectKaraoke karaoke)
{
karaoke.RightToLeft = true;
}
else if (effect is AdvancedEffectFancyKaraoke fancyKaraoke)
{
fancyKaraoke.RightToLeft = true;
}
}
}

if (audioVisualizer == null || audioVisualizer.WavePeaks == null || audioVisualizer.WavePeaks.Peaks.Count == 0)
{
// remove audio visualizer related effects if no audio visualizer data is available
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,23 @@ public AssaApplyAdvancedEffectWindow(AssaApplyAdvancedEffectViewModel vm)
settingsStack.Children.Add(speedRow);
panel.Children.Add(settingsStack);
}
else if (item is AdvancedEffectKaraoke karaokeItem)
{
var rightToLeftRow = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8, Margin = new Thickness(0, 6, 0, 0) };
rightToLeftRow.Children.Add(new TextBlock { Text = Se.Language.Assa.AdvancedEffectKaraokeRightToLeft, VerticalAlignment = VerticalAlignment.Center, FontSize = 12 });
rightToLeftRow.Children.Add(UiUtil.MakeCheckBox(karaokeItem, nameof(AdvancedEffectKaraoke.RightToLeft)));
panel.Children.Add(rightToLeftRow);
}
else if (item is AdvancedEffectFancyKaraoke fancyKaraokeItem)
{
var autoDetectActiveWordRow = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8, Margin = new Thickness(0, 6, 0, 0) };
autoDetectActiveWordRow.Children.Add(new TextBlock { Text = Se.Language.Assa.AdvancedEffectFancyKaraokeAutoDetectActiveWord, VerticalAlignment = VerticalAlignment.Center, FontSize = 12 });
autoDetectActiveWordRow.Children.Add(UiUtil.MakeCheckBox(fancyKaraokeItem, nameof(AdvancedEffectFancyKaraoke.AutoDetectActiveWord)));

var rightToLeftRow = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8, Margin = new Thickness(0, 4, 0, 0) };
rightToLeftRow.Children.Add(new TextBlock { Text = Se.Language.Assa.AdvancedEffectKaraokeRightToLeft, VerticalAlignment = VerticalAlignment.Center, FontSize = 12 });
rightToLeftRow.Children.Add(UiUtil.MakeCheckBox(fancyKaraokeItem, nameof(AdvancedEffectFancyKaraoke.RightToLeft)));

var activeGlowRow = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8, Margin = new Thickness(0, 4, 0, 0) };
activeGlowRow.Children.Add(new TextBlock { Text = Se.Language.Assa.AdvancedEffectFancyKaraokeGlow, VerticalAlignment = VerticalAlignment.Center, FontSize = 12 });
var applyGlowCheckBox = UiUtil.MakeCheckBox(fancyKaraokeItem, nameof(AdvancedEffectFancyKaraoke.ApplyGlow));
Expand All @@ -100,6 +111,7 @@ public AssaApplyAdvancedEffectWindow(AssaApplyAdvancedEffectViewModel vm)

var settingsStack = new StackPanel { Spacing = 2 };
settingsStack.Children.Add(autoDetectActiveWordRow);
settingsStack.Children.Add(rightToLeftRow);
settingsStack.Children.Add(activeGlowRow);
settingsStack.Children.Add(activeColorRow);
settingsStack.Children.Add(inactiveColorRow);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ public class AdvancedEffectFancyKaraoke : IAdvancedEffectDisplay
/// </summary>
public bool AutoDetectActiveWord { get; set; } = false;

/// <summary>
/// When true (and auto-sequencing by words), advance the active word from the end of the line
/// towards the start so the highlight progresses visually right-to-left. Auto-enabled for
/// right-to-left languages (Hebrew, Arabic, ...) where the default order runs backwards. (#12271)
/// </summary>
public bool RightToLeft { get; set; }

/// <summary>
/// Glow color used for the active word's 3c (ASS BGR).
/// </summary>
Expand Down Expand Up @@ -203,6 +210,10 @@ public List<SubtitleLineViewModel> ApplyEffect(
sb.Append(posTags);
}

// The active word for this step. In right-to-left mode advance from the last word
// towards the first so the highlight moves right-to-left visually.
var activeWordIndex = RightToLeft ? wordCount - 1 - w : w;

string? currentColor = null;
int localWordCounter = 0;

Expand Down Expand Up @@ -248,7 +259,7 @@ public List<SubtitleLineViewModel> ApplyEffect(

// Non-whitespace token = a word/punctuation group
// Only the current word is active
var colorTag = localWordCounter == w ? activeTags : inactiveTags;
var colorTag = localWordCounter == activeWordIndex ? activeTags : inactiveTags;
if (colorTag != currentColor)
{
sb.Append(colorTag);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public class AdvancedEffectKaraoke : IAdvancedEffectDisplay
private const string HighlightColor = @"{\1c&HFFFFFF&}";
private const string DimColor = @"{\1c&H808080&}";

/// <summary>
/// When true, reveal the characters from the end of the text towards the start so the
/// highlight progresses visually right-to-left. Auto-enabled for right-to-left languages
/// (Hebrew, Arabic, ...) where the default left-to-right reveal runs backwards. (#12271)
/// </summary>
public bool RightToLeft { get; set; }

public override string ToString() => Name;

public List<SubtitleLineViewModel> ApplyEffect(string header, List<SubtitleLineViewModel> subtitles, int width, int height, WavePeakData2? wavePeaks)
Expand Down Expand Up @@ -53,18 +60,22 @@ public List<SubtitleLineViewModel> ApplyEffect(string header, List<SubtitleLineV
: subtitle.StartTime.Add(TimeSpan.FromMilliseconds((i + 1) * msPerChar));
line.StartTime = start;
line.EndTime = end;
line.Text = BuildKaraokeText(chars, i);
line.Text = BuildKaraokeText(chars, i, RightToLeft);
result.Add(line);
}
}
return result;
}

private static string BuildKaraokeText(char[] chars, int highlightUpTo)
private static string BuildKaraokeText(char[] chars, int step, bool rightToLeft)
{
var sb = new StringBuilder(chars.Length * 12);
string? currentColor = null;

// step is 0-based, so step+1 characters are revealed. Left-to-right reveals the first
// step+1 characters; right-to-left reveals the last step+1 characters instead.
var revealFromEnd = chars.Length - 1 - step;

for (var j = 0; j < chars.Length; j++)
{
if (chars[j] == '\n')
Expand All @@ -74,7 +85,8 @@ private static string BuildKaraokeText(char[] chars, int highlightUpTo)
continue;
}

var color = j <= highlightUpTo ? HighlightColor : DimColor;
var highlight = rightToLeft ? j >= revealFromEnd : j <= step;
var color = highlight ? HighlightColor : DimColor;
if (color != currentColor)
{
sb.Append(color);
Expand Down
2 changes: 2 additions & 0 deletions src/ui/Logic/Config/Language/Assa/LanguageAssa.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ public class LanguageAssa
public string AdvancedEffectHeartsDescription { get; set; }
public string AdvancedEffectWordSpacing { get; set; }
public string AdvancedEffectWordSpacingDescription { get; set; }
public string AdvancedEffectKaraokeRightToLeft { get; set; }
public string AdvancedEffectFancyKaraokeAutoDetectActiveWord { get; set; }
public string AdvancedEffectFancyKaraokeGlow { get; set; }
public string AdvancedEffectFancyKaraokeActiveColor { get; set; }
Expand Down Expand Up @@ -401,6 +402,7 @@ public LanguageAssa()
AdvancedEffectHeartsDescription = "Bezier-drawn hearts in three shapes rain gently from the top of the screen, tumbling and fading throughout each subtitle";
AdvancedEffectWordSpacing = "Word spacing";
AdvancedEffectWordSpacingDescription = "Increases spacing between words using the \\fsp tag for better readability";
AdvancedEffectKaraokeRightToLeft = "Right-to-left";
AdvancedEffectFancyKaraokeAutoDetectActiveWord = "Auto-detect active word";
AdvancedEffectFancyKaraokeGlow = "Active word glow";
AdvancedEffectFancyKaraokeActiveColor = "Active color";
Expand Down