Code zu Grafikverarbeitung in .NET

Abrunden von Bildecken

Für die Elemente einer Menüleiste sollten Bilder auf eine bestimmte Größe verkleinert und mit abgerundeten Ecken versehen werden. Um dies zu erreichen, wird im ersten Schritt ein GraphicsPath aufgebaut, der jenen Bereich des Ergebnisbildes darstellt, welcher nicht vom Bild überdeckt werden soll. Dieser Bereich setzt sich zusammen aus den vier untereinander verbundenen, abgerundeten Ecken. Dann wird das Ausgangsbild auf das Ergebnisbild kopiert. Abschließend werden die zuvor durch den GraphicsPath umschlossenen Eckbereiche in einer bestimmten Farbe übermalt.

[Illustration] Abrunden der Ecken eines Bildes bei einem Eckenradius von 30 Prozent

Abrunden der Ecken eines Bildes bei einem Eckenradius von 30 Prozent.

Obenstehende Grafik zeigt ganz links das rechteckige Ausgangsbild, in der Mitte den Pfad, der die Ecken umschließt und rechts das Ergebnisbild mit den abgerundeten Ecken. Die im Folgenden angegebene Funktion RoundBitmap stellt eine Implementierung der beschriebenen Vorgehensweise dar:

Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging

⋮

''' <summary>
'''   Rundet die Ecken eines Bildes ab und gibt das Bild mit den
'''   abgerundeten Ecken zurück.
''' </summary>
''' <param name="SourceBitmap">
'''   Das Bild, dessen Ecken abgerundet werden sollen.
''' </param>
''' <param name="Brush">
'''   Der Hintergrundpinsel, mit dem die Ecken gefüllt werden sollen.
''' </param>
''' <param name="Radius">
'''   Der Radius der Ecken in Prozent (Zahl zwischen 0 und 1).
''' </param>
''' <returns>
'''   Das Bild aus <paramref name="SourceBitmap"/> mit abgerundeten Ecken.
''' </returns>
Private Function RoundBitmap( _
    ByVal SourceBitmap As Bitmap, _
    ByVal Brush As Brush, _
    Optional ByVal Radius As Double = 0.2 _
) As Bitmap
    Dim b As New Bitmap(SourceBitmap.Width, SourceBitmap.Height)
    Dim g As Graphics = Graphics.FromImage(b)
    g.SmoothingMode = SmoothingMode.AntiAlias
    Dim p As New GraphicsPath()
    p.StartFigure()
    Const Border As Integer = 1
    Dim Corner As Integer = CInt(SourceBitmap.Width * Radius)
    Dim Width As Integer = b.Width - 1
    Dim Height As Integer = b.Height - 1
    p.AddRectangle( _
        New Rectangle( _
            -Border, _
            -Border, _
            b.Width + Border, _
            b.Height + Border _
        ) _
    )
    p.AddArc(New Rectangle(0, 0, Corner, Corner), 180, 90)
    p.AddLine(Corner, 0, Width - Corner, 0)
    p.AddArc(New Rectangle(Width - Corner, 0, Corner, Corner), -90, 90)
    p.AddLine(Width, Corner, Width, b.Height - Corner)
    p.AddArc( _
        New Rectangle(Width - Corner, Height - Corner, Corner, Corner), _
        0, _
        90 _
    )
    p.AddLine(Width - Corner, Height, Corner, Height)
    p.AddArc(New Rectangle(0, Height - Corner, Corner, Corner), 90, 90)
    p.AddLine(0, Height - Corner, 0, Corner)
    p.CloseFigure()
    g.DrawImageUnscaled(SourceBitmap, 0, 0)
    g.FillPath(Brush, p)
    p.Dispose()
    g.Dispose()
    Return b
End Function
Implementierung der Funktion RoundBitmap.

Ein Beispielaufruf könnte wie folgt aussehen:

Dim b1 As New Bitmap("C:\WINDOWS\Angler.bmp")
Me.PictureBox1.Image = RoundBitmap(b1, Brushes.White, 0.4)
b1.Dispose()
Beispielaufruf der Funktion RoundBitmap.

Umgehen der Dateisperre duch Laden von Grafiken

Lädt man eine Grafik über Bitmap.FromFile("C:\WINDOWS\Angler.bmp") aus einer Datei, dann wird diese Datei gesperrt. Als Folge kann die Datei so lange nicht gelöscht werden, bis die Anwendung beendet wird. Mit einem kleinen Trick kann man das Sperren der Datei jedoch verhindern. Dabei wird ein weiteres Bild auf Basis des aus der Datei geladenen Originalbildes erstellt und anschließend das Originalbild entladen.

Dim OriginalImage As New Bitmap("C:\WINDOWS\Angler.bmp")
Dim Image As New Bitmap(OriginalImage)
OriginalImage.Dispose()
⋮
Image.Dispose()
Laden einer Grafik ohne Sperren der Datei über eine Kopie des Bildes.

Eine alternative Möglichkeit, die Dateisperre zu umgehen, liegt darin, die Daten aus der Bilddatei in einen Speicherstrom (Klasse System.IO.MemoryStream) zu schreiben und das Bildobjekt auf Basis der Daten im Speicherstrom zu instanzieren. Bei dieser Vorgehensweise ist darauf zu achten, daß das Schließen des Datenstroms erst dann erfolgen darf, wenn die auf dem Datenstrom basierende Grafik nicht mehr benötigt wird. Wird diese Regel nicht eingehalten, kann es zur Laufzeit zu Fehlern kommen, wenn .NET versucht, Bilddaten oder Metadaten aus der Datei zu lesen.

Dim ImageFile As New FileStream("C:\WINDOWS\Angler.bmp", FileMode.Open)
Dim Reader As New BinaryReader(ImageFile)
Dim ImageStream As New MemoryStream(Reader.ReadBytes(CInt(ImageFile.Length)))
Reader.Close()
Dim Image As Image = Image.FromStream(ImageStream)
⋮
Image.Dispose()
Laden einer Grafik ohne Sperren der Datei mittels eines Speicherstroms.

Mit der oben gezeigten Vorgehensweise lassen sich auch einfach Bilder aus einer Datenbank lesen oder in eine Datenbank schreiben. Zum Schreiben des Bildes in die Datenbank wird dieses über die Methode Save des Bildobjekts in einen Speicherstrom (MemoryStream) geschrieben. Anschließend kann man die Daten aus dem Speicherstrom mittels dessen ToArray-Methode in ein Bytearray umwandeln und dieses in der Datenbank persistieren. Das Konstruieren eines Bildes aus einem Bytearray folgt der im vorigen Listing gezeigten Vorgehensweise.

Auswerten des Klicks auf ein Gitter

Das folgende einfache Beispiel zeigt, wie man einen Raster auf ein Formular bzw. ein Steuerelement zeichnet und die vom Benutzer mit der Maus gewählte Zelle ermittelt. Das Beispiel zeichnet einen Raster mit einer Kästchengröße von 100 Pixeln in der Höhe und Breite. Zur Auswertung der Auswahl mit der Maus wird das Ereignis MouseUp behandelt und aus der Mausposition die Zeilen- und Spaltennummer des geklickten Kästchens ermittelt:

Private Sub PictureBox1_MouseUp( _
    ByVal sender As Object, _
    ByVal e As MouseEventArgs _
) Handles PictureBox1.MouseUp
    MsgBox( _
        "Zeile: " & (e.Y \ 100).ToString() & ControlChars.NewLine & _
        "Spalte: " & (e.X \ 100).ToString() _
    )
End Sub

Private Sub PictureBox1_Paint( _
    ByVal sender As Object, _
    ByVal e As PaintEventArgs _
) Handles PictureBox1.Paint
    For i As Integer = 0 To Me.ClientSize.Width Step 100
        e.Graphics.DrawLine(Pens.Red, i, 0, i, Me.ClientSize.Height)
        For j As Integer = 0 To Me.ClientSize.Height Step 100
            e.Graphics.DrawLine(Pens.Red, 0, j, Me.ClientSize.Width, j)
        Next j
    Next i
End Sub
Zeichnen eines anklickbaren Gitters.