405 lines
13 KiB
C#
405 lines
13 KiB
C#
using Matrix_App.PregeneratedMods;
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Reflection;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using static Matrix_App.Utils;
|
|
using static MatrixDesigner.Defaults;
|
|
using Timer = System.Windows.Forms.Timer;
|
|
|
|
namespace Matrix_App
|
|
{
|
|
public abstract class MatrixGifGenerator
|
|
{
|
|
private static readonly MatrixGifGenerator[] Generators =
|
|
{
|
|
new SimpleRainbow(),
|
|
new Rain(),
|
|
new Spiral(),
|
|
new UvGrid(),
|
|
new RandomPixels(),
|
|
new Boxblur(),
|
|
new ColorAdjust(),
|
|
new Grayscale(),
|
|
new Invert()
|
|
};
|
|
|
|
private static readonly Timer PlaybackTimer = new Timer();
|
|
|
|
private static int _playbackFrame;
|
|
|
|
protected static int totalFrames;
|
|
protected static byte[][]? actualStore;
|
|
|
|
protected static int width;
|
|
protected static int height;
|
|
|
|
private static readonly byte[][] Snapshot;
|
|
private static byte[][] _initialBuffer;
|
|
|
|
private static readonly ThreadQueue Renderer;
|
|
|
|
private static MatrixGifGenerator? _generator;
|
|
|
|
static MatrixGifGenerator()
|
|
{
|
|
PlaybackTimer.Tick += PlaybackFrame;
|
|
|
|
Snapshot = CreateImageRGB_NT(FILTER_PREVIEW_WIDTH, FILTER_PREVIEW_HEIGHT, 1);
|
|
_initialBuffer = CreateImageRGB_NT(FILTER_PREVIEW_WIDTH, FILTER_PREVIEW_HEIGHT, 1);
|
|
|
|
Renderer = new ThreadQueue("Matrix Gif Renderer", 2);
|
|
}
|
|
private static void PlaybackFrame(object? sender, EventArgs e)
|
|
{
|
|
if (_playbackFrame >= _animationBuffer.Length - 1)
|
|
{
|
|
_playbackFrame = 0;
|
|
}
|
|
|
|
_preview.SetImage(_animationBuffer[_playbackFrame]);
|
|
|
|
_playbackFrame++;
|
|
}
|
|
|
|
protected abstract void ColorFragment(in int x, in int y, in float u, in float v, in int frame, out float r, out float g, out float b);
|
|
|
|
private static byte[][] _animationBuffer = null!;
|
|
|
|
private static MatrixDesignerMain _form = null!;
|
|
private static Matrix _preview = null!;
|
|
|
|
public static void GenerateBaseUi(FlowLayoutPanel anchor, Matrix matrix, MatrixDesignerMain form1)
|
|
{
|
|
_form = form1;
|
|
|
|
// generate access buttons for available generators
|
|
foreach (var generator in Generators)
|
|
{
|
|
var button = new Button
|
|
{
|
|
Width = 215,
|
|
Text = GetBetterFieldName(generator.GetType().Name)
|
|
};
|
|
button.Click += (sender, e) => OpenGeneratorUi(generator, matrix);
|
|
button.Image = CreateSnapshot(generator);
|
|
button.TextImageRelation = TextImageRelation.ImageAboveText;
|
|
button.Height = FILTER_PREVIEW_HEIGHT * 2;
|
|
|
|
anchor.Controls.Add(button);
|
|
}
|
|
}
|
|
|
|
private static Image CreateSnapshot(MatrixGifGenerator matrixGifGenerator)
|
|
{
|
|
_generator = new RandomPixels();
|
|
// put some random pixels in as default initial image to operate on for filter
|
|
SetGlobalArgs(FILTER_PREVIEW_WIDTH, FILTER_PREVIEW_HEIGHT, 1, null, _initialBuffer);
|
|
InvokeGenerator();
|
|
|
|
BlockBuffer();
|
|
|
|
_generator = matrixGifGenerator;
|
|
|
|
// render filter
|
|
SetGlobalArgs(FILTER_PREVIEW_WIDTH, FILTER_PREVIEW_HEIGHT, 1, _initialBuffer, Snapshot);
|
|
InvokeGenerator();
|
|
|
|
BlockBuffer();
|
|
|
|
// convert to bitmap
|
|
return ImageWrap(Snapshot[0], width, height);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Blocks this thread until no more work is done by the Renderer thread queue
|
|
/// </summary>
|
|
private static void BlockBuffer()
|
|
{
|
|
while (Renderer.HasWork())
|
|
{
|
|
Thread.Sleep(50);
|
|
}
|
|
}
|
|
|
|
private static void OpenGeneratorUi(MatrixGifGenerator matrixGifGenerator, Matrix matrix)
|
|
{
|
|
_generator = matrixGifGenerator;
|
|
|
|
if (!ShowEditDialog(matrix))
|
|
return;
|
|
|
|
FlipColorStoreRG_GR(_animationBuffer, _form.Gif);
|
|
_form.ResetTimeline();
|
|
}
|
|
|
|
private static void SetGlobalArgs(int w, int h, int f, in byte[][]? previous, in byte[][] preview)
|
|
{
|
|
totalFrames = f;
|
|
|
|
width = w;
|
|
height = h;
|
|
|
|
_animationBuffer = preview;
|
|
actualStore = previous;
|
|
}
|
|
|
|
private static bool ShowEditDialog(Matrix matrix)
|
|
{
|
|
var success = false;
|
|
|
|
Initialize(matrix);
|
|
|
|
Form prompt = new Form
|
|
{
|
|
Width = 500,
|
|
Height = 320,
|
|
Text = @"Vorgenerierter Modus: " + _generator.GetType().Name
|
|
};
|
|
|
|
var confirmation = new Button {Text = "Apply", Anchor = AnchorStyles.Top | AnchorStyles.Left};
|
|
confirmation.Click += (sender, e) => {
|
|
success = true;
|
|
prompt.Close();
|
|
};
|
|
|
|
FlowLayoutPanel controlPanel = new FlowLayoutPanel
|
|
{
|
|
Anchor = AnchorStyles.Top | AnchorStyles.Left,
|
|
FlowDirection = FlowDirection.BottomUp,
|
|
Dock = DockStyle.Top,
|
|
WrapContents = false,
|
|
AutoSize = true
|
|
};
|
|
|
|
var fields = _generator.GetType().GetFields();
|
|
|
|
PlaybackTimer.Interval = _form.GetDelayTime();
|
|
PlaybackTimer.Enabled = true;
|
|
|
|
CreateDivider(controlPanel);
|
|
foreach (var field in fields)
|
|
{
|
|
if (field.IsStatic || !field.IsPublic)
|
|
continue;
|
|
|
|
var fieldValue = field.GetValue(_generator);
|
|
|
|
controlPanel.Controls.Add(GetFieldUi(field, fieldValue, _generator));
|
|
}
|
|
|
|
if (controlPanel.Controls.Count > 1)
|
|
{
|
|
CreateDivider(controlPanel);
|
|
|
|
var label = new Label() { Text = "Settings" };
|
|
label.Font = new Font(label.Font, FontStyle.Bold);
|
|
controlPanel.Controls.Add(label);
|
|
}
|
|
|
|
controlPanel.Controls.Add(_preview);
|
|
CreateDivider(controlPanel);
|
|
var playLabel = new Label() { Text = "Playback preview" };
|
|
playLabel.Font = new Font(playLabel.Font, FontStyle.Bold);
|
|
controlPanel.Controls.Add(playLabel);
|
|
|
|
FlowLayoutPanel southPane = new FlowLayoutPanel
|
|
{
|
|
Dock = DockStyle.Bottom,
|
|
Anchor = AnchorStyles.Top | AnchorStyles.Left
|
|
};
|
|
southPane.Controls.Add(confirmation);
|
|
southPane.AutoSize = true;
|
|
|
|
// render once
|
|
InvokeGenerator();
|
|
|
|
prompt.MinimumSize = prompt.Size;
|
|
prompt.Controls.Add(controlPanel);
|
|
prompt.Controls.Add(southPane);
|
|
prompt.Height = southPane.Height * 2 + controlPanel.Height + 16;
|
|
prompt.FormBorderStyle = FormBorderStyle.FixedDialog;
|
|
prompt.MaximizeBox = false;
|
|
prompt.ShowDialog();
|
|
|
|
PlaybackTimer.Enabled = false;
|
|
|
|
return success;
|
|
}
|
|
|
|
private static Control GetFieldUi(FieldInfo field, object? fieldValue, MatrixGifGenerator generator)
|
|
{
|
|
var panel = new FlowLayoutPanel
|
|
{
|
|
FlowDirection = FlowDirection.LeftToRight,
|
|
Anchor = AnchorStyles.Top | AnchorStyles.Left,
|
|
AutoSize = true
|
|
};
|
|
|
|
panel.Controls.Add(new Label
|
|
{
|
|
TextAlign = System.Drawing.ContentAlignment.MiddleLeft,
|
|
Text = GetBetterFieldName(field.Name),
|
|
Dock = DockStyle.Left,
|
|
Anchor = AnchorStyles.Top | AnchorStyles.Left,
|
|
Width = 100
|
|
});
|
|
|
|
switch (fieldValue)
|
|
{
|
|
case int value:
|
|
{
|
|
var upDown = new NumericUpDown
|
|
{
|
|
Dock = DockStyle.Fill,
|
|
Anchor = AnchorStyles.Top | AnchorStyles.Right,
|
|
Width = 360,
|
|
Value = value
|
|
};
|
|
upDown.ValueChanged += (a, b) =>
|
|
{
|
|
field.SetValue(generator, (int) upDown.Value);
|
|
InvokeGenerator();
|
|
};
|
|
|
|
panel.Controls.Add(upDown);
|
|
break;
|
|
}
|
|
case bool value1:
|
|
{
|
|
var upDown = new CheckBox
|
|
{
|
|
Dock = DockStyle.Fill, Anchor = AnchorStyles.Top | AnchorStyles.Right, Checked = value1
|
|
};
|
|
upDown.CheckedChanged += (a, b) =>
|
|
{
|
|
field.SetValue(generator, (bool) upDown.Checked);
|
|
InvokeGenerator();
|
|
};
|
|
|
|
panel.Controls.Add(upDown);
|
|
break;
|
|
}
|
|
case float floatValue:
|
|
{
|
|
var upDown = new TrackBar
|
|
{
|
|
Dock = DockStyle.Fill,
|
|
Anchor = AnchorStyles.Top | AnchorStyles.Right,
|
|
Maximum = 100,
|
|
Minimum = 0,
|
|
Value = (int) (floatValue * 100.0f),
|
|
TickFrequency = 10,
|
|
Width = 360
|
|
};
|
|
upDown.ValueChanged += (a, b) =>
|
|
{
|
|
field.SetValue(generator, upDown.Value / 1e2f);
|
|
InvokeGenerator();
|
|
};
|
|
|
|
panel.Controls.Add(upDown);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return panel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a new name from standard class names
|
|
/// Example: SomeClassA --> some class a
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <returns></returns>
|
|
private static string GetBetterFieldName(string name)
|
|
{
|
|
var groups = Regex.Match(name, @"([A-Z]*[a-z]+)([A-Z]+[a-z]*)|(.*)").Groups;
|
|
|
|
var newName = "";
|
|
|
|
for (var c = 1; c < groups.Count; c++)
|
|
{
|
|
newName += groups[c].Value.ToLower() + " ";
|
|
}
|
|
|
|
return newName;
|
|
}
|
|
|
|
private static void Initialize(in Matrix matrix)
|
|
{
|
|
// Create new initial buffer and copy what ever was in the Gif buffer to it
|
|
_initialBuffer = CreateImageRGB_NT(matrix.matrixWidth(), matrix.matrixHeight(), _form.Gif.Length);
|
|
FlipColorStoreRG_GR(_form.Gif, _initialBuffer);
|
|
|
|
// Set Generator args
|
|
SetGlobalArgs(matrix.matrixWidth(),
|
|
matrix.matrixHeight(),
|
|
_form.Gif.Length - 1,
|
|
_initialBuffer,
|
|
CreateImageRGB_NT(matrix.matrixWidth(), matrix.matrixHeight(), _form.Gif.Length)
|
|
);
|
|
|
|
// Create preview matrix
|
|
_preview = new Matrix();
|
|
_preview.SetEditable(false);
|
|
_preview.Anchor = AnchorStyles.Top | AnchorStyles.Left;
|
|
_preview.Size = new Size(480, 200);
|
|
_preview.resize(matrix.matrixWidth(), matrix.matrixHeight());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a separating line to the controls
|
|
/// </summary>
|
|
/// <param name="controlPanel"></param>
|
|
private static void CreateDivider(Control controlPanel)
|
|
{
|
|
var divider = new Label
|
|
{
|
|
BorderStyle = BorderStyle.Fixed3D,
|
|
AutoSize = false,
|
|
Height = 2,
|
|
Width = 500
|
|
};
|
|
|
|
controlPanel.Controls.Add(divider);
|
|
}
|
|
|
|
private static void InvokeGenerator()
|
|
{
|
|
Renderer.Enqueue(delegate
|
|
{
|
|
for (var frame = 0; frame < _animationBuffer.Length; frame++)
|
|
{
|
|
for (var x = 0; x < width; x++)
|
|
{
|
|
var u = x / (float)width;
|
|
|
|
for (var y = 0; y < height; y++)
|
|
{
|
|
var v = y / (float)height;
|
|
|
|
_generator.ColorFragment(x, y, u, v, frame, out var r, out var g, out var b);
|
|
|
|
var index = (x + y * width) * 3;
|
|
|
|
_animationBuffer[frame][index] = (byte) Math.Clamp( (int)(r * 255), 0, 255);
|
|
_animationBuffer[frame][index + 1] = (byte) Math.Clamp( (int)(g * 255), 0, 255);
|
|
_animationBuffer[frame][index + 2] = (byte) Math.Clamp( (int)(b * 255), 0, 255);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
public static void Close()
|
|
{
|
|
Renderer.Stop();
|
|
}
|
|
}
|
|
}
|