// Source thanks to https://github.com/vddCore/Starlight with some adjustments from me using GHelper.AnimeMatrix.Communication; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Text; namespace GHelper.AnimeMatrix { public class BuiltInAnimation { public enum Startup { GlitchConstruction, StaticEmergence } public enum Shutdown { GlitchOut, SeeYa } public enum Sleeping { BannerSwipe, Starfield } public enum Running { BinaryBannerScroll, RogLogoGlitch } public byte AsByte { get; } public BuiltInAnimation( Running running, Sleeping sleeping, Shutdown shutdown, Startup startup ) { AsByte |= (byte)(((int)running & 0x01) << 0); AsByte |= (byte)(((int)sleeping & 0x01) << 1); AsByte |= (byte)(((int)shutdown & 0x01) << 2); AsByte |= (byte)(((int)startup & 0x01) << 3); } } public enum MatrixRotation { Planar, Diagonal } internal class AnimeMatrixPacket : Packet { public AnimeMatrixPacket(byte[] command) : base(0x5E, 640, command) { } } public enum AnimeType { GA401, GA402, GU604 } public enum BrightnessMode : byte { Off = 0, Dim = 1, Medium = 2, Full = 3 } public class AnimeMatrixDevice : Device { int UpdatePageLength = 490; int LedCount = 1450; byte[] _displayBuffer; List frames = new List(); public int MaxRows = 61; public int MaxColumns = 34; public int LedStart = 0; public int FullRows = 11; private int frameIndex = 0; private static AnimeType _model = AnimeType.GA402; [System.Runtime.InteropServices.DllImport("gdi32.dll")] private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [System.Runtime.InteropServices.In] ref uint pcFonts); private PrivateFontCollection fonts = new PrivateFontCollection(); public AnimeMatrixDevice() : base(0x0B05, 0x193B, 640) { if (AppConfig.ContainsModel("401")) { _model = AnimeType.GA401; MaxColumns = 33; MaxRows = 55; LedCount = 1245; UpdatePageLength = 410; FullRows = 5; LedStart = 1; } if (AppConfig.ContainsModel("GU604")) { _model = AnimeType.GU604; MaxColumns = 39; MaxRows = 92; LedCount = 1711; UpdatePageLength = 630; FullRows = 9; } _displayBuffer = new byte[LedCount]; LoadMFont(); } public void WakeUp() { Set(Packet(Encoding.ASCII.GetBytes("ASUS Tech.Inc."))); } public void SetBrightness(BrightnessMode mode) { Set(Packet(0xC0, 0x04, (byte)mode)); } public void SetDisplayState(bool enable) { Set(Packet(0xC3, 0x01, enable ? (byte)0x00 : (byte)0x80)); } public void SetBuiltInAnimation(bool enable) { Set(Packet(0xC4, 0x01, enable ? (byte)0x00 : (byte)0x80)); } public void SetBuiltInAnimation(bool enable, BuiltInAnimation animation) { SetBuiltInAnimation(enable); Set(Packet(0xC5, animation.AsByte)); } public void Present() { int page = 0; int start, end; while (page * UpdatePageLength < LedCount) { start = page * UpdatePageLength; end = Math.Min(LedCount, (page + 1) * UpdatePageLength); Set(Packet(0xC0, 0x02) .AppendData(BitConverter.GetBytes((ushort)(start + 1))) .AppendData(BitConverter.GetBytes((ushort)(end - start))) .AppendData(_displayBuffer[start..end]) ); page++; } Set(Packet(0xC0, 0x03)); } private void LoadMFont() { byte[] fontData = GHelper.Properties.Resources.MFont; IntPtr fontPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(fontData.Length); System.Runtime.InteropServices.Marshal.Copy(fontData, 0, fontPtr, fontData.Length); uint dummy = 0; fonts.AddMemoryFont(fontPtr, GHelper.Properties.Resources.MFont.Length); AddFontMemResourceEx(fontPtr, (uint)GHelper.Properties.Resources.MFont.Length, IntPtr.Zero, ref dummy); System.Runtime.InteropServices.Marshal.FreeCoTaskMem(fontPtr); } public void PresentNextFrame() { if (frameIndex >= frames.Count) frameIndex = 0; _displayBuffer = frames[frameIndex]; Present(); frameIndex++; } public void ClearFrames() { frames.Clear(); frameIndex = 0; } public void AddFrame() { frames.Add(_displayBuffer.ToArray()); } public int Width() { switch (_model) { case AnimeType.GA401: return 33; case AnimeType.GU604: return 39; default: return 34; } } public int FirstX(int y) { switch (_model) { case AnimeType.GA401: if (y < 5 && y % 2 == 0) { return 1; } return (int)Math.Ceiling(Math.Max(0, y - 5) / 2F); case AnimeType.GU604: if (y < 9 && y % 2 == 0) { return 1; } return (int)Math.Ceiling(Math.Max(0, y - 9) / 2F); default: return (int)Math.Ceiling(Math.Max(0, y - 11) / 2F); } } public int Pitch(int y) { switch (_model) { case AnimeType.GA401: switch (y) { case 0: case 2: case 4: return 33; case 1: case 3: return 35; default: return 36 - y / 2; } case AnimeType.GU604: switch (y) { case 0: case 2: case 4: case 6: case 8: return 38; case 1: case 3: case 5: case 7: case 9: return 39; default: return Width() - FirstX(y); } default: return Width() - FirstX(y); } } public int RowToLinearAddress(int y) { int ret = LedStart; for (var i = 0; i < y; i++) ret += Pitch(i); return ret; } public void SetLedPlanar(int x, int y, byte value) { if (!IsRowInRange(y)) return; if (x >= FirstX(y) && x < Width()) SetLedLinear(RowToLinearAddress(y) - FirstX(y) + x, value); } public void SetLedDiagonal(int x, int y, byte color, int deltaX = 0, int deltaY = 0) { x += deltaX; y -= deltaY; int plX = (x - y) / 2; int plY = x + y; if (x - y == -1) plX = -1; SetLedPlanar(plX, plY, color); } public void SetLedLinear(int address, byte value) { if (!IsAddressableLed(address)) return; _displayBuffer[address] = value; } public void Clear(bool present = false) { for (var i = 0; i < _displayBuffer.Length; i++) _displayBuffer[i] = 0; if (present) Present(); } private void SetBitmapDiagonal(Bitmap bmp, int deltaX = 0, int deltaY = 0, int contrast = 100, int gamma = 0) { for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { var pixel = bmp.GetPixel(x, y); var color = Math.Min((pixel.R + pixel.G + pixel.B + gamma) * contrast / 300, 255); if (color > 20) SetLedDiagonal(x, y, (byte)color, deltaX, deltaY - (FullRows / 2) - 1); } } } private void SetBitmapLinear(Bitmap bmp, int contrast = 100, int gamma = 0) { for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) if (x % 2 == y % 2) { var pixel = bmp.GetPixel(x, y); var color = Math.Min((pixel.R + pixel.G + pixel.B + gamma) * contrast / 300, 255); if (color > 20) SetLedPlanar(x / 2, y, (byte)color); } } } public void Text(string text, float fontSize = 10, int x = 0, int y = 0) { int width = MaxRows; int height = MaxRows - FullRows; int textHeight, textWidth; using (Bitmap bmp = new Bitmap(width, height)) { using (Graphics g = Graphics.FromImage(bmp)) { g.CompositingQuality = CompositingQuality.HighQuality; g.SmoothingMode = SmoothingMode.AntiAlias; g.TextRenderingHint = TextRenderingHint.SingleBitPerPixel; using (Font font = new Font(fonts.Families[0], fontSize, FontStyle.Regular, GraphicsUnit.Pixel)) { SizeF textSize = g.MeasureString(text, font); textHeight = (int)textSize.Height; textWidth = (int)textSize.Width; g.DrawString(text, font, Brushes.White, x, height - y); } } SetBitmapDiagonal(bmp, 5, height); } } public void PresentClock() { string second = (DateTime.Now.Second % 2 == 0) ? ":" : " "; string time = DateTime.Now.ToString("HH" + second + "mm"); Clear(); Text(time, 15, 0, 25); Text(DateTime.Now.ToString("yy'. 'MM'. 'dd"), 11.5F, 0, 14); Present(); } public void GenerateFrame(Image image, float zoom = 100, int panX = 0, int panY = 0, InterpolationMode quality = InterpolationMode.Default, int contrast = 100, int gamma = 0) { int width = MaxColumns / 2 * 6; int height = MaxRows; int targetWidth = MaxColumns * 2; float scale; using (Bitmap bmp = new Bitmap(targetWidth, height)) { scale = Math.Min((float)width / (float)image.Width, (float)height / (float)image.Height) * zoom / 100; using (var graph = Graphics.FromImage(bmp)) { var scaleWidth = (float)(image.Width * scale); var scaleHeight = (float)(image.Height * scale); graph.InterpolationMode = quality; graph.CompositingQuality = CompositingQuality.HighQuality; graph.SmoothingMode = SmoothingMode.AntiAlias; graph.DrawImage(image, (float)Math.Round(targetWidth - (scaleWidth + panX) * targetWidth / width), -panY, (float)Math.Round(scaleWidth * targetWidth / width), scaleHeight); } Clear(); SetBitmapLinear(bmp, contrast, gamma); } } public void GenerateFrameDiagonal(Image image, float zoom = 100, int panX = 0, int panY = 0, InterpolationMode quality = InterpolationMode.Default, int contrast = 100, int gamma = 0) { int width = MaxRows + FullRows; int height = MaxColumns + FullRows; if ((image.Height / image.Width) > (height / width)) height = MaxColumns; float scale; using (Bitmap bmp = new Bitmap(width, height)) { scale = Math.Min((float)width / (float)image.Width, (float)height / (float)image.Height) * zoom / 100; using (var graph = Graphics.FromImage(bmp)) { var scaleWidth = (float)(image.Width * scale); var scaleHeight = (float)(image.Height * scale); graph.InterpolationMode = quality; graph.CompositingQuality = CompositingQuality.HighQuality; graph.SmoothingMode = SmoothingMode.AntiAlias; graph.DrawImage(image, (width - scaleWidth) / 2, height - scaleHeight, scaleWidth, scaleHeight); } Clear(); SetBitmapDiagonal(bmp, -panX, height + panY, contrast, gamma); } } private bool IsRowInRange(int row) { return (row >= 0 && row < MaxRows); } private bool IsAddressableLed(int address) { return (address >= 0 && address < LedCount); } } }