From 346088b532d2c064c1f2e5c7265861d3f76c086a Mon Sep 17 00:00:00 2001 From: Anton Mukhin Date: Thu, 19 Jun 2025 17:30:30 +0300 Subject: [PATCH] TODO features: - Custom cursor showing painting size - Alt-Scroll to change painting size --- McBitFont/Form1.Designer.cs | 105 +++++++++++++++++++++++------------- McBitFont/Form1.cs | 63 +++++++++++++++------- McBitFont/McCursor.cs | 105 ++++++++++++++++++++++++++++++++++++ README.md | 1 + TODO.txt | 4 +- 5 files changed, 221 insertions(+), 57 deletions(-) create mode 100644 McBitFont/McCursor.cs diff --git a/McBitFont/Form1.Designer.cs b/McBitFont/Form1.Designer.cs index 319c9cd..d6f70b6 100644 --- a/McBitFont/Form1.Designer.cs +++ b/McBitFont/Form1.Designer.cs @@ -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; } } diff --git a/McBitFont/Form1.cs b/McBitFont/Form1.cs index 4759651..1f3ceb1 100644 --- a/McBitFont/Form1.cs +++ b/McBitFont/Form1.cs @@ -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; @@ -1651,7 +1674,7 @@ namespace McBitFont { bool flag = false; for (int i = 0; i < frames.Count; i++) { - + if (IsFrameBlank(frames[i])) { frames[i] = FrameResize(frames[i], 0, dotHeight, true); @@ -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); + } } } diff --git a/McBitFont/McCursor.cs b/McBitFont/McCursor.cs new file mode 100644 index 0000000..c253520 --- /dev/null +++ b/McBitFont/McCursor.cs @@ -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); + + /// + /// Create a cursor from a bitmap without resizing and with the specified + /// hot spot + /// + 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); + } + + + /// + /// Create a 32x32 cursor from a bitmap, with the hot spot in the middle + /// + 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); + } + + + /// + /// Resize the image to the specified width and height. + /// + /// The image to resize. + /// The width to resize to. + /// The height to resize to. + /// The resized image. + 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); + } + + } +} diff --git a/README.md b/README.md index 00e08f3..711a5e1 100644 --- a/README.md +++ b/README.md @@ -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! diff --git a/TODO.txt b/TODO.txt index 886e0ec..d3977b5 100644 --- a/TODO.txt +++ b/TODO.txt @@ -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: