mirror of
https://github.com/YellowberryHN/WACCALauncher
synced 2024-11-23 22:51:01 +01:00
Add one-time version selection, VFD util updates, added launch option for offline version.
This commit is contained in:
parent
0400ee4472
commit
5da9d62616
67
MainForm.cs
67
MainForm.cs
@ -12,6 +12,7 @@ using IniParser.Model;
|
||||
using SharpDX.DirectInput;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using WACCA;
|
||||
|
||||
namespace WACCALauncher
|
||||
{
|
||||
@ -43,6 +44,7 @@ namespace WACCALauncher
|
||||
private bool _gameRunning = false;
|
||||
|
||||
public MenuManager _menuManager;
|
||||
private VFD _vfd;
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
@ -218,15 +220,16 @@ namespace WACCALauncher
|
||||
|
||||
private static void vfd_test()
|
||||
{
|
||||
var vfd = new WaccaVFD();
|
||||
vfd.Power(true);
|
||||
vfd.Clear();
|
||||
vfd.Brightness(WaccaVFD.bright.BRIGHT_50);
|
||||
vfd.Cursor(0, 0);
|
||||
var vfd = new VFD();
|
||||
vfd.Reset();
|
||||
vfd.PowerOn();
|
||||
vfd.Brightness(VFD.Bright._50);
|
||||
vfd.CanvasShift(0);
|
||||
vfd.Cursor(0, 0);
|
||||
vfd.FontSize(VFD.Font._16_16);
|
||||
vfd.Write("Testing VFD!");
|
||||
vfd.Cursor(0, 16);
|
||||
vfd.ScrollSpeed(2);
|
||||
vfd.Cursor(0, 2);
|
||||
vfd.ScrollSpeed(15);
|
||||
vfd.ScrollText(Math.PI.ToString() + " ");
|
||||
vfd.ScrollStart();
|
||||
}
|
||||
@ -266,8 +269,8 @@ namespace WACCALauncher
|
||||
LoadVersionsFromConfig();
|
||||
|
||||
var mainMenu = new ConfigMenu("Launcher Settings", items: new List<ConfigMenu>() {
|
||||
new ConfigMenu("one-time launch", ConfigMenuAction.Menu, items: GetOTLMenu()),
|
||||
new ConfigMenu("set default version", ConfigMenuAction.Menu, items: GetDefaultVersionMenu()),
|
||||
new ConfigMenu("test VFD", ConfigMenuAction.Command, method: vfd_test),
|
||||
new ConfigMenu("exit to windows", ConfigMenuAction.Command, method: Application.Exit),
|
||||
new ConfigMenu("launch game", ConfigMenuAction.Return)
|
||||
});
|
||||
@ -285,7 +288,7 @@ namespace WACCALauncher
|
||||
foreach (var ver in Versions)
|
||||
{
|
||||
var name = ver.GameVersion == VersionType.Custom ? ver.CustomName : ver.ToString();
|
||||
defVerMenu.Add(new ConfigMenu($"({(ver == DefaultVer ? 'X' : ' ')}) {name}", ConfigMenuAction.VersionSelect, version: ver));
|
||||
defVerMenu.Add(new ConfigMenu($"({(ver == DefaultVer ? 'X' : ' ')}) {name}", ConfigMenuAction.VersionSelect, version: ver, defVer: true));
|
||||
}
|
||||
|
||||
defVerMenu.Add(new ConfigMenu("Return to settings", ConfigMenuAction.Return));
|
||||
@ -293,6 +296,21 @@ namespace WACCALauncher
|
||||
return defVerMenu;
|
||||
}
|
||||
|
||||
public List<ConfigMenu> GetOTLMenu()
|
||||
{
|
||||
var otlMenu = new List<ConfigMenu>();
|
||||
|
||||
foreach (var ver in Versions)
|
||||
{
|
||||
var name = ver.GameVersion == VersionType.Custom ? ver.CustomName : ver.ToString();
|
||||
otlMenu.Add(new ConfigMenu(name, ConfigMenuAction.VersionSelect, version: ver, defVer: false));
|
||||
}
|
||||
|
||||
otlMenu.Add(new ConfigMenu("Return to settings", ConfigMenuAction.Return));
|
||||
|
||||
return otlMenu;
|
||||
}
|
||||
|
||||
private static void KillExplorer()
|
||||
{
|
||||
Process.Start(@"C:\Windows\System32\taskkill.exe", @"/F /IM explorer.exe");
|
||||
@ -304,7 +322,7 @@ namespace WACCALauncher
|
||||
if (processes.Length == 0) Process.Start("explorer.exe");
|
||||
}
|
||||
|
||||
private void LaunchGame(Version version)
|
||||
public void LaunchGame(Version version)
|
||||
{
|
||||
Console.WriteLine("launching game");
|
||||
_gameProcess.StartInfo.FileName = version.BatchPath;
|
||||
@ -438,6 +456,11 @@ namespace WACCALauncher
|
||||
Controls.Add(errorLabel);
|
||||
}
|
||||
|
||||
public void StopTimer()
|
||||
{
|
||||
_delayTimer.Stop();
|
||||
}
|
||||
|
||||
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
OpenExplorer();
|
||||
@ -452,6 +475,7 @@ namespace WACCALauncher
|
||||
Lily,
|
||||
Lily_R,
|
||||
Reverse,
|
||||
Offline,
|
||||
Custom = 10
|
||||
}
|
||||
|
||||
@ -516,6 +540,7 @@ namespace WACCALauncher
|
||||
private readonly Action _method;
|
||||
private readonly List<string> _options;
|
||||
private readonly Version _version;
|
||||
private readonly bool _defVer;
|
||||
|
||||
public void Select(MainForm form)
|
||||
{
|
||||
@ -535,15 +560,26 @@ namespace WACCALauncher
|
||||
}
|
||||
else if (_action == ConfigMenuAction.VersionSelect && _version != null)
|
||||
{
|
||||
Console.WriteLine($"setting default version to {_version}");
|
||||
form.SetDefaultVer(_version);
|
||||
// TODO: this is kinda jank, fix this
|
||||
form._menuManager.UpdateCurrentMenuItems(form.GetDefaultVersionMenu());
|
||||
if(_defVer)
|
||||
{
|
||||
Console.WriteLine($"setting default version to {_version}");
|
||||
form.SetDefaultVer(_version);
|
||||
// TODO: this is kinda jank, fix this
|
||||
form._menuManager.UpdateCurrentMenuItems(form.GetDefaultVersionMenu());
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"one-time launch for {_version}");
|
||||
form.MenuHide();
|
||||
form.StopTimer();
|
||||
form.LaunchGame(_version);
|
||||
}
|
||||
|
||||
}
|
||||
else if (_action == ConfigMenuAction.Return) { form._menuManager.MenuBack(); }
|
||||
}
|
||||
|
||||
public ConfigMenu(string name, ConfigMenuAction action = ConfigMenuAction.None, Action method = null, List<ConfigMenu> items = null, List<string> options = null, Version version = null)
|
||||
public ConfigMenu(string name, ConfigMenuAction action = ConfigMenuAction.None, Action method = null, List<ConfigMenu> items = null, List<string> options = null, Version version = null, bool defVer = false)
|
||||
{
|
||||
this.Name = name;
|
||||
this._action = action;
|
||||
@ -561,6 +597,7 @@ namespace WACCALauncher
|
||||
this._method = method;
|
||||
this._options = options;
|
||||
this._version = version;
|
||||
this._defVer = defVer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
393
WaccaVFD.cs
393
WaccaVFD.cs
@ -1,36 +1,239 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.IO.Ports;
|
||||
using System.Security.Policy;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
using System.Linq;
|
||||
using System.Data.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace WACCALauncher
|
||||
namespace WACCA
|
||||
{
|
||||
class WaccaVFD
|
||||
//
|
||||
// Summary:
|
||||
// Represents an ordered pair of integer x- and y-coordinates that defines a point
|
||||
// in a two-dimensional plane.
|
||||
[Serializable]
|
||||
[TypeConverter(typeof(PointConverter))]
|
||||
[ComVisible(true)]
|
||||
public struct VFDPoint
|
||||
{
|
||||
public static readonly VFDPoint Empty;
|
||||
|
||||
private short x;
|
||||
|
||||
private byte y;
|
||||
|
||||
|
||||
[Browsable(false)]
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
if (x == 0)
|
||||
{
|
||||
return y == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public short X
|
||||
{
|
||||
get
|
||||
{
|
||||
return x;
|
||||
}
|
||||
set
|
||||
{
|
||||
x = value;
|
||||
}
|
||||
}
|
||||
|
||||
public byte Y
|
||||
{
|
||||
get
|
||||
{
|
||||
return y;
|
||||
}
|
||||
set
|
||||
{
|
||||
y = value;
|
||||
}
|
||||
}
|
||||
|
||||
public VFDPoint(short x, byte y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public VFDPoint(Size sz)
|
||||
{
|
||||
x = (short)sz.Width;
|
||||
y = (byte)sz.Height;
|
||||
}
|
||||
|
||||
public static explicit operator Size(VFDPoint p)
|
||||
{
|
||||
return new Size(p.X, p.Y);
|
||||
}
|
||||
|
||||
public static VFDPoint operator +(VFDPoint pt, Size sz)
|
||||
{
|
||||
return Add(pt, sz);
|
||||
}
|
||||
|
||||
public static VFDPoint operator -(VFDPoint pt, Size sz)
|
||||
{
|
||||
return Subtract(pt, sz);
|
||||
}
|
||||
|
||||
public static bool operator ==(VFDPoint left, VFDPoint right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
return left.Y == right.Y;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool operator !=(VFDPoint left, VFDPoint right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static VFDPoint Add(VFDPoint pt, Size sz)
|
||||
{
|
||||
return new VFDPoint((short)(pt.X + sz.Width), (byte)(pt.Y + sz.Height));
|
||||
}
|
||||
|
||||
public static VFDPoint Subtract(VFDPoint pt, Size sz)
|
||||
{
|
||||
return new VFDPoint((short)(pt.X - sz.Width), (byte)(pt.Y - sz.Height));
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is VFDPoint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
VFDPoint point = (VFDPoint)obj;
|
||||
if (point.X == X)
|
||||
{
|
||||
return point.Y == Y;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x ^ y;
|
||||
}
|
||||
|
||||
public void Offset(short dx, byte dy)
|
||||
{
|
||||
X += dx;
|
||||
Y += dy;
|
||||
}
|
||||
|
||||
public void Offset(VFDPoint p)
|
||||
{
|
||||
Offset(p.X, p.Y);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{X=" + X.ToString(CultureInfo.CurrentCulture) + ",Y=" + Y.ToString(CultureInfo.CurrentCulture) + "}";
|
||||
}
|
||||
}
|
||||
|
||||
class VFD
|
||||
{
|
||||
SerialPort port;
|
||||
public Lang language { get; private set; } = Lang.SIMP_CHINESE;
|
||||
public Font font { get; private set; } = Font._16_16;
|
||||
public Bright brightness { get; private set; } = Bright._100;
|
||||
public bool power { get; private set; } = false;
|
||||
|
||||
public WaccaVFD(string portName = "COM2")
|
||||
/// <summary>
|
||||
/// Establish a connection to a VFD, and prepare it for use.
|
||||
/// </summary>
|
||||
/// <param name="portName">The port that the VFD connected to</param>
|
||||
public VFD(string portName = "COM2")
|
||||
{
|
||||
this.port = new SerialPort(portName, 115200);
|
||||
port = new SerialPort(portName, 115200);
|
||||
port.Open();
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void VFD_Write(byte number)
|
||||
{
|
||||
VFD_Write($"{(char)number}");
|
||||
Console.WriteLine(BitConverter.ToString(new byte[] { number }));
|
||||
port.Write(new byte[] { number }, 0, 1);
|
||||
}
|
||||
|
||||
private void VFD_Write(byte[] bytes)
|
||||
{
|
||||
Console.WriteLine(BitConverter.ToString(bytes));
|
||||
port.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
private void VFD_Write(string text)
|
||||
{
|
||||
Console.WriteLine(BitConverter.ToString(Encoding.Default.GetBytes(text)));
|
||||
port.Write(text);
|
||||
// Get correct encoding for current language
|
||||
int codeNumber;
|
||||
switch(language)
|
||||
{
|
||||
case Lang.SIMP_CHINESE:
|
||||
codeNumber = 936; // GB2312
|
||||
break;
|
||||
case Lang.TRAD_CHINESE:
|
||||
codeNumber = 950; // Big5
|
||||
break;
|
||||
case Lang.JAPANESE:
|
||||
codeNumber = 932; // Shift-JIS
|
||||
break;
|
||||
case Lang.KOREAN:
|
||||
codeNumber = 949; // KSC5601
|
||||
break;
|
||||
default:
|
||||
codeNumber = 932;
|
||||
break;
|
||||
}
|
||||
|
||||
// Convert Unicode string to encoded bytes
|
||||
Encoding unicodeEncoding = Encoding.Unicode;
|
||||
Encoding correctEncoding = Encoding.GetEncoding(codeNumber);
|
||||
byte[] unicodeBytes = unicodeEncoding.GetBytes(text);
|
||||
byte[] encodedBytes = Encoding.Convert(unicodeEncoding, correctEncoding, unicodeBytes);
|
||||
|
||||
Console.WriteLine(BitConverter.ToString(correctEncoding.GetBytes(text)));
|
||||
port.Write(encodedBytes, 0, encodedBytes.Length);
|
||||
}
|
||||
|
||||
/*
|
||||
#define LEFT_HI(x) (((x) & 0x100) >> 8)
|
||||
#define LEFT_LO(x) ((x) & 0xFF)
|
||||
|
||||
#define FTB_PORT_WRITE_LEFT(x) {FTB_PORT.write(LEFT_HI(x)); FTB_PORT.write(LEFT_LO(x));}
|
||||
*/
|
||||
|
||||
private void VFD_WriteShort(short x)
|
||||
{
|
||||
char hi = (char)((x & 0x100) >> 8);
|
||||
char lo = (char)(x & 0xFF);
|
||||
VFD_Write($"{hi}{lo}");
|
||||
byte hi = (byte)(((x) & 0x100) >> 8);
|
||||
byte lo = (byte)((x) & 0xFF);
|
||||
VFD_Write(new byte[] {hi, lo});
|
||||
}
|
||||
|
||||
public void Write(string text)
|
||||
@ -40,71 +243,89 @@ namespace WACCALauncher
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
VFD_Write("\x1B\x0B");
|
||||
VFD_Write(new byte[] { 0x1B, 0x0B });
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
VFD_Write("\x1B\x0C");
|
||||
VFD_Write(new byte[] { 0x1B, 0x0C });
|
||||
}
|
||||
|
||||
public enum bright {
|
||||
BRIGHT_0 = 0,
|
||||
BRIGHT_25 = 1,
|
||||
BRIGHT_50 = 2,
|
||||
BRIGHT_75 = 3,
|
||||
BRIGHT_100 = 4
|
||||
}
|
||||
|
||||
public void Brightness(bright brightness)
|
||||
public void TestPayload() // fucked
|
||||
{
|
||||
VFD_Write("\x1B\x20" + (char)brightness);
|
||||
VFD_Write(Encoding.ASCII.GetString(new byte[] { 0x1b, 0x0c, 0x1b, 0x52, 0x1b, 0x40, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x02, 0x1b, 0x41, 0x00, 0x1b, 0x50, 0x50, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x1b, 0x51}));
|
||||
}
|
||||
|
||||
public void Power(bool on)
|
||||
public enum Bright {
|
||||
_0 = 0,
|
||||
_25 = 1,
|
||||
_50 = 2,
|
||||
_75 = 3,
|
||||
_100 = 4
|
||||
}
|
||||
|
||||
public void Brightness(Bright brightness)
|
||||
{
|
||||
VFD_Write("\x1B\x21" + (on ? "\x01" : "\x00"));
|
||||
VFD_Write(new byte[] { 0x1B, 0x20, (byte)brightness });
|
||||
}
|
||||
|
||||
public void PowerOn()
|
||||
{
|
||||
Power(true);
|
||||
}
|
||||
|
||||
public void PowerOff()
|
||||
{
|
||||
Power(false);
|
||||
}
|
||||
|
||||
private void Power(bool on)
|
||||
{
|
||||
VFD_Write(new byte[] { 0x1B, 0x21, (byte)(on ? 0x01 : 0x00) });
|
||||
}
|
||||
|
||||
public void CanvasShift(short left)
|
||||
{
|
||||
VFD_Write("\x1B\x22");
|
||||
VFD_Write(new byte[] { 0x1B, 0x22 });
|
||||
VFD_WriteShort(left);
|
||||
}
|
||||
|
||||
public void Cursor(short left, byte top)
|
||||
{
|
||||
VFD_Write("\x1B\x30");
|
||||
VFD_Write(new byte[] { 0x1B, 0x30 });
|
||||
VFD_WriteShort(left);
|
||||
VFD_Write(top);
|
||||
}
|
||||
|
||||
public enum lang {
|
||||
public enum Lang {
|
||||
SIMP_CHINESE,
|
||||
TRAD_CHINESE,
|
||||
JAPANESE,
|
||||
KOREAN
|
||||
}
|
||||
|
||||
public void Language(lang language)
|
||||
public void Language(Lang lang)
|
||||
{
|
||||
VFD_Write("\x1B\x32" + (char)language);
|
||||
language = lang;
|
||||
VFD_Write(new byte[] { 0x1B, 0x32, (byte)language });
|
||||
}
|
||||
|
||||
public enum font_size
|
||||
public enum Font
|
||||
{
|
||||
FONT_16_16,
|
||||
FONT_6_8
|
||||
_16_16,
|
||||
_6_8
|
||||
}
|
||||
|
||||
public void FontSize(font_size size)
|
||||
public void FontSize(Font size)
|
||||
{
|
||||
VFD_Write("\x1B\x33" + (char)size);
|
||||
// 3
|
||||
font = size;
|
||||
VFD_Write(new byte[] { 0x1B, 0x33, (byte)size });
|
||||
}
|
||||
|
||||
public void CreateScrollBox(short left, byte top, short width, byte height)
|
||||
{
|
||||
VFD_Write("\x1B\x40");
|
||||
VFD_Write(new byte[] { 0x1B, 0x40 });
|
||||
VFD_WriteShort(left);
|
||||
VFD_Write(top);
|
||||
VFD_WriteShort(width);
|
||||
@ -113,20 +334,112 @@ namespace WACCALauncher
|
||||
|
||||
public void ScrollSpeed(byte divisor)
|
||||
{
|
||||
VFD_Write("\x1B\x33" + (char)divisor);
|
||||
VFD_Write(new byte[] { 0x1B, 0x41, (byte)divisor });
|
||||
}
|
||||
|
||||
public void ScrollText(string text)
|
||||
{
|
||||
if (text.Length > 255) throw new ArgumentOutOfRangeException("Text is too long.");
|
||||
VFD_Write("\x1B\x50");
|
||||
VFD_Write((byte)text.Length);
|
||||
if (text.Length >= 0x100) throw new ArgumentOutOfRangeException("Text is too long.");
|
||||
VFD_Write(new byte[] { 0x1B, 0x50, (byte)text.Length });
|
||||
VFD_Write(text);
|
||||
}
|
||||
|
||||
public void ScrollStart()
|
||||
{
|
||||
VFD_Write("\x1B\x51");
|
||||
VFD_Write(new byte[] { 0x1B, 0x51 });
|
||||
}
|
||||
|
||||
public void ScrollStop()
|
||||
{
|
||||
VFD_Write(new byte[] { 0x1B, 0x52 });
|
||||
}
|
||||
|
||||
public enum BlinkMode
|
||||
{
|
||||
Off = 0,
|
||||
Invert = 1,
|
||||
All = 2
|
||||
}
|
||||
|
||||
public void BlinkSet(BlinkMode blink, byte interval)
|
||||
{
|
||||
VFD_Write(new byte[] { 0x1B, 0x23, (byte)blink, interval });
|
||||
}
|
||||
|
||||
public void ClearLine(byte line)
|
||||
{
|
||||
Cursor(0, line);
|
||||
VFD_Write("".PadLeft(20));
|
||||
Cursor(0, line);
|
||||
}
|
||||
|
||||
public void DrawBitmap(Bitmap bmp, Point origin)
|
||||
{
|
||||
if (bmp.PixelFormat != PixelFormat.Format1bppIndexed)
|
||||
throw new ArgumentException("Provided bitmap is not monochrome");
|
||||
|
||||
// We have to do it this way because of a GDI+ bug
|
||||
bmp.RotateFlip(RotateFlipType.Rotate270FlipNone);
|
||||
RotateNoneFlipYMono(bmp);
|
||||
|
||||
Rectangle bounds = new Rectangle(new Point(), bmp.Size);
|
||||
|
||||
var data = bmp.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
|
||||
|
||||
VFD_Write("\x1B\x2E");
|
||||
VFD_WriteShort((short)origin.X);
|
||||
VFD_Write((byte)origin.Y);
|
||||
VFD_WriteShort((short)bmp.Height); // Inverted because image was flipped
|
||||
VFD_Write((byte)((bmp.Width / 8)-1));
|
||||
|
||||
int bytes = ( bmp.Width * bmp.Height ) / 8;
|
||||
|
||||
// Create a byte array to hold the pixel data
|
||||
byte[] pixelData = new byte[bytes];
|
||||
|
||||
// Copy the data from the pointer to the byte array
|
||||
Marshal.Copy(data.Scan0, pixelData, 0, bytes);
|
||||
|
||||
VFD_Write(pixelData);
|
||||
|
||||
bmp.UnlockBits(data);
|
||||
}
|
||||
|
||||
private static void RotateNoneFlipYMono(Bitmap bmp)
|
||||
{
|
||||
if (bmp == null || bmp.PixelFormat != PixelFormat.Format1bppIndexed)
|
||||
throw new ArgumentException("Provided bitmap is not monochrome");
|
||||
|
||||
var height = bmp.Height;
|
||||
var width = bmp.Width;
|
||||
// width in dwords
|
||||
var stride = (width + 31) >> 5;
|
||||
// total image size
|
||||
var size = stride * height;
|
||||
// alloc storage for pixels
|
||||
var bytes = new int[size];
|
||||
|
||||
// get image pixels
|
||||
var rect = new Rectangle(Point.Empty, bmp.Size);
|
||||
var bd = bmp.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
|
||||
Marshal.Copy(bd.Scan0, bytes, 0, size);
|
||||
|
||||
// flip by swapping dwords
|
||||
int halfSize = size >> 1;
|
||||
for (int y1 = 0, y2 = size - stride; y1 < halfSize; y1 += stride, y2 -= stride)
|
||||
{
|
||||
int end = y1 + stride;
|
||||
for (int x1 = y1, x2 = y2; x1 < end; x1++, x2++)
|
||||
{
|
||||
bytes[x1] ^= bytes[x2];
|
||||
bytes[x2] ^= bytes[x1];
|
||||
bytes[x1] ^= bytes[x2];
|
||||
}
|
||||
}
|
||||
|
||||
// copy pixels back
|
||||
Marshal.Copy(bytes, 0, bd.Scan0, size);
|
||||
bmp.UnlockBits(bd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user