private static void ApplyGlitchKaraokeEffect(AssLine stepLine, AssSection singingSection)
        {
            if (singingSection.Text.Length == 0)
            {
                return;
            }

            ApplySimpleKaraokeEffect(singingSection);
            DateTime glitchEndTime = TimeUtil.Min(stepLine.Start.AddMilliseconds(70), stepLine.End);

            Util.CharacterRange[] charRanges = GetGlitchKaraokeCharacterRanges(singingSection.Text[0]);
            singingSection.Animations.Add(new GlitchingCharAnimation(stepLine.Start, glitchEndTime, charRanges));
        }
Esempio n. 2
0
        protected override void Assign(Section section)
        {
            base.Assign(section);

            AssSection assSection = (AssSection)section;

            SecondaryColor          = assSection.SecondaryColor;
            CurrentWordForeColor    = assSection.CurrentWordForeColor;
            CurrentWordOutlineColor = assSection.CurrentWordOutlineColor;
            CurrentWordShadowColor  = assSection.CurrentWordShadowColor;
            Duration = assSection.Duration;
            Animations.Clear();
            Animations.AddRange(assSection.Animations);
        }
Esempio n. 3
0
        internal void ApplyStyle(AssSection section, AssStyle style, AssStyleOptions options)
        {
            section.Font           = style.Font;
            section.Scale          = style.LineHeight / DefaultStyle.LineHeight;
            section.Bold           = style.Bold;
            section.Italic         = style.Italic;
            section.Underline      = style.Underline;
            section.ForeColor      = style.PrimaryColor;
            section.SecondaryColor = style.SecondaryColor;
            if (options?.IsKaraoke ?? false)
            {
                section.CurrentWordForeColor    = options.CurrentWordTextColor;
                section.CurrentWordOutlineColor = options.CurrentWordOutlineColor;
                section.CurrentWordShadowColor  = options.CurrentWordShadowColor;
            }
            else
            {
                section.CurrentWordForeColor    = Color.Empty;
                section.CurrentWordOutlineColor = Color.Empty;
                section.CurrentWordShadowColor  = Color.Empty;
            }

            section.BackColor = Color.Empty;
            section.ShadowColors.Clear();

            if (style.HasShadow)
            {
                foreach (ShadowType shadowType in options?.ShadowTypes ?? new List <ShadowType> {
                    ShadowType.SoftShadow
                })
                {
                    section.ShadowColors[shadowType] = style.ShadowColor;
                }
            }

            if (style.HasOutline)
            {
                if (style.OutlineIsBox)
                {
                    section.BackColor = style.OutlineColor;
                }
                else
                {
                    section.ShadowColors[ShadowType.Glow] = style.OutlineColor;
                }
            }

            section.Blur = 0;
        }
Esempio n. 4
0
        private static void ApplyGlitchKaraokeEffect(AssLine stepLine, List <AssSection> singingSections)
        {
            AssSection singingSection = singingSections.LastOrDefault(s => s.RubyPart == RubyPart.None || s.RubyPart == RubyPart.Text);

            if (singingSection == null || singingSection.Text.Length == 0)
            {
                return;
            }

            ApplySimpleKaraokeEffect(singingSections);
            DateTime glitchEndTime = TimeUtil.Min(stepLine.Start.AddMilliseconds(70), stepLine.End);

            Util.CharacterRange[] charRanges = GetGlitchKaraokeCharacterRanges(singingSection.Text[0]);
            singingSection.Animations.Add(new GlitchingCharAnimation(stepLine.Start, glitchEndTime, charRanges));
        }
Esempio n. 5
0
        public AssLine(Line line)
            : base(line)
        {
            if (line is AssLine || Sections.All(s => s.StartOffset == TimeSpan.Zero))
            {
                return;
            }

            for (int i = 0; i < Sections.Count - 1; i++)
            {
                ((AssSection)Sections[i]).Duration = Sections[i + 1].StartOffset - Sections[i].StartOffset;
            }

            AssSection lastSection = (AssSection)Sections.Last();

            lastSection.Duration = End - Start - lastSection.StartOffset;
        }
Esempio n. 6
0
        private static void CreateRubySections(AssLine line)
        {
            for (int sectionIdx = line.Sections.Count - 1; sectionIdx >= 0; sectionIdx--)
            {
                AssSection section = (AssSection)line.Sections[sectionIdx];
                if (section.RubyPosition == RubyPosition.None)
                {
                    continue;
                }

                MatchCollection matches = Regex.Matches(section.Text, @"\[(?<text>.+?)/(?<ruby>.+?)\]");
                if (matches.Count == 0)
                {
                    continue;
                }

                line.Sections.RemoveAt(sectionIdx);

                int interStartPos  = 0;
                int numSubSections = 0;
                foreach (Match match in matches)
                {
                    if (match.Index > interStartPos)
                    {
                        InsertRubySection(line, section, section.Text.Substring(interStartPos, match.Index - interStartPos), RubyPart.None, sectionIdx, ref numSubSections);
                    }

                    InsertRubySection(line, section, match.Groups["text"].Value, RubyPart.Text, sectionIdx, ref numSubSections);
                    InsertRubySection(line, section, "(", RubyPart.Parenthesis, sectionIdx, ref numSubSections);
                    InsertRubySection(line, section, match.Groups["ruby"].Value, section.RubyPosition == RubyPosition.Below ? RubyPart.RubyBelow : RubyPart.RubyAbove, sectionIdx, ref numSubSections);
                    InsertRubySection(line, section, ")", RubyPart.Parenthesis, sectionIdx, ref numSubSections);

                    interStartPos = match.Index + match.Length;
                }

                if (interStartPos < section.Text.Length)
                {
                    InsertRubySection(line, section, section.Text.Substring(interStartPos), RubyPart.None, sectionIdx, ref numSubSections);
                }

                ((AssSection)line.Sections[sectionIdx]).Duration = section.Duration;
            }
        }
        private static void ApplySimpleKaraokeEffect(AssSection singingSection)
        {
            if (!singingSection.CurrentWordForeColor.IsEmpty)
            {
                singingSection.ForeColor = singingSection.CurrentWordForeColor;
            }

            if (!singingSection.CurrentWordShadowColor.IsEmpty)
            {
                foreach (ShadowType shadowType in singingSection.ShadowColors.Keys.ToList())
                {
                    singingSection.ShadowColors[shadowType] = singingSection.CurrentWordShadowColor;
                }
            }

            if (!singingSection.CurrentWordOutlineColor.IsEmpty && singingSection.ShadowColors.ContainsKey(ShadowType.Glow))
            {
                singingSection.ShadowColors[ShadowType.Glow] = singingSection.CurrentWordOutlineColor;
            }
        }
        private static void AddKaraokeEffects(AssLine originalLine, AssLine stepLine, SortedList <TimeSpan, int> activeSectionsPerStep, int stepIdx)
        {
            int        numActiveSections = activeSectionsPerStep.Values[stepIdx];
            AssSection singingSection    = (AssSection)stepLine.Sections[numActiveSections - 1];

            switch (stepLine.KaraokeType)
            {
            case KaraokeType.Simple:
                ApplySimpleKaraokeEffect(singingSection);
                break;

            case KaraokeType.Fade:
                ApplyFadeKaraokeEffect(originalLine, stepLine, activeSectionsPerStep, stepIdx);
                break;

            case KaraokeType.Glitch:
                ApplyGlitchKaraokeEffect(stepLine, singingSection);
                break;
            }
        }
        private static void MoveLineBreaksToSeparateSections(AssLine line)
        {
            for (int sectionIdx = line.Sections.Count - 1; sectionIdx >= 0; sectionIdx--)
            {
                AssSection section = (AssSection)line.Sections[sectionIdx];
                Match      match   = Regex.Match(section.Text, @"((?:\r\n)+|[^\r\n]+)+");
                if (!match.Success || match.Groups[1].Captures.Count == 1)
                {
                    continue;
                }

                line.Sections.RemoveAt(sectionIdx);
                for (int i = 0; i < match.Groups[1].Captures.Count; i++)
                {
                    AssSection subSection = (AssSection)section.Clone();
                    subSection.Text = match.Groups[1].Captures[i].Value;
                    line.Sections.Insert(sectionIdx + i, subSection);
                }
            }
        }
Esempio n. 10
0
        // Aegisub's background boxes can have padding just like on YouTube, and the horizontal and vertical padding
        // can even be different. The big downside of this feature, however, is that background boxes of adjacent
        // sections overlap each other which looks like a mess. Therefore we don't use it at all (all the styles
        // have an outline thickness of 0.01) and instead add a space at the start and end of each line of text
        // to emulate the YouTube horizontal padding manually.
        private static void EmulateBorders(AssLine line)
        {
            for (int i = 0; i < line.Sections.Count; i++)
            {
                AssSection section = (AssSection)line.Sections[i];
                if (Regex.IsMatch(section.Text, @"^[\r\n]+$"))
                {
                    continue;
                }

                if (i == 0 || line.Sections[i - 1].Text.EndsWith("\r\n"))
                {
                    section.Text = " " + section.Text;
                }

                if (i == line.Sections.Count - 1 || line.Sections[i + 1].Text.StartsWith("\r\n"))
                {
                    section.Text = section.Text + " ";
                }
            }
        }
Esempio n. 11
0
        private static void ApplyFadeOutKaraokeEffect(AssLine originalLine, AssLine stepLine, SortedList <TimeSpan, int> activeSectionsPerStep, int stepIdx)
        {
            int stepFirstSectionIdx = 0;

            for (int prevStepIdx = 0; prevStepIdx < stepIdx; prevStepIdx++)
            {
                DateTime fadeStartTime      = TimeUtil.SnapTimeToFrame((originalLine.Start + activeSectionsPerStep.Keys[prevStepIdx + 1]).AddMilliseconds(20));
                DateTime fadeEndTime        = fadeStartTime.AddMilliseconds(1000);
                int      stepLastSectionIdx = activeSectionsPerStep.Values[prevStepIdx] - 1;
                for (int sectionIdx = stepFirstSectionIdx; sectionIdx <= stepLastSectionIdx; sectionIdx++)
                {
                    AssSection section = (AssSection)stepLine.Sections[sectionIdx];
                    if (!section.CurrentWordForeColor.IsEmpty && section.CurrentWordForeColor != section.ForeColor)
                    {
                        section.Animations.Add(new ForeColorAnimation(fadeStartTime, section.CurrentWordForeColor, fadeEndTime, section.ForeColor));
                    }

                    if (!section.CurrentWordShadowColor.IsEmpty)
                    {
                        foreach (KeyValuePair <ShadowType, Color> shadowColor in section.ShadowColors)
                        {
                            if (section.CurrentWordShadowColor != shadowColor.Value)
                            {
                                section.Animations.Add(new ShadowColorAnimation(shadowColor.Key, fadeStartTime, section.CurrentWordShadowColor, fadeEndTime, shadowColor.Value));
                            }
                        }
                    }

                    if (!section.CurrentWordOutlineColor.IsEmpty && section.CurrentWordOutlineColor != section.ShadowColors.GetOrDefault(ShadowType.Glow))
                    {
                        section.Animations.Add(new ShadowColorAnimation(ShadowType.Glow, fadeStartTime, section.CurrentWordOutlineColor, fadeEndTime, section.ShadowColors[ShadowType.Glow]));
                    }
                }

                stepFirstSectionIdx = stepLastSectionIdx + 1;
            }
        }
Esempio n. 12
0
        private static void ApplyFadeInKaraokeEffect(AssLine stepLine, AssSection singingSection)
        {
            DateTime fadeEndTime = TimeUtil.Min(stepLine.Start.AddMilliseconds(500), stepLine.End);

            if (singingSection.CurrentWordForeColor.IsEmpty)
            {
                if (singingSection.ForeColor != singingSection.SecondaryColor)
                {
                    singingSection.Animations.Add(new ForeColorAnimation(stepLine.Start, singingSection.SecondaryColor, fadeEndTime, singingSection.ForeColor));
                }
            }
            else
            {
                if (singingSection.CurrentWordForeColor != singingSection.SecondaryColor)
                {
                    singingSection.Animations.Add(new ForeColorAnimation(stepLine.Start, singingSection.SecondaryColor, fadeEndTime, singingSection.CurrentWordForeColor));
                }
            }

            if (!singingSection.CurrentWordShadowColor.IsEmpty)
            {
                foreach (KeyValuePair <ShadowType, Color> shadowColor in singingSection.ShadowColors)
                {
                    if (singingSection.CurrentWordShadowColor != shadowColor.Value)
                    {
                        singingSection.Animations.Add(new ShadowColorAnimation(shadowColor.Key, stepLine.Start, shadowColor.Value, fadeEndTime, singingSection.CurrentWordShadowColor));
                    }
                }
            }

            if (!singingSection.CurrentWordOutlineColor.IsEmpty && singingSection.CurrentWordOutlineColor != singingSection.ShadowColors.GetOrDefault(ShadowType.Glow))
            {
                singingSection.Animations.Add(new ShadowColorAnimation(
                                                  ShadowType.Glow, stepLine.Start, singingSection.ShadowColors[ShadowType.Glow], fadeEndTime, singingSection.CurrentWordOutlineColor));
            }
        }
Esempio n. 13
0
        private void AppendSectionTags(AssSection section, AssSection prevSection, AssLineContentBuilder lineContent)
        {
            if (section.Font != prevSection.Font)
            {
                lineContent.AppendTag("fn", section.Font);
            }

            float prevLineHeight = ScaleToLineHeight(prevSection.Font, prevSection.Scale);
            float lineHeight     = ScaleToLineHeight(section.Font, section.Scale);

            if (lineHeight != prevLineHeight)
            {
                lineContent.AppendTag("fs", lineHeight);
            }

            if (section.Bold != prevSection.Bold)
            {
                lineContent.AppendTag("b", section.Bold);
            }

            if (section.Italic != prevSection.Italic)
            {
                lineContent.AppendTag("i", section.Italic);
            }

            if (section.Underline != prevSection.Underline)
            {
                lineContent.AppendTag("u", section.Underline);
            }

            AppendColorTags("c", "1a", prevSection.ForeColor, section.ForeColor, lineContent);
            AppendColorTags("2c", "2a", prevSection.SecondaryColor, section.SecondaryColor, lineContent);

            if (section.BackColor.A > 0)
            {
                AppendColorTags("3c", "3a", prevSection.BackColor, section.BackColor, lineContent);
                if (prevSection.ShadowColors.Count == 1 && section.ShadowColors.Count == 1)
                {
                    AppendColorTags("4c", "4a", prevSection.ShadowColors.Values.First(), section.ShadowColors.Values.First(), lineContent);
                }
            }
            else if (prevSection.ShadowColors.Count == 1 && section.ShadowColors.Count == 1)
            {
                if (section.ShadowColors.Keys.First() == ShadowType.Glow)
                {
                    AppendColorTags("3c", "3a", prevSection.ShadowColors.Values.First(), section.ShadowColors.Values.First(), lineContent);
                }
                else
                {
                    AppendColorTags("4c", "4a", prevSection.ShadowColors.Values.First(), section.ShadowColors.Values.First(), lineContent);
                }
            }

            if (section.Offset != prevSection.Offset)
            {
                lineContent.AppendTag(
                    section.Offset switch
                {
                    OffsetType.Subscript => "ytsub",
                    OffsetType.Superscript => "ytsup",
                    OffsetType.Regular => "ytsur",
                }
Esempio n. 14
0
        protected virtual void WriteLine(AssLine line, StreamWriter writer)
        {
            if (line.Sections.Count == 0)
            {
                return;
            }

            AssStyle style = GetStyleMatchingStructure((AssSection)line.Sections[0]);

            WriteLineMetadata(line, style, writer);

            AssSection prevSection = new AssSection();
            AssStyle   prevStyle   = style;

            ApplyStyle(prevSection, prevStyle);

            AssLineContentBuilder lineContent = new AssLineContentBuilder();

            AppendLineTags(line, style, lineContent);

            RubyPart currentRubyPosition = RubyPart.None;

            for (int i = 0; i < line.Sections.Count; i++)
            {
                AssSection section = (AssSection)line.Sections[i];
                style = GetStyleMatchingStructure(section);
                if (style != prevStyle || (currentRubyPosition != RubyPart.None && section.RubyPart == RubyPart.None))
                {
                    lineContent.AppendTag("r", style);
                    currentRubyPosition = RubyPart.None;
                    prevSection         = (AssSection)prevSection.Clone();
                    ApplyStyle(prevSection, style);
                }

                AppendSectionTags(section, prevSection, lineContent);

                if (section.RubyPart == RubyPart.Text)
                {
                    RubyPart rubyPart;
                    if (i + 4 > line.Sections.Count || ((rubyPart = line.Sections[i + 2].RubyPart) != RubyPart.RubyAbove && rubyPart != RubyPart.RubyBelow))
                    {
                        throw new InvalidDataException("Invalid ruby sequence");
                    }

                    if (rubyPart != currentRubyPosition)
                    {
                        lineContent.AppendTag("ytruby", rubyPart == RubyPart.RubyAbove ? 8 : 2);
                        currentRubyPosition = rubyPart;
                    }
                    lineContent.AppendText($"[{section.Text}/{line.Sections[i + 2].Text}]");
                    i += 3;
                }
                else
                {
                    lineContent.AppendText(section.Text);
                }

                prevSection = section;
                prevStyle   = style;
            }

            writer.WriteLine(lineContent);
        }
Esempio n. 15
0
 internal void ApplyStyle(AssSection section, AssStyle style)
 {
     ApplyStyle(section, style, GetStyleOptions(style.Name));
 }