Files
McBitFont/McBitFont/Form1.cs

1669 lines
64 KiB
C#

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<FrameMiniature> frames;
}
public FrameMiniature f;
public List<FrameMiniature> frames = new List<FrameMiniature>();
//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.5";
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() + ',' + 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();
}
[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 (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) {
Bitmap bmp;
for (int i = 0; i < frames.Count; i++) {
frames[i] = FrameResize(frames[i], (int)nudX.Value, dotHeight);
bmp = GetMiniPictue(frames[i]);
string s = frames[i].code.ToString().PadLeft(3, '0');
ilMiniatures.Images.RemoveByKey(s);
ilMiniatures.Images.Add(s, (Image)bmp);
miniList.Items[s].ImageKey = s;
}
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;
Bitmap bmp;
for (int i = 0; i < frames.Count; i++) {
frames[i] = FrameResize(frames[i], frames[i].width, (int)nudY.Value);
bmp = GetMiniPictue(frames[i]);
string s = frames[i].code.ToString().PadLeft(3, '0');
ilMiniatures.Images.RemoveByKey(s);
ilMiniatures.Images.Add(s, (Image)bmp);
miniList.Items[s].ImageKey = s;
}
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) {
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;
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;
}
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();
} //else history.AddPre(f, false);
}
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)) {
j = lastY;
} else if (ModifierKeys.HasFlag(Keys.Control)) {
i = lastX;
}
lastX = i;
lastY = j;
// Paint black / white
if (e.Button == MouseButtons.Left && !f.data[i, j]) {
f.data[i, j] = true;
fChanged = true;
int x = pixelOffset + i * (cellSize + gap) - hScroll.Value;
int y = pixelOffset + j * (cellSize + gap) - vScroll.Value;
SetModified();
rect1 = new Rectangle(x, y, cellSize, cellSize);
dotPanel.Invalidate(rect1);
}
if (e.Button == MouseButtons.Right && f.data[i, j]) {
f.data[i, j] = false;
fChanged = true;
int x = pixelOffset + i * (cellSize + gap) - hScroll.Value;
int y = pixelOffset + j * (cellSize + gap) - vScroll.Value;
SetModified();
rect1 = new Rectangle(x, y, cellSize, cellSize);
dotPanel.Invalidate(rect1);
}
}
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;
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<SaveBlock>(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;
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<FrameMiniature>(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;
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;
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<uint> 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<FrameMiniature> 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
for (int i = 0; i < frames.Count; i++) {
if (IsFrameBlank(frames[i])) {
Bitmap bmp;
frames[i] = FrameResize(frames[i], 0, dotHeight);
bmp = GetMiniPictue(frames[i]);
string s = frames[i].code.ToString().PadLeft(3, '0');
ilMiniatures.Images.RemoveByKey(s);
ilMiniatures.Images.Add(s, (Image)bmp);
miniList.Items[s].ImageKey = s;
SetModified(true, true);
}
}
}
}
}