15.5 Bilddateien
Grafiken, Bilder und Zeichnungen werden mithilfe verschiedener Technologien dargestellt, bearbeitet und letztendlich auch gespeichert. Prinzipiell werden dabei zwei Kategorien zur Bearbeitung grafischer Elemente unterschieden:
- Rastergrafiken
- Vektorgrafiken
Rastergrafiken
Oft werden Bilder oder Fotos als Rastergrafik im Bitmap-Format hinterlegt. Ein solches Bild ist eine Aneinanderreihung einzelner Bildpunkte, die durch je eine Farbe beschrieben werden. Weil eine Rastergrafik sehr simpel aufgebaut wird, ist auch ihre Erzeugung bzw. Nachbearbeitung sehr einfach. Allerdings erkauft man sich diesen Vorteil zu einem hohen Preis:
- Kurven sind immer stufig, weil sie sich aus einzelnen Rechtecken zusammensetzen.
- Eine Rastergrafik benötigt sehr viel Speicherplatz. Zur Abmilderung wurden zahlreiche Komprimierungsverfahren entwickelt, um die Datenmenge zu reduzieren. GIF für Liniengrafiken und JPEG für Fotos sind die bekanntesten Beispiele.
- Bei einer Skalierung (Größenänderung) kommt es zu sehr unschönen optischen Effekten. Beispielsweise werden bei einer Verkleinerung einfach Spalten oder Zeilen weggelassen.
Vektorgrafiken
Bei einer Vektorgrafik werden Linien, Kurven und Texte durch wenige Koordinaten beschrieben. Verschiedene Funktionen beschreiben, wie aus Koordinaten Grafiken werden. Jede Koordinate wird durch einen Vektor beschrieben. Ihn können Sie sich als Linie vorstellen, die durch einen Anfangspunkt, eine Richtung und eine Länge beschrieben ist. Gibt es für ein Bild-element keine passende Funktion, wird es aus Einzelteilen zusammengesetzt, für die jeweils eine Funktion existiert. Die funktionale Beschreibung hat einige Vorteile.
- Eine Vektorgrafik kann sehr kompakt gespeichert werden. Ein Bild der Größe 300 × 500 mit einer Diagonalen benötigt als Rastergrafik 150.000 Bildpunkte, als Vektorgrafik genügen der Anfangs- und der Endpunkt ((0,0),(300,500)).
- Da Grafiken durch Koordinaten beschrieben werden, kann eine Vektorgrafik beliebig skaliert werden. Das Programm rechnet die Koordinaten um und zeichnet die Grafik neu.
- In einigen Programmen (Beispiel CAD) ist das Bearbeiten einer Vektorgrafik einfacher, denn das Programm kennt die einzelnen Elemente und hat direkten Zugriff darauf. Mit der Maus lassen sich die einzelnen Bildelemente sehr einfach greifen und bearbeiten.
Das hört sich ausgesprochen vorteilhaft an, und es stellt sich die Frage, warum Vektorgrafiken die Rastergrafiken nicht ablösen konnten. Der Grund ist, dass in vielen Bildern die einzelnen Bildelemente unkorreliert sind, zum Beispiel die Pixel eines Fotos. Es gibt keine Funktion, die mehrere Elemente beschreiben kann. So müsste jedes Pixel durch einen eigenen Vektor beschrieben werden, was sehr viel Speicherplatz braucht. Daraus folgt:
- Zeichenprogramme, die hauptsächlich auf Linien und Kurven basieren, speichern die Daten des Bildes als Vektordatei, die oft binäre Daten enthält.
- Malprogramme und Bilder werden im Bitmap-Format gespeichert.
15.5.1 Bilder und Grafiken in .NET
Rastergrafiken werden als Bitmaps gespeichert, Vektorgrafiken als Metadateien. Die Klassenbibliothek stellt die Klassen Image, Bitmap, Icon und Metafile bereit, deren Klassenhierarchie in Abbildung 15.10 gezeigt ist.
Die Klasse Image ist die abstrakte Basisklasse für die beiden Typen Bitmap und Metafile und stellt diesen Funktionen zur Bildbearbeitung zur Verfügung. Ein wenig aus dem Rahmen fällt die Klasse Icon. Grundsätzlich ist auch ein Icon-Objekt eine Bitmap, es wird aber in einem Windows-eigenen Dateiformat gespeichert und hat nur wenig Bearbeitungsmethoden.
Abbildung 15.10 Hierarchie der Bildklassen
Der Bitmap-Klasse kommt eine deutlich höhere Bedeutung zu als den Klassen Icon und Metafile. Im Weiteren beschränke ich mich auf die Bitmap-Klasse.
15.5.2 Bitmap-Dateiformate
Eine Bitmap ist die Ansammlung von Pixeln in einem Array. Höhe und Breite werden in Pixeln angegeben. Die Anzahl der darstellbaren Farben wird Farbtiefe genannt. Sie wird als Anzahl der Bits gegeben, die jedem Pixel einer Bitmap gleichermaßen zur Verfügung steht. Der Bereich reicht von 1 bis 32 Bits, die Anzahl der darstellbaren Farben ergibt sich zu:
Farbenanzahl = 2Bits pro PixelFür RGB-Farben wird für den Rot-, Grün- und Blauanteil (die sogenannten Primärfarben) jeweils ein Byte bereitgestellt, das insgesamt 256 Farbabstufungen umfasst. Die ARGB-Farbskala hat noch ein viertes Byte, mit dem die Transparenz ebenfalls zwischen 0 (durchsichtig) und 255 (undurchsichtig) eingestellt wird.
Es haben sich einige feste Bitmap-Formate etabliert. Werden die Farben eines Pixels durch acht Bit beschrieben, ergeben sich 256 Farbabstufungen. Meistens wird dieses Format zur Darstellung von Graustufen-Bitmaps benutzt. Stehen jedem Pixel drei Byte mit je acht Bit zur Farbdarstellung zur Verfügung, erhöht sich die Farbanzahl sofort deutlich auf 224 = 16.777.216. Wir kennen diese Farbskala unter dem Namen True Color.
Der Nachteil herkömmlicher Bitmaps ist die Größe der Datei. Eine Bitmap mit den Abmessungen 300 × 300 Pixel und einer Farbtiefe von 24 Bit beansprucht bereits etwas über 260 KByte. Als die Rechner noch nicht so üppig mit Hauptspeicher ausgerüstet waren wie heutzutage, waren das schon Größenordnungen, die kaum noch akzeptabel waren. Heutzutage mangelt es den Maschinen zwar nicht mehr an RAM, allerdings gibt es ein anderes Nadelöhr hinsichtlich des Speichervolumens: das Web. Eine Internetseite, die mehrere Bitmaps beinhaltet, kann ein unzumutbares Ladeverhalten zeigen.
Die Tabelle 15.6 beschreibt kurz verschiedene Dateiformate für Bitmaps.
Dateiformat | Beschreibung |
BMP |
BMP ist das Standardformat von Windows für Bitmap-Dateien. Die Farbtiefe ist auf 24 Bit beschränkt, und es wird unkomprimiert gespeichert. |
GIF |
Das Graphics Interchange Format hat maximal 256 Farben, aber ein effizientes Komprimierungsverfahren. Es eignet sich für Bilder mit transparenten Bereichen sowie für im Internet oft anzutreffende Animationen. |
JPEG |
JPEG (Joint Photographic Experts Group) ist ein international standardisiertes Komprimierungsverfahren, das für Fotos entwickelt wurde. Das Format ist auch als JFIF (JPEG File Interchange Format) bekannt geworden. |
PNG |
PNG (Portable Network Graphics) ist ein 1995 als Konkurrenz zu GIF entwickelter und von den meisten Browsern neben GIF und JPEG unterstützter Bildstandard. PNG bietet hohe Kompressionsraten, Halbtransparenz und Echtfarben. |
TIFF |
TIFF (Tagged Image File Format) dient dem Austausch von Dateien zwischen Programmen und Plattformen. TIFF ist ein flexibles Bitmap-Format, das von fast allen Mal- und Bildbearbeitungsprogrammen unterstützt wird. |
15.5.3 Bilder vom Typ Image
Ein Bild laden
Image ist die abstrakte Basisklasse der beiden abgeleiteten Klassen Bitmap und Metafile. Daher gibt es keinen Konstruktor in der Klasse Image, um daraus ein Objekt zu erzeugen.
AlleDateienAnzeigen.tifMehrere klassengebundene Methoden in Image liefern die Referenz auf ein Bild. Die am häufigsten benutzte Methode erstellt ein Image aus einer Datei, deren Zugriffspfad als Zeichenkette übergeben wird. Wir wollen sie im folgenden Beispielprogramm benutzen und eine JPEG-Datei in einer Form anzeigen, die dem Projekt so, wie in Abbildung 15.11 zu sehen ist, hinzugefügt wird. Gegebenenfalls müssen Sie mit der Symbolleistenschaltfläche im Projektmappen-Explorer alle Dateien anzeigen lassen.
Abbildung 15.11 Bilddatei zum Projekt hinzufügen
Im Eigenschaftsfenster sorgen wir dafür, dass die Datei mit im Ausgabeverzeichnis landet (siehe Abbildung 15.12).
Abbildung 15.12 Bilddatei zum Programm hinzufügen
Der Programmcode ist kurz:
'...\GDI\Bilddateien\Laden.vb |
Public Class Laden Private Sub Laden_Paint(sender As Object, ByVal e As PaintEventArgs) _ Handles MyBase.Paint Try Dim strFile As String = IO.Path.Combine("Bilder", "Egypt.jpg") Dim img As Image = Image.FromFile(strFile) e.Graphics.DrawImage(img, 10, 20) Catch ex As Exception MessageBox.Show("Ladefehler: " & ex.Message, "Fehler") End Try End Sub End Class
Eine Referenz auf das Bild gibt uns die an die Klasse Image gebundene Methode FromFile, der wir den Pfad zur Bilddatei übergeben. Durch den Einschluss der Bilddatei in das Projektausgabeverzeichnis können wir die Angabe relativ zur ausführbaren Datei machen. Die Anzeige übernimmt die Methode DrawImage des Graphics-Objekts der Form. Hier wird das unveränderte Bild mit seiner linken oberen Ecke an der Stelle (10,20) gezeichnet. Wird das Bild nicht gefunden, zum Beispiel weil nur die ausführbare Datei vorliegt, wird im Catch-Zweig eine Fehlermeldung produziert.
Bildanzeige mit DrawImage
DrawImage bietet 30 verschiedene Arten, ein Bild zu zeichnen. Bilder können gestreckt, gestaucht oder gedreht angezeigt werden. In der folgenden Syntaxübersicht sind optionale Teile kursiv gesetzt.
Public Sub DrawImage(bild As Image, <Ort>) Public Sub DrawImage(bild As Image, <Bereich>) Public Sub DrawImage(bild As Image, <Raute>) Public Sub DrawImage(<bild>, <Ziel>, <Quelle>, <Attr>, <Check>, <Daten>) |
<Ort> : linke obere Ecke als Point/PointF oder 2 Integer/Single <Bereich>: Ort und Größe als Rectangle/RectangleF oder 4 Integer/Single <Raute> : Einpassung in Parallelogramm, gegeben als Point()/PointF() <Ziel> : Ort, Bereich oder Scherung <Quelle> : Bildausschnitt (Zahlenart wie Ziel) und Einheit als GraphicsUnit 4 Punkte->Rectangle, Rechteck->2 Punkte/Rechteck/Parallelogramm <Attr> : ImageAttributes (wie Quelle außer 2 Punkte/Rechteck) <Check> : Callback Prüffunktion vom Typ DrawImageAbort (Ladekontrolle) <Daten> : Daten für Callback als Integer
Zwei weitere Beispiele sollen die Flexibilität der Zeichenmethode DrawImage demonstrieren. Für das erste brauchen Sie nur die Zeile des Methodenaufrufs von DrawImage im Beispiel oben gegen die folgende Anweisung auszutauschen:
e.Graphics.DrawImage(img, New Rectangle(0, 0, 250, 150))
Der zweite Methodenparameter ist ein Rectangle-Objekt, das die Lage und die Abmessungen des Bereichs beschreibt, in dem das Bild angezeigt werden soll. Die daraus resultierende Bildausgabe sehen Sie in Abbildung 15.13 – das Bild wird in seiner Breite gestreckt.
Abbildung 15.13 Ausgabe eines gestreckten Bildes
Tauschen wir noch einmal den DrawImage-Aufruf gegen einen anderen aus, und übergeben wir ein Point-Array. Dieser Übergabe liegt die folgende überladene Methode zugrunde:
Public Sub DrawImage(image As Image, destPoints As Point()) |
In diesem Point-Array sind drei Punkte definiert: Der erste gibt die obere linke Ecke des Bildes an, der zweite die obere rechte Ecke und der dritte die untere linke Ecke. Der vierte Punkt wird automatisch so ermittelt, dass das Ergebnis ein Parallelogramm bildet, wie in Abbildung 15.14 zu sehen.
e.Graphics.DrawImage(img, New Point() _ {New Point(80, 0), New Point(300, 80), New Point(30, 100)})
Abbildung 15.14 Ausgabe eines gedrehten und gestreckten Bildes
Zeichnen auf Bildern
Bilder sind bezüglich nachfolgender Zeichenoperationen nichts Besonderes. Im folgenden Beispiel zeichnen wir mit DrawImage ein Bild und schreiben mit DrawString an der Stelle (5,5) einen gelben Text darauf (siehe Abbildung 15.15). Um den Code kurz zu halten, hat er keinen Try-Block.
'...\GDI\Bilddateien\Beschriftung.vb |
Public Class Beschriftung Private Sub Beschriftung_Paint(sender As Object, e As PaintEventArgs) _ Handles MyBase.Paint Dim strFile As String = IO.Path.Combine("Bilder", "Egypt.jpg") Dim img As Image = Image.FromFile(strFile) e.Graphics.DrawImage(img, New Rectangle(0, 0, 257, 172)) Dim strText As String = "Ägyptischer Restaurateur" Dim ft As New Font("Arial", 12, FontStyle.Bold Or FontStyle.Underline) e.Graphics.DrawString(strText, ft, Brushes.Yellow, New Point(5, 5)) End Sub End Class
Abbildung 15.15 Bildausgabe mit Beschriftung
Eigenschaften der Klasse Image
Als abstrakte Basisklasse stellt Image den beiden abgeleiteten Klassen Metafile und Bitmap die in Tabelle 15.7 gezeigten Eigenschaften zur Verfügung, die Informationen über das Image-Objekt liefern.
Eigenschaft | Beschreibung | |
Flags |
Details wie Skalierbarkeit und Farbraum |
R |
FrameDimensionsList |
IDs im Bild enthaltener Frames und Auflösungen |
R |
Height |
Höhe des Bildes in Pixel |
R |
HorizontalResolution |
Horizontale Auflösung des Bildes in DPI (Dots per Inch) |
R |
Palette |
Farbpalette des Bildes (ARGB Farben) |
|
PhysicalDimension |
Größe des Bildes als SizeF-Objekt. Einheiten: Bitmaps in Pixel, Metafiles in 1/100 mm |
R |
PixelFormat |
Gibt an, wie ein Pixelwert auf eine Farbe abgebildet wird. |
R |
PropertyIdList |
IDs der Metainformationen |
R |
PropertyItems |
Metainformationen zum Bild |
R |
RawFormat |
Bildformat des Bildes als ImageFormat |
R |
Size |
Bildgröße in Pixel |
R |
Tag |
Benutzerdefinierte Zusatzdaten |
|
VerticalResolution |
Vertikale Auflösung des Bildes in DPI (Dots per Inch) |
R |
Width |
Breite des Bildes in Pixel |
R |
15.5.4 Bitmaps
Die abstrakte Klasse Image ist im Gegensatz zur beerbenden Klasse Bitmap nicht instanziierbar. Die Konstruktoren zerfallen in zwei Gruppen. Entweder übernehmen Sie ein Bild, das als Datenfeld, Image, Stream oder Dateipfad gegeben ist. Sie können es skalieren oder Farben korrigieren. Die zweite Gruppe erstellt eine leere Bitmap mit optionaler Auflösung (gegeben als Graphics) oder optionaler Farbcodierung (gegeben als PixelFormat). Am einfachsten lässt sich eine Bitmap mit dem Konstruktor erzeugen, der die Breite und Höhe in Pixel angibt:
Dim bmp As New Bitmap(300, 400)
Die erzeugte Bitmap ist 300 Pixel breit und 400 Pixel hoch. Es handelt sich um eine ARGB-Bitmap (A = Alpha-Kanal, R = Rot, G = Grün, B = Blau), deren Werte mit 0 initialisiert werden. Da der A-Kanal zur Darstellung der Transparenz den Startwert 0 hat, bedeutet das, dass eine Bitmap (zunächst) durchsichtig ist.
»Malen« mit SetPixel
Die Methode SetPixel ändert den Zustand eines einzelnen Pixels in der Bitmap:
bmp.SetPixel(10, 10, Color.Blue)
Die ersten beiden Parameter geben die Koordinaten eines Pixels an, das mit der Farbe gezeichnet wird, die im dritten Parameter genannt ist. Mit der DrawImage-Methode des Graphics-Objekts kann die Bitmap auf einem beliebigen Steuerelement angezeigt werden, da Control die Methode OnPaint bereitstellt. Das folgende Beispiel zeichnet einen zweidimensionalen Farbverlauf.
Private Sub SetPixel_Paint(sender As Object, ByVal e As PaintEventArgs)
Dim bmp As New Bitmap(255, 255)
For i As Integer = 0 To 254
For j As Integer = 0 To 254
bmp.SetPixel(j, i, Color.FromArgb(i, 0, j))
Next j, i
e.Graphics.DrawImage(bmp, 0, 0)
End Sub
Die Bitmap hat eine Höhe und Breite von jeweils 255 Pixeln. In zwei For-Schleifen wird für jedes Pixel in der Bitmap eine neue Farbe festgelegt. Die äußere Schleife durchläuft dabei jede Pixelzeile, die innere jede Pixelspalte. Die Farbe des Rot- und Blauanteils wird aus dem jeweiligen Zählerstand mit der statischen FromArgb-Methode der Color-Klasse ermittelt.
Die Farbe eines Pixels liefert das Pendant der SetPixel-Methode: GetPixel. Sie müssen wieder die Koordinaten des Pixels angeben und erhalten die Farbe als Color-Struktur.
Speichern von Bitmaps
Ebenso einfach wie das Laden ist das Speichern einer Datei. Die Klasse Image bietet dazu die überladene Methode Save an, die von der abgeleiteten Klasse Bitmap geerbt wird. Die einparametrige Version, die nur die Angabe des Speicherpfades als Zeichenfolge entgegennimmt, erzeugt ein PNG-Format. Allerdings sollten Sie beim Speichern immer das Bildformat angeben, zum Beispiel mit folgender Überladung:
Public Sub Save(filename As String, format As ImageFormat) |
Anstatt an eine Datei können Sie die Bitmap auch an ein Stream-Objekt übergeben. Ein Objekt für den Formatparameter vom Typ System.Drawing.Imaging.ImageFormat liefern die zehn in Tabelle 15.8 aufgelisteten klassengebundenen Eigenschaften dieser Klasse. Alle sind schreibgeschützt.
Eigenschaft | Beschreibung |
Bmp |
Bitmap-Bildformat (BMP) |
Emf |
Windows-Bildformat Erweiterte Metadatei (Enhanced Meta File – EMF) |
Exif |
Exif-Format (Exchangeable Image File) |
Gif |
GIF-Bildformat (Graphics Interchange Format) |
Icon |
Bildformat für Windows-Symbole |
Jpeg |
JPEG-Format (Joint Photographic Experts Group) |
MemoryBmp |
Bitmap-Bildformat im Speicher |
Png |
PNG-Bildformat (W3C Portable Network Graphics) |
Tiff |
TIFF-Bildformat (Tagged Image File Format) |
Wmf |
WMF-Bildformat (Windows Metafile) |
Zum Speichern geben Sie den Pfad zur Datei an. Die Dateierweiterung sollte mit der Angabe des Bildformats übereinstimmen.
bmp.Save("C:\MyBitmap.jpg", ImageFormat.Jpeg)
Die Bitmap wird vor dem Speichern in das angegebene Format konvertiert. Wenn Sie die Bitmap beispielsweise als GIF-Datei speichern, verringert sich die Anzahl der Farben auf 256, was zu einem Verlust der Darstellungsqualität führen kann.
Ein einfaches Malprogramm
Im folgenden Beispielprogramm kann mit der Maus eine Grafik auf die Clientfläche der Form gezeichnet werden (siehe Abbildung 15.16). Dazu wird die linke Maustaste gedrückt und gleichzeitig gezogen. Die folgende Abbildung zeigt eine damit erstellte Zeichnung.
Abbildung 15.16 Einfaches Malprogramm
Zeichnungen auf dem Bildschirm sind sehr flüchtig. Kaum wird ein Fenster(bereich) verdeckt, sind sie weg. Kommt das Fenster wieder zum Vorschein, muss die Zeichnung wieder restauriert werden. Unter allen gängigen grafischen Oberflächen wird dazu der sichtbar werdende Bereich neu gezeichnet. Eine besonders einfache Wiederherstellung ist möglich, wenn die Pixelinformation des Fensterbereichs zuvor gespeichert wurde. Anstatt bei jeder Änderung den Bildschirmbereich des Fensters abzuspeichern, gehen wir hier den umgekehrten Weg. Wir zeichnen auf eine Bitmap, die wir bei Änderungen und der Wiederherstellung zur Anzeige bringen.
Das folgende Codefragment zeigt den Initialisierungsteil des Beispiels. Die wesentlichen Teile sind die Erzeugung einer Bitmap zur Speicherung und die Erzeugung eines Graphics-Objekts, das auf die Bitmap zeichnen kann. Die Dialoge werden später zum Laden und Speichern verwendet.
'...\GDI\Bilddateien\Malen.vb |
Public Class Malen Private grafik As Bitmap, blatt As Graphics Private dö As New OpenFileDialog(), ds As New SaveFileDialog() Private Sub Laden(sender As Object, e As EventArgs) Handles MyBase.Load grafik = New Bitmap(ClientSize.Width, ClientSize.Height) blatt = Graphics.FromImage(grafik) Dim exe As String = Application.ExecutablePath dö.InitialDirectory = IO.Directory.GetParent(exe).FullName ds.InitialDirectory = IO.Directory.GetParent(exe).FullName ds.DefaultExt = "bmp" : ds.AddExtension = True End Sub ... End Class
Die eigentliche Zeichnerei besteht aus zwei Teilen. Durch einen Mausklick wird ein Linienzug begonnen. Im Ereignishandler wird der Linienbeginn gespeichert. Bei jeder Mausbewegung bei gedrückter Maustaste wird der Linienzug um ein Stück verlängert. Dazu wird zwischen dem bisherigen Ende des Linienzugs, der in start gespeichert ist, und der in ende gespeicherten Mausposition mit DrawLine eine Linie in das Graphics-Objekt blatt gezeichnet, das die Linie an die darunterliegende Bitmap grafik weiterreicht (noch erscheint die Linie nicht auf dem Bildschirm). Die linke Maustaste zeichnet in Gelb, die rechte löscht, indem sie in der Hintergrundfarbe Schwarz zeichnet. Das neu gezeichnete Element des Linienzugs liegt innerhalb des in quelle gespeicherten Rechtecks und wird mit DrawImage auf das Graphics-Objekt g des Formulars gezeichnet und erscheint damit auf dem Bildschirm. Da das Ereignis MouseMove nicht primär zum Zeichnen gedacht ist, stellt es keinen Grafikkontext zur Verfügung. Wir beschaffen ihn uns mit der Methode CreateGraphics(). Es reicht, den Bereich von quelle zu zeichnen, da nur er neu ist. Alternativ zu DrawImage können Sie Inavidate(quelle) aufrufen (CreateGraphics() ist dann überflüssig). Schließlich wird die aktuelle Mausposition durch start=ende zum neuen Ende des Linienzugs. Der Using-Block gibt automatisch Ressourcen des durch CreateGraphics() erzeugten Objekts durch den impliziten Aufruf von Dispose() frei.
'...\GDI\Bilddateien\Malen.vb |
Public Class Malen ... Private start As Point Private Sub Anfang(sender As Object, ByVal e As MouseEventArgs) _ Handles MyBase.MouseDown start = New Point(e.X, e.Y) End Sub Private Sub Zeichnen(sender As Object, e As MouseEventArgs) _ Handles MyBase.MouseMove If e.Button = Windows.Forms.MouseButtons.None Then Return 'schneller Dim ende As New Point(e.X, e.Y) Using g As Graphics = Me.CreateGraphics() Dim w As Integer If e.Button = MouseButtons.Left Then w = 2 : blatt.DrawLine(New Pen(Brushes.Yellow, 2), start, ende) ElseIf e.Button = MouseButtons.Right Then w = 8 : blatt.DrawLine(New Pen(Brushes.Black, 8), start, ende) End If Dim quelle As New Rectangle( _ Math.Min(start.X, ende.X) – w\2, Math.Min(start.Y, ende.Y) – w\2, _ Math.Abs(start.X – ende.X) + w, Math.Abs(start.Y – ende.Y) + w) g.DrawImage(grafik, quelle.Left, quelle.Top, quelle, GraphicsUnit.Pixel) start = ende End Using End Sub ... End Class
Die Wiederherstellung eines verdeckten Bereichs im Ereignishandler von Paint ist schon fast trivial. Der wieder sichtbare Bereich wird aus der Bitmap grafik restauriert.
'...\GDI\Bilddateien\Malen.vb |
Public Class Malen ... Private Sub Auffrischen(sender As Object, ByVal e As PaintEventArgs) _ Handles MyBase.Paint Dim r As Rectangle = e.ClipRectangle e.Graphics.DrawImage(grafik, r.Left, r.Top, r, GraphicsUnit.Pixel) End Sub ... End Class
Zur Abrundung hat das Beispiel noch drei Menüpunkte zum Neustart des Zeichnens, zum Öffnen einer auf Platte gespeicherten Bitmap und zum Speichern von Zeichnungen. Analog zum Konstruktor werden für eine neue Zeichnung die Zeichenfläche und das zugehörige Graphics-Objekt erstellt. Der Aufruf von Invalidate() erzwingt ein Neuzeichnen. Eine zu öffnende Bitmapdatei wird mit OpenFileDialog.ShowDialog() ermittelt und in ein Bitmap-Objekt geladen. Die einfachste Konstruktorüberladung von Bitmap
Public Sub New(filename As String)
ist leider ungeeignet, da sie die Bitmap zwar lädt, aber gleichzeitig geöffnet lässt. Dadurch ist sie gesperrt und kann nicht mehr unter dem gleichen Namen im selben Verzeichnis gespeichert werden. Der Umweg über einen expliziten Stream umgeht das Problem. Die Anpassung der Auflösung ist zum Beispiel beim Öffnen von JPEG-Dateien nötig. Die Auswahl einer Datei durch einen Doppelklick im Öffnen-Dialog erzeugt implizit ein MouseMove-Ereignis im Formular, wenn sich die Mausposition innerhalb des Formulars befindet (auch wenn dieses durch den Dialog verdeckt war). Um dann keine Linie zu zeichnen, wird der Beginn des Linienzugs auf die aktuelle Mausposition gesetzt. Im Ereignishandler von MouseMove führt dies zu einem Liniensegment der Länge null. Die Speicherung ist deutlich einfacher. SaveFileDialog.ShowDialog() ermittelt den Dateinamen, und den Rest übernimmt die Methode Bitmap.Save().
'...\GDI\Bilddateien\Malen.vb |
Public Class Malen ... Private Sub Löschen(sender As Object, e As EventArgs) Handles Neu.Click grafik = New Bitmap(ClientSize.Width, ClientSize.Height) blatt = Graphics.FromImage(grafik) Me.Invalidate() End Sub Private Sub Bild(sender As Object, e As EventArgs) Handles Öffnen.Click If dö.ShowDialog() = DialogResult.OK Then Dim st As New IO.FileStream(dö.FileName, IO.FileMode.Open) grafik = New Bitmap(st) st.Close() grafik.SetResolution(blatt.DpiX, blatt.DpiY) blatt = Graphics.FromImage(grafik) start = Control.MousePosition Me.Invalidate() End If End Sub Private Sub Sichern(sender As Object, e As EventArgs) _ Handles Speichern.Click If ds.ShowDialog() = DialogResult.OK Then _ grafik.Save(ds.FileName, Imaging.ImageFormat.Bmp) End Sub End Class
Das Beispiel ist bewusst einfach gehalten und kann Ihnen als Startpunkt eigener Entwicklungen dienen.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.