using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.IO; using System.Text; using System.Windows.Forms; namespace TeslaConsole; public class PlasmaBitmaps { public const string DefaultFontFamily = "Microsoft Sans Serif"; private static bool sEnableCustomBitmaps; public static bool EnableCustomBitmaps { get { return sEnableCustomBitmaps; } set { sEnableCustomBitmaps = value; } } public static void GenerateBitmaps(out Bitmap large, out Bitmap small, string str) { GenerateBitmaps(out large, out small, "Microsoft Sans Serif", str); } public static void GenerateBitmaps(out Bitmap large, out Bitmap small, string fontName, string str) { large = GenerateBitmap(128, 32, fontName, str); small = GenerateBitmap(64, 16, fontName, str); } public static Bitmap GenerateBitmap(int width, int height, string str) { return GenerateBitmap(width, height, "Microsoft Sans Serif", str); } public static Bitmap GenerateBitmap(int width, int height, string fontName, string str) { if (sEnableCustomBitmaps) { Bitmap bitmap = LoadCustomBitmap(width, height, str); if (bitmap != null) { return bitmap; } } Bitmap bitmap2 = new Bitmap(width, height); using Graphics graphics = Graphics.FromImage(bitmap2); graphics.SmoothingMode = SmoothingMode.None; graphics.CompositingQuality = CompositingQuality.AssumeLinear; graphics.InterpolationMode = InterpolationMode.NearestNeighbor; graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit; graphics.Clear(Color.Black); int num = 24; while (true) { using Font font = new Font(fontName, num); SizeF sizeF = graphics.MeasureString(str, font); if (sizeF.Width <= (float)bitmap2.Width && sizeF.Height <= (float)bitmap2.Height) { Rectangle rectangle = new Rectangle(0, 0, bitmap2.Width, bitmap2.Height); StringFormat stringFormat = new StringFormat(); stringFormat.Alignment = StringAlignment.Center; stringFormat.LineAlignment = StringAlignment.Center; graphics.DrawString(str, font, Brushes.White, rectangle, stringFormat); return bitmap2; } num = ((num <= 2) ? (num / 2) : (num - 2)); } } /// /// The operator-supplied plasma image for this string at this size, or null /// when there is none (the caller then renders the text procedurally). /// /// Two folders are searched, in order: /// 1. %ProgramData%\Tesla Console\Plasma Images - this machine's own art, /// which survives reinstalls and wins over anything shipped. /// 2. Plasma Images\ next to TeslaConsole.exe - the set that rolls with /// the release, so art can be version-controlled and deployed. /// The file name is "<text>_<width>x<height>.bmp", e.g. Camera_128x32.bmp. /// /// Nothing here may throw: participant names come from operator input, and a /// name containing a character that is illegal in a path used to take out /// egg generation entirely once this option was switched on. /// private static Bitmap LoadCustomBitmap(int width, int height, string str) { string fileName = $"{str}_{width}x{height}.bmp"; if (fileName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) { return null; } string[] searchRoots = new string[2] { Program.GetCommonAppDataDirectory(), Path.GetDirectoryName(Application.ExecutablePath) }; foreach (string searchRoot in searchRoots) { try { string path = Path.Combine(Path.Combine(searchRoot, "Plasma Images"), fileName); if (!File.Exists(path)) { continue; } // Copied out of the file rather than Image.FromFile'd: that keeps the // bitmap backed by the file for its whole lifetime, which locks the art // against an operator swapping it while the console is running. using FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read); using Image image = Image.FromStream(stream); if (image.Width == width && image.Height == height) { return new Bitmap(image); } } catch { } } return null; } public static void GenerateStrings(out string large, out string small, string str) { GenerateStrings(out large, out small, "Microsoft Sans Serif", str); } public static void GenerateStrings(out string large, out string small, string fontName, string str) { large = GenerateString(128, 32, fontName, str); small = GenerateString(64, 16, fontName, str); } public static string GenerateString(int width, int height, string str) { return GenerateString(width, height, "Microsoft Sans Serif", str); } public static string GenerateString(int width, int height, string fontName, string str) { using Bitmap bitmap = GenerateBitmap(width, height, fontName, str); return ConvertBitmap(bitmap); } public static string ConvertBitmap(Bitmap bitmap) { if (bitmap.Width % 8 != 0 || bitmap.Height % 8 != 0) { throw new ArgumentOutOfRangeException("bitmap"); } StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < bitmap.Height; i++) { stringBuilder.Append("bitmap="); for (int j = 0; j < bitmap.Width / 8; j++) { byte b = 0; for (int k = 0; k < 8; k++) { if (bitmap.GetPixel(j * 8 + k, i).GetBrightness() >= 0.5f) { b = (byte)(b | (byte)(128 >> k)); } } stringBuilder.Append(b.ToString("X2")); } stringBuilder.Append("\n"); } stringBuilder.AppendFormat("x={0}\n", bitmap.Width); stringBuilder.AppendFormat("y={0}\n", bitmap.Height); return stringBuilder.ToString(); } }