venerdì 4 novembre 2011

[VB.NET] Disegnare su Bitmap

Descrizione :
Una mia tecnica per disegnare su Bitmap di sfondo e salvare il risultato finale nel formato immagine desiderato.

+ Articolo :

"Disegnare in una PictureBox" è un task piuttosto comune e richiesto in svariate applicazioni.
Qui si tratta di disegnare "a mano", ovvero con il Mouse :


Per ottenere questo risultato occorre naturalmente una Form "FormMain", con una PictureBox "PBX" e un paio di Button : "cmd_salva" e "cmd_reset".
Inoltre ho aggiunto anche un NumericUpDown "num_tratto" e una ColorPickerCombo "CPC".
La "ColorPickerCombo" ovviamente non fa parte del FrameWork 4.0, ma è una speciale ComboBox per la scelta rapida dei Known Colors, il cui articolo completo di codice è qui :
http://marcoggblog.blogspot.com/2011/11/vbnet-un-color-picker-per-known-colors.html
La disponibilità di una scelta sia sullo spessore del tratto, sia sul colore, permette già una certa varietà.

In questo articolo disegno su una Bitmap in memoria ( che ha le stesse dimensioni in pixels della PBX... ), e chiaramente le coordinate del Mouse sono prese nell'area della PBX.
L'immagine base di partenza usata per questo articolo è chiaramente la semplice pianta in bianco e nero dell'immagine precedente :


Con un semplice PBX.Refresh() mi assicuro che l'immagine visualizzata nella PBX sia sempre aggiornata, quindi, all'atto pratico sembra proprio che stia "disegnando nella PBX"...

Qui di seguito la figura che mostra FormMain in Design e relativi controlli :


Faccio uso di due Bmp in memoria, una per l'immagine di sfondo ( caricamento da disco una volta sola ), e una ( copia ) su cui disegnare e salvare.
L'operazione di Reset elimina ogni disegno precedente creando una nuova Bmp Corrente dalla Bmp di Sfondo.

--> Codice FormMain :
Public Class FormMain

    Private drawOn As Boolean
    Private drawColor As Color
    Private drawWidth As Single
    Private P As New Pen(drawColor, drawWidth)
    Private B As New SolidBrush(drawColor)
    Private prevX As Integer
    Private prevY As Integer
    Private X As Integer
    Private Y As Integer
    Private bmpSfondo As Bitmap
    Private bmpCorrente As Bitmap
    Private percorsoImgs As String = Application.StartupPath & "\"

    Private Function GetBitmap(ByVal nomeFile As String) As Bitmap

        Using FS As New System.IO.FileStream(nomeFile, IO.FileMode.Open, IO.FileAccess.Read)
            Return Bitmap.FromStream(FS)
        End Using

    End Function

    Private Sub FormMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        With num_tratto
            .Minimum = 1
            .Maximum = 10
            .Value = 2
        End With
        bmpSfondo = GetBitmap(percorsoImgs & "background.jpg")
        bmpCorrente = New Bitmap(bmpSfondo)
        PBX.Image = bmpCorrente
        drawWidth = num_tratto.Value
        drawColor = CPC.PickedColor
        P = New Pen(drawColor, drawWidth)

    End Sub

    Private Sub PBX_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PBX.MouseDown

        If e.Button = Windows.Forms.MouseButtons.Left Then
            drawOn = True
            PBX.Cursor = Cursors.Cross
            X = e.X - drawWidth / 2
            Y = e.Y - drawWidth / 2
            prevX = e.X
            prevY = e.Y
            Using G As Graphics = Graphics.FromImage(bmpCorrente)
                G.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
                G.FillEllipse(B, X, Y, drawWidth, drawWidth)
            End Using
            PBX.Refresh()
        End If

    End Sub

    Private Sub PBX_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PBX.MouseUp

        If e.Button = Windows.Forms.MouseButtons.Left Then
            drawOn = False
            PBX.Cursor = Cursors.Default
        End If

    End Sub

    Private Sub PBX_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PBX.MouseMove

        If drawOn = False Then Exit Sub
        X = e.X - drawWidth / 2
        Y = e.Y - drawWidth / 2

        Using G As Graphics = Graphics.FromImage(bmpCorrente)

            G.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
            G.DrawLine(P, prevX, prevY, e.X, e.Y)
            G.FillEllipse(B, X, Y, drawWidth, drawWidth)

        End Using

        prevX = e.X
        prevY = e.Y
        PBX.Refresh()

    End Sub

    Private Sub cmd_salva_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_salva.Click

        bmpCorrente.Save(percorsoImgs & "output.gif", Imaging.ImageFormat.Gif)

    End Sub

    Private Sub cmd_reset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_reset.Click

        bmpCorrente = New Bitmap(bmpSfondo)
        PBX.Image = bmpCorrente

    End Sub

    Private Sub num_tratto_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles num_tratto.ValueChanged

        drawWidth = num_tratto.Value
        P.Width = drawWidth

    End Sub

    Private Sub CPC_OnPickedColorChanged() Handles CPC.OnPickedColorChanged

        drawColor = CPC.PickedColor
        P.Color = drawColor
        B.Color = drawColor

    End Sub

End Class

Note :

--> Il metodo di disegno tiene traccia delle coordinate Mouse precedenti e integra il semplice disegno del "pennello" ( in questo caso una Ellipse ) con una linea, per assicurare un'ottima continuità al tratto. Le possibilità di definire pennelli e tratti sono infinite...

--> Grazie alla straordinaria varietà di GDI+, non ci vuole molto per capirne le possibilità di upgrade...

+ Fine Articolo.

Un Click su "Mi Piace" è il modo migliore per ringraziare l'autore di questo articolo.



0 commenti:

Posta un commento

 
Design by Free WordPress Themes Modificato da MarcoGG