Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
Guida al Visual Basic .NET - Utilizzo avanzato della classe Graphics

Guida al Visual Basic .NET

Capitolo 97° - Utilizzo avanzato della classe Graphics

<< Precedente Prossimo >>
Penne alternative

Nel capitolo precedente abbiamo impiegato penne predefinite. Ora vogliamo cercare qualcosa di più accattivante. Dopo aver inizializzato un nuovo oggetto Pen, possiamo modificarne le proprietà:

  • Brush : associando un pennello a questa proprietà è possibile riempire il tratto della penna mediante tale pennello con sfumature, disegni, immagini, eccetera...
  • CompoundArray : ammettiamo che l'oggetto Pen abbia una larghezza abbondante, ad esempio 10. Tutto il tratto viene riempito unfirmemente con un colore (a meno che non abbiate modificato la proprietà Brush). Mediante questa proprietà possiamo decidere di rimpiazzare il blocco cromatico con più strisce colorate di largezza definita. Ecco un esempio:
    Dim b As New Bitmap(300, 300)
    Dim g As Graphics = Graphics.FromImage(b)
    Dim p As New Pen(Color.Black, 14)
    
    p.CompoundArray = New Single() {0, 0.2, 0.4, 0.8, 0.9, 1}
    g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
    g.Clear(Color.White)
    g.DrawLine(p, 10, 10, 80, 100)
    PenCompound.jpg La penna lascia un tratto di larghezza 14, ma esso non è uniforma. La proprietà CompoundArray è di tipo array di Single. In questo array vanno specificate delle posizioni percentuali, che indicano l'intervallarsi di strisce e spazi. Nel codice sopra, ho specificato che da 0% a 20% della larghezza ci deve essere una linea, dal 20% al 40% uno spazio, dal 40% all'80% una linea, dall'80% al 90% uno spazio e dal 90% al 100% una linea. Il tutto ha prodotto un risultato come quello in figura.
  • DashStyle : permette di scegliere un differente tipo di tratteggio tra alcuni predefiniti. Eccone un esempio:

    DashStyles.jpg
    L'ultimo è stato creato modificando la proprietà DashPattern: è anch'essa un'array di Single, ma specifica la lunghezza in pixel di un tratto e di uno spazio (in figura era 5, 5, 10, 5, ossia 5px di linea, 5 di spazio, 10 di linea e 10 di spazio). Si tratta di pixel poiché il tratto si estende in lunghezza (quindi non si possono specificare valori relativi)
  • DashCap : determina la forma degli estremi dei punti o delle linee che costituiscono una linea tratteggiata. Ecco un esempio, combinato con la proprietà CompoundArray:

    DashCaps.jpg
  • StartCap / EndCap : specifica la forma geometrica posta all'inizio o alla fine della linea (di tutta la linea). Simile a DashCap, ma con molte più varianti.

 

Pennelli alternativi

Oltre a SolidBrush, esistono alcuni altri pennelli con caratteristiche peculiari. Ad esempio:

  • HatchBrush : riempie una superficie con una trama specificata. Qui ci sono tutte le varianti;
  • LinearGradientBrush : esegue una sfumature sull'area da riempire. Potete consultare alcuni esempi nella sezione Appunti;
  • TextureBrush : riempie un'area specificata con un'immagine, eventualmente ripetendola e/o allungandola. Esempio

 

Esempio 1: Creare una userbar

Per mostrarvi qualche utilizzo pratico e non proprio basilare alla grafica in .NET voglio mostrare come sia possibile disegnare una userbar simile a quelle create con Photoshop.
Per creare una userbar si seguono più o meno sempre questi passaggi:

  • si prende uno sfondo di dimensioni 350x19 (formato standard), oppure si riempie quest'area con un gradiente colorato e vi si applica sopra un'altra porzione di immagine;
  • si applica sullo sfondo una trama omogenea formata da righe diagonali molto sottili e ravvicinate di colore scuro;
  • si crea un effetto lucido sovrapponendo all'immagine così creata una semiellisse di colore bianco, con una trasparenza del 20-30%;
  • per ultimare il lavoro, si pone una scritta sulla parte destra della barra, di solito usando il font Visitor TT2 BRK.

Noi automatizzeremo tutto questo creando una classe apposita:

Namespace Userbars

    Class Userbar
        Private _BackgroundImage As Image
        Private _BackgroundImagePosition As Int32
        Private _Text As String
        Private _Size As Size = New Size(350, 25)

        Public Property Size() As Size
            Get
                Return _Size
            End Get
            Set(ByVal value As Size)
                If value.Width > 0 And value.Height > 0 Then
                    _Size = value
                Else
                    _Size = New Size(350, 25)
                End If
            End Set
        End Property

        Public Property BackgroundImage() As Image
            Get
                Return _BackgroundImage
            End Get
            Set(ByVal value As Image)
                If value.Width > Me.Size.Width Or value.Height > Me.Size.Height Then
                    Throw New ArgumentOutOfRangeException()
                Else
                    _BackgroundImage = value
                End If
            End Set
        End Property

        Public Property BackgroundImagePosition() As Int32
            Get
                Return _BackgroundImagePosition
            End Get
            Set(ByVal value As Int32)
                If value > Me.Size.Width Then
                    Throw New ArgumentOutOfRangeException()
                Else
                    _BackgroundImagePosition = value
                End If
            End Set
        End Property

        Public Property Text() As String
            Get
                Return _Text
            End Get
            Set(ByVal value As String)
                _Text = value
            End Set
        End Property


        Public Function Create() As Image
            Dim Result As New Bitmap(Me.Size.Width + 1, Me.Size.Height + 1)
            Dim G As Graphics = Graphics.FromImage(Result)

            'Questi valori sono statici poiché costanti. Una 
            'volta inizializzati saranno sempre uguali e non 
            'verranno creati ulteriori oggetti ad ogni invocazione
            'di Create()
            
            'HatchBrush permette di riempire un'area con una trama
            'prefissata. Noi vogliamo disegnare delle sottili righe
            'scure, un motivo a cui corrisponde l'enumeratore
            'indicato.
            'Il colore usato è un Nero con opacità pari
            'a 48: dato che il valore massimo è 255, si tratta di
            'un nero al 19% di opacità. Il colore di sfondo è 
            'invece trasparente (come se non ci fosse).
            Static LinesBrush As New HatchBrush(HatchStyle.DarkUpwardDiagonal, Color.FromArgb(48, Color.Black), Color.Transparent)
            'Il font usato è Visitor TT2 BRK, grandezza 11pt.
            '11 va molto bene per le userbar di dimensione
            'standard. Se volete qualcosa di più generale,
            'il font deve dipendere dall'altezza
            Static FontUsed As New Font("Visitor TT2 BRK", 11, FontStyle.Regular)
            'Questo pennello servirà per l'effetto lucido. Dato
            'che si tratta di un SolidBrush, riempirà l'area con
            'un colore omogeneo, in questo caso un bianco 
            'trasparente
            Static TranspBrush As New SolidBrush(Color.FromArgb(70, Color.White))

            'Imposta la modalità di smussamento delle linee. Dato 
            'che questa funzione viene usata solo quando l'utente  
            'la richiede (quindi non molte volte al secondo), e che 
            'il risultato deve essere il migliore possibile,
            'utilizziamo un algoritmo ad alto rendimento e
            'bassa velocità di rendering.
            G.SmoothingMode = SmoothingMode.HighQuality
            'Imposta la modalità di sovrapposizione. Poiché
            'dobbiamo disegnare molte cose le una sopra alle altre, ci
            'serve che i nuovi disegni non sovrascrivano quelli
            'precedenti, ma vi si applichino sopra rispettandone
            'le trasparenze
            G.CompositingMode = CompositingMode.SourceOver
            'Disegna l'immagine di sfondo
            G.DrawImage(Me.BackgroundImage, Me.BackgroundImagePosition, 0)
            'Disegna le righe su tutta l'immagine
            G.FillRectangle(LinesBrush, 0, 0, Me.Size.Width, Me.Size.Height)
            'Applica il velo di bianco trasparente. Notate che
            'utilizzo delle coordinate negative per disegnare
            'l'ellisse fuori dall'immagine. In questo modo, 
            'noi vedremo solo la parte di ellisse che rientra
            'nell'area effettivamente esistente, ossia solo metà.
            'Inoltre ho messo qualche coefficiente per aggiustare
            'la larghezza e rendere migliore l'aspetto
            G.FillEllipse(TranspBrush, -5, -Me.Size.Height + 3, Me.Size.Width + 10, CInt(Me.Size.Height * 1.5))
            'Disegna il contorno della barra
            G.DrawRectangle(Pens.Black, 0, 0, Me.Size.Width, Me.Size.Height)

            'Calcola la dimensione del testo (in pixel)
            Dim TextSize As SizeF = G.MeasureString(Me.Text, FontUsed)
            'Quindi disegna la stringa Text in bianco, spostata
            'rispetto al margine destro in modo che il testo non
            'vada fuori dall'immagine.
            G.DrawString(Me.Text, FontUsed, Brushes.White, Me.Size.Width - CInt(TextSize.Width) - 30, Me.Size.Height  3)

            'Restituisce il risultato
            Return Result
        End Function

    End Class
    
End Namespace

Ed ecco un esempio:

AbstractUserbar.jpg
Come noterete, la proprietà BackgroundImagePosition ha senso solo se l'immagine è di larghezza inferiore alla userbar, ossia nel caso in cui lo sfondo debba essere riempito con un gradiente uniforme (LinearGradientBrush). Non ho implementato questa funzionalità nel codice, ma la lascio come esercizio. Potete usare come riferimento il mio articolo al riguardo nella sezione Appunti.

Esempio 2: Orologio analogico

Ecco uno stupidissimo codice di esempio per disegnare un orogoloio:

Class Form1
    Private ClockImage As New Bitmap(230, 230)
    Private G As Graphics = Graphics.FromImage(ClockImage)

    Private Sub tmrClock_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrClock.Tick
        G.Clear(Me.BackColor)

        Dim Time As Date = Date.Now

        Static BorderPen As New Pen(Color.Black, 5)
        Static HourPen As New Pen(Color.Red, 4) With {.EndCap = LineCap.Triangle}
        Static MinutePen As New Pen(Color.Blue, 2) With {.EndCap = LineCap.Triangle}
        Static SecondPen As New Pen(Color.Green, 1)

        Dim C As Point = New Point(ClockImage.Width / 2, ClockImage.Height / 2)
        Dim h, m, s As Single

        h = Time.Hour + (Time.Minute / 60)
        m = Time.Minute + (Time.Second / 60)
        s = Time.Second

        G.SmoothingMode = SmoothingMode.AntiAlias

        G.FillEllipse(Brushes.White, 5, 5, ClockImage.Width - 10, ClockImage.Height - 10)
        G.DrawEllipse(BorderPen, 5, 5, ClockImage.Width - 10, ClockImage.Height - 10)

        For I As Int32 = 0 To 11
            G.FillEllipse(Brushes.Black, _
                C.X + CInt(ClockImage.Width / 2.3 * Math.Cos(Math.PI / 2 - I / 6 * Math.PI)) - 1, _
                C.Y - CInt(ClockImage.Width / 2.3 * Math.Sin(Math.PI / 2 - I / 6 * Math.PI)) - 1, _
                2, 2)
        Next

        G.DrawLine(HourPen, C.X, C.Y, _
            C.X + CInt(ClockImage.Width / 4 * Math.Cos(Math.PI / 2 - h / 6 * Math.PI)), _
            C.Y - CInt(ClockImage.Width / 4 * Math.Sin(Math.PI / 2 - h / 6 * Math.PI)))

        G.DrawLine(MinutePen, C.X, C.Y, _
               C.X + CInt(ClockImage.Width / 3 * Math.Cos(Math.PI / 2 - m / 30 * Math.PI)), _
               C.Y - CInt(ClockImage.Width / 3 * Math.Sin(Math.PI / 2 - m / 30 * Math.PI)))

        G.DrawLine(SecondPen, C.X, C.Y, _
            C.X + CInt(ClockImage.Width / 2.2 * Math.Cos(Math.PI / 2 - s / 30 * Math.PI)), _
            C.Y - CInt(ClockImage.Width / 2.2 * Math.Sin(Math.PI / 2 - s / 30 * Math.PI)))

        imgClock.Image = ClockImage
    End Sub
End Class


DrawClock.jpg

<< Precedente Prossimo >>
A proposito dell'autore

C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...