如何在实际绘画之前在光标处显示画笔?

How to display brush at cursor before actually paint?

我想制作像 MS-Paint 这样的照片编辑器。 画笔模式,当用户点击鼠标并移动它时,将绘制描边。

Private Sub pbPhoto_MouseDown(sender As Object, e As MouseEventArgs) Handles pbPhoto.MouseDown
    bStart = true
End Sub
Private Sub pbPhoto_MouseMove(sender As Object, e As MouseEventArgs) Handles pbPhoto.MouseMove
    If bStart Then
         Dim b As SolidBrush(Color.Red)
        pbPhoto.CreateGraphics.FillEllipse(b, e.X, e.Y, 10, 10)
    End If
End Sub
Private Sub pbPhoto_MouseUp(sender As Object, e As MouseEventArgs) Handles pbPhoto.MouseUp
     bStart = false
End Sub

画的很好。但是如果你看一下 MS-Paint,会在光标的中心看到一个画笔。它总是显示,但在按下鼠标之前不会绘制到图片框。如果您更改画笔大小或颜色,您可以在光标处看到预览。

如何在没有绘画的情况下显示笔触?

您可以创建一个图像来反映画笔工具的颜色和大小,并使用它为绘图创建自定义光标 canvas。每当您 select 画笔工具或在 select 编辑画笔工具时更改 color/size 时,都应调用此例程。自定义游标的句柄在不再使用时应销毁。例如,当您 select 另一个绘图工具或关闭表单时。

图像


有些接近。 penWidthmaxWidth 参数来计算画笔的大小。图片中的红色圆圈。例如,TrackBar 控件的 ValueMaximum 属性。

' +
Imports System.Drawing.Drawing2D

' ...

Private Function CreateBrushBitmap(color As Color, penWidth As Integer,
                                    maxWidth As Integer) As Bitmap
    Dim bmp As New Bitmap(32, 32)
    Dim rec As New Rectangle(0, 0, 32, 32)

    Using g = Graphics.FromImage(bmp),
            br = New SolidBrush(color),
            pn = New Pen(Color.Gray) With {.DashStyle = DashStyle.Dot}

        g.SetClip(Rectangle.Inflate(rec, -8, -8), CombineMode.Exclude)
        g.DrawLine(pn, (rec.Width - 1) \ 2, rec.Y, (rec.Width - 1) \ 2, rec.Height)
        g.DrawLine(pn, rec.X, (rec.Height - 1) \ 2, rec.Width, (rec.Height - 1) \ 2)
        g.ResetClip()

        Dim ellipseRec = Rectangle.Inflate(rec, -10, -10)
        Dim sz = CSng(Math.Round(ellipseRec.Width * penWidth / maxWidth))
        Dim r = New RectangleF(
            ellipseRec.X + (ellipseRec.Width - sz) / 2 - 1,
            ellipseRec.Y + (ellipseRec.Height - sz) / 2 - 1,
            sz, sz)

        g.SmoothingMode = SmoothingMode.AntiAlias
        g.FillEllipse(br, r)
    End Using

    Return bmp
End Function

自定义光标


CreateBrushCursor 函数调用下面显示的本机函数来创建自定义光标。 Credit

' +
Imports System.Runtime.InteropServices

' ...

Private Function CreateBrushCursor(bmp As Bitmap,
                                    xHotspot As Integer,
                                    yHotspot As Integer) As Cursor
    Dim inf As New ICONINFO With {
        .xHotspot = xHotspot,
        .yHotspot = yHotspot,
        .fIcon = False,
        .hbmMask = bmp.GetHbitmap(),
        .hbmColor = bmp.GetHbitmap()
    }

    Dim pnt As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(inf))

    Marshal.StructureToPtr(inf, pnt, True)

    Dim curPtr As IntPtr = CreateIconIndirect(pnt)

    DestroyIcon(pnt)
    DeleteObject(inf.hbmMask)
    DeleteObject(inf.hbmColor)

    Return New Cursor(curPtr)
End Function

Private Structure ICONINFO
    Public fIcon As Boolean
    Public xHotspot As Integer
    Public yHotspot As Integer
    Public hbmMask As IntPtr
    Public hbmColor As IntPtr
End Structure

<DllImport("user32.dll", EntryPoint:="CreateIconIndirect")>
Private Shared Function CreateIconIndirect(ByVal iconInfo As IntPtr) As IntPtr
End Function

<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function DestroyIcon(ByVal hIcon As IntPtr) As Boolean
End Function

<DllImport("gdi32.dll")>
Private Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean
End Function

实施


画笔工具select时调用CreateBrushCursor方法创建和设置自定义光标,设置画笔color/size。在此示例中,picSelectedColortbPenWidth 是用于创建光标图像的输入控件。 picCanvas 是绘图 canvas.

Private Sub CreateBrushCursor()
    DestroyBrushCursor()
    customBrushCursor = CreateBrushCursor(
        CreateBrushBitmap(
        picSelectedColor.BackColor, tbPenWidth.Value, tbPenWidth.Maximum), 16, 16)
    picCanvas.Cursor = customBrushCursor
End Sub

最后调用DestroyBrushCursor方法进行清理。当你select另一个工具时,关闭绘图窗体...等等

Private Sub DestroyBrushCursor()
    If customBrushCursor IsNot Nothing Then
        customBrushCursor.Dispose()
        customBrushCursor = Nothing
    End If
End Sub

注:绘图例程无关故不列出。