Michael Neuhold Homepage
Startseite >
Informatikunterricht >
Graphik mit Turbo Pascal
Graphik mit Turbo Pascal
Graphikmodus ein- und ausschalten
Alle für die Graphik notwendigen Datentypen, Konstanten, Funktionen
und Prozeduren sind Bestandteil der Unit Graph; daher: USES ...,
graph;
- Für die Graphik benötigt Pascal eine Graphiktreiberdatei, sie
trägt die Erweiterung .BGI (Borland Graphics
Interface), für den VGA-Modus heißt der Treiber
EGAVGA.BGI.
- Zum Einschalten des Graphikmodus muß die Prozedur INITGRAPH
(GraphikTreiber, GraphikModus, BGIPfad) aufgerufen werden. GraphikTreiber
und GraphikModus sind Variablenparameter vom Typ Integer.
BGIPfad ist ein String und bezeichnet das Verzeichnis, in dem
Pascal die BGI-Datei suchen soll. Wird hier ein Leerstring übergeben,
sucht Pascal im aktuellen Verzeichnis.
- Vor dem Aufruf von InitGraph muß GraphikTreiber auf 0 (oder
Konstante Detect) gesetzt werden, was Turbo Pascal veranlaßt, mit dem
Aufruf von DETECTGRAPH (Graphiktreiber, Graphikmodus) eine Überprüfung
der Hardware vorzunehmen und den Graphikmodus mit der höchstmöglichen
Auflösung zu aktivieren (meist VGAHi = 640x480 Pixel, 16 Farben, 1 Videopage).
Man kann DetectGraph auch selbst aufrufen, wenn man einen bestimmten Graphikmodus
aktivieren möchte.
- Konnte der Graphikmodus aktiviert werden, so liefert die Funktion GRAPHRESULT
den Wert 0 (Konstante grOK). Die Funktion GRAPHERRORMSG (FehlerNr) liefert
die der Fehlernummer entsprechende Fehlermeldung.
- Der Graphikmodus wird mit CLOSEGRAPH wieder abgeschaltet. Dadurch wird
der Textmodus aktiviert, der vom Graphiktreiber und den Zeichensatzdateien
belegte Speicher wird wieder freigeben.
USES graph,crt;
VAR GraphDriver, GraphMode: Integer;
ErrorCode: Integer;
BEGIN
GraphDriver:= Detect;
InitGraph (GraphDriver, GraphMode, ''); {sucht BGI-Datei im aktuellen Verz.}
ErrorCode:= GraphResult;
IF ErrorCode <> grOk THEN {wenn Graphikinitialisierung mißlungen}
BEGIN
Writeln('Graphik-Error: ', GraphErrorMsg(ErrorCode)); {Fehlermeldung}
Readln;
Halt(1); {Programm abbrechen}
END;
DirectVideo:= False;
... {Beginn des Graphikprogrammes}
CloseGraph;
END.
- SETGRAPHMODE (Graphikmodus) aktiviert den angegebenen Graphikmodus,
löscht den Bildschirm und reaktiviert die Defaulteinstellungen
(Graphikfenster, Farbpalette, usw.).
- GETGRAPHMODE ermittelt den gesetzten Graphikmodus.
- RESTORECRTMODE schaltet in den zuletzt aktiven Textmodus zurück.
Man kann mit SetGraphMode (GetGraphMode) und RestoreCrtMode zwischen Graphik
und Text hin- und herschalten. Voraussetzung ist allerding, daß mit
InitGraph ein Graphikmodus aktiviert wurde.
- Will man, daß auch im Graphikmodus Textausgaben mit Write und WriteLn
möglich sind, muß die Variable DIRECTVIDEO (aus der Unit crt) auf
FALSE gesetzt werden (standardmäßig ist sie auf TRUE). Dies bewirkt,
daß die Ausgabe nicht direkt in den Bildschirmspeicher geschrieben wird
(was im Graphikmodus manchmal keine Wirkung hat), sondern über die
(langsameren) BIOS-Routinen erfolgt.
Farben
- Zeichen- und Hintergrundfarbe werden festgelegt mit SETCOLOR (FarbNr)
und SETBKCOLOR (FarbNr). Die Farbnummern bzw. -konstanten sind dieselben
wie im Textmodus:
0 | Black | |
1 | Blue | |
2 | Green | |
3 | Cyan | |
4 | Red | |
5 | Magenta | |
6 | Brown | |
7 | LightGray | |
8 | DarkGray | |
9 | LightBlue | |
10 | LightGreen | |
11 | LightCyan | |
12 | LightRed | |
13 | LightMagenta | |
14 | Yellow | |
15 | White | |
- Die gerade gesetzten Farben kann man mit den Funktionen GETCOLOR und
GETBKCOLOR ermitteln. Mit der Funktion GETPIXEL (x, y) kann man die Farbe
eines bestimmten Punktes eruieren.
- Mit SETPALETTE (FarbNr, Farbe) kann die Zuordnung einer Farbe zu einer
Farbnummer geändert werden:
SetPalette (4, LightBlue)
bewirkt, daß alle in der Farbe 4 gezeichneten Objekte in Hellblau
erscheinen.
- Mit SETRGBPALETTE (FarbNr, RotAnteil, GrünAnteil, BlauAnteil) kann
einer Farbnummer eine individuelle Farbe zugeordnet werden. Die Werte
für Rot, Grün und Blau können zwischen 0 und 63 liegen.
SetRGBPalette ist nur auf VGA-Karten, die mindestens 256 Farben darstellen
können, anwendbar:
SetRGBPalette (6,63,26,0)
ordnet der Farbnummer 6 ein schönes
Orange zu.
Bildschirmfenster
- Die linke obere Bildschirmecke hat immer die Koordinaten 0/0. Die maximalen
Bildschirmkoordinaten lassen sich mit den Funktionen GETMAXX und GETMAXY
ermitteln (für VGA 639/479). Alle Koordinaten sind vom Typ Integer.
- Mit CLEARDEVICE wird der gesamte Bildschirm gelöscht, indem er in der
gesetzten Hintergrundfarbe "überschrieben" wird. Der Graphikcursor
wird in die linke obere Ecke des aktiven Bildschirmfensters gesetzt.
- Bildschirmausschnitte definiert man mit SETVIEWPORT (x1, y1, x2, y2, Clipping).
x1/y1 ist der linke obere Eckpunkt, x2/y2 der rechte untere. Clipping
bestimmt, ob Zeichenaktionen am Rand des Fensters abgeschnitten werden sollen
(TRUE, ClipOn) oder nicht (FALSE, ClipOff). SetViewPort löscht das
definierte Fenster und setzt den Cursor in dessen linke obere Ecke.
- Bildschirmausschnitte löscht man mit CLEARVIEWPORT. Alle Zeichenaktionen
beziehen sich immer relativ zu diesem Fenster. Das Fenster auf den Vollbildschirm
umstellen geschieht mit SetViewPort (0, 0, GetMaxX, GetMaxY, ClipOn).
- Den Graphikcursor bewegt man mit MOVETO (x, y) (die Entsprechung zu GotoXY).
MOVEREL (Dx, Dy) bewegt den Graphikcursor um Dx bzw. Dy Pixel.
- Die aktuelle Cursorposition ermittelt man mit den Funktionen GETX und GETY.
Die Koordinaten gelten relativ für das gesetzte Fenster.
Zeichnen
- PUTPIXEL (x, y, FarbNr)
- zeichnet einen Punkt in der angegebenen Farbe.
- LINE (x1, y1, x2, y2)
- zeichnet eine Linie zwischen zwei Punkten.
- LINETO (x, y)
- zieht eine Line von der momentanen Graphikcursorposition bis
zum angegebenen Punkt. Der Graphikcursor steht dann an Position x/y
(während sich seine Position bei LINE nicht ändert).
- LINEREL (Dx, Dy)
- zeichnet eine Linie ausgehend von der momentanen Cursorposition.
Dx und Dy geben den Abstand des Endpunktes vom Anfangspunkt an.
- RECTANGLE (x1, y1, x2 y2)
- zeichnet ein Rechteck.
- CIRCLE (x, y, Radius)
- zeichnet einen Kreis um den Mittelpunkt x/y.
- ARC (x, y, StartWinkel, EndWinkel, Radius)
- zeichnet einen Kreisbogen. StartWinkel und EndWinkel
werden in Grad angegeben, die Zählung erfolgt gegen den Uhrzeigersinn:
- ELLIPSE (x, y, StartWinkel, EndWinkel, xRadius, yRadius)
- zeichnet einen Ellipsenbogen.
- DRAWPOLY (PunktAnzahl, Punkte)
- zeichnet eine Figur aus PunktAnzahl Punkten, indem die Punkte mit
geraden Linien verbunden werden. Punkte sind die Koordinatenpaare
der Punkte, wofür es den vordefinierten Typ POINTTYPE gibt
(
POINTTYPE = RECORD x, y: Integer END;
):
PROCEDURE DrawHexagon;
CONST NumPoints = 6;
TYPE Polygon = array [1..NumPoints] of PointType;
VAR P: Polygon;
BEGIN
P[1].x:= 100; P[1].y:= 100;
P[2].x:= 150; P[2].y:= 50;
P[3].x:= 200; P[3].y:= 100;
P[4].x:= 220; P[4].y:= 150;
P[5].x:= 80; P[5].y:= 150;
P[6]:= P[1]; {Endpunkt = Startpunkt}
DrawPoly (NumPoints, P);
END;
Füllen
- Die Art der Füllung setzt man mit SETFILLSTYLE (Muster, Farbe) fest.
Muster kann dabei folgende Werte annehmen:
0 | EmptyFill | Füllen mit Hintergrundfarbe (=keine Füllung) |
1 | SolidFill | völliges Ausfüllen |
2 | LineFill | waagrechte Linien |
3 | LtSlashFill | dünne schräge Linien in /-Richtung (Slash = Schrägstrich) |
4 | SlashFill | dicke schräge Linien in /-Richtung |
5 | BkSlashFill | dicke schräge Linien in \-Richtung (Backslash = \) |
6 | LtBkSlashFill | dünne schräge Linien in \-Richtung (Lt = light) |
7 | HatchFill | kariert |
8 | XHatchFill | kariert, die Karos stehen auf der Spitze |
9 | InterleaveFill | abwechselnde Linien |
10 | WideDotFill | weit auseinanderstehende Punkte |
11 | CloseDotFill | eng beieinanderstehende Punkte |
12 | UserFill | mittels SetFillPattern definiertes Füllmuster verwenden |
- Setzt man Muster auf UserFill, kann man mit SETFILLPATTERN
(MusterDefinition) ein eigenes Füllmuster definieren. MusterDefinition
ist vom vordefinierten Typ FILLPATTERNTYPE (
FillPatternType = ARRAY [1..8]
OF Byte
).
- Die momentan gesetzten Füllparameter Muster und Farbe ermittelt man
mit GETFILLSETTINGS (FüllInfo), FüllInfo ist vom
vordefinierten Typ FILLSETTINGSTYPE; ein benutzerdefiniertes Füllmuster
ermittelt man mit GETFILLPATTERN (MusterDefinition).
- FLOODFILL (x, y, Begrenzungsfarbe)
- füllt, ausgehend von Punkt x/y, einen in Begrenzungsfarbe
umschlossenen Bereich ("flutet" ihn).
- BAR (x1, y1, x2, y2)
- zeichnet ein gefülltes Rechteck.
- BAR3D (x1, y1, x2, y2, Tiefe, Deckel)
- zeichnet einen ausgefüllten dreidimensionalen Balken. Tiefe
gibt die räumliche Tiefe (wie weit der Balken "nach hinten geht")
an, am besten 25% der Balkenbreite (
(x2-x1) DIV 4
). Deckel
ist vom Typ Boolean und gibt an, ob ein oberer Balkenabschluß gezeichnet
werden soll (TRUE, TopOn) oder nicht (FALSE, TopOff), letzteres benötigt
man, um Balken übereinander zu stapeln.
- FILLELLIPSE (x, y, xRadius, yRadius)
- zeichnet eine gefüllte Ellipse.
- PIESLICE (x, y, StartWinkel, EndWinkel, Radius)
- zeichnet ein gefülltes Tortenstück.
- SECTOR (x, y, StartWinkel, EndWinkel, xRadius, yRadius)
- zeichnet ein gefülltes elliptisches Tortenstück.
- FILLPOLY (PunktAnzahl, Punkte)
- zeichnet ein gefülltes Vieleck.
Text
- Wenn DirectVideo auf False gesetzt worden ist, kann Text auch im Graphikmodus
mit Write und WriteLn ausgegeben werden.
- Doch um Text punktgenau positionieren zu können, verwendet man
OUTTEXT (Text) und OUTTEXTXY (x, y, Text). OutText gibt den Text dort aus,
wo der Graphikcursor gerade steht, bei OutTextXY werden die Textkoordinaten
explizit angegeben.
- Im Gegensatz zu WriteLn kann OutText(XY) keine Zahlen ausgeben. Diese
müssen vorher mit STR (Zahl, TextString) in einen String verwandelt werden.
Zahl ist ein Integer- oder Real-Ausdruck und kann auch
Formatierungsanweisungen enthalten:
VAR PiQuadrat: String;
Str (Pi*Pi:0:5, PiQuadrat); {Zahl mit 5 Nachkommast. in String verwandeln)
OutTextXY (100, 100, PiQuadrat); {String ausgeben}
- Die Koordinaten beziehen sich auf den mit SETTEXTJUSTIFY (Horizontal, Vertikal)
angegebenen "Punkt". Horizontal und Vertikal
können folgende Werte annehmen:
Horizontal | Vertikal |
0 | LeftText | 0 | BottomText |
1 | CenterText | 1 | CenterText |
2 | RightText | 2 | TopText |
- Schriftart und -ausrichtung werden mit der Anweisung SETTEXTSTYLE
(Schriftart, Richtung, Schriftgröße) festgelegt.
Schriftgröße muß zwischen 1 und 10 liegen:
Schriftart |
0 | DefaultFont | 8x8 Bitmuster-Zeichensatz |
1 | TriplexFont | Vektorfont |
2 | SmallFont | Vektorfont (kleine Buchstaben) |
3 | SansSerifFont | Vektorfont (serifenlose Schrift) |
4 | GothicFont | Vektorfont (Frakturbuchstaben) |
Richtung |
0 | HorizDir | horizontal |
1 | VertDir | vertikal |
SetTextJustify (CenterText, CenterText); {horizontal und vertikal zentrieren)
SetTextStyle (SansSerifFont, HorizDir, 2); {horizont. Text, SansSerif Größe 2)
OutTextXY (100, 100, 'Dies ist ein Text'); {Text ausgeben}
- Die Vektorzeichensätze sind in CHR-Dateien abgelegt, die erst zur
Laufzeit des Programmes in den Speicher geladen werden und die sich daher
im selben Verzeichnis wie die BGI-Dateien befinden müssen.
- Den Platzbedarf eines Textes in der zuvor festgelegten Schriftart und
-größe ermittelt man mit den Funktionen TEXTHEIGHT (Text) und
TEXTWIDTH (Text). Sie liefern die Höhe bzw. Weite des übergebenen
Textstrings in Pixel zurück.
Animation
- Um Objekte über den Bildschirm zu bewegen, muß ständig
der vom Objekt überdeckte Hintergrund wiederhergestellt werden. Dazu
überschreibt man den Bildschirm mit dem Objekt in XOR-Verknüpfung.
Bei neuerlicher XOR-Verknüpfung mit dem Bildschirm ist der Hintergrund
wiederhergestellt:
Hintergrund H |
Objekt O |
Überschreiben H XOR O |
Wiederherstellen (H XOR O) XOR O |
0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 1 | 1 |
1 | 1 | 0 | 1 |
- Um ein Objekt im Heap zu speichern, verwendet man GETIMAGE (x1, y1, x2,
y2, Bitmap). Die Koordinaten bezeichnen den Bildschirmausschnitt, in dem
sich das (zuvor gezeichnete) Objekt befindet. Bitmap wird mit
einer Zeigervariablen dereferenziert.
- Um das Objekt auf dem Bildschirm (an anderer) Stelle erscheinen zu lassen,
verwendet man PUTIMAGE (x1, y1, Bitmap, Verknüpfung). Die Koordinaten
bezeichnen den linken oberen Eckpunkt des gespeicherten Ausschnittes,
Verknüpfung kann folgende Werte annehmen:
0 | NormalPut, CopyPut | überschreiben |
1 | XOrPut | XOr-Verknüpfung (s. o.) |
2 | OrPut | Or-Verknüpfung |
3 | AndPut | And-Verknüpfung |
4 | NotPut | Not-Verknüpfung (invertieren) |
- Vor GetImage muß der benötigte Speicher im Heap mit GETMEM
(Zeiger, Speichergröße) reserviert ("alloziert") werden.
Zeiger ist eine Variable vom Typ Pointer, die die Anfangsadresse
der Bitmap, die mit GetImage abgespeichert werden soll, enthält.
Speichergröße ist vom Typ Word.
- Die Berechnung des Speicherbedarfs erledigt die Funktion IMAGESIZE (x1,
y1, x2, y2). Das Objekt darf max. 64 KB Speicher benötigen (etwa 130.000 Pixel).
VAR size: Word;
picture: Pointer;
x1, y1, x2, y2: Integer;
BEGIN
x1:=0; y1:=100; x2:=29; y2:=149;
{Zeichnen des Objektes im Ausschnitt x1, y1, x2, y2}
size:= ImageSize (x1, y1, x2, y2); {Platzbedarf ermitteln}
GetMem (picture, size); {Speicher reservieren}
GetImage (x1, y1, x2, y2, picture^); {Objekt abspeichern}
WHILE x2 <= GetMaxX-5 DO
BEGIN
PutImage (x1, y1, picture^, XorPut); {Objekt löschen}
Inc (x1, 5); {neue Koordinaten}
PutImage (x1, y1, picture^, XorPut); {Objekt neu setzen}
END;
END;
- Für die Linienoperationen Line, LineTo, LineRel, DrawPoly und Rectangle
kann mit SETWRITEMODE (Verknüpfung) ebenfalls XOr-Verknüpfung
festgelegt werden (z.B. für bewegte Drahtgittermodelle).
Graphiktreiber und Zeichensätze einbinden
- Normalerweise werden Graphiktreiber- und Zeichensatzdateien erst zur
Laufzeit des Programmes in den Speicher geladen. Das spart Speicherplatz,
da immer nur ein Zeichensatz im Speicher gehalten wird. Das setzt aber
voraus, daß sich diese Dateien im aktuellen oder dem vom Programmierer
(nicht vom Benutzer!) festgelegten Verzeichnis befinden.
- Oft möchte man aber, daß diese Dateien in die EXE-Datei
eingebunden werden. Dazu muß man zunächst diese Dateien mit dem
Programm BINOBJ.EXE (gehört zum Lieferumfang von Turbo Pascal) in
Objektdateien umwandeln:
BINOBJ Quelldatei Zieldatei PublicName
- PublicName ist derjenige Name, unter dem die Datei im
Pascal-Programm angesprochen wird.
- Diese Objektdateien müssen dann im Pascal-Progamm als External-Prozeduren
deklariert (man schreibt hinter den Prozedurkopf die Anweisung EXTERNAL) und
mit dem Compilerschalter {$L ObjektdateiName} mit dem Programmcode gelinkt
werden.
- Zuletzt müssen diese Dateien mit den Funktionen REGISTERBGIDRIVER
(ProzedurZeiger) bzw. REGISTERBGIFONT (ProzedurZeiger) angemeldet werden.
Diese beiden Funktionen liefern einen negativen Wert, falls die Registrierung
nicht erfolgreich war. ProzedurZeiger ist ein Pointer auf die Prozedur,
man erhält ihn mit der Funktion ADDR (Objekt) oder kürzer mit dem
Adreßoperator @Objekt. Objekt ist in unserem Fall der
Prozedurname der zu registrierenden Prozedur.
Um z.B. den VGA-Treiber und die Zeichensatzdatei SANS.CHR mit dem Programm
zu linken, gibt man zunächst auf der DOS-Ebene ein:
binobj egavga.bgi egavga.obj vgadriver
binobj sans.chr sans.obj sansserifproc
Der Pascal-Code für eine Prozedur, die den Graphikmodus einschaltet,
könnte etwa so aussehen:
USES graph, crt;
PROCEDURE VGADriver; EXTERNAL;
{$L c:\tp6\egavga.obj} {Code des Graphiktreibers einbinden}
PROCEDURE SansSerifProc; EXTERNAL;
{$L c:\tp6\sans.obj} {Code des Zeichensatzes einbinden}
PROCEDURE GraphOn;
VAR GraphDriver, GraphMode, ErrorCode: Integer;
{lokale Prozedur zum Programmabruch mit Fehlermeldung:}
PROCEDURE Abort (SourceMsg, ErrMsg: String);
BEGIN
WriteLn (SourceMsg, ': ', ErrMsg); {Fehlermeldung}
halt (1); {Programmabruch}
END;
BEGIN
{Graphiktreiber registrieren:}
IF RegisterBGIDriver (@VGADriver) < 0
THEN Abort ('VGA-Treiber', GraphErrorMsg(GraphResult));
{Zeichensatz registrieren:}
IF RegisterBGIFont (@SansSerifProc) < 0
THEN Abort ('SansSerif-Font', GraphErrorMsg(GraphResult));
{Graphikkarte suchen:}
DetectGraph (GraphDriver, GraphMode);
IF NOT GraphDriver in [EGA, EGA64, EGAMono, VGA]
THEN Abort ('Keine EGA- oder VGA-Karte gefunden', '');
{Graphikmodus einschalten:}
InitGraph (GraphDriver, GraphMode, '');
ErrorCode:= GraphResult;
IF ErrorCode <> grOK
THEN Abort ('Graphikinitialisierung', GraphErrorMsg(ErrorCode));
{Text nicht direkt in Bildschirmspeicher schreiben:}
DirectVideo:=false;
END;