How to display brush at cursor before actually paint?

Viewed 227

I want to make photo editor like a MS-Paint. Brush Mode, Stroke will be drew while user was clicking mouse and move it.

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

Drawing is fine. But If you look at MS-Paint, will see a brush at center of the cursor. It always show but it's not paint to picturebox until mouse down. If you change brush size or color, you can see a preview at the cursor.

enter image description here

How to show brush stroke without paint?

1 Answers

You can create an image to reflect the color and size of the brush tool, and use it to create a custom cursor for the drawing canvas. This routine should be called whenever you select the brush tool or change the color/size while the brush tool is selected. The custom cursor's handle should be destroyed when it's no longer in use. For example, when you select another drawing tool or close the Form.

The Image


Something close. The penWidth and maxWidth params to calculate the size of the brush. The red circle in your image. The Value and Maximum properties of a TrackBar control for example.

' +
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

The Custom Cursor


The CreateBrushCursor function calls the native functions shown below to create the custom cursor. 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

Implement


Call CreateBrushCursor method to create and set the custom cursor when you select the brush tool, set the color/size of the brush. In this example, the picSelectedColor, tbPenWidth are the input controls for creating the cursor's image. The picCanvas is the drawing canvas.

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

Lastly, call the DestroyBrushCursor method to cleanup. When you select another tool, close the drawing Form ...etc.

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

SOQ68208917

Note: The drawing routine is irrelevant thus not listed.

Related