This repository has been archived on 2023-12-10. You can view files and clone it, but cannot push or open issues or pull requests.
PainterlyUNO/Matrix App/PregeneratedMods/MatrixGifGenerator.cs

359 lines
13 KiB
C#
Raw Normal View History

2021-06-09 16:43:27 +00:00
using Matrix_App.PregeneratedMods;
using System;
using System.Diagnostics;
2021-06-09 16:43:27 +00:00
using System.Drawing;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using Matrix_App.minecraft;
2021-06-11 11:36:55 +00:00
using Matrix_App.PregeneratedMods.reflection;
2021-06-09 16:43:27 +00:00
using static Matrix_App.Utils;
2021-06-09 17:53:36 +00:00
using static Matrix_App.Defaults;
2021-06-09 16:43:27 +00:00
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(),
new Transfom(),
new Minecraft()
2021-06-09 16:43:27 +00:00
};
2021-06-11 11:36:55 +00:00
// Static generator accessible members
// must work on multiple threads
protected static int totalFrames; // total amount of frames to generate
protected static byte[][]? actualStore; // image copy of previous GIF for generator
2021-06-09 16:43:27 +00:00
protected static int width;
protected static int height;
2021-06-11 11:36:55 +00:00
// updates the preview matrix for animation
private static readonly Timer PlaybackTimer = new Timer();
// current frame to play
private static int _playbackFrame;
2021-06-09 16:43:27 +00:00
2021-06-11 11:36:55 +00:00
// temporary buffer for storing snapshots for buttons
2021-06-09 16:43:27 +00:00
private static readonly byte[][] Snapshot;
2021-06-11 11:36:55 +00:00
private static byte[][] _initialBuffer; // temporary buffer for swapping
2021-06-09 16:43:27 +00:00
2021-06-11 11:36:55 +00:00
// Generator renderer
2021-06-09 16:43:27 +00:00
private static readonly ThreadQueue Renderer;
2021-06-11 11:36:55 +00:00
// Current generator to use
2021-06-09 16:43:27 +00:00
private static MatrixGifGenerator? _generator;
static MatrixGifGenerator()
{
PlaybackTimer.Tick += PlaybackFrame;
2021-06-11 11:36:55 +00:00
// Generate buffer for button filter snapshots
2021-06-09 17:53:36 +00:00
Snapshot = CreateImageRGB_NT(FilterPreviewWidth, FilterPreviewHeight, 1);
_initialBuffer = CreateImageRGB_NT(FilterPreviewWidth, FilterPreviewHeight, 1);
2021-06-09 16:43:27 +00:00
Renderer = new ThreadQueue("Matrix Gif Renderer", 2);
}
2021-06-11 11:36:55 +00:00
/// <summary>
/// Plays the next frame of what is currently in the animation buffer
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
2021-06-09 16:43:27 +00:00
private static void PlaybackFrame(object? sender, EventArgs e)
{
if (_playbackFrame >= _animationBuffer.Length - 1)
{
_playbackFrame = 0;
}
_preview.SetImage(_animationBuffer[_playbackFrame]);
_playbackFrame++;
}
public delegate void update();
2021-06-11 11:36:55 +00:00
/// <summary>
/// Colors a single fragment at the specified pixel location (x|y) at frame frame.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="u">Normalized pixel X-coordinate</param>
/// <param name="v">Normalized pixel Y-coordinate</param>
/// <param name="frame">Current frame</param>
/// <param name="r">Pixel Red value in range [0, 1] (saturated)</param>
/// <param name="g">Pixel Green value in range [0, 1] (saturated)</param>
/// <param name="b">Pixel Blue value in range [0, 1] (saturated)</param>
2021-06-09 16:43:27 +00:00
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);
protected abstract void CreateUi(FlowLayoutPanel anchor, update runner);
2021-06-11 11:36:55 +00:00
// Buffer to store generator result in
2021-06-09 16:43:27 +00:00
private static byte[][] _animationBuffer = null!;
2021-06-11 11:36:55 +00:00
// Main application reference
2021-06-09 16:43:27 +00:00
private static MatrixDesignerMain _form = null!;
2021-06-11 11:36:55 +00:00
private static Matrix _preview = null!; // preview matrix
2021-06-09 16:43:27 +00:00
public static void GenerateBaseUi(FlowLayoutPanel anchor, Matrix matrix, MatrixDesignerMain form1)
{
_form = form1;
// generate access buttons for available generators
foreach (var generator in Generators)
{
2021-06-11 11:36:55 +00:00
// generate button
2021-06-09 16:43:27 +00:00
var button = new Button
{
AutoSize = true,
2021-06-11 11:36:55 +00:00
Text = FieldWidgets.GetBetterFieldName(generator.GetType().Name)
2021-06-09 16:43:27 +00:00
};
button.Width = anchor.ClientSize.Width - button.Margin.Right - button.Margin.Left;
2021-06-09 16:43:27 +00:00
button.Click += (sender, e) => OpenGeneratorUi(generator, matrix);
button.Image = CreateSnapshot(generator);
2021-06-09 19:19:30 +00:00
button.TextImageRelation = TextImageRelation.ImageBeforeText;
button.TextAlign = ContentAlignment.MiddleRight;
button.ImageAlign = ContentAlignment.MiddleLeft;
2021-06-09 16:43:27 +00:00
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
2021-06-09 17:53:36 +00:00
SetGlobalArgs(FilterPreviewWidth, FilterPreviewHeight, 1, null, _initialBuffer);
2021-06-09 16:43:27 +00:00
InvokeGenerator();
BlockBuffer();
_generator = matrixGifGenerator;
// render filter
2021-06-09 17:53:36 +00:00
SetGlobalArgs(FilterPreviewWidth, FilterPreviewHeight, 1, _initialBuffer, Snapshot);
2021-06-09 16:43:27 +00:00
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);
}
}
2021-06-11 11:36:55 +00:00
2021-06-09 16:43:27 +00:00
private static void OpenGeneratorUi(MatrixGifGenerator matrixGifGenerator, Matrix matrix)
{
_generator = matrixGifGenerator;
if (!ShowEditDialog(matrix))
return;
2021-06-11 11:36:55 +00:00
if (Renderer.HasWork())
{
if (DialogResult.Yes ==
MessageBox.Show($@"The filter {_generator.GetType().Name} hasn't finished yet, wait for completion?",
@"Filter incomplete", MessageBoxButtons.YesNo, MessageBoxIcon.Warning))
{
BlockBuffer();
ColorStore(_animationBuffer, MatrixDesignerMain.gifBuffer);
2021-06-11 11:36:55 +00:00
_form.ResetTimeline();
}
else
{
MessageBox.Show($@"The filter {_generator.GetType().Name} has timedout", @"Failed applying filter", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
ColorStore(_animationBuffer, MatrixDesignerMain.gifBuffer);
2021-06-11 11:36:55 +00:00
_form.ResetTimeline();
}
2021-06-09 16:43:27 +00:00
}
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)
{
2021-06-11 11:36:55 +00:00
if (_generator == null)
{
return false;
}
2021-06-09 16:43:27 +00:00
var success = false;
Initialize(matrix);
Form prompt = new Form
{
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowOnly,
2021-06-09 16:43:27 +00:00
Text = @"Vorgenerierter Modus: " + _generator.GetType().Name
};
2021-06-11 11:36:55 +00:00
var confirmation = new Button {Text = @"Apply", Anchor = AnchorStyles.Top | AnchorStyles.Left};
2021-06-09 16:43:27 +00:00
confirmation.Click += (sender, e) => {
success = true;
prompt.Close();
};
FlowLayoutPanel controlPanel = new FlowLayoutPanel
{
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
2021-06-09 16:43:27 +00:00
FlowDirection = FlowDirection.BottomUp,
Dock = DockStyle.Fill,
2021-06-09 16:43:27 +00:00
WrapContents = false,
AutoSizeMode = AutoSizeMode.GrowOnly,
2021-06-09 16:43:27 +00:00
AutoSize = true
};
_generator.CreateUi(controlPanel, InvokeGenerator);
2021-06-09 16:43:27 +00:00
PlaybackTimer.Interval = _form.GetDelayTime();
PlaybackTimer.Enabled = true;
2021-06-11 11:36:55 +00:00
var type = _generator.GetType();
var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
2021-06-09 19:19:30 +00:00
CreateDivider(controlPanel, 2);
2021-06-09 16:43:27 +00:00
foreach (var field in fields)
{
2021-06-11 11:36:55 +00:00
var widget = FieldWidgets.GetFieldWidget(field, _generator, InvokeGenerator);
if (widget == null)
2021-06-09 16:43:27 +00:00
continue;
2021-06-11 11:36:55 +00:00
controlPanel.Controls.AddRange(widget);
2021-06-09 19:19:30 +00:00
CreateDivider(controlPanel, 1);
2021-06-09 16:43:27 +00:00
}
controlPanel.Controls.Add(_preview);
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.MaximizeBox = false;
prompt.FormBorderStyle = FormBorderStyle.FixedDialog;
prompt.StartPosition = FormStartPosition.CenterScreen;
2021-06-09 16:43:27 +00:00
prompt.ShowDialog();
PlaybackTimer.Enabled = false;
return success;
}
private static void Initialize(in Matrix matrix)
{
// Create new initial buffer and copy what ever was in the Gif buffer to it
2021-06-11 11:36:55 +00:00
_initialBuffer = CreateImageRGB_NT(matrix.matrixWidth(), matrix.matrixHeight(), MatrixDesignerMain.gifBuffer.Length);
ColorStore(MatrixDesignerMain.gifBuffer, _initialBuffer);
2021-06-09 16:43:27 +00:00
// Set Generator args
SetGlobalArgs(matrix.matrixWidth(),
matrix.matrixHeight(),
2021-06-11 11:36:55 +00:00
MatrixDesignerMain.gifBuffer.Length - 1,
2021-06-09 16:43:27 +00:00
_initialBuffer,
2021-06-11 11:36:55 +00:00
CreateImageRGB_NT(matrix.matrixWidth(), matrix.matrixHeight(), MatrixDesignerMain.gifBuffer.Length)
2021-06-09 16:43:27 +00:00
);
// 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>
2021-06-11 11:36:55 +00:00
/// <param name="lineHeight"></param>
private static void CreateDivider(Control controlPanel, int lineHeight)
2021-06-09 16:43:27 +00:00
{
var divider = new Label
{
BorderStyle = BorderStyle.Fixed3D,
AutoSize = false,
Dock = DockStyle.Fill,
Anchor = AnchorStyles.Top | AnchorStyles.Left,
Height = lineHeight,
2021-06-09 16:43:27 +00:00
};
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;
2021-06-09 16:43:27 +00:00
for (var y = 0; y < height; y++)
{
var v = y / (float) height;
2021-06-09 16:43:27 +00:00
2021-06-11 11:36:55 +00:00
_generator!.ColorFragment(x, y, u, v, frame, out var r, out var g, out var b);
2021-06-09 16:43:27 +00:00
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();
}
}
}