using MessagePack; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; namespace McBitFont { public partial class MainForm : Form { [Serializable] [MessagePackObject] public struct FrameMiniature { public FrameMiniature(int cc, int ww, int hh) { code = cc; width = ww; height = hh; data = new bool[ww, hh]; } [Key(0)] public int code; [Key(1)] public int width; [Key(2)] public int height; [Key(3)] public bool[,] data; }; [Serializable] [MessagePackObject] public struct SaveBlock { [Key(4)] public bool monospaced; [Key(5)] public int codepage; [Key(6)] public int baseline; [Key(7)] public List frames; } public FrameMiniature f; public List frames = new List(); //private CanvasHistory history = new(); private ChangeHistory history; private int cellSize = 10; public int dotWidth, dotHeight; private readonly int pixelOffset = 5; private int gap; private int w, h; public bool monospaced = false; private bool modified = false; private bool prjModified = false; public const string version = "2.8"; public string prjName = "Untitled"; public string prjFileName = ""; public int codepage = 1251; private FrameMiniature fbuf; private readonly DataFormats.Format clpbFormat = DataFormats.GetFormat("McBitFontFrame"); private int baseline = 0; private bool set_base = false; private Point selection1, selection2; private Point[,] sidebarLocs = new Point[2, 3]; public MainForm() { InitializeComponent(); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); this.dotPanel.MouseWheel += new MouseEventHandler(this.DotPanel_MouseWheel); } public void SetNewWH() { w = pixelOffset + dotWidth * (cellSize + gap); h = pixelOffset + dotHeight * (cellSize + gap); } private void UpdateSelectionLabel(int width, int height) { lblSelection.Text = width.ToString() + 'x' + height.ToString(); } public void SetModified(bool modif = true, bool prj = false) { string suffix = ""; if (prj) { prjModified = modif; if (modif) suffix = " *"; SetWindowCap(suffix); } else { modified = modif; lblModified.Visible = modif; } } // Check if a frame is "blank" private static bool IsFrameBlank(FrameMiniature frame) { if (frame.code == 32) return false; // Space character is always blank, so skip it for (int i = 0; i < frame.width; i++) { for (int j = 0; j < frame.height; j++) { if (frame.data[i, j]) return false; } } return true; } // Remember sidebar panels locations private void SideBarRecalc() { sidebarLocs[0, 0] = new Point(this.Width - 459, 31); sidebarLocs[0, 1] = new Point(this.Width - 316, 24); sidebarLocs[0, 2] = new Point(this.Width - 487, 31); sidebarLocs[1, 0] = new Point(this.Width - panel1.Width - 70, 180); sidebarLocs[1, 1] = new Point(this.Width - pnlInfo.Width - 110, 320); sidebarLocs[1, 2] = new Point(dotPanel.Width + 17, 31); } private void Form1_Load(object sender, EventArgs e) { lblType.Text = monospaced ? "Monospaced" : "Variable width / Single"; tsmiMakeVarWidth.Visible = monospaced; makeVarWidthToolStripMenuItem.Visible = monospaced; dotWidth = (int)nudX.Value; dotHeight = (int)nudY.Value; selection1 = new Point(0, 0); selection2 = new Point(dotWidth - 1, dotHeight - 1); UpdateSelectionLabel(dotWidth, dotHeight); gap = (cellSize < 5) ? 0 : 1; SetNewWH(); cbZoom.SelectedIndex = 3; cbZoom.SelectedIndexChanged += cbZoom_SelectedIndexChanged; frames.Add(new FrameMiniature(0, dotWidth, dotHeight)); miniList.Items.Add("000", "000 Single", "000"); miniList.Refresh(); miniList.Items[0].Selected = true; miniList.Select(); f = CopyFrame(frames.Find(x => x.code == 0)); ListViewItem_SetSpacing(miniList, 50 + 1, 50 + 22); SetWindowCap(); fbuf = new FrameMiniature(0, dotWidth, dotHeight); history = new(this); // Chek for arguments if (Environment.GetCommandLineArgs().Length > 1) { LoadProject(Environment.GetCommandLineArgs()[1]); } lblType.Text = monospaced ? "Monospaced" : "Variable width / Single"; tsmiMakeVarWidth.Visible = monospaced; makeVarWidthToolStripMenuItem.Visible = monospaced; tsmiCodeShift.Visible = frames.Count > 1; CodeShiftToolStripMenuItem.Visible = frames.Count > 1; CheckForAdd(); SideBarRecalc(); // Create default cursor dotPanel.Cursor = McCursor.GetCursor((int)nudBrush.Value, cellSize, gap); } [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); public static int MakeLong(short lowPart, short highPart) { return (int)(((ushort)lowPart) | (uint)(highPart << 16)); } public FrameMiniature CopyFrame(FrameMiniature frame, bool clipboard = false) { int width = chkRectSelect.Checked && clipboard ? selection2.X - selection1.X + 1 : frame.width; int height = chkRectSelect.Checked && clipboard ? selection2.Y - selection1.Y + 1 : frame.height; var ff = new FrameMiniature(frame.code, width, height); if (chkRectSelect.Checked && clipboard) { for (int i = 0; i <= selection2.X - selection1.X; i++) for (int j = 0; j <= selection2.Y - selection1.Y; j++) { ff.data[i, j] = frame.data[i + selection1.X, j + selection1.Y]; } } else Array.Copy(frame.data, ff.data, frame.data.Length); return ff; } public static void ListViewItem_SetSpacing(ListView listview, short leftPadding, short topPadding) { const int LVM_FIRST = 0x1000; const int LVM_SETICONSPACING = LVM_FIRST + 53; SendMessage(listview.Handle, LVM_SETICONSPACING, IntPtr.Zero, (IntPtr)MakeLong(leftPadding, topPadding)); } private void DotPanel_MouseWheel(object sender, MouseEventArgs e) { int t = e.Delta / 120; if (e.Delta == 0) return; if (ModifierKeys.HasFlag(Keys.Control)) { t += cbZoom.SelectedIndex; if (t > cbZoom.Items.Count - 1) return; if (t < 0) return; cbZoom.SelectedIndex = t; } else if (ModifierKeys.HasFlag(Keys.Shift)) { if (hScroll.Enabled) { t = t * -1 * (cellSize + gap) + hScroll.Value; if (t < hScroll.Minimum) t = hScroll.Minimum; if (t > hScroll.Maximum) t = hScroll.Maximum; hScroll.Value = t; } } else if (ModifierKeys.HasFlag(Keys.Alt)) { t += (int)nudBrush.Value; if (t < nudBrush.Minimum) t = (int)nudBrush.Minimum; if (t > nudBrush.Maximum) t = (int)nudBrush.Maximum; if (t > dotWidth || t > dotHeight) t = dotWidth < dotHeight ? dotWidth : dotHeight; nudBrush.Value = t; } else { if (vScroll.Enabled) { t = t * -1 * (cellSize + gap) + vScroll.Value; if (t < vScroll.Minimum) t = vScroll.Minimum; if (t > vScroll.Maximum) t = vScroll.Maximum; vScroll.Value = t; } } } public void nudX_ValueChanged(object sender, EventArgs e) { Cursor.Current = Cursors.WaitCursor; if (monospaced) { for (int i = 0; i < frames.Count; i++) { frames[i] = FrameResize(frames[i], (int)nudX.Value, dotHeight, true); } SetModified(true, true); } if (nudX.Focused) { SetModified(); } DotResize((int)nudX.Value, dotHeight); if (monospaced) history.Add(frames); else history.Add(f); Cursor.Current = Cursors.Default; } public void nudY_ValueChanged(object sender, EventArgs e) { Cursor.Current = Cursors.WaitCursor; for (int i = 0; i < frames.Count; i++) { frames[i] = FrameResize(frames[i], frames[i].width, (int)nudY.Value, true); } if (nudY.Focused) { SetModified(); SetModified(true, true); } DotResize(dotWidth, (int)nudY.Value); history.Add(frames); Cursor.Current = Cursors.Default; } private FrameMiniature FrameResize(FrameMiniature ff, int neww, int newh, bool updateMiniList = false) { int oldw = ff.width; int oldh = ff.height; int di = 0, dj = 0; int imax, jmax; bool[,] t; if (chkLeftSide.Checked) di = oldw - neww; if (chkTopSide.Checked) dj = oldh - newh; imax = (neww < oldw) ? neww : oldw; jmax = (newh < oldh) ? newh : oldh; if (neww > oldw) imax -= di; if (newh > oldh) jmax -= dj; ff.width = neww; ff.height = newh; t = new bool[neww, newh]; for (int i = 0; i < imax; i++) { for (int j = 0; j < jmax; j++) { if (i + di >= 0 && j + dj >= 0) t[i, j] = ff.data[i + di, j + dj]; } } ff.data = t; // update miniList with images if (updateMiniList) { Bitmap bmp = GetMiniPictue(ff); string s = ff.code.ToString().PadLeft(3, '0'); ilMiniatures.Images.RemoveByKey(s); ilMiniatures.Images.Add(s, (Image)bmp); miniList.Items[s].ImageKey = s; } return ff; } private void DotResize(int ww, int hh) { f = FrameResize(f, ww, hh); dotWidth = ww; dotHeight = hh; if (selection2.X > dotWidth - 1) { selection2.X = dotWidth - 1; UpdateSelectionLabel(selection2.X - selection1.X + 1, selection2.Y - selection1.Y + 1); } if (selection2.Y > dotHeight - 1) { selection2.Y = dotHeight - 1; UpdateSelectionLabel(selection2.X - selection1.X + 1, selection2.Y - selection1.Y + 1); } SetNewWH(); cbZoom_SelectedIndexChanged(cbZoom, null); } private void cbZoom_SelectedIndexChanged(object sender, EventArgs e) { cellSize = Convert.ToInt32(cbZoom.Text); gap = (cellSize < 5) ? 0 : 1; SetNewWH(); if (w <= dotPanel.Width) { hScroll.Enabled = false; hScroll.Value = 0; vScroll.Maximum = 0; } else { hScroll.Maximum = w - dotPanel.Width + 12; hScroll.Minimum = 0; hScroll.Enabled = true; } if (h <= dotPanel.Height) { vScroll.Enabled = false; vScroll.Value = 0; vScroll.Maximum = 0; } else { vScroll.Maximum = h - dotPanel.Height + 12; vScroll.Minimum = 0; vScroll.Enabled = true; } if (!chkRectSelect.Checked) dotPanel.Cursor = McCursor.GetCursor((int)nudBrush.Value, cellSize, gap); dotPanel.Refresh(); } // Deconstruct selection coords from points private (int x, int y, int x2, int y2) RectSelCoords() { int x, y, x2, y2; if (chkRectSelect.Checked) { x = selection1.X; y = selection1.Y; x2 = selection2.X; y2 = selection2.Y; } else { x = y = 0; x2 = dotWidth - 1; y2 = dotHeight - 1; } return (x, y, x2, y2); } private void btnShiftUp_Click(object sender, EventArgs e) { int x, y, x2, y2; bool c; (x, y, x2, y2) = RectSelCoords(); for (int i = x; i <= x2; i++) { c = f.data[i, y]; for (int j = y; j <= y2; j++) { if (j == y2) { f.data[i, j] = c; } else { f.data[i, j] = f.data[i, j + 1]; } } } history.Add(f); CheckHistoryButtons(); SetModified(); dotPanel.Refresh(); } private void btnShiftDown_Click(object sender, EventArgs e) { int x, y, x2, y2; bool c; (x, y, x2, y2) = RectSelCoords(); for (int i = x; i <= x2; i++) { c = f.data[i, y2]; for (int j = y2; j >= y; j--) { if (j == y) { f.data[i, j] = c; } else { f.data[i, j] = f.data[i, j - 1]; } } } history.Add(f); CheckHistoryButtons(); SetModified(); dotPanel.Refresh(); } private void btnShiftLeft_Click(object sender, EventArgs e) { int x, y, x2, y2; bool c; (x, y, x2, y2) = RectSelCoords(); for (int j = y; j <= y2; j++) { c = f.data[x, j]; for (int i = x; i <= x2; i++) { if (i == x2) { f.data[i, j] = c; } else { f.data[i, j] = f.data[i + 1, j]; } } } history.Add(f); CheckHistoryButtons(); SetModified(); dotPanel.Refresh(); } private void btnShiftRight_Click(object sender, EventArgs e) { int x, y, x2, y2; bool c; (x, y, x2, y2) = RectSelCoords(); for (int j = y; j <= y2; j++) { c = f.data[x2, j]; for (int i = x2; i >= x; i--) { if (i == x) { f.data[i, j] = c; } else { f.data[i, j] = f.data[i - 1, j]; } } } history.Add(f); CheckHistoryButtons(); SetModified(); dotPanel.Refresh(); } private bool mouseDown = false; // Used in canvas history tracking and rectangle selection logics private bool fChanged = false; // Used in canvas history (undo / redo) tracking private bool mouseDownMiddle = false; // Used in middle mouse dragging logic private int mouseX, mouseY; // To remember last mouse X and Y (used in middle mouse dragging logic) private int lastX = 0, lastY = 0; // Used for drawing straight lines private void dotPanel_MouseMove(object sender, MouseEventArgs e) { var rectSel = chkRectSelect.Checked; bool rectSelUpdated = false; // Drag with middle mouse button if (vScroll.Enabled || hScroll.Enabled) { if (mouseDownMiddle) { var dY = mouseY - e.Y <= -cellSize - gap || mouseY - e.Y >= cellSize + gap; var dX = mouseX - e.X <= -cellSize - gap || mouseX - e.X >= cellSize + gap; int newY = vScroll.Value; int newX = hScroll.Value; if (dX) { newX += (mouseX - e.X); if (newX < hScroll.Minimum) newX = hScroll.Minimum; if (newX > hScroll.Maximum) newX = hScroll.Maximum; mouseX = e.X; hScroll.Value = newX; } if (dY) { newY += (mouseY - e.Y); if (newY < vScroll.Minimum) newY = vScroll.Minimum; if (newY > vScroll.Maximum) newY = vScroll.Maximum; mouseY = e.Y; vScroll.Value = newY; } } if (!mouseDownMiddle && e.Button == MouseButtons.Middle) { mouseDownMiddle = true; mouseX = e.X; mouseY = e.Y; } if (mouseDownMiddle && e.Button == MouseButtons.None) { mouseDownMiddle = false; } } // Moving baseline Rectangle rect1, rect2; if (set_base) { int yy = pixelOffset + baseline * (cellSize + gap) - vScroll.Value - 1; rect1 = new Rectangle(pixelOffset, yy, w, 2); baseline = (e.Y - pixelOffset + vScroll.Value) / (cellSize + gap); if (baseline >= dotHeight) baseline = dotHeight - 1; if (baseline < 0) baseline = 0; yy = pixelOffset + baseline * (cellSize + gap) - vScroll.Value - 1; rect2 = new Rectangle(pixelOffset, yy, w, 2); dotPanel.Invalidate(rect1); dotPanel.Invalidate(rect2); if (e.Button == MouseButtons.Left) { set_base = false; return; } } if (e.X >= w || e.X <= pixelOffset || e.Y >= h || e.Y <= pixelOffset) return; // Change coordinates in the panel int i = (e.X - pixelOffset + hScroll.Value) / (cellSize + gap); int j = (e.Y - pixelOffset + vScroll.Value) / (cellSize + gap); lblCoords.Text = i.ToString() + ',' + j.ToString(); // History and Rectangle selection management if (rectSel && mouseDown && e.Button != MouseButtons.None) { if (selection2.X != i || selection2.Y != j) { rectSelUpdated = true; selection2.X = i; selection2.Y = j; } } if (e.Button != MouseButtons.None && !mouseDown) { // Started to move a mouse with button held mouseDown = true; if (rectSel) { selection1.X = i; selection1.Y = j; selection2.X = i; selection2.Y = j; dotPanel.Invalidate(); } } if (e.Button == MouseButtons.None && mouseDown) { // Released a mouse button mouseDown = false; if (rectSel) { NormPoints(ref selection1, ref selection2); dotPanel.Invalidate(); } else { if (fChanged) { fChanged = false; history.Add(f); } CheckHistoryButtons(); } } if (rectSel) { if (mouseDown && rectSelUpdated) { int x, xx, x2, xx2, y, yy, y2, yy2; Point p1 = new(selection1.X, selection1.Y); Point p2 = new(selection2.X, selection2.Y); NormPoints(ref p1, ref p2); UpdateSelectionLabel(p2.X - p1.X + 1, p2.Y - p1.Y + 1); x = pixelOffset + (p1.X - 1) * (cellSize + gap) - hScroll.Value - 1; y = pixelOffset + (p1.Y - 1) * (cellSize + gap) - vScroll.Value - 1; x2 = pixelOffset + (p2.X + 2) * (cellSize + gap) - hScroll.Value - 1; y2 = pixelOffset + (p2.Y + 2) * (cellSize + gap) - vScroll.Value - 1; xx = x + 2 * (cellSize + gap); yy = y + 2 * (cellSize + gap); xx2 = x2 - 2 * (cellSize + gap); yy2 = y2 - 2 * (cellSize + gap); Region reg, reg2; reg = new(new Rectangle(x, y, x2 - x + 1, y2 - y + 1)); reg.Exclude(new Rectangle(x + 1, y + 1, x2 - x - 1, y2 - y - 1)); reg2 = new(new Rectangle(xx, yy, xx2 - xx + 1, yy2 - yy + 1)); reg2.Exclude(new Rectangle(xx + 1, yy + 1, xx2 - xx - 1, yy2 - yy - 1)); reg.Union(reg2); dotPanel.Invalidate(reg); } return; } // Check for Shift / Ctrl keys for straight lines if (ModifierKeys.HasFlag(Keys.Shift) && mouseDown) { j = lastY; } else if (ModifierKeys.HasFlag(Keys.Control) && mouseDown) { i = lastX; } lastX = i; lastY = j; // Paint black / white if (e.Button == MouseButtons.Left) { if (PaintPixel(i, j, true)) fChanged = true; } if (e.Button == MouseButtons.Right) { if (PaintPixel(i, j, false)) fChanged = true; } } private bool PaintPixel(int i, int j, bool color) { bool changed = false; for (int a = 0; a < (int)nudBrush.Value; a++) { if (i + a >= f.width) break; for (int b = 0; b < (int)nudBrush.Value; b++) { if (j + b >= f.height) break; if (f.data[i + a, j + b] != color) { f.data[i + a, j + b] = color; int x = pixelOffset + (i + a) * (cellSize + gap) - hScroll.Value; int y = pixelOffset + (j + b) * (cellSize + gap) - vScroll.Value; Rectangle rect1 = new(x, y, cellSize, cellSize); dotPanel.Invalidate(rect1); SetModified(); changed = true; } } } return changed; } private void btnInvert_Click(object sender, EventArgs e) { int x, y, x2, y2; (x, y, x2, y2) = RectSelCoords(); for (int i = x; i <= x2; i++) { for (int j = y; j <= y2; j++) { f.data[i, j] = !f.data[i, j]; } } history.Add(f); CheckHistoryButtons(); SetModified(); dotPanel.Refresh(); } private void btnMirrorX_Click(object sender, EventArgs e) { int a, b, j, x, y, x2, y2; bool c; (x, y, x2, y2) = RectSelCoords(); for (j = y; j <= y2; j++) { a = x; b = x2; while (a < b) { c = f.data[a, j]; f.data[a, j] = f.data[b, j]; f.data[b, j] = c; a++; b--; } } history.Add(f); CheckHistoryButtons(); SetModified(); dotPanel.Refresh(); } private void btnMirrorY_Click(object sender, EventArgs e) { int a, b, i, x, y, x2, y2; bool c; (x, y, x2, y2) = RectSelCoords(); for (i = x; i <= x2; i++) { a = y; b = y2; while (a < b) { c = f.data[i, a]; f.data[i, a] = f.data[i, b]; f.data[i, b] = c; a++; b--; } } history.Add(f); CheckHistoryButtons(); SetModified(); dotPanel.Refresh(); } private void Export_Click(object sender, EventArgs e) { if (modified) { if (MessageBox.Show("Current symbol is modified.\nDo you want to save the changes?", "Symbol was modified!", MessageBoxButtons.YesNo) == DialogResult.Yes) { SaveFrame(); } else { f = CopyFrame(frames.Find(x => x.code == f.code)); } SetModified(false); ; } Export eform = new Export(this); eform.ShowDialog(); } private void button2_Click(object sender, EventArgs e) { SaveFrame(); } private void SaveFrame() { int index = frames.FindIndex(x => x.code == f.code); frames[index] = f; var sizedBMP = GetMiniPictue(f); string s = f.code.ToString().PadLeft(3, '0'); ilMiniatures.Images.RemoveByKey(s); ilMiniatures.Images.Add(s, (Image)sizedBMP); sizedBMP.Dispose(); SetModified(false); SetModified(true, true); } public static Bitmap GetMiniPictue(FrameMiniature m) { int picSize = (m.width > m.height) ? m.width : m.height; var bmp = new Bitmap(picSize, picSize); int imin = m.width < picSize ? (picSize - m.width) / 2 : 0; int jmin = m.height < picSize ? (picSize - m.height) / 2 : 0; Color c; for (int i = 0; i < m.width; i++) { for (int j = 0; j < m.height; j++) { c = m.data[i, j] ? Color.Black : Color.White; bmp.SetPixel(i + imin, j + jmin, c); } } Bitmap sbmp = new(50, 50); using (Graphics g = Graphics.FromImage(sbmp)) { g.InterpolationMode = InterpolationMode.NearestNeighbor; g.PixelOffsetMode = PixelOffsetMode.Half; g.DrawImage(bmp, 0, 0, 50, 50); } bmp.Dispose(); return sbmp; } private void dotPanel_Paint(object sender, PaintEventArgs e) { Graphics g = dotPanel.CreateGraphics(); SolidBrush sbb = new SolidBrush(Color.Black); SolidBrush sbw = new SolidBrush(Color.White); SolidBrush sb; Pen p = new Pen(Color.FromArgb(100, 20, 120, 20)); int x, y; // Draw the grid for (int i = 0; i < dotWidth; i++) { x = pixelOffset + i * (cellSize + gap) - hScroll.Value; // Green lines every 8 cells if (gap > 0 && i != 0 && (i % 8) == 0) { g.DrawLine(p, x - 1, pixelOffset - vScroll.Value, x - 1, h); } for (int j = 0; j < dotHeight; j++) { y = pixelOffset + j * (cellSize + gap) - vScroll.Value; // Green lines every 8 cells if (gap > 0 && i == 0 && j != 0 && (j % 8) == 0) { g.DrawLine(p, pixelOffset - hScroll.Value, y - 1, w, y - 1); } // Fill the cell with color if (f.data[i, j]) sb = sbb; else sb = sbw; g.FillRectangle(sb, x, (baseline == j ? y + 1 : y), cellSize, (baseline == j ? cellSize - 1 : cellSize)); } } // Draw the baseline if (baseline > 0 && gap > 0) { x = pixelOffset - hScroll.Value; y = pixelOffset + baseline * (cellSize + gap) - vScroll.Value; Pen sbBase = new(Color.Blue, 2); g.DrawLine(sbBase, x, y, w, y); } // Draw the Rect selection if (chkRectSelect.Checked) { Point p1 = new(selection1.X, selection1.Y); Point p2 = new(selection2.X, selection2.Y); NormPoints(ref p1, ref p2); Pen sbRect = new(Color.FromArgb(200, 0, 200, 0), 3); x = pixelOffset + p1.X * (cellSize + gap) - hScroll.Value - 1; y = pixelOffset + p1.Y * (cellSize + gap) - vScroll.Value - 1; int ww = pixelOffset + (p2.X + 1) * (cellSize + gap) - hScroll.Value - x - 1; int hh = pixelOffset + (p2.Y + 1) * (cellSize + gap) - vScroll.Value - y - 1; g.DrawRectangle(sbRect, x, y, ww, hh); } } private static void NormPoints(ref Point a, ref Point b) { int bot; if (a.X > b.X) { bot = a.X; a.X = b.X; b.X = bot; } if (a.Y > b.Y) { bot = a.Y; a.Y = b.Y; b.Y = bot; } } private void exitToolStripMenuItem_Click(object sender, EventArgs e) { Application.Exit(); } public string DecodeSymbol(int code) { var enc = Encoding.GetEncoding(codepage); if (code < 32) return ""; return enc.GetString(new byte[] { (byte)code }); } private FrameMiniature DrawFrameChar(FrameMiniature ff, Font font, int sx, int sy) { string s = DecodeSymbol(ff.code); Bitmap bmp = new Bitmap(ff.width, ff.height); Graphics g = Graphics.FromImage(bmp); g.Clear(Color.White); g.SmoothingMode = SmoothingMode.None; g.InterpolationMode = InterpolationMode.NearestNeighbor; g.PixelOffsetMode = PixelOffsetMode.Half; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit; g.DrawString(s, font, Brushes.Black, sx, sy); g.Flush(); for (int i = 0; i < ff.width; i++) for (int j = 0; j < ff.height; j++) { if (bmp.GetPixel(i, j).Name != "ffffffff") ff.data[i, j] = true; } bmp.Dispose(); g.Dispose(); return ff; } private void newToolStripMenuItem_Click(object sender, EventArgs e) { CheckModifiedFrame(); if (CheckModifiedProject()) return; New form = new New(this); if (form.ShowDialog() == DialogResult.OK) { Cursor.Current = Cursors.WaitCursor; frames.Clear(); miniList.Clear(); ilMiniatures.Images.Clear(); string append = ""; int neww = (int)form.nudNewX.Value; int newh = (int)form.nudNewY.Value; nudX.Value = neww; nudY.Value = newh; FrameMiniature newf; if (form.cbSingle.Checked) { frames.Add(new FrameMiniature(0, neww, newh)); append = "Single"; monospaced = false; } else { int i, imin, imax; if (form.cbDigits.Checked) { imin = 48; imax = 57; } else { if (form.cbNotPrintable.Checked) imin = 0; else if (form.cbLatin.Checked) imin = 32; else imin = 128; if (form.cbExtended.Checked) imax = 255; else if (form.cbLatin.Checked) imax = 127; else imax = 31; } for (i = imin; i <= imax; i++) { newf = new FrameMiniature(i, neww, newh); if (form.cbFontBased.Checked) newf = DrawFrameChar(newf, form.dlgFont.Font, (int)form.nudShiftX.Value, (int)form.nudShiftY.Value); frames.Add(newf); } monospaced = form.rbMono.Checked; } makeVarWidthToolStripMenuItem.Visible = monospaced; tsmiMakeVarWidth.Visible = monospaced; zerofyWidthToolStripMenuItem.Enabled = !monospaced; CodeShiftToolStripMenuItem.Visible = !form.cbSingle.Checked; tsmiCodeShift.Visible = !form.cbSingle.Checked; lblType.Text = monospaced ? "Monospaced" : "Variable width / Single"; codepage = (form.cbEncoding.SelectedItem as New.EncodingItem).Code; foreach (FrameMiniature ff in frames) { var s = ff.code.ToString().PadLeft(3, '0'); ilMiniatures.Images.Add(s, (Image)GetMiniPictue(ff)); var sss = DecodeSymbol(ff.code); miniList.Items.Add(s, s + ' ' + append + sss, s); } f = CopyFrame(frames.First()); form.Dispose(); baseline = 0; prjName = "Untitled"; prjFileName = ""; SetWindowCap(); SetModified(false); ; CheckForAdd(); miniList.Items[0].Selected = true; miniList.Refresh(); dotPanel.Refresh(); history.Clear(); Cursor.Current = Cursors.Default; } } private void MiniList_SelectedIndexChanged(object sender, EventArgs e) { if (miniList.FocusedItem == null) return; CheckModifiedFrame(); if (miniList.SelectedItems.Count == 0) { removeSymbolToolStripMenuItem.Enabled = false; removeBeforeToolStripMenuItem.Enabled = false; removeAfterToolStripMenuItem.Enabled = false; tsmiRemoveSymbol.Enabled = false; tsmiRemoveBefore.Enabled = false; tsmiRemoveAfter.Enabled = false; return; } dotPanel.SuspendLayout(); var sel = miniList.SelectedItems[0]; int code = Convert.ToInt32(sel.ImageKey); FrameMiniature ff = CopyFrame(frames.Find(x => x.code == code)); nudX.Value = ff.width; nudY.Value = ff.height; f = ff; history.Add(code); ff = frames.Find(x => x.code == code); if (frames.Count > 1 && (ff.Equals(frames.First()) || ff.Equals(frames.Last()))) { removeSymbolToolStripMenuItem.Enabled = true; tsmiRemoveSymbol.Enabled = true; } else { removeSymbolToolStripMenuItem.Enabled = false; tsmiRemoveSymbol.Enabled = false; } dotPanel.ResumeLayout(); dotPanel.Refresh(); if (frames.Count > 1 && ff.Equals(frames.First())) { removeBeforeToolStripMenuItem.Enabled = false; removeAfterToolStripMenuItem.Enabled = true; tsmiRemoveBefore.Enabled = false; tsmiRemoveAfter.Enabled = true; } else if (frames.Count > 1 && ff.Equals(frames.Last())) { removeBeforeToolStripMenuItem.Enabled = true; removeAfterToolStripMenuItem.Enabled = false; tsmiRemoveBefore.Enabled = true; tsmiRemoveAfter.Enabled = false; } else if (frames.Count > 1) { removeBeforeToolStripMenuItem.Enabled = true; removeAfterToolStripMenuItem.Enabled = true; tsmiRemoveBefore.Enabled = true; tsmiRemoveAfter.Enabled = true; } } private void SaveToolStripMenuItem_Click(object sender, EventArgs e) { CheckModifiedFrame(); if (dlgSave.ShowDialog() == DialogResult.OK) { SaveProject(dlgSave.FileName); } } public void FillFrameLists() { foreach (FrameMiniature f in frames) { var s = f.code.ToString().PadLeft(3, '0'); var sHex = 'x' + Convert.ToString(f.code, 16).PadLeft(2, '0').ToUpper(); var sss = DecodeSymbol(f.code); ilMiniatures.Images.Add(s, (Image)GetMiniPictue(f)); miniList.Items.Add(s, (chkHexCodes.Checked ? sHex : s) + ' ' + sss, s); } } private void LoadProject(string filename) { SaveBlock sav; Cursor.Current = Cursors.WaitCursor; using (FileStream fs = File.Open(filename, FileMode.Open)) { sav = MessagePackSerializer.Deserialize(fs); fs.Close(); } monospaced = sav.monospaced; codepage = sav.codepage; baseline = sav.baseline; lblType.Text = monospaced ? "Monospaced" : "Variable width / Single"; frames = sav.frames; makeVarWidthToolStripMenuItem.Visible = monospaced; tsmiMakeVarWidth.Visible = monospaced; miniList.Items.Clear(); ilMiniatures.Images.Clear(); FillFrameLists(); nudX.ValueChanged -= nudX_ValueChanged; nudY.ValueChanged -= nudY_ValueChanged; nudX.Value = frames.First().width; nudY.Value = frames.First().height; selection1.X = 0; selection1.Y = 0; selection2.X = (int)nudX.Value - 1; selection2.Y = (int)nudY.Value - 1; DotResize((int)nudX.Value, (int)nudY.Value); nudX.ValueChanged += nudX_ValueChanged; nudY.ValueChanged += nudY_ValueChanged; f = CopyFrame(frames.First()); dotPanel.Refresh(); miniList.Refresh(); SetModified(false); SetModified(false, true); prjFileName = filename; prjName = Path.GetFileNameWithoutExtension(filename); dlgSavePNG.FileName = prjName + ".png"; dlgSave.FileName = prjName + ".mbfont"; SetWindowCap(); miniList.Items[0].Selected = true; CheckForAdd(); history.Clear(); tsmiMakeVarWidth.Visible = monospaced; makeVarWidthToolStripMenuItem.Visible = monospaced; zerofyWidthToolStripMenuItem.Enabled = !monospaced; tsmiCodeShift.Visible = frames.Count > 1; CodeShiftToolStripMenuItem.Visible = frames.Count > 1; Cursor.Current = Cursors.Default; } private void SaveProject(string filename) { SaveBlock sav; sav.monospaced = monospaced; sav.frames = frames; sav.codepage = codepage; sav.baseline = baseline; using (Stream ms = File.OpenWrite(filename)) { MessagePackSerializer.Serialize(ms, sav); ms.Close(); } prjName = Path.GetFileNameWithoutExtension(filename); prjFileName = filename; SetModified(false, true); } private void SetWindowCap(string suffix = "") { this.Text = "McBitFont v" + version + " - " + prjName + suffix; } private void openToolStripMenuItem_Click(object sender, EventArgs e) { CheckModifiedFrame(); if (CheckModifiedProject()) return; dlgOpen.FilterIndex = 1; if (dlgOpen.ShowDialog() == DialogResult.OK) { LoadProject(dlgOpen.FileName); } } private void removeSymbolToolStripMenuItem_Click(object sender, EventArgs e) { if (miniList.SelectedItems.Count == 0) { removeSymbolToolStripMenuItem.Enabled = false; return; } var sel = miniList.SelectedItems[0].ImageKey; int code = Convert.ToInt32(miniList.SelectedItems[0].ImageKey); FrameMiniature ff = frames.Find(x => x.code == code); bool isLast = frames.Last().Equals(ff); frames.Remove(ff); miniList.SelectedItems[0].Remove(); miniList.Items[isLast ? miniList.Items.Count - 1 : 0].Selected = true; } private void prependSymbolToolStripMenuItem_Click(object sender, EventArgs e) { FrameMiniature ff; if (sender == prependSymbolToolStripMenuItem || sender == tsmiPrepensSymbol) { ff = new FrameMiniature(frames.First().code - 1, dotWidth, dotHeight); frames.Insert(0, ff); } else { ff = new FrameMiniature(frames.Last().code + 1, dotWidth, dotHeight); frames.Add(ff); } var s = ff.code.ToString().PadLeft(3, '0'); ilMiniatures.Images.Add(s, (Image)GetMiniPictue(ff)); var sss = DecodeSymbol(ff.code); miniList.Items.Add(s, s + ' ' + sss, s); CheckForAdd(); } private void CheckForAdd() { if (frames.Count > 1) { if (frames.First().code > 0) { prependSymbolToolStripMenuItem.Enabled = true; tsmiPrepensSymbol.Enabled = true; } else { prependSymbolToolStripMenuItem.Enabled = false; tsmiPrepensSymbol.Enabled = false; } if (frames.Last().code < 255) { appendSymbolToolStripMenuItem.Enabled = true; tsmiAppendSymbol.Enabled = true; } else { appendSymbolToolStripMenuItem.Enabled = false; tsmiAppendSymbol.Enabled = false; } } else { prependSymbolToolStripMenuItem.Enabled = false; appendSymbolToolStripMenuItem.Enabled = true; tsmiPrepensSymbol.Enabled = false; tsmiAppendSymbol.Enabled = true; } } private void copyToolStripMenuItem_Click(object sender, EventArgs e) { var bb = MessagePackSerializer.Serialize(CopyFrame(f, true)); DataObject clpbObj = new DataObject(clpbFormat.Name, bb); Clipboard.SetDataObject(clpbObj, true); } private void pasteToolStripMenuItem_Click(object sender, EventArgs e) { // Try to read from clipboard try { IDataObject clpbObj = Clipboard.GetDataObject(); byte[] bb = (byte[])clpbObj.GetData(clpbFormat.Name); fbuf = MessagePackSerializer.Deserialize(bb); } catch { return; } int di, dj, wmax, hmax, selw, selh; if (chkRectSelect.Checked) { di = selection1.X; dj = selection1.Y; selw = selection2.X - selection1.X + 1; selh = selection2.Y - selection1.Y + 1; wmax = fbuf.width > selw ? selw : fbuf.width; hmax = fbuf.height > selh ? selh : fbuf.height; } else { di = 0; dj = 0; wmax = (fbuf.width > f.width) ? f.width : fbuf.width; hmax = (fbuf.height > f.height) ? f.height : fbuf.height; } for (int i = 0; i < wmax; i++) { for (int j = 0; j < hmax; j++) { f.data[i + di, j + dj] = fbuf.data[i, j]; } } history.Add(f); CheckHistoryButtons(); dotPanel.Refresh(); SetModified(); } private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { About form = new About(); form.ShowDialog(); } private void btnBaseline_Click(object sender, EventArgs e) { set_base = !set_base; } // Check modified / Save frame private void CheckModifiedFrame() { if (modified) { if (MessageBox.Show("Current symbol is modified.\nDo you want to save the changes?", "Symbol was modified!", MessageBoxButtons.YesNo) == DialogResult.Yes) { SaveFrame(); } SetModified(false); } } // Check if project was modified private bool CheckModifiedProject() { if (prjModified) { if (MessageBox.Show("The project is modified.\nDo you want to save it first?", "Project was modified!", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { saveToolStripMenuItem.PerformClick(); return true; } } return false; } private void saveToolStripMenuItem_Click_1(object sender, EventArgs e) { if (prjFileName == "") { saveAsToolStripMenuItem.PerformClick(); } else { CheckModifiedFrame(); SaveProject(prjFileName); } } private void FillFrame(bool val) { int x, y, x2, y2; (x, y, x2, y2) = RectSelCoords(); for (int i = x; i <= x2; i++) { for (int j = y; j <= y2; j++) { f.data[i, j] = val; } } history.Add(f); CheckHistoryButtons(); SetModified(); dotPanel.Refresh(); } private void btnClear_Click(object sender, EventArgs e) { FillFrame(false); } private void btnFill_Click(object sender, EventArgs e) { FillFrame(true); } private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { CheckModifiedFrame(); if (prjModified) { if (MessageBox.Show("The project is modified.\nAre you sure you want to quit?", "Are you sure?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { e.Cancel = true; } else { e.Cancel = false; } } } private void scroll_ValueChanged(object sender, EventArgs e) { dotPanel.Refresh(); } // Remove all before or after specified symbol private void removeBA(bool before) { if (miniList.SelectedItems.Count == 0) { removeBeforeToolStripMenuItem.Enabled = false; removeAfterToolStripMenuItem.Enabled = false; return; } int step = before ? -1 : 1; var sel = miniList.SelectedItems[0].ImageKey; int code = Convert.ToInt32(sel); int findex; while (miniList.Items.ContainsKey((code + step).ToString().PadLeft(3, '0'))) { code += step; findex = frames.FindIndex(x => x.code == code); frames.RemoveAt(findex); miniList.Items.RemoveByKey(code.ToString().PadLeft(3, '0')); } //dotPanel.Refresh(); miniList.Refresh(); SetModified(true, true); } private void removeBeforeToolStripMenuItem_Click(object sender, EventArgs e) { removeBA(true); } private void removeAfterToolStripMenuItem_Click(object sender, EventArgs e) { removeBA(false); } private void makeVarWidthToolStripMenuItem_Click(object sender, EventArgs e) { monospaced = false; makeVarWidthToolStripMenuItem.Visible = false; tsmiMakeVarWidth.Visible = false; zerofyWidthToolStripMenuItem.Enabled = true; lblType.Text = "Variable width / Single"; SetModified(true, true); } public void CheckHistoryButtons() { undoToolStripMenuItem.Enabled = history.Undos > 0; redoToolStripMenuItem.Enabled = history.Redos > 0; undoToolStripMenuItem.Text = "Undo (" + history.Undos + ")"; redoToolStripMenuItem.Text = "Redo (" + history.Redos + ")"; } private void editToolStripMenuItem_DropDownOpening(object sender, EventArgs e) { CheckHistoryButtons(); } private void undoToolStripMenuItem_Click(object sender, EventArgs e) { history.Undo(); dotPanel.Refresh(); CheckHistoryButtons(); } private void redoToolStripMenuItem_Click(object sender, EventArgs e) { history.Redo(); dotPanel.Refresh(); CheckHistoryButtons(); } private void CodeShiftToolStripMenuItem_Click(object sender, EventArgs e) { if (!monospaced && frames.Count == 1) return; CheckModifiedFrame(); CodeShift csform = new CodeShift(this); if (csform.ShowDialog() == DialogResult.OK) { Cursor.Current = Cursors.WaitCursor; FrameMiniature ff; int sel; if (miniList.SelectedItems.Count > 0) sel = miniList.SelectedItems[0].Index; else sel = 0; var val = Convert.ToInt32(csform.nudValue.Value); if (csform.rbSpecify.Checked) val -= csform.sc; if (csform.rbShiftLeft.Checked) val *= -1; miniList.Clear(); ilMiniatures.Images.Clear(); for (int i = 0; i < frames.Count; i++) { ff = frames[i]; ff.code += val; frames[i] = ff; var key = ff.code.ToString().PadLeft(3, '0'); var text = DecodeSymbol(ff.code); ilMiniatures.Images.Add(key, (Image)GetMiniPictue(ff)); miniList.Items.Add(key, key + ' ' + text, key); } miniList.Refresh(); miniList.Items[sel].Selected = true; dotPanel.Refresh(); Cursor.Current = Cursors.Default; } csform.Dispose(); history.Clear(); CheckForAdd(); SetModified(true, true); } private void importImageToolStripMenuItem_Click(object sender, EventArgs e) { ImageImporter iform = new ImageImporter(f.width, f.height); if (iform.ShowDialog() == DialogResult.OK) { for (int i = 0; i < iform.bmpScaled.Width; i++) { for (int j = 0; j < iform.bmpScaled.Height; j++) { f.data[i, j] = iform.bmpScaled.GetPixel(i, j).ToArgb().Equals(Color.Black.ToArgb()); } } history.Add(f); CheckHistoryButtons(); dotPanel.Refresh(); SetModified(); } iform.Dispose(); } private void chkHexCodes_CheckedChanged(object sender, EventArgs e) { if (frames.Count == 1) return; foreach (ListViewItem item in miniList.Items) { var code = Convert.ToInt32(item.ImageKey); var symbol = DecodeSymbol(code); if (chkHexCodes.Checked) { var sHex = 'x' + Convert.ToString(code, 16).PadLeft(2, '0').ToUpper(); item.Text = sHex + ' ' + symbol; } else { item.Text = item.ImageKey + ' ' + symbol; } } } private void chkRectSelect_CheckedChanged(object sender, EventArgs e) { lblSelection.Visible = lblSelectionLabel.Visible = chkRectSelect.Checked; selectAllToolStripMenuItem.Enabled = chkRectSelect.Checked; if (chkRectSelect.Checked) dotPanel.Cursor = McCursor.GetCursorSelect(); else dotPanel.Cursor = McCursor.GetCursor((int)nudBrush.Value, cellSize, gap); dotPanel.Refresh(); } private void selectToolStripMenuItem_Click(object sender, EventArgs e) { chkRectSelect.Checked = !chkRectSelect.Checked; selectToolStripMenuItem.Checked = !selectToolStripMenuItem.Checked; } private void importTextToolStripMenuItem1_Click(object sender, EventArgs e) { if (CheckModifiedProject()) return; dlgOpen.FilterIndex = 2; if (dlgOpen.ShowDialog() == DialogResult.OK) { var lines = File.ReadAllLines(dlgOpen.FileName); int bits = 0; List data = []; foreach (var line in lines) { if (line.TrimStart().StartsWith("//")) continue; // Ignore comments if (line.Trim().Length < 1) continue; // Ignore empty lines if (bits == 0) { if (line.Contains("uint8_t")) bits = 8; // Found 8 bit array if (line.Contains("uint16_t")) bits = 16; // Found 16 bit array if (line.Contains("uint31_t")) bits = 32; // Found 32 bit array } string s = line; if (s.Contains("//")) s = s[..s.IndexOf("//")]; // Remove comments if (s.Contains('{')) s = s[(s.IndexOf('{') + 1)..]; // Remove everything before '{' var words = s.Split(','); // split a line by commas string w; foreach (var word in words) { int numBase = 10; w = word.Trim(); if (w.Length < 1) continue; if (w.Contains("0b") && !w.Contains("0x")) { // Check if value is written as binary w = w.Remove(w.IndexOf("0b"), 2); numBase = 2; } if (w.Contains("0x")) { // Check if value is written as hexadecimal w = w.Remove(w.IndexOf("0x"), 2); numBase = 16; } try { // Try to convert a number from text data.Add(Convert.ToUInt32(w, numBase)); } catch { continue; } //MessageBox.Show(w + ": Length: " + w.Length + " Bits: " + bits + " Converted: " + data.Last() + "\nData length: " + data.Count); } } if (MessageBox.Show(bits + "-bit font found. " + data.Count + " numbers (" + data.Count * bits / 8 + " bytes) total\n" + "Start code: " + data.ElementAt(4) + " End code: " + data.ElementAt(5) + "\nDo you want to load it?", "Import from text file", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { // Font header bool packed = data.ElementAt(0) == 1; int width = (int)data.ElementAt(1); int height = (int)data.ElementAt(2); int first = (int)data.ElementAt(4); int last = (int)data.ElementAt(5); frames.Clear(); miniList.Clear(); ilMiniatures.Images.Clear(); FrameMiniature newf = new(); int neww = width; int curFrame = -1; bool newFrame = true; int i = 0, j = 0; for (int b = 6; b < data.Count; b++) { if (newFrame) { newFrame = false; curFrame++; i = 0; j = 0; if (width == 0) neww = (int)data[b]; newf = new(curFrame + first, neww, height); if (width == 0 && neww != 0) continue; } // Fill frame data with current number if (neww != 0) for (int bit = 0; bit < bits; bit++) { if (j >= height) { j = 0; i++; if (!packed && bit != 0) break; } if (i >= neww) break; newf.data[i, j] = (data[b] & (1 << bit)) > 0; j++; } if (i >= neww || b == data.Count - 1 || (i == neww - 1 && j == height) || neww == 0) { newFrame = true; frames.Add(newf); var s = (last - first > 0) ? newf.code.ToString().PadLeft(3, '0') : "000"; var sHex = (last - first > 0) ? 'x' + Convert.ToString(newf.code, 16).PadLeft(2, '0').ToUpper() : "0x0"; var sss = (last - first > 0) ? DecodeSymbol(newf.code) : "Single"; ilMiniatures.Images.Add(s, (Image)GetMiniPictue(newf)); miniList.Items.Add(s, (chkHexCodes.Checked ? sHex : s) + ' ' + sss, s); } } SetModified(true, true); if (miniList.Items.Count > 0) miniList.Items[0].Selected = true; f = frames[0]; dotWidth = f.width; dotHeight = f.height; SetNewWH(); miniList.Refresh(); dotPanel.Refresh(); } //MessageBox.Show(bits + "-font found. " + data.Count + " numbers (" + data.Count * bits / 8 + " bytes) total\n" + "Start code: " + data.ElementAt(4) + " End code: " + data.ElementAt(5)); } } private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) { selection1.X = 0; selection1.Y = 0; selection2.X = dotWidth - 1; selection2.Y = dotHeight - 1; dotPanel.Refresh(); } private void TestFont_Click(object sender, EventArgs e) { var tester = new FontTester(codepage, dotHeight, baseline, frames); tester.ShowDialog(); } private void ExportPNG(object sender, EventArgs e) { CheckModifiedFrame(); CheckModifiedProject(); if (dlgSavePNG.ShowDialog() == DialogResult.OK) { int pixelSize = 3; int symbolMargin = 2 * pixelSize; int headerHeight = 100; int maxWidth = 0; int i, j, v, h, x, y; string s; foreach (FrameMiniature f in frames) { if (f.width > maxWidth) maxWidth = f.width; } int cellWidth = (pixelSize * maxWidth + 2 * symbolMargin); int cellHeight = (pixelSize * dotHeight + 2 * symbolMargin); if (cellWidth < 10) cellWidth = 10; if (cellHeight < 10) cellHeight = 10; int width = cellWidth * 16 + 17 + 50; int height = cellHeight * 16 + 17 + 25 + headerHeight; Bitmap bmp = new(width > 450 ? width : 450, height); Graphics g = Graphics.FromImage(bmp); g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; Font font = new("Consolas", 14, FontStyle.Bold); Brush tb = Brushes.Black; // Draw basic information g.DrawString("Font project name: " + prjName, font, tb, 10, 10); g.DrawString("Font height: " + dotHeight, font, tb, 10, 38); g.DrawString("Font max width: " + maxWidth, font, tb, 10, 62); g.DrawString("Symbols count: " + frames.Count, font, tb, 10, 86); g.DrawString("First code: " + frames.First().code.ToString(), font, tb, 250, 38); g.DrawString("Last code: " + frames.Last().code.ToString(), font, tb, 250, 62); g.DrawString("Codepage: " + codepage.ToString(), font, tb, 250, 86); // Draw grid Pen p = new(Color.FromArgb(64, 0, 0, 0), 1); SolidBrush b = new SolidBrush(Color.FromArgb(160, Color.Black)); //Brush b = Brushes.Black; int xCapOffset = cellWidth / 2 - 8; int yCapOffset = cellHeight / 2 - 10; for (i = 0; i < 17; i++) { x = 50 + 17 + i * cellWidth - 1; y = headerHeight + 17 + 25 + i * cellHeight - 1; g.DrawLine(p, 1, y, width - 1, y); g.DrawLine(p, x, headerHeight + 20, x, headerHeight + height - 20); if (i != 16) { s = Convert.ToString(i, 16).ToUpper(); g.DrawString(s, font, b, x + xCapOffset, headerHeight + 20); s = "0x" + Convert.ToString(i * 16, 16).PadLeft(2, '0').ToUpper(); g.DrawString(s, font, b, 10, y + yCapOffset); } } // Draw symbols int code; List ff; Rectangle rect; // Cycle through grid cells for (v = 0; v < 16; v++) { for (h = 0; h < 16; h++) { // Check if the font has a symbol with the code code = 16 * v + h; ff = frames.FindAll(x => x.code == code); if (ff.Count == 1) { x = 50 + 17 + h * cellWidth + symbolMargin; y = headerHeight + 17 + 25 + v * cellHeight + symbolMargin; // Cycly through symbol's pixels and draw the black ones for (i = 0; i < ff[0].width; i++) { for (j = 0; j < ff[0].height; j++) { if (ff[0].data[i, j]) { rect = new Rectangle(x + i * pixelSize, y + j * pixelSize, pixelSize, pixelSize); g.FillRectangle(tb, rect); } } } } } } bmp.Save(dlgSavePNG.FileName, ImageFormat.Png); } } private void toggleBarToolStripMenuItem_Click(object sender, EventArgs e) { int state; dotPanel.Width = this.Width - (toggleBarToolStripMenuItem.Checked ? 260 : 504); SideBarRecalc(); if (toggleBarToolStripMenuItem.Checked) { toggleBarToolStripMenuItem.Text = "<<"; state = 1; } else { toggleBarToolStripMenuItem.Text = ">>"; state = 0; } panel1.Location = sidebarLocs[state, 0]; pnlInfo.Location = sidebarLocs[state, 1]; miniList.Visible = !toggleBarToolStripMenuItem.Checked; vScroll.Location = sidebarLocs[state, 2]; hScroll.Width = dotPanel.Width; cbZoom.Focus(); } private void PrevNextMenuCheck() { if (frames.Count < 2) { nextSymbolToolStripMenuItem.Enabled = false; previousSymbolToolStripMenuItem.Enabled = false; return; } previousSymbolToolStripMenuItem.Enabled = f.code != frames.First().code; nextSymbolToolStripMenuItem.Enabled = f.code != frames.Last().code; } private void fontToolStripMenuItem_DropDownOpening(object sender, EventArgs e) { PrevNextMenuCheck(); } private void previousSymbolToolStripMenuItem_Click(object sender, EventArgs e) { if (f.code != frames.First().code && miniList.SelectedItems.Count > 0) { miniList.Items[miniList.SelectedIndices[0] - 1].Selected = true; } PrevNextMenuCheck(); } private void nextSymbolToolStripMenuItem_Click(object sender, EventArgs e) { if (f.code != frames.Last().code && miniList.SelectedItems.Count > 0) { miniList.Items[miniList.SelectedIndices[0] + 1].Selected = true; } PrevNextMenuCheck(); } private void ZerofyBlankWidth(object sender, EventArgs e) { if (monospaced) return; // Does not work for monospaced fonts if (frames.Count < 2) return; // Does not work for single images bool flag = false; for (int i = 0; i < frames.Count; i++) { if (IsFrameBlank(frames[i])) { frames[i] = FrameResize(frames[i], 0, dotHeight, true); flag = true; } } if (flag) { SetModified(true, true); MiniList_SelectedIndexChanged(miniList, EventArgs.Empty); } } private void nudBrush_ValueChanged(object sender, EventArgs e) { if (!chkRectSelect.Checked) dotPanel.Cursor = McCursor.GetCursor((int)nudBrush.Value, cellSize, gap); } private void frameScreenshotToolStripMenuItem_Click(object sender, EventArgs e) { FrameScreenshot iform = new(f); iform.ShowDialog(); iform.Dispose(); } } }