TODO features:

- Custom cursor showing painting size
- Alt-Scroll to change painting size
This commit is contained in:
Anton Mukhin
2025-06-19 17:30:30 +03:00
parent ac7981d40a
commit 346088b532
5 changed files with 221 additions and 57 deletions

View File

@@ -28,8 +28,8 @@
dotPanel = new System.Windows.Forms.Panel();
nudX = new System.Windows.Forms.NumericUpDown();
nudY = new System.Windows.Forms.NumericUpDown();
label1 = new System.Windows.Forms.Label();
label2 = new System.Windows.Forms.Label();
lblWidth = new System.Windows.Forms.Label();
lblHeight = new System.Windows.Forms.Label();
lblType = new System.Windows.Forms.Label();
cbZoom = new System.Windows.Forms.ComboBox();
label4 = new System.Windows.Forms.Label();
@@ -113,18 +113,21 @@
chkTopSide = new System.Windows.Forms.CheckBox();
chkHexCodes = new System.Windows.Forms.CheckBox();
chkRectSelect = new System.Windows.Forms.CheckBox();
nudBrush = new System.Windows.Forms.NumericUpDown();
label3 = new System.Windows.Forms.Label();
lblSelectionLabel = new System.Windows.Forms.Label();
lblSelection = new System.Windows.Forms.Label();
lblModified = new System.Windows.Forms.Label();
dlgSavePNG = new System.Windows.Forms.SaveFileDialog();
pnlRightButtons = new System.Windows.Forms.Panel();
lblBrush = new System.Windows.Forms.Label();
pnlInfo = new System.Windows.Forms.Panel();
((System.ComponentModel.ISupportInitialize)nudX).BeginInit();
((System.ComponentModel.ISupportInitialize)nudY).BeginInit();
panel1.SuspendLayout();
cmMinilist.SuspendLayout();
menuStrip1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)nudBrush).BeginInit();
pnlRightButtons.SuspendLayout();
pnlInfo.SuspendLayout();
SuspendLayout();
@@ -146,7 +149,7 @@
//
// nudX
//
nudX.Location = new System.Drawing.Point(59, 8);
nudX.Location = new System.Drawing.Point(59, 3);
nudX.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
nudX.Maximum = new decimal(new int[] { 255, 0, 0, 0 });
nudX.Name = "nudX";
@@ -158,7 +161,7 @@
//
// nudY
//
nudY.Location = new System.Drawing.Point(59, 34);
nudY.Location = new System.Drawing.Point(59, 27);
nudY.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
nudY.Maximum = new decimal(new int[] { 255, 0, 0, 0 });
nudY.Minimum = new decimal(new int[] { 1, 0, 0, 0 });
@@ -169,25 +172,25 @@
nudY.Value = new decimal(new int[] { 32, 0, 0, 0 });
nudY.ValueChanged += nudY_ValueChanged;
//
// label1
// lblWidth
//
label1.AutoSize = true;
label1.Location = new System.Drawing.Point(8, 10);
label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
label1.Name = "label1";
label1.Size = new System.Drawing.Size(42, 15);
label1.TabIndex = 3;
label1.Text = "Width:";
lblWidth.AutoSize = true;
lblWidth.Location = new System.Drawing.Point(8, 5);
lblWidth.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
lblWidth.Name = "lblWidth";
lblWidth.Size = new System.Drawing.Size(42, 15);
lblWidth.TabIndex = 3;
lblWidth.Text = "Width:";
//
// label2
// lblHeight
//
label2.AutoSize = true;
label2.Location = new System.Drawing.Point(4, 36);
label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
label2.Name = "label2";
label2.Size = new System.Drawing.Size(46, 15);
label2.TabIndex = 4;
label2.Text = "Height:";
lblHeight.AutoSize = true;
lblHeight.Location = new System.Drawing.Point(4, 29);
lblHeight.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
lblHeight.Name = "lblHeight";
lblHeight.Size = new System.Drawing.Size(46, 15);
lblHeight.TabIndex = 4;
lblHeight.Text = "Height:";
//
// lblType
//
@@ -363,7 +366,7 @@
// btnExport
//
btnExport.Image = Properties.Resources.z_export;
btnExport.Location = new System.Drawing.Point(96, 103);
btnExport.Location = new System.Drawing.Point(94, 109);
btnExport.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
btnExport.Name = "btnExport";
btnExport.Size = new System.Drawing.Size(88, 27);
@@ -480,7 +483,7 @@
// btnApply
//
btnApply.Image = Properties.Resources.z_tick;
btnApply.Location = new System.Drawing.Point(2, 103);
btnApply.Location = new System.Drawing.Point(4, 109);
btnApply.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
btnApply.Name = "btnApply";
btnApply.Size = new System.Drawing.Size(88, 27);
@@ -661,7 +664,7 @@
undoToolStripMenuItem.Image = Properties.Resources.arrow_undo;
undoToolStripMenuItem.Name = "undoToolStripMenuItem";
undoToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Z;
undoToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
undoToolStripMenuItem.Size = new System.Drawing.Size(164, 22);
undoToolStripMenuItem.Text = "Undo";
undoToolStripMenuItem.ToolTipText = "Undo last canvas change";
undoToolStripMenuItem.Click += undoToolStripMenuItem_Click;
@@ -671,7 +674,7 @@
redoToolStripMenuItem.Image = Properties.Resources.arrow_redo;
redoToolStripMenuItem.Name = "redoToolStripMenuItem";
redoToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Y;
redoToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
redoToolStripMenuItem.Size = new System.Drawing.Size(164, 22);
redoToolStripMenuItem.Text = "Redo";
redoToolStripMenuItem.ToolTipText = "Redo canvas change";
redoToolStripMenuItem.Click += redoToolStripMenuItem_Click;
@@ -682,7 +685,7 @@
copyToolStripMenuItem.Name = "copyToolStripMenuItem";
copyToolStripMenuItem.ShortcutKeyDisplayString = "";
copyToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C;
copyToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
copyToolStripMenuItem.Size = new System.Drawing.Size(164, 22);
copyToolStripMenuItem.Text = "Copy";
copyToolStripMenuItem.ToolTipText = "Copy current symbol to clipboard";
copyToolStripMenuItem.Click += copyToolStripMenuItem_Click;
@@ -693,7 +696,7 @@
pasteToolStripMenuItem.Name = "pasteToolStripMenuItem";
pasteToolStripMenuItem.ShortcutKeyDisplayString = "";
pasteToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.V;
pasteToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
pasteToolStripMenuItem.Size = new System.Drawing.Size(164, 22);
pasteToolStripMenuItem.Text = "Paste";
pasteToolStripMenuItem.ToolTipText = "Paste from clipboard to current symbol";
pasteToolStripMenuItem.Click += pasteToolStripMenuItem_Click;
@@ -703,7 +706,7 @@
selectToolStripMenuItem.Image = Properties.Resources.fam_rectt;
selectToolStripMenuItem.Name = "selectToolStripMenuItem";
selectToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.R;
selectToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
selectToolStripMenuItem.Size = new System.Drawing.Size(164, 22);
selectToolStripMenuItem.Text = "Select";
selectToolStripMenuItem.ToolTipText = "Toggle Rectangle selection tool";
selectToolStripMenuItem.Click += selectToolStripMenuItem_Click;
@@ -714,7 +717,7 @@
selectAllToolStripMenuItem.Image = Properties.Resources.arrow_out;
selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem";
selectAllToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.A;
selectAllToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
selectAllToolStripMenuItem.Size = new System.Drawing.Size(164, 22);
selectAllToolStripMenuItem.Text = "Select All";
selectAllToolStripMenuItem.ToolTipText = "Select entire canvas";
selectAllToolStripMenuItem.Click += selectAllToolStripMenuItem_Click;
@@ -996,7 +999,7 @@
//
btnBaseline.Image = Properties.Resources.fam_base;
btnBaseline.ImageAlign = System.Drawing.ContentAlignment.MiddleRight;
btnBaseline.Location = new System.Drawing.Point(96, 70);
btnBaseline.Location = new System.Drawing.Point(94, 81);
btnBaseline.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
btnBaseline.Name = "btnBaseline";
btnBaseline.Size = new System.Drawing.Size(88, 27);
@@ -1016,7 +1019,7 @@
// chkLeftSide
//
chkLeftSide.AutoSize = true;
chkLeftSide.Location = new System.Drawing.Point(113, 10);
chkLeftSide.Location = new System.Drawing.Point(113, 5);
chkLeftSide.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
chkLeftSide.Name = "chkLeftSide";
chkLeftSide.Size = new System.Drawing.Size(70, 19);
@@ -1028,7 +1031,7 @@
// chkTopSide
//
chkTopSide.AutoSize = true;
chkTopSide.Location = new System.Drawing.Point(113, 35);
chkTopSide.Location = new System.Drawing.Point(113, 28);
chkTopSide.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
chkTopSide.Name = "chkTopSide";
chkTopSide.Size = new System.Drawing.Size(70, 19);
@@ -1055,9 +1058,9 @@
chkRectSelect.Appearance = System.Windows.Forms.Appearance.Button;
chkRectSelect.Image = Properties.Resources.fam_rectt;
chkRectSelect.ImageAlign = System.Drawing.ContentAlignment.MiddleRight;
chkRectSelect.Location = new System.Drawing.Point(2, 70);
chkRectSelect.Location = new System.Drawing.Point(4, 81);
chkRectSelect.Name = "chkRectSelect";
chkRectSelect.Size = new System.Drawing.Size(87, 27);
chkRectSelect.Size = new System.Drawing.Size(88, 27);
chkRectSelect.TabIndex = 23;
chkRectSelect.Text = " Select";
chkRectSelect.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -1066,6 +1069,19 @@
chkRectSelect.UseVisualStyleBackColor = true;
chkRectSelect.CheckedChanged += chkRectSelect_CheckedChanged;
//
// nudBrush
//
nudBrush.Location = new System.Drawing.Point(59, 51);
nudBrush.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
nudBrush.Maximum = new decimal(new int[] { 32, 0, 0, 0 });
nudBrush.Minimum = new decimal(new int[] { 1, 0, 0, 0 });
nudBrush.Name = "nudBrush";
nudBrush.Size = new System.Drawing.Size(47, 23);
nudBrush.TabIndex = 24;
toolTip1.SetToolTip(nudBrush, "Symbol height");
nudBrush.Value = new decimal(new int[] { 1, 0, 0, 0 });
nudBrush.ValueChanged += nudBrush_ValueChanged;
//
// label3
//
label3.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
@@ -1121,11 +1137,13 @@
// pnlRightButtons
//
pnlRightButtons.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
pnlRightButtons.Controls.Add(nudBrush);
pnlRightButtons.Controls.Add(lblBrush);
pnlRightButtons.Controls.Add(chkLeftSide);
pnlRightButtons.Controls.Add(nudX);
pnlRightButtons.Controls.Add(nudY);
pnlRightButtons.Controls.Add(label1);
pnlRightButtons.Controls.Add(label2);
pnlRightButtons.Controls.Add(lblWidth);
pnlRightButtons.Controls.Add(lblHeight);
pnlRightButtons.Controls.Add(chkRectSelect);
pnlRightButtons.Controls.Add(lblType);
pnlRightButtons.Controls.Add(btnExport);
@@ -1138,6 +1156,16 @@
pnlRightButtons.Size = new System.Drawing.Size(184, 154);
pnlRightButtons.TabIndex = 27;
//
// lblBrush
//
lblBrush.AutoSize = true;
lblBrush.Location = new System.Drawing.Point(10, 54);
lblBrush.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
lblBrush.Name = "lblBrush";
lblBrush.Size = new System.Drawing.Size(40, 15);
lblBrush.TabIndex = 25;
lblBrush.Text = "Brush:";
//
// pnlInfo
//
pnlInfo.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
@@ -1183,6 +1211,7 @@
cmMinilist.ResumeLayout(false);
menuStrip1.ResumeLayout(false);
menuStrip1.PerformLayout();
((System.ComponentModel.ISupportInitialize)nudBrush).EndInit();
pnlRightButtons.ResumeLayout(false);
pnlRightButtons.PerformLayout();
pnlInfo.ResumeLayout(false);
@@ -1195,8 +1224,8 @@
#endregion
private System.Windows.Forms.Panel dotPanel;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label lblWidth;
private System.Windows.Forms.Label lblHeight;
private System.Windows.Forms.Label lblType;
private System.Windows.Forms.ComboBox cbZoom;
private System.Windows.Forms.Label label4;
@@ -1289,6 +1318,8 @@
private System.Windows.Forms.ToolStripMenuItem toggleBarToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
private System.Windows.Forms.ToolStripMenuItem zerofyWidthToolStripMenuItem;
public System.Windows.Forms.NumericUpDown nudBrush;
private System.Windows.Forms.Label lblBrush;
}
}

View File

@@ -120,6 +120,8 @@ namespace McBitFont {
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;
@@ -166,6 +168,10 @@ namespace McBitFont {
CheckForAdd();
SideBarRecalc();
// Create default cursor
dotPanel.Cursor = McCursor.GetCursor((int)nudBrush.Value, cellSize, gap);
}
[DllImport("user32.dll")]
@@ -212,6 +218,12 @@ namespace McBitFont {
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;
@@ -334,6 +346,7 @@ namespace McBitFont {
vScroll.Enabled = true;
}
dotPanel.Cursor = McCursor.GetCursor((int)nudBrush.Value, cellSize, gap);
dotPanel.Refresh();
}
@@ -525,7 +538,7 @@ namespace McBitFont {
selection2.X = i;
selection2.Y = j;
dotPanel.Invalidate();
} //else history.AddPre(f, false);
}
}
if (e.Button == MouseButtons.None && mouseDown) {
// Released a mouse button
@@ -583,27 +596,37 @@ namespace McBitFont {
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.Left) {
if (PaintPixel(i, j, true)) fChanged = true;
}
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);
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;
@@ -1663,5 +1686,9 @@ namespace McBitFont {
MiniList_SelectedIndexChanged(miniList, EventArgs.Empty);
}
}
private void nudBrush_ValueChanged(object sender, EventArgs e) {
dotPanel.Cursor = McCursor.GetCursor((int)nudBrush.Value, cellSize, gap);
}
}
}

105
McBitFont/McCursor.cs Normal file
View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace McBitFont {
internal class McCursor {
public struct IconInfo {
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
[DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect(ref IconInfo icon);
/// <summary>
/// Create a cursor from a bitmap without resizing and with the specified
/// hot spot
/// </summary>
public static Cursor CreateCursorNoResize(Bitmap bmp, int xHotSpot, int yHotSpot) {
IntPtr ptr = bmp.GetHicon();
IconInfo tmp = new IconInfo();
GetIconInfo(ptr, ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
ptr = CreateIconIndirect(ref tmp);
return new Cursor(ptr);
}
/// <summary>
/// Create a 32x32 cursor from a bitmap, with the hot spot in the middle
/// </summary>
public static Cursor CreateCursor(Bitmap bmp) {
int xHotSpot = 16;
int yHotSpot = 16;
IntPtr ptr = ((Bitmap)ResizeImage(bmp, 32, 32)).GetHicon();
IconInfo tmp = new IconInfo();
GetIconInfo(ptr, ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
ptr = CreateIconIndirect(ref tmp);
return new Cursor(ptr);
}
/// <summary>
/// Resize the image to the specified width and height.
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
public static Bitmap ResizeImage(Image image, int width, int height) {
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage)) {
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes()) {
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
public static Cursor GetCursor(int penSize, int cellSize, int gap) {
int size = (cellSize + gap) * penSize;
Bitmap bmp = new(size, size);
Pen pb = new(Color.Black, 1);
SolidBrush bw = new(Color.FromArgb(160, Color.White));
using (Graphics g = Graphics.FromImage(bmp)) {
g.DrawRectangle(pb, 0, 0, size-1, size-1);
g.FillRectangle(bw, 1, 1, size - 2, size - 2);
}
return CreateCursorNoResize(bmp, cellSize / 2, cellSize / 2);
}
}
}

View File

@@ -25,6 +25,7 @@ Some basic hints on the interface:
- Mouse Scroll to scroll up and down
- Shift + scroll to scroll left and right
- Crtl + scroll to zoom
- Alt + Scroll to change painting brush size
Download in the [Releases](https://gitea.mcflyer.ru/McFLY/McBitFont/releases) section!

View File

@@ -2,11 +2,11 @@ Application:
- Consider migrating to WPF in order to make DPI aware UI
V Option to hide symbols list to narrow the side bar
V Buttons to select previous/next symbol with shortcuts
- Custom cursor showing painting size
V Custom cursor showing painting size
Functionality:
V Fix straight (Ctrl/Shift) lines paint to reset coordinate on mouse-up even if Ctrl/Shift is still held
V Command to make all blank symbols zero-width
- Alt-Scroll to change painting size
V Alt-Scroll to change painting size
Bugs: