JD2022-TU1/main/tools/framework/JD.Application.TeaBox/View/Tracks/SongBackground.cs

169 lines
6.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using JD.Application.View.Timeline;
using JD.Application.View.WindowLess;
using JD.Audio.Annotations;
using JD.Kernel.Structs;
using TeaBox;
namespace JD.Application.View.Tracks
{
/// <summary>
/// Paints the color in the background of the tracks.
/// </summary>
public class SongBackground : OverlappingTrack
{
/// <summary>
/// Constructor.
/// </summary>
public SongBackground(global::JD.Sequencers.Times.Timeline timeline)
: base(timeline)
{
}
/// <summary>
/// When set, the colors are only paint inside the active area.
/// </summary>
public Range<BeatUnit> ActiveArea
{
get;
set;
}
protected override void OnPaint(PaintEventArgs e)
{
if (Wave != null)
{
PaintSongParts(e, Bounds, Wave.Annotations, Timeline);
PaintVideoSections(e, Bounds, Wave.Annotations, Timeline);
var bounds = Bounds;
bounds.Height--; // Do not paint over the track line.
bounds.Intersect(e.ClipRectangle);
BeatsGrid.PaintMeasure(e.Graphics, bounds, Timeline);
base.OnPaint(e);
}
}
/// <summary>
/// Paints each part of the song with a light gradient that spans through all tracks.
/// </summary>
public virtual void PaintSongParts(PaintEventArgs eventArgs, Rectangle bounds, AnnotationCollection annotations, global::JD.Sequencers.Times.Timeline timeline, int alpha = 255)
{
var sections = annotations.SongSections;
for (int i = 0; i < sections.Count; i++)
{
SongSection section = sections[i];
BeatUnit end = timeline.Wave().UnitConverter.End(section);
int colorModulation = CountSameSectionType(i, sections)%2 == 0 ? 0 : 35;
Color baseColor = sections[i].Type.Color().Add(colorModulation);
Color colorWithAlpha = Color.FromArgb(alpha, baseColor);
var sectionRange = new Range<BeatUnit>(section.Time, end);
sectionRange = ClampToActiveArea(sectionRange);
if (!sectionRange.IsEmpty)
{
PaintSection(eventArgs, timeline, bounds, colorWithAlpha, sectionRange);
}
}
PaintActiveArea(eventArgs, bounds, timeline);
}
/// <summary>
/// Paints the start beat/end beat section on the track
/// </summary>
private void PaintVideoSections(PaintEventArgs eventArgs, Rectangle bounds, AnnotationCollection annotations, global::JD.Sequencers.Times.Timeline timeline)
{
var sections = annotations.SongSections;
if (sections.Count > 0)
{
var videoIntro = ClampToActiveArea(new Range<BeatUnit>(annotations.StartBeat, sections[0].Time));
PaintSection(eventArgs, timeline, bounds, Color.SlateGray, videoIntro);
var videoOutro = ClampToActiveArea(new Range<BeatUnit>(timeline.Wave().UnitConverter.End(sections[sections.Count - 1]), annotations.EndBeat));
PaintSection(eventArgs, timeline, bounds, Color.SlateGray, videoOutro);
}
}
protected void PaintActiveArea(PaintEventArgs eventArgs, Rectangle bounds, global::JD.Sequencers.Times.Timeline timeline)
{
if (!ActiveArea.IsEmpty)
{
PaintSection(eventArgs, timeline, bounds, Color.Gray, new Range<BeatUnit>(timeline.VisibleStartBeat(), ActiveArea.Min));
PaintSection(eventArgs, timeline, bounds, Color.Gray, new Range<BeatUnit>(ActiveArea.Max, timeline.VisibleEndBeat()));
using (var pen = new Pen(Color.Black, 2))
{
int x = timeline.TimeToPixel(ActiveArea.Min);
eventArgs.Graphics.DrawLine(pen, x, bounds.Top, x, bounds.Bottom);
x = timeline.TimeToPixel(ActiveArea.Max);
eventArgs.Graphics.DrawLine(pen, x, bounds.Top, x, bounds.Bottom);
}
}
}
protected void PaintSection(PaintEventArgs eventArgs, global::JD.Sequencers.Times.Timeline timeline, Rectangle backGroundBounds, Color color, Range<BeatUnit> sectionRange)
{
PaintSection(eventArgs, backGroundBounds, color, timeline.TimeToPixel(sectionRange.Min), timeline.TimeToPixel(sectionRange.Max));
}
protected Range<BeatUnit> ClampToActiveArea(Range<BeatUnit> range)
{
var activeArea = ActiveArea;
if (activeArea.IsEmpty)
{
return range;
}
return activeArea.ClampRange(range);
}
protected static void PaintSection(PaintEventArgs eventArgs, Rectangle backGroundBounds, Color color, int pixelStart, int pixelEnd)
{
var toPaint = backGroundBounds;
toPaint.X = pixelStart;
toPaint.Width = pixelEnd - pixelStart;
if (toPaint.IntersectsWith(eventArgs.ClipRectangle))
{
using (var brush = CreateBrush(backGroundBounds, color))
{
eventArgs.Graphics.FillRectangle(brush, toPaint);
}
}
}
protected static int CountSameSectionType(int songSectionIndex, IList<SongSection> sections)
{
SongSectionType type = sections[songSectionIndex].Type;
int i = songSectionIndex - 1;
while (i >= 0 && sections[i].Type == type)
{
i--;
}
return songSectionIndex - i - 1;
}
/// <summary>
/// Use a small gradient going from lighter to darker.
/// </summary>
private static LinearGradientBrush CreateBrush(Rectangle bounds, Color baseColor)
{
return new LinearGradientBrush(new Rectangle(0, bounds.Top, 1, bounds.Height),
baseColor.Add(40),
baseColor.Add(-25), LinearGradientMode.Vertical);
}
}
}