using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using Timer = System.Windows.Forms.Timer;
namespace TestMatrixRain
{
public class MatrixRainControl : Panel
{
const string CharactersToChoose = "ハミヒーウシナモニサワツオリアホテマケメエカキムユラセネスタヌヘヲイクコソチトノフヤヨルレロン";
const int FramesPerSecond = 12;
const int MaxShadowCount = 19;
const int MaxHaloCount = 9;
static Random randomVar = new Random();
BackgroundWorker worker = new BackgroundWorker();
Timer workerLoop = new Timer();
Timer renderLoop = new Timer();
Bitmap [] renderBitmap = new Bitmap[2];
int currentBitmapIndex = 0;
List<DrawElement> ElementsList = new List<DrawElement>();
private int minElements = 10;
private int currMaxElements = 55;
private int maxElement = 200;
private int timeToUpMaxElement = 10;
private int currTime = 0;
Color elementForeColor;
Color elementBackColor;
Brush[,] shadowBrush = new Brush[MaxShadowCount, MaxShadowCount];
Pen [] haloPen = new Pen[MaxHaloCount];
Brush backColorBrush;
public MatrixRainControl()
{
worker.DoWork += Worker_DoWork;
workerLoop.Interval = 66;
workerLoop.Tick += WorkerLoop_Tick;
renderLoop.Interval = 1000 / FramesPerSecond;
renderLoop.Tick += (o, e) => Invalidate();
Color baseColor = Color.MediumAquamarine;
elementBackColor = ControlPaint.LightLight(baseColor);
elementForeColor = ControlPaint.Dark(baseColor);
for(int i = 1; i < MaxShadowCount; i++)
{
for(int j = 1; j < MaxShadowCount; j++)
{
int alpha = 255 - Math.Min(230, ((230 / i) * j));
shadowBrush[i, j] = new SolidBrush(Color.FromArgb(alpha, elementForeColor));
}
}
for (int i = 0; i < MaxHaloCount; i++)
{
haloPen[i] = new Pen(Color.FromArgb(32, elementBackColor), i);
haloPen[i].LineJoin = LineJoin.Round;
}
backColorBrush = new SolidBrush(elementBackColor);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
UpdateStyles();
}
public void StartAnimation()
{
renderBitmap[0] = new Bitmap(this.Width, this.Height);
renderBitmap[1] = new Bitmap(this.Width, this.Height);
workerLoop.Start();
renderLoop.Start();
AddUnlimitedMatrixChars(250);
}
void AddUnlimitedMatrixChars(int interval)
{
var t = new Timer();
t.Interval = interval;
t.Tick += delegate (object sender, EventArgs args)
{
if (ElementsList.Count < currMaxElements)
{
AddMatrixChar();
}
};
t.Start();
}
void AddMatrixChar()
{
Color baseColor = Color.MediumAquamarine;
var el = new DrawElement()
{
Location = new PointF(randomVar.Next(0, this.Width), -350),
Size = new SizeF(20, 20),
BackColor = elementBackColor,
ForeColor = elementForeColor
};
int step = randomVar.Next(2, 6);
float fStep = (float)(step / 10);
int kanaChangeTime = randomVar.Next(2, 10);
int currKanaChange = 0;
float textWidth = randomVar.Next(5, 34);
int shadowCount = randomVar.Next(6, MaxShadowCount);
Action<Graphics> draw = delegate (Graphics graphics)
{
string character = (string) el.Tag;
using (var gp = new GraphicsPath())
{
gp.AddString(character, this.Font.FontFamily, 0, textWidth, el.Location, StringFormat.GenericDefault);
for (int i = 1; i < shadowCount; i++)
{
string shadowChar = "";
int shadowTagIndex = (el.TagHistory.Count - 1) - i;
if (shadowTagIndex >= 0)
{
shadowChar = (string) el.TagHistory[shadowTagIndex];
}
using (var gpShadow = new GraphicsPath())
{
float yLocation = el.Location.Y - (((float)(textWidth * 1.2)) * i);
gpShadow.AddString(shadowChar, this.Font.FontFamily, 0, textWidth, new PointF(el.Location.X, yLocation), StringFormat.GenericDefault);
graphics.FillPath(shadowBrush[shadowCount, i], gpShadow);
}
}
int maxHalo = Math.Min((int)(textWidth / 2), MaxHaloCount);
for (int i = 1; i < maxHalo; i++)
{
graphics.DrawPath(haloPen[i], gp);
}
graphics.FillPath(backColorBrush, gp);
}
if (el.Location.Y < (this.Height) + ((float)(textWidth * 1.2) * shadowCount))
{
el.Location.Y += step;
if (currKanaChange < kanaChangeTime)
{
currKanaChange += 1;
}
else
{
currKanaChange = 0;
el.AddTag(GetRandomKana().ToString());
}
}
else
{
el.DeleteMySelf = true;
}
};
el.AddTag(GetRandomKana().ToString());
el.DrawAction = draw;
ElementsList.Add(el);
}
char GetRandomKana()
{
return CharactersToChoose[randomVar.Next(0, CharactersToChoose.Length)];
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawImageUnscaled(renderBitmap[currentBitmapIndex], new Rectangle(0, 0, this.Width, this.Height));
}
private void WorkerLoop_Tick(object sender, EventArgs e)
{
if (!worker.IsBusy)
{
worker.RunWorkerAsync();
if (currTime < timeToUpMaxElement)
{
currTime += 1;
}
else
{
currTime = 0;
if (currMaxElements < maxElement)
{
currMaxElements += 1;
}
}
}
else
{
if (currMaxElements > minElements)
{
currMaxElements -= 1;
currTime = 0;
}
}
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
int nextBitmapIndex = (currentBitmapIndex + 1) % renderBitmap.Length;
Bitmap tempPic = renderBitmap[nextBitmapIndex];
List<DrawElement> tempList = new List<DrawElement>();
Graphics tempPicGraphs = Graphics.FromImage(tempPic);
tempList.AddRange(ElementsList);
tempPicGraphs.Clear(this.BackColor);
foreach (DrawElement drawElement in tempList)
{
drawElement.Draw(tempPicGraphs);
if (drawElement.DeleteMySelf)
{
ElementsList.Remove(drawElement);
}
}
e.Result = tempPic;
Interlocked.Exchange(ref currentBitmapIndex, nextBitmapIndex);
}
}
public class DrawElement
{
public PointF Location;
public SizeF Size;
public Color BackColor;
public Color ForeColor;
public Object Tag;
public Action<Graphics> DrawAction;
public List<Object> TagHistory = new List<object>();
public bool DeleteMySelf = false;
public DrawElement()
{
}
public void AddTag(string tagString)
{
if (Tag != null)
{
TagHistory.Add(Tag);
Tag = tagString;
}
else
{
Tag = tagString;
TagHistory.Add(Tag);
}
}
public void AddTag(Object tagObj)
{
if (Tag != null)
{
TagHistory.Add(tagObj);
Tag = tagObj;
}
else
{
TagHistory.Add(tagObj);
Tag = tagObj;
}
}
async public void Draw(Graphics g)
{
DrawAction(g);
}
}
}