2021-06-09 16:43:27 +00:00
#define DEBUG_ENABLED
#define DEBUG_ENABLED
using System ;
using System.Collections.Generic ;
2021-06-09 17:53:36 +00:00
using System.Diagnostics.CodeAnalysis ;
2021-06-09 16:43:27 +00:00
using System.Drawing ;
using System.Linq ;
using System.Windows.Forms ;
using System.IO.Ports ;
using System.Timers ;
using System.Drawing.Imaging ;
using System.IO ;
using System.Management ;
using System.Text.RegularExpressions ;
2021-06-09 17:53:36 +00:00
using static Matrix_App . Defaults ;
using static Matrix_App . ArduinoInstruction ;
2021-06-09 16:43:27 +00:00
using static Matrix_App . Utils ;
using Matrix_App.Themes ;
2021-06-09 17:53:36 +00:00
using Timer = System . Timers . Timer ;
2021-06-09 16:43:27 +00:00
namespace Matrix_App
{
public partial class MatrixDesignerMain : Form
{
#region Private - Members
/// <summary>
/// Port update Timer
/// Reloads available port names at consecutive rates
/// </summary>
2021-06-09 17:53:36 +00:00
private Timer ? portNameUpdater ;
private Timer ? delay ;
2021-06-09 16:43:27 +00:00
2021-06-09 17:53:36 +00:00
private static SerialPort _port = new SerialPort ( ) ;
2021-06-09 16:43:27 +00:00
private uint portNumber ;
2021-06-09 17:53:36 +00:00
private bool runningGif ;
2021-06-09 16:43:27 +00:00
2021-06-09 17:53:36 +00:00
private readonly PortCommandQueue commandQueue = new PortCommandQueue ( ref _port ) ;
private readonly Regex comRegex = new Regex ( @"COM[\d]+" ) ;
2021-06-09 16:43:27 +00:00
/// <summary>
/// Gif like frame video buffer
/// </summary>
2021-06-09 17:53:36 +00:00
public byte [ ] [ ] gifBuffer = CreateImageRGB_NT ( MatrixStartWidth , MatrixStartHeight , MatrixStartFrames ) ;
2021-06-09 16:43:27 +00:00
#endregion
#region Setup
public MatrixDesignerMain ( )
{
// generate UI
InitializeComponent ( ) ;
// Set Parent of our matrix
matrixView . Instance ( this ) ;
// Generate filter access buttons
MatrixGifGenerator . GenerateBaseUi ( pregeneratedModsBase , matrixView , this ) ;
2021-06-09 17:53:36 +00:00
Init ( ) ;
// apply light-mode by default
2021-06-09 16:43:27 +00:00
new LightMode ( ) . ApplyTheme ( this ) ;
}
2021-06-09 17:53:36 +00:00
private void Init ( )
2021-06-09 16:43:27 +00:00
{
// Create port name update timer
2021-06-09 17:53:36 +00:00
portNameUpdater = new Timer ( PortNameUpdateInterval ) ;
portNameUpdater . Elapsed + = UpdatePortNames ;
2021-06-09 16:43:27 +00:00
portNameUpdater . AutoReset = true ;
portNameUpdater . Enabled = true ;
// create gif playback timer
2021-06-09 17:53:36 +00:00
delay = new Timer ( ( int ) Delay . Value ) ;
delay . Elapsed + = Timelineupdate ;
2021-06-09 16:43:27 +00:00
delay . AutoReset = true ;
// Set color wheel event handler
2021-06-09 17:53:36 +00:00
ZeichnenFarbRad . handler = ColorWheel_Handler ! ;
2021-06-09 16:43:27 +00:00
// setup port settings
2021-06-09 17:53:36 +00:00
_port . BaudRate = BaudRate ;
_port . ReadTimeout = ReadTimeoutMs ;
_port . WriteTimeout = WriteTimeoutMs ;
2021-06-09 16:43:27 +00:00
// setup matrix
AdjustMatrixTable ( ) ;
2021-06-09 17:53:36 +00:00
// search for initial ports
2021-06-09 16:43:27 +00:00
GatherPortNames ( ) ;
HideEasterEgg ( ) ;
}
private void HideEasterEgg ( )
{
2021-06-09 17:53:36 +00:00
if ( ( ( int ) DateTime . Now . DayOfWeek ) ! = 3 )
return ;
if ( new Random ( ) . Next ( 0 , 9 ) < = 1 )
return ;
using ( Bitmap wednesdayFrog = new Bitmap ( Properties . Resources . Frosch ) )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
matrixWidth . Value = wednesdayFrog . Width ;
matrixHeight . Value = wednesdayFrog . Height ;
ResizeGif ( ) ;
for ( var x = 0 ; x < wednesdayFrog . Width ; x + + )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
for ( var y = 0 ; y < wednesdayFrog . Height ; y + + )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
var pixel = wednesdayFrog . GetPixel ( x , y ) ;
2021-06-09 16:43:27 +00:00
2021-06-09 17:53:36 +00:00
matrixView . SetPixelNoRefresh ( x , y , pixel ) ;
2021-06-09 16:43:27 +00:00
}
}
}
2021-06-09 17:53:36 +00:00
matrixView . Refresh ( ) ;
2021-06-09 16:43:27 +00:00
}
#endregion
#region UI - Methods
#region Port - ComboBox
/// <summary>
/// Updates the port names to newest available ports.
/// Called by <see cref="portNameUpdater"/>.
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
2021-06-09 17:53:36 +00:00
private void UpdatePortNames ( object source , ElapsedEventArgs e )
2021-06-09 16:43:27 +00:00
{
if ( Ports . InvokeRequired )
{
2021-06-09 17:53:36 +00:00
// invoke on the combo-boxes thread
Ports . Invoke ( new Action ( GatherPortNames ) ) ;
2021-06-09 16:43:27 +00:00
}
else
{
// run on this thread
GatherPortNames ( ) ;
}
}
/// <summary>
2021-06-09 17:53:36 +00:00
/// Gathers all available ports and sets them to the combobox <see cref="Ports"/>
2021-06-09 16:43:27 +00:00
/// </summary>
2021-06-09 17:53:36 +00:00
[SuppressMessage("ReSharper", "CoVariantArrayConversion", Justification = "Never got an exception, so seems to be just fine")]
2021-06-09 16:43:27 +00:00
private void GatherPortNames ( )
{
var ports = SerialPort . GetPortNames ( ) ;
// save previously selected
var selected = this . Ports . SelectedItem ;
// get device names from ports
var newPorts = GetDeviceNames ( ports ) ;
// add virtual port
newPorts . AddLast ( "Virtual-Unlimited (COM257)" ) ;
// search for new port
foreach ( var newPort in newPorts )
{
// find any new port
var found = Ports . Items . Cast < object? > ( ) . Any ( oldPort = > ( string ) oldPort ! = = newPort ) ;
// some port wasn't found, recreate list
if ( ! found )
{
commandQueue . InvalidatePort ( ) ;
Ports . Items . Clear ( ) ;
2021-06-09 17:53:36 +00:00
Ports . Items . AddRange ( newPorts . ToArray ( ) ! ) ;
2021-06-09 16:43:27 +00:00
// select previously selected port if port is still accessible
2021-06-09 17:53:36 +00:00
if ( selected ! = null & & Ports . Items . Contains ( selected ) )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
Ports . SelectedItem = selected ;
2021-06-09 16:43:27 +00:00
} else
{
2021-06-09 17:53:36 +00:00
Ports . SelectedIndex = 0 ;
2021-06-09 16:43:27 +00:00
}
break ;
}
}
}
private static LinkedList < string > GetDeviceNames ( string [ ] ports )
{
ManagementClass processClass = new ManagementClass ( "Win32_PnPEntity" ) ;
2021-06-09 17:53:36 +00:00
ManagementObjectCollection devicePortNames = processClass . GetInstances ( ) ;
2021-06-09 16:43:27 +00:00
var newPorts = new LinkedList < string > ( ) ;
2021-06-09 17:53:36 +00:00
foreach ( var currentPort in ports )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
foreach ( var o in devicePortNames )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
var name = ( ( ManagementObject ) o ) . GetPropertyValue ( "Name" ) ;
if ( name = = null | | ! name . ToString ( ) ! . Contains ( currentPort ) )
continue ;
newPorts . AddLast ( name . ToString ( ) ! ) ;
break ;
2021-06-09 16:43:27 +00:00
}
}
return newPorts ;
}
/// <summary>
/// Invoked when the selected port has changed.
/// Applies the new port settings.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Ports_SelectedIndexChanged ( object sender , EventArgs e )
{
2021-06-09 17:53:36 +00:00
lock ( _port )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
if ( ! _port . IsOpen )
2021-06-09 16:43:27 +00:00
{
var item = ( string ) ( ( ComboBox ) sender ) . SelectedItem ;
if ( item ! = null )
{
// extract port
var matches = comRegex . Matches ( item ) ;
if ( matches . Count > 0 )
{
// only select valid port numbers (up to (including) 256)
portNumber = UInt32 . Parse ( matches [ 0 ] . Value . Split ( 'M' ) [ 1 ] ) ;
if ( portNumber < = 256 )
{
// set valid port
2021-06-09 17:53:36 +00:00
_port . PortName = matches [ 0 ] . Value ;
2021-06-09 16:43:27 +00:00
commandQueue . ValidatePort ( ) ;
} else if ( portNumber = = 257 )
{
// virtual mode, increase limitations as no real arduino is connected
2021-06-09 17:53:36 +00:00
matrixWidth . Maximum = MatrixLimitedWidth ;
matrixHeight . Maximum = MatrixLimitedHeight ;
2021-06-09 16:43:27 +00:00
} else
{
// no port selected reset settings
2021-06-09 17:53:36 +00:00
matrixWidth . Maximum = MatrixStartWidth ;
matrixHeight . Maximum = MatrixStartHeight ;
2021-06-09 16:43:27 +00:00
}
}
}
}
}
}
#endregion
#region Scale
/// <summary>
/// Applies a new size to the gif and matrix
/// </summary>
private void AdjustMatrixTable ( )
{
int width = ( int ) this . matrixWidth . Value ;
int height = ( int ) this . matrixHeight . Value ;
matrixView . resize ( width , height ) ;
ResizeGif ( ) ;
// Delay.Minimum = Math.Min(Width.Value * Height.Value * 5, 500);
}
private void Width_ValueChanged ( object sender , EventArgs e )
{
AdjustMatrixTable ( ) ;
commandQueue . EnqueueArduinoCommand (
2021-06-09 17:53:36 +00:00
OpcodeScale , // opcode
2021-06-09 16:43:27 +00:00
( byte ) matrixWidth . Value ,
( byte ) matrixHeight . Value
) ;
}
private void Height_ValueChanged ( object sender , EventArgs e )
{
AdjustMatrixTable ( ) ;
commandQueue . EnqueueArduinoCommand (
2021-06-09 17:53:36 +00:00
OpcodeScale , // opcode
2021-06-09 16:43:27 +00:00
( byte ) matrixWidth . Value ,
( byte ) matrixHeight . Value
) ;
}
#endregion
#region Edit / Draw
#region TextBoxen
2021-06-09 17:53:36 +00:00
private void DrawTextBoxRed_KeyUp ( object sender , KeyEventArgs e )
2021-06-09 16:43:27 +00:00
{
if ( int . TryParse ( ZeichnenTextBoxRed . Text , out var value ) & & value < 256 & & value > = 0 )
{
ZeichnenTrackBarRed . Value = value ;
ZeichnenFarbRad . setRGB ( ( byte ) ZeichnenTrackBarRed . Value , ( byte ) ZeichnenTrackBarGreen . Value , ( byte ) ZeichnenTrackBarBlue . Value ) ;
}
else if ( value > = 256 )
{
ZeichnenTrackBarRed . Value = 255 ;
2021-06-09 17:53:36 +00:00
ZeichnenTextBoxRed . Text = @"255" ;
2021-06-09 16:43:27 +00:00
ZeichnenFarbRad . setRGB ( ( byte ) ZeichnenTrackBarRed . Value , ( byte ) ZeichnenTrackBarGreen . Value , ( byte ) ZeichnenTrackBarBlue . Value ) ;
}
matrixView . SetPaintColor ( Color . FromArgb ( ZeichnenTrackBarRed . Value , ZeichnenTrackBarGreen . Value , ZeichnenTrackBarBlue . Value ) ) ;
}
2021-06-09 17:53:36 +00:00
private void DrawTextBoxGreen_KeyUp ( object sender , KeyEventArgs e )
2021-06-09 16:43:27 +00:00
{
if ( int . TryParse ( ZeichnenTextBoxGreen . Text , out var value ) & & value < 256 & & value > = 0 )
{
ZeichnenTrackBarGreen . Value = value ;
ZeichnenFarbRad . setRGB ( ( byte ) ZeichnenTrackBarRed . Value , ( byte ) ZeichnenTrackBarGreen . Value , ( byte ) ZeichnenTrackBarBlue . Value ) ;
}
else if ( value > = 256 )
{
ZeichnenTrackBarGreen . Value = 255 ;
2021-06-09 17:53:36 +00:00
ZeichnenTextBoxGreen . Text = @"255" ;
2021-06-09 16:43:27 +00:00
ZeichnenFarbRad . setRGB ( ( byte ) ZeichnenTrackBarRed . Value , ( byte ) ZeichnenTrackBarGreen . Value , ( byte ) ZeichnenTrackBarBlue . Value ) ;
}
matrixView . SetPaintColor ( Color . FromArgb ( ZeichnenTrackBarRed . Value , ZeichnenTrackBarGreen . Value , ZeichnenTrackBarBlue . Value ) ) ;
}
2021-06-09 17:53:36 +00:00
private void DrawTextBoxBlue_KeyUp ( object sender , KeyEventArgs e )
2021-06-09 16:43:27 +00:00
{
if ( int . TryParse ( ZeichnenTextBoxBlue . Text , out var value ) & & value < 256 & & value > = 0 )
{
ZeichnenTrackBarBlue . Value = value ;
ZeichnenFarbRad . setRGB ( ( byte ) ZeichnenTrackBarRed . Value , ( byte ) ZeichnenTrackBarGreen . Value , ( byte ) ZeichnenTrackBarBlue . Value ) ;
}
else if ( value > = 256 )
{
ZeichnenTrackBarBlue . Value = 255 ;
2021-06-09 17:53:36 +00:00
ZeichnenTextBoxBlue . Text = @"255" ;
2021-06-09 16:43:27 +00:00
ZeichnenFarbRad . setRGB ( ( byte ) ZeichnenTrackBarRed . Value , ( byte ) ZeichnenTrackBarGreen . Value , ( byte ) ZeichnenTrackBarBlue . Value ) ;
}
matrixView . SetPaintColor ( Color . FromArgb ( ZeichnenTrackBarRed . Value , ZeichnenTrackBarGreen . Value , ZeichnenTrackBarBlue . Value ) ) ;
}
#endregion
#region TackBars
private void ZeichnenTrackBarRed_Scroll ( object sender , EventArgs e )
{
ZeichnenTextBoxRed . Text = ZeichnenTrackBarRed . Value . ToString ( ) ;
ZeichnenFarbRad . setRGB ( ( byte ) ZeichnenTrackBarRed . Value , ( byte ) ZeichnenTrackBarGreen . Value , ( byte ) ZeichnenTrackBarBlue . Value ) ;
matrixView . SetPaintColor ( Color . FromArgb ( ZeichnenTrackBarRed . Value , ZeichnenTrackBarGreen . Value , ZeichnenTrackBarBlue . Value ) ) ;
}
private void ZeichnenTrackBarGreen_Scroll ( object sender , EventArgs e )
{
ZeichnenTextBoxGreen . Text = ZeichnenTrackBarGreen . Value . ToString ( ) ;
ZeichnenFarbRad . setRGB ( ( byte ) ZeichnenTrackBarRed . Value , ( byte ) ZeichnenTrackBarGreen . Value , ( byte ) ZeichnenTrackBarBlue . Value ) ;
matrixView . SetPaintColor ( Color . FromArgb ( ZeichnenTrackBarRed . Value , ZeichnenTrackBarGreen . Value , ZeichnenTrackBarBlue . Value ) ) ;
}
private void ZeichnenTrackBarBlue_Scroll ( object sender , EventArgs e )
{
ZeichnenTextBoxBlue . Text = ZeichnenTrackBarBlue . Value . ToString ( ) ;
ZeichnenFarbRad . setRGB ( ( byte ) ZeichnenTrackBarRed . Value , ( byte ) ZeichnenTrackBarGreen . Value , ( byte ) ZeichnenTrackBarBlue . Value ) ;
matrixView . SetPaintColor ( Color . FromArgb ( ZeichnenTrackBarRed . Value , ZeichnenTrackBarGreen . Value , ZeichnenTrackBarBlue . Value ) ) ;
}
#endregion
/// <summary>
/// Sets a new color to the edit tab
/// </summary>
/// <param name="color"></param>
public void SetColor ( Color color )
{
ZeichnenTrackBarRed . Value = color . R ;
ZeichnenTrackBarGreen . Value = color . G ;
ZeichnenTrackBarBlue . Value = color . B ;
ZeichnenTextBoxRed . Text = color . R . ToString ( ) ;
ZeichnenTextBoxGreen . Text = color . G . ToString ( ) ;
ZeichnenTextBoxBlue . Text = color . B . ToString ( ) ;
ZeichnenFarbRad . setRGB ( color . R , color . G , color . B ) ;
}
/// <summary>
/// Updates trackbars and RGB-textboxes according to color wheel settings
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
2021-06-09 17:53:36 +00:00
private void ColorWheel_Handler ( object sender , EventArgs e )
2021-06-09 16:43:27 +00:00
{
ZeichnenTrackBarRed . Value = ZeichnenFarbRad . getRed ( ) ;
ZeichnenTrackBarGreen . Value = ZeichnenFarbRad . getGreen ( ) ;
ZeichnenTrackBarBlue . Value = ZeichnenFarbRad . getBlue ( ) ;
ZeichnenTextBoxRed . Text = ZeichnenFarbRad . getRed ( ) . ToString ( ) ;
ZeichnenTextBoxGreen . Text = ZeichnenFarbRad . getGreen ( ) . ToString ( ) ;
ZeichnenTextBoxBlue . Text = ZeichnenFarbRad . getBlue ( ) . ToString ( ) ;
matrixView . SetPaintColor ( Color . FromArgb ( ZeichnenTrackBarRed . Value , ZeichnenTrackBarGreen . Value , ZeichnenTrackBarBlue . Value ) ) ;
}
/// <summary>
/// Fills the entire Matrix with a color
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
2021-06-09 17:53:36 +00:00
private void DrawFill_Click ( object sender , EventArgs e )
2021-06-09 16:43:27 +00:00
{
var color = Color . FromArgb ( ZeichnenTrackBarRed . Value , ZeichnenTrackBarGreen . Value , ZeichnenTrackBarBlue . Value ) ;
matrixView . SetPaintColor ( color ) ;
matrixView . Fill ( color ) ;
commandQueue . EnqueueArduinoCommand (
2021-06-09 17:53:36 +00:00
OpcodeFill , // Opcode
2021-06-09 16:43:27 +00:00
( byte ) ZeichnenTrackBarRed . Value , // Red
( byte ) ZeichnenTrackBarGreen . Value , // Green
( byte ) ZeichnenTrackBarBlue . Value // Blue
) ;
}
/// <summary>
/// Sets the entire Matrix to black
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
2021-06-09 17:53:36 +00:00
private void DrawClear_Click ( object sender , EventArgs e )
2021-06-09 16:43:27 +00:00
{
matrixView . Fill ( Color . Black ) ;
commandQueue . EnqueueArduinoCommand (
2021-06-09 17:53:36 +00:00
OpcodeFill , // opcode
2021-06-09 16:43:27 +00:00
0 , // red
0 , // green
0 // blue
) ;
}
#endregion
#region Image - Drag - Drop
/// <summary>
/// Handles click event, opens a file dialog to choose and image file
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DragDrop_Click ( object sender , EventArgs e )
{
2021-06-09 17:53:36 +00:00
using OpenFileDialog openFileDialog = new OpenFileDialog
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
InitialDirectory = "c:\\" ,
Filter = @"image files (*.PNG;*.JPG;*.GIF)|*.*" ,
FilterIndex = 2 ,
RestoreDirectory = true
} ;
2021-06-09 16:43:27 +00:00
2021-06-09 17:53:36 +00:00
if ( openFileDialog . ShowDialog ( ) = = DialogResult . OK )
{
string filePath = openFileDialog . FileName ;
2021-06-09 16:43:27 +00:00
2021-06-09 17:53:36 +00:00
LoadFromFile ( filePath ) ;
2021-06-09 16:43:27 +00:00
}
}
/// <summary>
/// Loads an image file froim disk and sets the matrix to it.
/// If the image is an gif, the gif buffer will be set to the gif, as well as the matrix itself.
/// </summary>
/// <param name="filePath"></param>
2021-06-09 17:53:36 +00:00
private void LoadFromFile ( string filePath )
2021-06-09 16:43:27 +00:00
{
// load gif
if ( filePath . ToLower ( ) . EndsWith ( ".gif" ) )
{
var gif = Image . FromFile ( filePath ) ;
2021-06-09 17:53:36 +00:00
var frames = Math . Min ( gif . GetFrameCount ( FrameDimension . Time ) , 120 ) ;
2021-06-09 16:43:27 +00:00
if ( gif . GetFrameCount ( FrameDimension . Time ) > 120 )
{
2021-06-09 17:53:36 +00:00
MessageBox . Show ( @"Das Gif ist zu Groß. Die Maximalgröße sind 120 Frames. Das Gif wird abgeschnitten sein, damit es in die Maximalgröße passt." , @"Gif to large" ) ;
2021-06-09 16:43:27 +00:00
}
FrameCount . Value = frames ;
Timeline . Maximum = frames - 1 ;
// resize gif buffer to fit loaded gif frame count
ResizeGif ( ) ;
// fetch and store frames
2021-06-09 17:53:36 +00:00
for ( var i = 0 ; i < frames ; i + + )
2021-06-09 16:43:27 +00:00
{
gif . SelectActiveFrame ( FrameDimension . Time , i ) ;
// resize gif to fit scale
var bitmap = ResizeImage ( gif , matrixView . matrixWidth ( ) , matrixView . matrixHeight ( ) ) ;
// fetch each pixel and store
2021-06-09 17:53:36 +00:00
for ( var x = 0 ; x < bitmap . Width ; x + + )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
for ( var y = 0 ; y < bitmap . Height ; y + + )
2021-06-09 16:43:27 +00:00
{
var pixel = bitmap . GetPixel ( x , y ) ;
2021-06-09 17:53:36 +00:00
var index = x + y * bitmap . Width ;
2021-06-09 16:43:27 +00:00
matrixView . SetPixelNoRefresh ( x , y , pixel ) ;
2021-06-09 17:53:36 +00:00
gifBuffer [ i ] [ index * 3 ] = pixel . G ;
gifBuffer [ i ] [ index * 3 + 1 ] = pixel . R ;
gifBuffer [ i ] [ index * 3 + 2 ] = pixel . B ;
2021-06-09 16:43:27 +00:00
}
}
}
matrixView . Refresh ( ) ;
Timeline . Value = 0 ;
}
else
{
Bitmap bitmap = new Bitmap ( filePath ) ;
bitmap = ResizeImage ( bitmap , matrixView . matrixWidth ( ) , matrixView . matrixHeight ( ) ) ;
matrixView . SetImage ( bitmap ) ;
for ( int x = 0 ; x < bitmap . Width ; x + + )
{
for ( int y = 0 ; y < bitmap . Height ; y + + )
{
var pixel = bitmap . GetPixel ( x , y ) ;
int index = x + y * bitmap . Width ;
2021-06-09 17:53:36 +00:00
gifBuffer [ Timeline . Value ] [ index * 3 ] = pixel . G ;
gifBuffer [ Timeline . Value ] [ index * 3 + 1 ] = pixel . R ;
gifBuffer [ Timeline . Value ] [ index * 3 + 2 ] = pixel . B ;
2021-06-09 16:43:27 +00:00
}
}
}
2021-06-09 17:53:36 +00:00
WriteImage ( gifBuffer [ Timeline . Value ] ) ;
2021-06-09 16:43:27 +00:00
}
private void DragDrop_DragEnter ( object sender , DragEventArgs e )
{
if ( e . Data . GetDataPresent ( DataFormats . FileDrop ) )
e . Effect = DragDropEffects . Copy ;
else
e . Effect = DragDropEffects . None ;
}
private void DragDrop_DragDrop ( object sender , DragEventArgs e )
{
string [ ] picturePath = ( string [ ] ) e . Data . GetData ( DataFormats . FileDrop ) ;
2021-06-09 17:53:36 +00:00
LoadFromFile ( picturePath [ 0 ] ) ;
2021-06-09 16:43:27 +00:00
}
#endregion
#region Timeline
public void ResetTimeline ( )
{
Timeline . Value = 1 ;
Timeline . Value = 0 ;
}
public int GetDelayTime ( )
{
return ( int ) Delay . Value ;
}
private void FrameCount_ValueChanged ( object sender , EventArgs e )
{
ResizeGif ( ) ;
Timeline . Value = 0 ;
if ( FrameCount . Value = = 1 )
{
Timeline . Enabled = false ;
}
else
{
Timeline . Enabled = true ;
Timeline . Maximum = ( int ) FrameCount . Value - 1 ;
}
}
private void Timeline_ValueChanged ( object sender , EventArgs e )
{
int width = matrixView . matrixWidth ( ) ;
int height = matrixView . matrixHeight ( ) ;
for ( int y = 0 ; y < height ; y + + )
{
int index = y * width ;
for ( int x = 0 ; x < width ; x + + )
{
int tmp = ( index + x ) * 3 ;
2021-06-09 17:53:36 +00:00
var color = Color . FromArgb ( gifBuffer [ Timeline . Value ] [ tmp + 1 ] , gifBuffer [ Timeline . Value ] [ tmp ] , gifBuffer [ Timeline . Value ] [ tmp + 2 ] ) ;
2021-06-09 16:43:27 +00:00
matrixView . SetPixelNoRefresh ( x , y , color ) ;
}
}
matrixView . Refresh ( ) ;
2021-06-09 17:53:36 +00:00
WriteImage ( gifBuffer [ Timeline . Value ] ) ;
2021-06-09 16:43:27 +00:00
}
/// <summary>
/// Stores the current matrix at the index noted by the timeline into the Gif
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Apply_Click ( object sender , EventArgs e )
{
int width = matrixView . matrixWidth ( ) ;
int height = matrixView . matrixHeight ( ) ;
for ( int y = 0 ; y < height ; y + + )
{
int i = y * width ;
for ( int x = 0 ; x < width ; x + + )
{
int tmp = ( i + x ) * 3 ;
var color = matrixView . GetPixel ( x , y ) ;
2021-06-09 17:53:36 +00:00
gifBuffer [ Timeline . Value ] [ tmp ] = color . G ;
gifBuffer [ Timeline . Value ] [ tmp + 1 ] = color . R ;
gifBuffer [ Timeline . Value ] [ tmp + 2 ] = color . B ;
2021-06-09 16:43:27 +00:00
}
}
}
2021-06-09 17:53:36 +00:00
private void Timelineupdate ( Object source , ElapsedEventArgs e )
2021-06-09 16:43:27 +00:00
{
if ( Timeline . InvokeRequired )
{
// invoke on the comboboxes thread
Timeline . Invoke ( new Action ( ( ) = >
{
if ( Timeline . Value < Timeline . Maximum )
{
Timeline . Value = Timeline . Value + 1 ;
}
else
{
Timeline . Value = 0 ;
}
} ) ) ;
}
}
/// <summary>
/// Starts playing the timeline
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Play_Click ( object sender , EventArgs e )
{
2021-06-09 17:53:36 +00:00
if ( FrameCount . Value ! = 1 )
2021-06-09 16:43:27 +00:00
{
if ( ! runningGif )
{
2021-06-09 17:53:36 +00:00
Play . Text = @"Stop" ;
2021-06-09 16:43:27 +00:00
Timeline . Value = 0 ;
runningGif = true ;
2021-06-09 17:53:36 +00:00
if ( delay ! = null )
delay . Enabled = true ;
2021-06-09 16:43:27 +00:00
2021-06-09 17:53:36 +00:00
Play . Image = new Bitmap ( Properties . Resources . Stop ) ;
2021-06-09 16:43:27 +00:00
}
else
{
2021-06-09 17:53:36 +00:00
Play . Image = new Bitmap ( Properties . Resources . Play ) ;
Play . Text = @"Play" ;
2021-06-09 16:43:27 +00:00
runningGif = false ;
2021-06-09 17:53:36 +00:00
if ( delay ! = null )
delay . Enabled = false ;
2021-06-09 16:43:27 +00:00
}
}
}
private void Timeline_MouseDown ( object sender , MouseEventArgs e )
{
if ( runningGif )
{
2021-06-09 17:53:36 +00:00
Play . Image = new Bitmap ( Properties . Resources . Play ) ;
Play . Text = @"Play" ;
2021-06-09 16:43:27 +00:00
runningGif = false ;
2021-06-09 17:53:36 +00:00
if ( delay ! = null )
delay . Enabled = false ;
2021-06-09 16:43:27 +00:00
}
}
2021-06-09 17:53:36 +00:00
private void Delay_ValueChanged ( object sender , EventArgs _ )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
if ( delay ! = null )
delay . Interval = ( int ) Delay . Value ;
2021-06-09 16:43:27 +00:00
}
#endregion
#region Properties
private void Save_Click ( object sender , EventArgs e )
{
2021-06-09 17:53:36 +00:00
SaveFileDialog save = new SaveFileDialog
{
InitialDirectory = "c:\\" ,
Filter = @"image files (*.PNG;*.JPG;*.GIF)|*.*" ,
FilterIndex = 2 ,
RestoreDirectory = true
} ;
2021-06-09 16:43:27 +00:00
if ( save . ShowDialog ( ) = = DialogResult . OK )
{
string filePath = save . FileName ;
2021-06-09 17:53:36 +00:00
Bitmap [ ] gifBitmap = new Bitmap [ gifBuffer . Length ] ;
2021-06-09 16:43:27 +00:00
GifWriter writer = new GifWriter ( File . Create ( filePath ) ) ;
2021-06-09 17:53:36 +00:00
for ( var i = 0 ; i < FrameCount . Value ; i + + )
2021-06-09 16:43:27 +00:00
{
gifBitmap [ i ] = new Bitmap ( ( int ) matrixWidth . Value , ( int ) matrixHeight . Value ) ;
2021-06-09 17:53:36 +00:00
for ( var j = 0 ; j < gifBuffer [ i ] . Length / 3 ; j + + )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
var y = j / ( int ) matrixWidth . Value ;
var x = j % ( int ) matrixWidth . Value ;
2021-06-09 16:43:27 +00:00
2021-06-09 17:53:36 +00:00
gifBitmap [ i ] . SetPixel ( x , y , Color . FromArgb ( gifBuffer [ i ] [ j * 3 + 1 ] , gifBuffer [ i ] [ j * 3 ] , gifBuffer [ i ] [ j * 3 + 2 ] ) ) ;
2021-06-09 16:43:27 +00:00
}
writer . WriteFrame ( gifBitmap [ i ] , ( int ) Delay . Value ) ;
}
writer . Dispose ( ) ;
}
}
private void ConfigButton_Click ( object sender , EventArgs e )
{
commandQueue . EnqueueArduinoCommand ( 4 ) ;
commandQueue . WaitForLastDequeue ( ) ;
byte [ ] data = commandQueue . GetLastData ( ) ;
if ( commandQueue . GetMark ( ) > 0 )
{
int width = data [ 0 ] ;
int height = data [ 1 ] ;
this . matrixWidth . Value = width ;
this . matrixHeight . Value = height ;
2021-06-09 17:53:36 +00:00
for ( var x = 0 ; x < width * height * 3 ; x + + )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
gifBuffer [ 0 ] [ x ] = data [ 2 + x ] ;
2021-06-09 16:43:27 +00:00
}
Timeline . Value = 1 ;
Timeline . Value = 0 ;
}
}
private void showGridCheckbox_CheckedChanged ( object sender , EventArgs e )
{
matrixView . DrawGrid = showGridCheckbox . Checked ;
matrixView . Refresh ( ) ;
}
private void BrushSizeSlider_Scroll ( object sender , EventArgs e )
{
matrixView . brushSize = BrushSizeSlider . Value ;
}
/// <summary>
/// Resizes the Gif image buffer
/// </summary>
private void ResizeGif ( )
{
int frames = ( int ) FrameCount . Value ;
2021-06-09 17:53:36 +00:00
gifBuffer = new byte [ frames + 1 ] [ ] ;
2021-06-09 16:43:27 +00:00
for ( int i = 0 ; i < = frames ; i + + )
{
2021-06-09 17:53:36 +00:00
gifBuffer [ i ] = new byte [ matrixView . matrixWidth ( ) * matrixView . matrixHeight ( ) * 3 ] ;
2021-06-09 16:43:27 +00:00
}
}
#endregion
#region Appearance
private void ApplyDarkModeButton_Click ( object sender , EventArgs e )
{
new DarkMode ( ) . ApplyTheme ( this ) ;
}
private void ApplyLightModeButton_Click ( object sender , EventArgs e )
{
new LightMode ( ) . ApplyTheme ( this ) ;
}
#endregion
#endregion
#region IO - Utils
2021-06-09 17:53:36 +00:00
private void WriteImage ( byte [ ] rgbImageData )
2021-06-09 16:43:27 +00:00
{
2021-06-09 17:53:36 +00:00
commandQueue . EnqueueArduinoCommand ( OpcodeImage , rgbImageData ) ;
2021-06-09 16:43:27 +00:00
}
/// <summary>
/// Converts the matrix's pixel ARGB buffer to an 3-byte RGB tuple array and sends them to the arduino
/// </summary>
public void EnqueuePixelSet ( )
{
var pixels = matrixView . getPixels ( ) ;
byte [ ] image = new byte [ pixels . Length * 3 ] ;
for ( int x = 0 ; x < pixels . Length ; x + + )
{
image [ x * 3 ] = ( byte ) ( pixels [ x ] > > 8 & 0xFF ) ;
image [ x * 3 + 1 ] = ( byte ) ( pixels [ x ] > > 16 & 0xFF ) ;
image [ x * 3 + 2 ] = ( byte ) ( pixels [ x ] & 0xFF ) ;
}
2021-06-09 17:53:36 +00:00
WriteImage ( image ) ;
2021-06-09 16:43:27 +00:00
}
#endregion
}
}