public override void Apply(AssLine line, AssSection section, float t) { float x = Interpolate(StartPosition.X, EndPosition.X, t); float y = Interpolate(StartPosition.Y, EndPosition.Y, t); line.Position = new PointF(x, y); }
public override IEnumerable <AssLine> Apply(AssKaraokeStepContext context) { base.Apply(context); int startCursorStepIdx = (int)(context.StepLine.Start - context.OriginalLine.Start).TotalMilliseconds / _intervalMs; int endCursorStepIdx = (int)(context.StepLine.End - context.OriginalLine.Start).TotalMilliseconds / _intervalMs; for (int cursorStepIdx = startCursorStepIdx; cursorStepIdx <= endCursorStepIdx; cursorStepIdx++) { AssLine cursorStepLine = (AssLine)context.StepLine.Clone(); if (cursorStepIdx > startCursorStepIdx) { cursorStepLine.Start = TimeUtil.RoundTimeToFrameCenter(context.OriginalLine.Start.AddMilliseconds(cursorStepIdx * _intervalMs)); } if (cursorStepIdx < endCursorStepIdx) { cursorStepLine.End = TimeUtil.RoundTimeToFrameCenter(context.OriginalLine.Start.AddMilliseconds((cursorStepIdx + 1) * _intervalMs)); } if (cursorStepLine.Start == cursorStepLine.End) { continue; } int cursorSectionIdx = _beforeSinging ? context.NumActiveSections - context.SingingSections.Count : context.NumActiveSections; AssSection initialFormatting = (AssSection)cursorStepLine.Sections[Math.Max(cursorSectionIdx - 1, 0)]; List <Section> cursorSections = GenerateCursor(context.Document, initialFormatting, _cursors[cursorStepIdx % _cursors.Count]); cursorStepLine.Sections.InsertRange(cursorSectionIdx, cursorSections); yield return(cursorStepLine); } }
public override void Handle(AssTagContext context, string arg) { List <float> args = ParseFloatList(arg); if (args == null || args.Count < 4) { return; } AssLine line = context.Line; PointF startPos = new PointF(args[0], args[1]); PointF endPos = new PointF(args[2], args[3]); DateTime startTime = line.Start; DateTime endTime = line.End; if (args.Count >= 6) { startTime = line.Start.AddMilliseconds(args[4]); endTime = line.Start.AddMilliseconds(args[5]); } if (endTime > startTime) { line.Animations.Add(new MoveAnimation(startTime, startPos, endTime, endPos)); } }
public override void Handle(AssTagContext context, string arg) { List <float> times = ParseNumberList(arg); if (times == null || times.Count != 2) { return; } AssLine line = context.Line; DateTime fadeInStartTime = line.Start; DateTime fadeInEndTime = line.Start.AddMilliseconds(times[0]); DateTime fadeOutStartTime = line.End.AddMilliseconds(-times[1]); DateTime fadeOutEndTime = line.End; if (fadeInEndTime > fadeInStartTime) { line.Animations.Add(new FadeAnimation(fadeInStartTime, 0, fadeInEndTime, 255)); } if (fadeOutEndTime > fadeOutStartTime) { line.Animations.Add(new FadeAnimation(fadeOutStartTime, 255, fadeOutEndTime, 0)); } }
public static IEnumerable<AssLine> Expand(AssDocument document, AssLine originalLine) { List<AnimationWithSectionIndex> anims = GetAnimationsWithSectionIndex(originalLine); if (anims.Count == 0) { yield return originalLine; yield break; } SortedList<TimeRange, List<AnimationWithSectionIndex>> animClusters = ClusterAnimations(originalLine, anims); AssLine lastLine = CreateInitialLine(originalLine, anims); if (animClusters.Count == 0 || animClusters.Keys[0].Start > lastLine.Start) yield return lastLine; for (int i = 0; i < animClusters.Count; i++) { TimeRange clusterRange = animClusters.Keys[i]; List<AnimationWithSectionIndex> clusterAnims = animClusters.Values[i]; foreach (AssLine frameLine in CreateFrameLines(document, lastLine, clusterRange, clusterAnims)) { lastLine.End = frameLine.Start; yield return lastLine = frameLine; } DateTime interAnimStart = clusterRange.End; DateTime interAnimEnd = i < animClusters.Count - 1 ? animClusters.Keys[i + 1].Start : originalLine.End; if (interAnimEnd > interAnimStart) yield return lastLine = CreatePostAnimationClusterLine(originalLine, lastLine, interAnimStart, interAnimEnd, clusterAnims); } lastLine.End = originalLine.End; }
private static void ResetText(AssLine frameLine, AssLine originalLine) { for (int i = 0; i < frameLine.Sections.Count; i++) { frameLine.Sections[i].Text = originalLine.Sections[i].Text; } }
public override void Handle(AssTagContext context, string arg) { List <float> args = ParseFloatList(arg); if (args == null || args.Count != 7) { return; } AssLine line = context.Line; int initialAlpha = 255 - (int)args[0]; int midAlpha = 255 - (int)args[1]; int finalAlpha = 255 - (int)args[2]; DateTime fadeInStartTime = line.Start.AddMilliseconds(args[3]); DateTime fadeInEndTime = line.Start.AddMilliseconds(args[4]); DateTime fadeOutStartTime = line.Start.AddMilliseconds(args[5]); DateTime fadeOutEndTime = line.Start.AddMilliseconds(args[6]); if (fadeInEndTime > fadeInStartTime) { line.Animations.Add(new FadeAnimation(fadeInStartTime, initialAlpha, fadeInEndTime, midAlpha)); } if (fadeOutEndTime > fadeOutStartTime) { line.Animations.Add(new FadeAnimation(fadeOutStartTime, midAlpha, fadeOutEndTime, finalAlpha)); } }
private static List<AnimationWithSectionIndex> GetAnimationsWithSectionIndex(AssLine line) { List<AnimationWithSectionIndex> animations = new List<AnimationWithSectionIndex>(); foreach (Animation anim in line.Animations.Where(a => a is MoveAnimation)) { animations.Add(new AnimationWithSectionIndex(anim, -1)); } foreach (Animation anim in line.Animations.Where(a => !(a is MoveAnimation))) { animations.Add(new AnimationWithSectionIndex(anim, -1)); } for (int i = 0; i < line.Sections.Count; i++) { AssSection section = (AssSection)line.Sections[i]; foreach (Animation anim in section.Animations) { animations.Add(new AnimationWithSectionIndex(anim, i)); } } return animations; }
public override void Apply(AssLine line, AssSection section, float t) { if (t > 0 && t < 1) { section.Text = GetRandomChar().ToString(); } }
public override void Apply(AssLine line, AssSection section, float t) { if (t <= 0 || t >= 1 || line.Position == null) { return; } line.Position = new PointF( line.Position.Value.X + Radius.Width * ((float)_random.NextDouble() * 2 - 1.0f), line.Position.Value.Y + Radius.Height * ((float)_random.NextDouble() * 2 - 1.0f) ); }
private static AssLine CreatePostAnimationClusterLine(AssLine originalLine, AssLine lastLine, DateTime start, DateTime end, List<AnimationWithSectionIndex> animsWithSection) { AssLine newLine = (AssLine)lastLine.Clone(); newLine.Start = start; newLine.End = end; foreach (AnimationWithSectionIndex animWithSection in animsWithSection.OrderBy(a => a.Animation.EndTime)) { if (animWithSection.Animation.AffectsText) ResetText(newLine, originalLine); ApplyAnimation(newLine, animWithSection, 1); } return newLine; }
public override void Apply(AssLine line, AssSection section, float t) { int alpha = Interpolate(StartAlpha, EndAlpha, t); switch (line) { case AssLine assLine: assLine.Alpha = alpha; break; default: throw new NotSupportedException(); } }
public override void Apply(AssLine line, AssSection section, float t) { if (t > 0 && t < 1) { line.Position = new PointF( Center.X + Radius.Width * ((float)_random.NextDouble() * 2 - 1.0f), Center.Y + Radius.Height * ((float)_random.NextDouble() * 2 - 1.0f) ); } else { line.Position = Center; } }
private static IEnumerable <AssLine> CreateChromaLines(AssLine originalLine, List <Color> colors, PointF center, int maxOffsetX, int maxOffsetY, int durationMs, bool moveIn) { if (originalLine.Sections.Count == 0) { yield break; } for (int i = 0; i < colors.Count; i++) { if (colors[i].A == 0) { continue; } AssLine chromaLine = new AssLine(originalLine.Start, originalLine.End) { AnchorPoint = originalLine.AnchorPoint }; chromaLine.Sections.Add( new AssSection(originalLine.Text) { ForeColor = colors[i], Bold = originalLine.Sections[0].Bold, Italic = originalLine.Sections[0].Italic, Underline = originalLine.Sections[0].Underline } ); float offsetFactor = colors.Count > 1 ? (float)i / (colors.Count - 1) : 0.5f; float offsetX = offsetFactor * (-maxOffsetX * 2) + maxOffsetX; float offsetY = offsetFactor * (-maxOffsetY * 2) + maxOffsetY; PointF farPosition = new PointF(center.X + offsetX, center.Y + offsetY); PointF nearPosition = new PointF(center.X + offsetX / 5, center.Y + offsetY / 5); if (moveIn) { chromaLine.End = TimeUtil.SnapTimeToFrame(originalLine.Start.AddMilliseconds(durationMs)).AddMilliseconds(32); chromaLine.Animations.Add(new MoveAnimation(chromaLine.Start, farPosition, chromaLine.End, nearPosition)); } else { chromaLine.Start = TimeUtil.SnapTimeToFrame(originalLine.End.AddMilliseconds(-durationMs)); chromaLine.Animations.Add(new MoveAnimation(chromaLine.Start, nearPosition, chromaLine.End, farPosition)); } yield return(chromaLine); } }
private static AssLine CreateInitialLine(AssLine originalLine, List <AnimationWithSectionIndex> anims) { AssLine newLine = (AssLine)originalLine.Clone(); foreach (AnimationWithSectionIndex anim in anims.Where(a => a.Animation.EndTime < originalLine.Start) .OrderBy(a => a.Animation.EndTime)) { ApplyAnimation(newLine, anim, 1); } foreach (AnimationWithSectionIndex anim in anims.Where(a => a.Animation.AffectsPast && a.Animation.StartTime >= originalLine.Start) .OrderByDescending(a => a.Animation.StartTime)) { ApplyAnimation(newLine, anim, 0); } return(newLine); }
private static IEnumerable <AssLine> CreateFrameLines(AssDocument document, AssLine originalLine, TimeRange timeRange, List <AnimationWithSectionIndex> animations) { int rangeStartFrame = TimeUtil.StartTimeToFrame(timeRange.Start); int rangeEndFrame = TimeUtil.EndTimeToFrame(timeRange.End); const int frameStepSize = 2; int subStepFrames = (rangeEndFrame + 1 - rangeStartFrame) % frameStepSize; int lastIterationFrame = rangeEndFrame + 1 - subStepFrames - frameStepSize; bool needTextReset = animations.Any(a => a.Animation.AffectsText); AssLine frameLine = originalLine; for (int frame = rangeStartFrame; frame <= lastIterationFrame; frame += frameStepSize) { frameLine = (AssLine)frameLine.Clone(); frameLine.Start = TimeUtil.FrameToStartTime(frame); frameLine.End = frame < lastIterationFrame?TimeUtil.FrameToEndTime(frame + frameStepSize - 1) : timeRange.End; frameLine.Position = originalLine.Position ?? document.GetDefaultPosition(originalLine.AnchorPoint); if (needTextReset) { ResetText(frameLine, originalLine); } float interpFrame = frame + (frameStepSize - 1) / 2.0f; foreach (AnimationWithSectionIndex animWithSection in animations) { int animStartFrame = TimeUtil.StartTimeToFrame(animWithSection.Animation.StartTime); int animEndFrame = TimeUtil.EndTimeToFrame(animWithSection.Animation.EndTime); if (interpFrame >= animStartFrame && interpFrame < animEndFrame) { float t = (interpFrame - animStartFrame) / (animEndFrame - animStartFrame); ApplyAnimation(frameLine, animWithSection, t); } else if (interpFrame >= animEndFrame && interpFrame < animEndFrame + frameStepSize) { ApplyAnimation(frameLine, animWithSection, 1); } } yield return(frameLine); } }
private List <Section> GenerateCursor(AssDocument doc, AssSection initialFormatting, string cursor) { AssSection section = (AssSection)initialFormatting.Clone(); section.Text = string.Empty; AssLine line = new AssLine(SubtitleDocument.TimeBase, SubtitleDocument.TimeBase); AssTagContext context = new AssTagContext { Document = doc, Line = line, Section = section }; doc.CreateTagSections(line, cursor, context); return(line.Sections); }
private static IEnumerable <AssLine> CreateFrameLines(AssLine originalLine, TimeRange timeRange, List <AnimationWithSectionIndex> animations) { int rangeStartFrame = TimeUtil.TimeToFrame(timeRange.Start); int rangeEndFrame = TimeUtil.TimeToFrame(timeRange.End); const int frameStepSize = 2; int lastIterationFrame = rangeStartFrame + (rangeEndFrame - 1 - rangeStartFrame) / frameStepSize * frameStepSize; bool needTextReset = animations.Any(a => a.Animation.AffectsText); AssLine frameLine = originalLine; for (int frame = rangeStartFrame; frame <= lastIterationFrame; frame += frameStepSize) { frameLine = (AssLine)frameLine.Clone(); frameLine.Start = TimeUtil.FrameToTime(frame); frameLine.End = frame < lastIterationFrame?TimeUtil.FrameToTime(frame + frameStepSize) : timeRange.End; if (needTextReset) { ResetText(frameLine, originalLine); } int interpFrame = frame + frameStepSize / 2; foreach (AnimationWithSectionIndex animWithSection in animations) { int animStartFrame = TimeUtil.TimeToFrame(animWithSection.Animation.StartTime); int animEndFrame = TimeUtil.TimeToFrame(animWithSection.Animation.EndTime); if (interpFrame >= animStartFrame && interpFrame < animEndFrame) { float t = (float)(interpFrame - animStartFrame) / (animEndFrame - animStartFrame); ApplyAnimation(frameLine, animWithSection, t); } else if (interpFrame >= animEndFrame && interpFrame < animEndFrame + frameStepSize) { ApplyAnimation(frameLine, animWithSection, 1); } } yield return(frameLine); } }
private static IEnumerable <AssLine> CreateChromaLines(AssLine originalLine, List <Color> colors, PointF center, int maxOffsetX, int maxOffsetY, int durationMs, bool moveIn) { if (originalLine.Sections.Count == 0) { yield break; } for (int i = 0; i < colors.Count; i++) { if (colors[i].A == 0) { continue; } AssLine chromaLine = (AssLine)originalLine.Clone(); foreach (Section section in chromaLine.Sections) { int alpha = (int)(section.ForeColor.A * (colors[i].A / 255.0f)); section.ForeColor = Color.FromArgb(alpha, colors[i].R, colors[i].G, colors[i].B); section.BackColor = Color.Empty; section.ShadowColors.Clear(); } float offsetFactor = colors.Count > 1 ? (float)i / (colors.Count - 1) : 0.5f; float offsetX = offsetFactor * (-maxOffsetX * 2) + maxOffsetX; float offsetY = offsetFactor * (-maxOffsetY * 2) + maxOffsetY; PointF farPosition = new PointF(center.X + offsetX, center.Y + offsetY); PointF nearPosition = new PointF(center.X + offsetX / 5, center.Y + offsetY / 5); if (moveIn) { chromaLine.End = TimeUtil.RoundTimeToFrameCenter(originalLine.Start.AddMilliseconds(durationMs)); chromaLine.Animations.Add(new MoveAnimation(chromaLine.Start, farPosition, chromaLine.End, nearPosition)); } else { chromaLine.Start = TimeUtil.RoundTimeToFrameCenter(originalLine.End.AddMilliseconds(-durationMs)); chromaLine.Animations.Add(new MoveAnimation(chromaLine.Start, nearPosition, chromaLine.End, farPosition)); } yield return(chromaLine); } }
private static void ApplyFadeInKaraokeEffect(AssLine stepLine, List <AssSection> singingSections) { DateTime fadeEndTime = TimeUtil.Min(stepLine.Start.AddMilliseconds(500), stepLine.End); foreach (AssSection singingSection in singingSections) { if (singingSection.CurrentWordForeColor.IsEmpty) { if (singingSection.ForeColor != singingSection.SecondaryColor) { singingSection.Animations.Add(new ForeColorAnimation(stepLine.Start, singingSection.SecondaryColor, fadeEndTime, singingSection.ForeColor, 1)); } } else { if (singingSection.CurrentWordForeColor != singingSection.SecondaryColor) { singingSection.Animations.Add(new ForeColorAnimation(stepLine.Start, singingSection.SecondaryColor, fadeEndTime, singingSection.CurrentWordForeColor, 1)); } } 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, 1)); } } } 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, 1)); } } }
public override void Handle(AssTagContext context, string arg) { if (!TryParseArgs(arg, out List <Color> colors, out int alpha, out int maxOffsetX, out int maxOffsetY, out int chromaInMs, out int chromeOutMs)) { return; } context.PostProcessors.Add( () => { AssLine originalLine = context.Line; PointF center = originalLine.Position ?? context.Document.GetDefaultPosition(originalLine.AnchorPoint); List <AssLine> chromaLines = new List <AssLine>(); if (colors.Count == 0) { Color baseColor = context.Line.Sections.Count > 0 ? context.Line.Sections[0].ForeColor : Color.White; colors.Add(Color.FromArgb((int)(baseColor.R * alpha / 255.0f), 255, 0, 0)); colors.Add(Color.FromArgb((int)(baseColor.G * alpha / 255.0f), 0, 255, 0)); colors.Add(Color.FromArgb((int)(baseColor.B * alpha / 255.0f), 0, 0, 255)); } if (chromaInMs > 0) { chromaLines.AddRange(CreateChromaLines(originalLine, colors, center, maxOffsetX, maxOffsetY, chromaInMs, true)); originalLine.Start = TimeUtil.SnapTimeToFrame(originalLine.Start.AddMilliseconds(chromaInMs)); } if (chromeOutMs > 0) { chromaLines.AddRange(CreateChromaLines(originalLine, colors, center, maxOffsetX, maxOffsetY, chromeOutMs, false)); originalLine.End = TimeUtil.SnapTimeToFrame(originalLine.End.AddMilliseconds(-chromeOutMs)); } return(chromaLines); } ); }
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 = originalLine.Start + activeSectionsPerStep.Keys[prevStepIdx + 1]; 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, 1)); } 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, 1)); } } } 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], 1)); } } stepFirstSectionIdx = stepLastSectionIdx + 1; } }
public override void Apply(AssLine line, AssSection section, float t) { line.Alpha = Interpolate(StartAlpha, EndAlpha, t); }
public override void Apply(AssLine line, AssSection section, float t) { section.Scale = Interpolate(StartScale, EndScale, t); }
private static SortedList<TimeRange, List<AnimationWithSectionIndex>> ClusterAnimations(AssLine line, List<AnimationWithSectionIndex> allAnims) { List<TimeRange> clusterRanges = GetAnimationClusterTimeRanges(allAnims.Select(a => a.Animation)); TimeRange lineRange = new TimeRange(line.Start, line.End); var clusters = new SortedList<TimeRange, List<AnimationWithSectionIndex>>(); foreach (TimeRange clusterRange in clusterRanges) { if (!clusterRange.Intersects(lineRange)) continue; List<AnimationWithSectionIndex> clusterAnims = new List<AnimationWithSectionIndex>(); foreach (AnimationWithSectionIndex animWithSection in allAnims) { if (clusterRange.Contains(animWithSection.Animation.StartTime) && animWithSection.Animation.EndTime > animWithSection.Animation.StartTime) clusterAnims.Add(animWithSection); } clusterRange.IntersectWith(lineRange); clusterRange.Start = TimeUtil.RoundTimeToFrameCenter(clusterRange.Start); clusterRange.End = TimeUtil.RoundTimeToFrameCenter(clusterRange.End); clusters.FetchValue(clusterRange, () => new List<AnimationWithSectionIndex>()).AddRange(clusterAnims); } return clusters; }
private static SortedList <TimeRange, List <AnimationWithSectionIndex> > ClusterAnimations(AssLine line, List <AnimationWithSectionIndex> allAnims) { List <TimeRange> clusterRanges = GetAnimationClusterTimeRanges(allAnims.Select(a => a.Animation)); TimeRange lineRange = new TimeRange(line.Start, line.End); var clusters = new SortedList <TimeRange, List <AnimationWithSectionIndex> >(); foreach (TimeRange clusterRange in clusterRanges) { if (!clusterRange.Intersects(lineRange)) { continue; } List <AnimationWithSectionIndex> clusterAnims = new List <AnimationWithSectionIndex>(); foreach (AnimationWithSectionIndex animWithSection in allAnims) { if (clusterRange.Contains(animWithSection.Animation.StartTime) && animWithSection.Animation.EndTime > animWithSection.Animation.StartTime) { clusterAnims.Add(animWithSection); } } clusterRange.IntersectWith(lineRange); clusterRange.Start = TimeUtil.SnapTimeToFrame(clusterRange.Start.AddMilliseconds(32)); clusterRange.End = TimeUtil.SnapTimeToFrame(clusterRange.End).AddMilliseconds(32); clusters.Add(clusterRange, clusterAnims); } return(clusters); }
public override void Apply(AssLine line, AssSection section, float t) { section.BackColor = GetColor(t); }
public abstract void Apply(AssLine line, AssSection section, float t);
public override void Apply(AssLine line, AssSection section, float t) { section.ShadowColors[ShadowType] = GetColor(t); }
private static void ApplyAnimation(AssLine line, AnimationWithSectionIndex animWithSectionIdx, float t) { animWithSectionIdx.Animation.Apply(line, animWithSectionIdx.SectionIndex >= 0 ? (AssSection)line.Sections[animWithSectionIdx.SectionIndex] : null, t); }