Konstantinos Lavassas und Ulrich Hilgeffort
Basteln Sie mit uns ein neues Grafikprogramm. Lernen Sie seine Arbeitsweise zu verstehen und es nach eigenen Vorstellungen zu erweitern. So entsteht Software nach Maß.
Ein gutes Grafikprogramm zu entwickeln, kostet viel Arbeit. K. Lavassas, einer der Väter von Lavadraw, machte sich die Mühe. In GFA-Basic erschafft er Ihren speziellen Zeichenkasten.
Die Funktionsfülle unseres GFA-Basic-Programms »Mikrodraw« umfaßt je nach Speicherausbau 15 und mehr Bildschirmseiten und neben allen Standard-Zeichenoperationen auch Spezialitäten wie proportionales Vergrößern/Verkleinern, Verzerren und Biegen, reduzierendes Lasso oder verstellbare Sprühdose. Programmtechnische Feinheiten wie flimmerfreie Grafik oder Zeichnen von beweglichen Objekten runden den Kurs ab.
Die »erfolgreiche Teilnahme« erfordert einen Atari ST mit mindestens 520 KByte Arbeitsspeicher plus ROM-TOS und Schwarzweiß-Monitor sowie GFA-Basic 3.0 oder höher. Bringen Sie ein wenig Zeit mit, denn ein Handbuch für das Endprodukt gibt es nicht.
Wenn Sie verstanden haben, wie die einzelnen Routinen arbeiten, sind weitere Hinweise überflüssig. Unser Kurs läuft über vier Teile: Im ersten bauen wir die Menüleiste auf und implementieren Systemfunktionen, Diskettenoperationen, und Zeichenfunktionen. Im nächsten Teil beschäftigen wir uns mit Blockoperationen wie Lasso-Aktionen, kopieren, bewegen und biegen. Der dritte Teil erklärt die Grafik-Effekte, die Textdarstellung und die Ausgabe auf den Drucker. Im letzten Teil werten wir unser Programm mit Erweiterungen und Feinheiten auf. Als zusätzliches Bonbon entwickeln wir ein Modul, mit dem Sie unter GFA-Basic Zugang zur Zeichenfläche von »Lavadraw Plus« bekommen.
Zunächst einige grundlegende Anmerkungen zum Programm: Insgesamt stehen 15 verschiedene Bild-Puffer zu je 32000 Byte zur Verfügung. Jeder Puffer entspricht einem Bildschirm. Um mißglückte Aktionen zu revidieren, bedarf es eines zusätzlichen Undo-Puffers, der bei Druck auf diese Taste den alten Bildschirminhalt wiederherstellt.
Mikrodraw funktioniert ab der ersten Folge. Stößt der Computer auf einen Sprungbefehl zu einer noch nicht enthaltenen Routine, gibt er eine Fehlermeldung aus.
Doch nun genug der Vorrede. Auf der beiliegenden Diskette finden Sie die Datei »M_DRAW1.LST«, die den Programmtext der ersten Folge enthält. Holen Sie das Programm in den GFA-Editor und vollziehen Sie unsere Erläuterungen am Monitor nach. Einige besonders knifflige Beispiele erläutern wir im Heft. Wir gehen Routine für Routine vor, in der Reihenfolge, in der sie im Listing erscheinen.
Die Grundlage für viele Aktionen legt die Routine »Vorbereitung«, die zu Beginn in Aktion tritt. Sie ermittelt den verfügbaren Speicher und berechnet daraus die Anzahl der Bildschirme, die darin Platz finden. Diese Zahl ist vorsorglich auf 15 begrenzt, was sich jedoch durch Entfernen der IF-Abfrage abschalten läßt.
Nachdem die verschiedenen Feldvariablen samt Bild-Puffern DIMensioniert sind, richten wir aus der Sammlung von DATA-Zeilen das Haupt-Menü ein. Wie das ganze fertig aussieht, zeigt das Bild. Die verschiedenen Grafik-Parameter setzen wir auf definierte Start-Parameter, die als Dualzahlen niedergelegten Linienmuster werden in den entsprechenden Variablen angelegt. Am Ende kommen die 32 Füllmuster dran, die eine Schleife per DATA-Zeilen zusammensetzt. Die Routine »menu_aufbauen« aktiviert das Haupt-Menü. Obendrein erscheint in der oberen rechten Ecke die Nummer (»aktiv%«) des gerade sichtbaren Bildschirms. Damit das Programm nicht nur per Mausklick auf Menü-Zeilen zu dirigieren ist, haben wir eine »kleine Portion« Tastatursteuerung für die Disketten-Befehle integriert. Die Funktionstasten F1 bis F4 lösen die Operationen »Bild laden/speichern« und »Objekt laden/speichern« aus. Die Undo-Taste rettet das jeweils letzte Bild. Der Bildschirmwechsel funktioniert ausschließlich über die Tastatur. Die Tasten »Pfeil nach oben/nach unten« holen das nächste bzw. vorhergehende Bild auf den Schirm.
Dazu zählt die betreffende Subroutine »cursor_h« oder »cursor_r« den Pufferzeiger hoch oder runter. Der Menü-Abfrage widmet sich die Procedure »menue«. Je nachdem, welchen Menüpunkt der Anwender angeklickt hat, steuert sie den Programmlaufzu den entsprechenden Subroutinen, von denen manche für mehrere Aufgaben zuständig sind. Die Routine »Vollbild« übernimmt die Neubelegung des Undo-Puffers, durch sie räumt der ST seinen Speicher auf (er führt eine Garbage-Collection aus). Die Routine kopiert das aktuelle Bild auf den Schirm, wobei die Menüleiste verschwindet.
Zur Funktion »fehler« ist kein Kommentar nötig. Ihre Wirkung sieht jeder, der z.B. eine der noch nicht eingebundenen Block-Operationen aufruft.
Zum nächsten Kapitel, den Systemfunktionen. Den bereits genannten Unterprogrämmchen »cursor_r« und »cursor_h« folgt die Sicherheitsabfrage bei Programmende. Daran schließt sich die Dreier-Gruppe für die flimmerfreie Grafik an. Jeder, der schon einmal mit Grafikprogrammierung zu tun hatte, kennt das Problem: Beim Verschieben oder Bewegen von Bit-Blöcken flimmert das Bild beim Löschen des zu verschiebenden Teils. Das läßt sich vermeiden, wenn wir die Eigenschaft des Atari ausnutzen, den Bildschirmspeicher blitzschnell auszuwechseln. Beispiel: Wir zeichnen ein großes Rechteck und bewegen es über die Bildfläche. Ist das Rechteck groß genug, stört es, daß der Computer nach jeder Mausabfrage und Feststellung der neuen Position das alte Rechteck löscht und es an neuer Position wieder zeichnet. Die Störung ist behoben, wenn der Hintergrund ohne das zu zeichnende Rechteck blitzschnell restauriert wird - und zwar vor jeder Zeichenaktion, also dem Schreiben der Rechteck-Daten in den Bildschirm-RAM.
Genau das besorgen die drei Routinen »b_i«, »b_s« und »b_o«. Zu »b_i«: Mit »FRE(O)« erzwingen wir eine Garbage-Collection. Die Dimensionierung von »bild%« erfolgt nicht ohne Grund als 32256-Byte-Block, der durch vier geteilt wird: Eine Integer-Variable belegt 4 Byte, wir benötigen aber lediglich eines.
Auch die »verschenkten« 256 Byte haben ihre Berechtigung. Zunächst retten wir den aktuellen Bildschirm nach »c$« und ermitteln die Startadresse des sichtbaren Bildschirms per »XBIOS(3)«. Physikalischer und logischer Bildschirm sind zu diesem Zeitpunkt identisch. Dann setzen wir die Variable »b%« als Zeiger auf unseren im Hintergrund liegenden unsichtbaren Bildschirm, einen Speicherbereich, der an einer Wortgrenze beginnt und 32000 Bytes lang ist. Damit ist die Vorbereitung unseres Wechselspiels abgeschlossen.
Die Routine »b_s«, die während der Zeichenarbeit in Aktion tritt, vertauscht lediglich sichtbaren und unsichtbaren Bildschirm mit dem SWAP-Befehl, danach informiert sie das Betriebssystem per XBI-OS(5) über die neuen Adressen und bringt den Inhalt des Hintergrund-Bildes auf den Schirm. Wollen Sie die dargestellten, beweglichen Objekte übernehmen, so retten Sie den Bildschirm in der Variable »c$« - fertig.
Mit »b_o« bringen wir unsere Adress-Wechselei wieder ins Lot. Dazu setzen wir die höhere der beiden Adressen - also die, die weiter »hinten« ins RAM zeigt -als gültige Adresse für logischen und physikalischen Bildschirm. Wir kopieren das Bild aus »c$« darauf und löschen die nun nicht länger benötigten Variablen. Wer Zweifel an der Notwendigkeit dieser Aktivitäten hegt, sollte sich den Spaß erlauben, die Routine »b_s« bis auf Kopfzeile und RETURN-Befehl zu Kommentarzeilen umzufunktionieren. Die Wirkung überzeugt jeden.
Das folgende Unterprogramm »kreuz« leistet zweierlei: Zunächst blendet sie nach der obligatorischen UNDO-Abfrage das oft benötigte Fadenkreuz per ALINE-Funktion ein, was den Vorteil bringt, dafür jedes gewünschte Linienmuster verwenden zu können. Obendrein sorgt die Routine durch den PRINT USING-Befehl dafür, daß je nach Position der horizontal verlaufenden Kreuz-Linie die aktuellen Koordinaten oben bzw. unten rechts erscheinen. Dazu setzen wir einfach die Hilfsvariable »y« in Abhängigkeit von »y%«.
»m_leer« löscht den Inhalt des Mauspuffers. »Vollbild« ist eine Hilfsroutine für die Zeichenaktionen; sie haben wir bereits genannt. Die »raster«-Routine gehört zu den häufig benutzten Unterprogrammen. Damit wählen wir einen bestimmten Bildschirmbereich zur Bearbeitung aus. Wir denken uns, die Routine sei zum Aufziehen eines Rechtecks im Einsatz. Nachdem die benötigten Variablen gelöscht sind - mit lokalen Variablen erlebt man mitunter Seltsames - aktivieren wir unsere Anti-Flimmer-Routine. Daran berechnet das Programm eine» später beim Format ändern (formen) benötigten Faktor, je nachdem, ob das »f_flag« gesetzt ist. Die erste REFEAT-UNTIL-Schleife positioniert das Fadenkreuz an der abgefragten Mausposition und hält es beweglich, solange kein Mausknopf gedrückt wurde. Hat der Anwender den einen Eckpunkt per Mausklick festgelegt, springt das Programm in die zweite Schleife und zeichnet neben das Fadenkreuz auch ein Auswahlrechteck. Ist auch der zweite Eckpunkt fixiert, berechnet die Routine die Größe des ausgewählten Bereiches.
Mit den Parameterfunktionen modifizieren Sie die Wirkung der Zeichenaktionen. Nach »fuellattribute«, die lediglich aus einer Alert-Box besteht, erscheint »line-art« auf den ersten Blick als kompliziert. Die Zeilen bis zur DO-LOOP-Schleife sorgen dafür, daß das Auswahlfeld mit den greifbaren Linienmustern und -stärken erscheint. In der Schleife reagiert das Programm in Abhängigkeit der Mausposition mit einer Markierung des gerade angewählten Musters, wobei es die vorherige Markierung löscht. Wer sich diese Funktion in Aktion angesehen hat, versteht den Programmablauf leichter.
Ähnlich arbeitet prinzipiell die Auswahlfunktion für die Füllmuster. Sie zeigt in vier Zeilen zu je acht Mustern die 32 GEM-Muster an. Die Mausabfrage ist knapp ausgefallen: Eine einfache DO-LOOP-Schleife beobachtet die Mausbewegungen. Bei Klick auf eine Maustaste überprüft das Programm, ob sich der Mauszeiger innerhalb des Auswahlfeldes befindet, und berechnet aus der Position das jeweils angeklickte Füllmuster. Dieses Verfahren vermeidet die häufig zu solchen Zwecken benutzten endlosen IF-THEN-ELSE-Kettenabfragen und SELECT-CASE-Konstruktionen zur Verknüpfung von Mausposition und gewünschter Wirkung. Bevor wir uns den Zeichenoperationen zuwenden, ein kurzer Blick auf die Disk-Routinen. Die Routine »bild_laden« verarbeitet Screen- (das sind Bild-Dateien mit exakt 32000 Byte Länge) und Degas- (die sind 32034 Bytes lang) Format. Soll ein anderes Dateiformat geladen werden, verweigert der Computer dies mit einer Fehlermeldung. Die Routinen zum Laden bzw. Speichern von Objekten verarbeiten GFA-Blöcke beliebiger Größe, maximal 640 x 400 Pixel.
Nun zur Zeichnerei. Halten Sie sich folgenden Ablauf vor Augen:
Die Hauptarbeit beim Zeichnen übernehmen die beiden Unterprogramme »zeichnen« und »bew_zeichnen«. Das erste stellt zunächst das gültige Füllmuster und den Füllmodus ein. Nach Aktivierung der Flimmer-Frei-Routine startet eine DO-LOOP-Schleife, die fast alle Arbeit erledigt: »flag%« zeigt die Zahl der absolvierten Durchläufe an. Beim ersten Rutsch (flag%=0) erscheint das Fadenkreuz auf dem Schirm, bei Mausklick links erhöht sich flag% auf 1; wir sind beim dritten Schritt in obiger Aufstellung. Nun erfolgt der Aufruf der entsprechenden Zeichenroutine bei »ON me%-19 GOSUB«. Den Startpunkt halten wir in x1% bzw. y1% fest. Dann geht's zum nächsten Streich. Ein in der Größe den Mausbewegungen folgendes Rechteck erscheint solange auf dem Schirm, bis ein Mausklick links die Größenveränderungen beendet. Bei vielen Zeichenprogrammen ist an dieser Stelle Schluß. Wir setzen noch eins drauf: Das Unterprogramm »bew_zeichnen« löst die erste Routine ab, damit sich die Position des Rechtecks noch verändern läßt. Das Fadenkreuz und die Mausposition bekommen die Koordinaten des zuerst fixierten Punktes (obere linke Ecke). Eine Undo-Routine gestattet den Abbruch bzw. das Retten des unveränderten Zustandes. Erneut bekommt der Zeichenknecht Arbeit, wenn »ON me%-19 GOSUB« auf die verschiedenen Routinen delegiert. Damit erscheint ein Rechteck der bereits gewählten Größe, das in der IF-Schleife positioniert wird. Erst ein Druck auf die rechte Maustaste beendet diese Funktion, sodaß auch mehrere Rechtecke der gleichen Größe ohne aufwendige Kopieraktionen schnell gezeichnet sind.
Ohne das Auswahlrechteck funktioniert die Routine für das freie Zeichnen. Dem Undo-Block folgt ein Jonglieren mit zwei Koordinatenpaaren: »x%«/»y%« und »x1%«/»y1%«. Zu Beginn haben beide Paare die gleichen Werte. Auf Mausklick stellt der ST die neue Mausposition fest und zieht vom alten, in »x1%«/»y1%« abgelegten Punkt zu den neuen Koordinaten eine Linie, bevor er die neue Position als »alte« speichert. Genau genommen zeichnen wir also keine freie Linie, sondern verbinden einzelne Punkte durch kurze Geraden. Je langsamer wir die Mausbewegungen ausführen, desto weniger fallen die geraden Linien auf - wie beim »richtigen« Zeichnen, wo mit sinkender Geschwindigkeit die Führungsgenauigkeit des Bleistiftes zunimmt.
Von den übrigen Routinen interessieren uns noch »sprühen« und »radiergummi«. Die erste stellt eine in der Größe frei einstellbare Sprühdose zur Verfügung. Die erste DO-LOOP-Schleife läßt den Anwender einen Kreis aufziehen, dessen Radius später die Sprühfläche bestimmt. In der zweiten DO-LOOP-Schleife geht es zur Sache: Nach der unvermeidlichen UNDO-Abfrage lassen wir pro Durchlauf mit der RANDOM-Funktion des GFA-Basic sechs zufällige Punkte berechnen, die in einem den eingestellten Kreis umschließenden Quadrat liegen. Ihre Koordinaten lagern in »xx%« und »yy%«. Doch wollen wir eine kreisförmige Sprühfläche. Also berechnen wir - nach Pythagoras - den Abstand des Zufallspunktes vom Kreismittelpunkt »l%« und prüfen, ob der Zufallspunkt innerhalb des »Streukreises« liegt. Ist dies der Fall, erscheint er auf dem Bildschirm.
Das Radiergummi schließlich funktioniert im Prinzip wie ein randloses, mit Weiß gefülltes Rechteck, das auf Mausklick links im Überschreibemodus den Bildschirminhalt löscht. Die eigentliche Löscharbeit übernimmt das per PBOX erzeugte Rechteck, während der BOX-Befehl lediglich dafür sorgt, daß Sie die potentielle Löschfläche positionieren können.
Soweit zum ersten Teil unserer Programmierserie. Haben Sie etwas nicht so recht verstanden, hilft ein Blick in das kommentierte Listing weiter, das Sie auf der TOS-Diskette finden. Sonst schreiben Sie uns, was Ihnen schleierhaft geblieben ist. Wir antworten umfassend auf Ihre Fragen. (uh)
Teil 1: Menüleiste aufbauen, Systemfunktionen, Diskettenoperationen, Zeichenfunktionen und -Optionen
Teil 2: Blockoperationen wie Lasso-Aktionen, kopieren, bewegen, biegen
Teil 3: Grafik-Effekte, Textdarstellung, Ausgabe auf den Drucker Teil 4: Erweiterungen und Feinheiten
Wir haben sie durch das Minus-Zeichen vor den Ziffern 1 bis 6 in der ersten DATA-Zeile zum Hauptmenü lahmgelegt. Die Mehrzahl der verbreiteten Hintergrund-Helfer zerstört den aktuellen Bildschirm, ohne ihn nach getaner Arbeit zu restaurieren. Unserer Grafik kann so etwas nichts anhaben, denn die Bilddaten liegen sicher in den Puffervariablen »puffer$(anzahl%)«. Dennoch bleibt es unangenehm, nach dem Aufruf eines Accessories einen großen, grauen Fleck auf dem Schirm zu finden, der erst durch Umschalten des aktuellen Bildschirms - eins vor, eins zurück - wieder verschwindet. Diese Störmanöver lassen sich ohne erheblichen AES-Programmaufwand nicht abfangen. Wer sie in Kauf nimmt, um auf seine Accessories zuzugreifen, der beseitigt die genannten Minus-Zeichen vor den Ziffern 1 bis 6.