Line-A: Füllung

Auf geht’s zur letzten Runde unserer Line A-Vorlesung. Sind alle Zuhörer wieder beisammen? Sehr gut! Heute, meinen Damen und Herren, werden wir ein wenig auf alten Stoff zurückkommen, denn wir schauen uns die Line A-Routine A00E an, die in der Lage ist, Blöcke zu kopieren, und dabei ähnlich zu handhaben ist wie die Bitblt-Routine A007. Im weiteren Verlauf werden wir die Routine AOOF erläutern, mit der man eine Fläche füllen kann, von der man mindestens einen Punkt im Inneren kennt. Doch zuvor sei noch erwähnt, daß in diesem Heft nun endlich das verschollene Listing 7 aus der letzten Ausgabe zu finden ist.

Geblockt

Sie werden sich fragen, warum Line-A eine weitere Blockkopierroutine zur Verfügung stellt, wenn es doch schon A007, die BitBblt-Routine gibt. Die Antwort ist sicherlich nicht in unterschiedlichen Fähigkeiten zu finden, denn die sind gleich, sondern in der unterschiedlichen Art, die Parameter zu übergeben. Während die Übergabe der Parameter für BitBlt näher an die Originalversion von BitBlt angelehnt ist, sind die Parameter von A00E an die GEM-Parameterübergabe von vro_cpyfm angepaßt. Dadurch kann vro_cpyfm direkt auf A00E zugreifen, falls der Bildschirm als Ausgabestation angemeldet wurde. Dies ist auch der Grund, warum bei der Beschreibung von AOOE meist auf die GEM-Funktion verwiesen wird, was ich allerdings nicht ganz so gut finde, da man dadurch wieder zum Blättern gezwungen wird. Obwohl ich auf vro_cpyfm in Rahmen der ST-Ecke schon einmal eingegangen bin. möchte ich hier doch noch einmal der Vollständigkeit Line As wegen darauf eingehen.

Parameter

Welches sind nun die Parameter, die beim Kopieren von einem Block angegeben werden müssen? Zum einen sind es die Koordinaten des Ausgangsblocks (mit Breite und Höhe) sowie die des Ziels. Zum anderen muß, um die Routine vielfach nutzbar zu machen, bestimmt werden, wie das Bildspeicherformat des Quell- und Zielblocks aussieht. Drittens können wir angeben, wie der Block mit dem Zielbild verknüpft werden soll. Im Gegensatz zu A007, bei dem alles irgendwie in die vielen Line A-Variablen geschrieben wird, geht es bei A00E etwas geordneter zu. Außerdem läßt A00E nicht ganz so viele Parameterfreiheiten zu, was den Vorteil hat, daß man sich nicht um alles kümmern muß, aber auf der anderen Seite nicht so viele Möglichkeiten offen läßt.

MFDB - Mein Freund, der Block

Wie so oft führt man dann Abkürzungen ein, wenn man eine Struktur definieren möchte. Böse Zungen behaupten, daß MFDB die Abkürzung für ‘Mein Freund, der Block' sei, aber Spaß beiseite, die Abkürzung bedeutet Memory Form Definition Block (Speicherformdefinitionsblock), den Sie in Listing 2 und in der Include-Datei gemdefs.h finden. Schauen wir ihn uns an: Der erste Eintrag stellt die Adresse des Bildschirmbereichs, in dem die Bilddaten zu finden sind, zur Verfügung. Steht dort eine 0L, wird die aktuelle Bildschirmadresse automatisch eingetragen, wobei dies aber nur beim rechnerabhängigen Format der Fall ist (siehe unten). Im folgenden wird die Breite und Höhe unseres Bildschirmspeichers definiert, was in Pixeln angegeben sein muß. Zusätzlich benötigt man die Definition des Speicherblocks in Wortlänge (nicht in Bytes!). Das Strukturelement fdstand zeigt an, ob es sich bei dem Bildspeicherformat um ein GEM-Standard oder um das spezielle Bildspeicherformat des Rechners handelt. Da sie wahrscheinlich hauptsächlich von Bild zu Bild kopieren werden, handelt es sich bei Ihnen nicht um das Standardformat - der Eintrag ist also auf Null zu setzen. Sollten Sie allerdings Bilder im Standardformat auf andere Rechner unter GEM (beispielsweise IBM und Co) übertragen wollen, haben Sie die Aufgabe, dieses Format mit einer 1 in fd_stand zu wählen. Der letzte Eintrag gibt an. wieviele Planes in der gewünschten Grafik genutzt werden. Wenn Sie sich noch an die BitBlt-Routine erinnern, bemerken Sie sicherlich, daß zum Beispiel die Angabe über den Wortabstand zwischen den Planes fehlt. Diese Art von Angaben setzt AOOE durch Standard- oder Rechnerformat fest. Die letzten drei Einträge des MFDBs sind für zukünftige Anwendungen reserviert und werden daher bisher (auch von Ihnen) nicht verwendet. Wollen wir einen MFDB für den Monochromgrafikmodus des ATARI im Rechnerformat definieren, sieht er folgendermaßen aus:

MFDB bildschirm={ 0L, 640, 400. 40,0,0,0,0};

Diesen MFDB 'bildschirm', den Sie auch in der Demo (beziehungsweise im Hauptprogramm Listing 1) wiederfinden, können Sie sowohl als Definition des Quell- als auch des Zielbildschirms verwenden, wenn Sie innerhalb eines normalen Bildschirmspeichers kopieren wollen. Daraus ergibt sich, das Quell- und Ziel-MFDB der gleiche ist. Sollten Sie aber nicht innerhalb eines Bildes kopieren wollen, sondern von einem Speicherbereich in den anderen, dann müssen Sie natürlich zwei verschiedene MFDBs verwenden, da der MFDB die Adresse des Bildspeichers enthält. Die Adressen der beiden MFDBs werden in die Line A-Variablen CONTRL[7]/Control[8] für den Quellbild-MFDB und in CONTRL[9]/CONTRL[10] für den Zielbild-MFDB geschrieben.

Ausmaße

Nachdem nun die Definition des Bildspeichers erledigt ist, wollen wir den eigentlichen Bildschirmausschnitt definieren, der kopiert werden soll. Die Koordinaten des Bildschirmausschnitts werden wie immer als linke obere und rechte untere Rechteckskoordinate angegeben und finden sich in PTSIN[0] bis PTSIN[3] wieder. Das Zielrechteck wird ebenso definiert und in PTSIN[4] bis PTSIN[7] vermerkt. Im Normalfall wird die Breite und Höhe, das heißt, der Abstand zwischen X1/X2 und Y1/Y2, identisch mit den Werten des Quellrechtecks sein. Ist das Zielrechteck kleiner definiert, wirken die kleinere Höhe und Breite als Clipping des Quellbildausschnitts, der an die linke obere Ecke des Zielrechtecks angelegt wird. An sich bleibt als Parameter nur noch der Verknüpfungsmodus übrig, der in aller Ausführlichkeit in der ST-Ecke ST Computer 5/89 in Zusammenhang mit der BitBlt-Routine A007 erklärt und auch ‘hergeleitet’ worden ist. Trotzdem finden Sie die unterschiedlichen Modi, damit Sie nicht so viel blättern müssen, in Listing 5 noch einmal zusammengestellt vor.

Eingebunden

Bevor wir auf die Demonstration der Routine kommen, schauen wir uns zunächst einmal das Binding von A00E an. Übergeben werden in dieser Reihenfolge der Verknüpfungsmodus, das Integer-Feld, in dem die Punktkoordinaten der beiden Rechtecke hintereinander zu finden sind, sowie die MFDBs des Quell-und Zielbildausschnitts. Den Verknüpfungsmodus schreiben wir in Line As INTIN[]-Feld. Die Adressen der beiden MFDBs müssen in High- und Low-Byte aufgetrennt und in das CONTRL[]-Feld geschrieben werden. Normalerweise müßte man nun die acht Punkte der Rechtecke aus dem übergebenen Feld in das INTIN[]-Feld kopieren. Sie werden sich sicherlich erinnern, daß wir ein ähnliches Problem bei der Routine A006, mit der man ein gefülltes Polygon zeichnen kann, hatten. Zunächst merken wir uns den Originalzeiger des INTIN[]-Feldes. Dann schreiben wir den Zeiger des Punktfeldes in den INTIN[]-Zeiger, so daß Line A unser Punktefeld als INTIN[]-Feld vorgespiegelt bekommt. Nach der Ausführung der Routine restaurieren wir den Zeiger wieder, so daß er auf das ursprüngliche INTIN[]-Feld zeigt.

Zur Demonstration läßt sich sagen, daß auch hier wieder auf Adressen zugegriffen wird, in denen sich die aktuellen Mauskoordinaten sowie der -status befinden. Das ‘Geheimnis’ des Zugriffs möchte ich in der nächsten ST-Ecke lüften. Zunächst wird die Demo für A006 aufgerufen, die einfach ein paar Bildchen auf den Bildschirm bringt, damit Sie auch etwas zum Kopieren haben - Sie können hier auch etwas anderes einbauen. Dann werden Sie aufgefordert, die linke obere und dann die rechte untere Ecke des Quellrechtecks anzuklicken. Danach geben Sie mit der Maus an, wo dieser Bildschirmausschnitt hinkopiert werden soll.

ACHTUNG: A007 und A00E reagieren sehr empfindlich darauf, wenn beispielsweise die X-Koordinate der rechten unteren Ecke kleiner als die der linken oberen Ecke ist. Intern wird nämlich daraus die Breite errechnet, die dadurch negativ wird, was wiederum prompt zum Absturz führt - gleiches gilt natürlich auch für den Y-Wert. Achten Sie also darauf, daß Sie wirklich LINKE OBERE und RECHTE UNTERE Ecke anklicken. Sie können aber auch die Demo oder das Binding so ändern, daß automatisch die Koordinaten so verändert werden, daß sich tatsächlich linke obere und rechte untere Ecke ergeben.

#include <line_a.h>
#include <gemdefs.h>
#include <stdio.h>
#include <osbind.h>

idefine BIG_CHAR 16 /* größtes Zeichen, sollte mind. max (s. set_line_font) */
                    /* aller Zeichensätze sein */

#define void /**/   /* Nur definieren, falls nicht bekannt */

#ifndef abs         /* Macro für Absolutbetrag */
    #define abs(x) ((x<0) ? x*-1:x)
#endif

/* Folgende Strukturdefinitionen nur einbinden, falls noch nicht vorhanden */

/*
typedef struct mfstr 
{
    int     mf_xhot;
    int     mf_yhot;
    int     mf_nplanes;
    int     mf_fg;
    int     mf_bg;
    int     mf_mask[16];
    int     mf_data[16];
} MFORM;

typedef struct sfstr 
{
    int xhot; 
    int yhot; 
    int form; 
    int bgcol; 
    int fgcol; 
    int image[32];
} SDB;
*/

LINE A *linea, *a000(); /* Zeiger auf Line-A-Variablen */
long *fonts, *routines; /* Zeiger auf Routinen und Fonts */

int scratch_buf[BIG_CHAR*2];  /* Puffer für Texteffekte */
MFDB bildschirm={OL,640,400,40,0,1,0,0,0}; /* Monochrom */

int fill[]={              /* Füllmuster, 16 Zeilen hoch */
0x2E74,0xA5A5,0x300C,0x9C39,
0xCFF3,0x4812,0x6426,0xD3CB,
0xD3CB,0x6426,0x4812,0xCFF3,
0x9C39,0x300C,0xA5A5,0x2E74};

/* Ein Ufo-Sprite */
int sprite array[]={0x0000,0x0000,0x0001,0x0002,0x0005,
                    0x0000,0x0000,0x0FF0,0x0000,
                    0x1FF8,0x0FF0,0x3FFC,0x1FF8,
                    0x7FFE,0x3FFC,0xFFFF,0x7FFE,
                    0x4002,0x2AAC,0x3FFC,0x1FF8,
                    0x1FF8,0x0FF0,0x0FF0,0x0000,
                    0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000};

/* Das gleiche Sprite als Struktur definiert */

SDB sprite= { 0x0000,0x0000,0x0001,0x0002,0x0005,
                {   0x0000,0x0000,0x0FF0,0x0000,
                    0x1FF8,0x0FF0,0x3FFC,0x1FF8,
                    0x7FFE,0x3FFC,0xFFFF,0x7FFE,
                    0x4002,0x2AAC,0x3FFC,0x1FF8,
                    0x1FF8,0x0FF0,0x0FF0,0x0000,
                    0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000 }
                };

/* Eine Maus-Maus */
int mouse[]= { 0x0000,0x0008,0x0001,0x0000,0x0001,
               0x0000,0x0030,0x0038,0x0FFC,
               0x1FFC,0x3FFC,0x6FFC,0xFFFC,
               0xFFF8,0xFFF8,0x0000,0x0000,
               0x0000,0x0000,0x0000,0x0000,
               0x0000,0x0000,0x0010,0x0028,
               0x0F88,0x1FC8,0x2FF8,0x7FF8,
               0xFFF0,0x1040,0x0000,0x0000, 
               0x0000,0x0000,0x0000,0x0000} ;

/* Maus als Struktur: Sinnvollere Art, die Maus zu definieren */ 
MFORM maus= { 0x0000,0x0008,0x0001,0x0000,0x0001,
                { 0x0000,0x0030,0x0038,0x0FFC,
                  0x1FFC,0x3FFC,0x6FFC,0xFFFC,
                  0xFFF8,0xFFF8,0x0000,0x0000, 
                  0x0000,0x0000,0x0000,0x0000 },
                { 0x0000,0x0000,0x0010,0x0028,
                  0x0F88,0x1FC8,0x2FF8,0x7FF8,
                  0xFFF0,0x1040,0x0000,0x0000,
                  0x0000,0x0000,0x0000,0x0000},
            };

main()
{
    /* Hier kommen die unter AUFRUF aufgeführten Aufrufe hinein */
}

Listing 1: Der Hauptteil für unsere Demos


typedef struct fdbstr { long fd_addr; /* Adresse des Speichers */ int fd_w; /* Breite */ int fd_h; /* Höhe */ int fd_wdwidth; /* Breite in Worten */ int fd_stand; /* Standardformat (1)? */ int fd_nplanes; /* Anzahl der Planes */ int fd_r1; /* reserviert */ int fd_r2; /* reserviert */ int fd_r3; /* reserviert */ } MFDB;

Listing 2: memory form definition block

Eingabe: INTIN[0]:      Blit-Modus
         CONTRL[7/8]:   Adresse des Quell-MFDBs
         CONTRL[9/10]:  Adresse des Ziel-MFDBs
         PTSIN[0]:      Quellrechteck linke obere Ecke, X
         PTSIN[1]:      Quellrechteck linke obere Ecke, Y
         PTSIN[2]:      Quellrechteck rechte untere Ecke, X
         PTSIN[3]:      Quellrechteck rechte untere Ecke, Y
         PTSIN[4]:      Zielrechteck linke obere Ecke, X
         PTSIN[5]:      Zielrechteck linke obere Ecke, Y
         PTSIN[6]:      Zielrechteck rechte untere Ecke, X
         PTSIN[7]:      Zielrechteck rechte untere Ecke, Y

Ausgabe: keine

Routine:

void a00e(mode, p_array, s_mfdb, d_mfdb)
int mode;               /* Blitter-Mode */
int *p_array;           /* Rechtecke */
long s_mfdb, d_mfdb;    /* Speicherdefinition */
{
    int i; 
    int *poi;

    linea->INTIN[0]=mode;                       /* Blitter-Mode */
    linea->CONTRL[7]=(int)(s_mfdb>>16)&0xFFFF;  /* Hi-Byte */ 
    linea->CONTRL[8]=(int)s_mfdb&0xFFFF;        /* Lo-Byte */
    linea->CONTRL[9]=(int)(d_mfdb>>16)&0xFFFF;  /* Hi-Byte */
    linea->CONTRL[10]=(int)d_mfdb&0xFFFF;       /* Lo-byte */

    poi=linea->PTSIN;       /* PTSIN merken */
    linea->PTSIN=p_array;   /* unser Punkte-Feld 'ist' jetzt PTSIN */

    asm
    {
        dc.w 0xa00e         /* Kopiere Block */
    }
    linea->PTSIN=poi;       /* PTSIN restaurieren */
}

Aufruf:

void demo_a00e(res)         /* auch a009, a00a && a00b */
int res;
{
    int *gcur_x, *gcur_y, x1, y1, x2, y2, x3, y3;
    char *cur_ms_stat;
    int punkte[8], fill1=0xffff;

    a00a(0);                /* Maus aus */
    printf("\33E \n");      /* Bildschirm löschen */
    a009(0);                /* Maus an */

    while (Cconis())        /* Tastaturpuffer löschen */
        Crawcin();

    demo_a006();        /* Ein bißchen was auf den Bildschirm bringen */

    set_line_a(&fill1,0,0,1,1,100,100,540,300); /* Line-A-Vars setzen */

    bildschirm.fd_addr= Logbase();      /* Bildschirmadresse setzen */
    gcur_x=(int*)((long)linea-0x25aL);  /* aktuelle Mauspositionsadresse */
    gcur_y=(int*)((long)linea-0x258L);  /* aktuelle Mauspositionsadresse */

    cur_ms_stat=(char*)((long)linea-0x15cL); /* Mausstatusadresse */

    while(!Cconis())                /* Bis Taste gedrückt */
    {
        printf("\33H Bitte obere Ecke des Rechtecks wählen\n");
        while(!(*cur_ms_statf&1));  /* Warten, bis Maustaste gedrückt */
        while(*cur_ms_stat&1);      /* und losgelassen */
        x1=*gcur_x;                 /* aktuelle Mausposition x */
        y1=*gcur_y;                 /* aktuelle Mausposition y */

        printf("\33H\n Bitte untere Ecke des Rechtecks wahlen\n"); 
        while(!(*cur_ms_stat&1));   /* Warten, bis Maustaste gedrückt */
        while(*cur_ms_stat&1);      /* und losgelassen */
        x2= *gcur_x;                /* aktuelle Mausposition x */
        y2= *gcur_y;                /* aktuelle Mausposition y */

        printf("\33H\n Wohin kopieren ?\n");
        while(!(*cur_ms_stat&1));   /* Warten, bis Maustaste gedrückt */
        while(*cur_ms_stat&1);      /* und losgelassen */
        x3= *gcur_x;                /* aktuelle Mausposition x */
        y3= *gcur_y;                /* aktuelle Mausposition y */
        punkte[0]=x1;               /* Ausgangsrechteck X1 < X2 !!! */
        punkte[1]=y1;               /* Ausgangsrechteck Y1 < Y2 !!! */
        punkte[2]=x2;               /* sonst droht Absturz !!*•!!*! */
        punkte[3]=y2;
        punkte[4]=x3;               /* Zielrechteck */
        punkte[5]=y3;
        punkte[6]=x3+(x2-x1);       /* Keine negative Breite !!!!!! */
        punkte[7]=y3+(y2-y1);       /* Keine negative Höhe !!!!!!!! */
        a00a(0);                    /* Maus ausschalten */
        a00e(3, punkte, &bildschirm, &bildschirm);
        a009(0);                    /* Maus einschalten */
    }
    Crawcin();                      /* Zeichen holen */
}

Listing 3: Blockkopieren mit A00E

Seedfill

Die zweite Routine, die wir uns in diesem Monat vornehmen wollen, ist eine Füllroutine zum Füllen von geschlossenen Kurven, bei denen man einen Punkt im Innern der Fläche kennt. Sie hört bei Line A auf den Namen A00F und ist damit die letzte Routine des Line A, die es zu besprechen gilt. Verblüffend ist, daß diese Routine in der ersten Line A-Dokumentation von ATARI überhaupt nicht erwähnt wurde, obgleich sie doch recht interessant und relativ einfach zu handhaben ist, wenn man nur wüßte wie...

Füllmethoden

Überlegt man sich, wie man eine Fläche füllt, wird man bemerken, daß es mehrere Methoden gibt, von denen wir zwei herausgreifen wollen, die in LINE A verwirklicht sind. Zum einen gibt es die Füllroutine, die bei A006, dem gefüllten Polygon, zum Einsatz kommt. Dort wird oberste und unterste Ecke des Polygons aus den Punktkoordinaten ermittelt und dieses Polygon von oben nach unten zeilenweise gefüllt, wobei an dessen Begrenzung gestoppt wird. Man kann es so beschreiben, daß man zeilenweise bis zu den Rändern ausmalt, dabei aber von oben nach unten vorgeht. Bei dieser Methode muß die Umrandung also als Polygonzug in der Form von vorgegebenen Punkten definiert sein, aus denen sich der Algorithmus für die Umrandung herleitet, In der Praxis tritt häufig aber der Fall auf, daß eine Umrandung schon in Form einer geschlossenen Linie und nicht als punktdefinierter Polygonzug vorliegt. Trotzdem wünscht man, die Umrandung als Fläche zu füllen. Den Algorithmus, der dies in die Tat umsetzt, nennt man Seedfill (‘Keim-’ oder ‘Samen-‘Füller). Ihm gibt man als einzige Information einen Punkt (Keim) innerhalb dieser Fläche vor, von dem aus er die gesamte Fläche bis zu den Rändern füllt. Dabei geht der Seedfill-Algorithmus des Line A auch zeilenweise nach oben und nach unten vor. (Wenn Sie die unten angesprochene hallo()-Routine einbauen, können Sie sich dies schrittweise anschauen.) Wer Näheres über Füllalgorithmen wie zum Beispiel den Seedfill-Algorithmus nachlesen möchte, sei auf die Literaturangabe [1] verwiesen, die ich sehr empfehlen kann.

Realisierung

Wie schon erwähnt, setzt dieser Algorithmus einen geschlossenen Linienzug voraus, den Sie in der Demo mit gedrückter Maustaste zeichnen sollen. Danach müssen Sie einen Punkt angeben, der innerhalb der zu füllenden Fläche liegt. Diese Punktkoordinaten werden in Line As PTSIN[0] und PTSIN[1] geschrieben. Die Farbe, mit der die Fläche gefüllt werden soll, steht in INTIN[0]. WICHTIG: Diese Routine unterstützt einmal mehr ein angegebenes Füllmuster, wie sie es auch schon von den vorhergehenden Routinen kennen - eine Tatsache, die ich zunächst nicht beachtete, was bei nicht gesetzter Musteradresse zu einem herrlichen Absturz führte. Also nicht vergessen: das Füllmuster setzen!

Eingabe: INTIN[0]:      Füllfarbe
         PTSIN[0]:      X-Koordinate des Startpunktes
         PTSIN[1]:      Y-Koordinate des Startpunktes
         SEEDABORT:     Zeilenende-Routine

Ausgabe: keine 
Routine:

void a00f(x,y,farbe, routine) /* Startpunkt,Farbe, Zeilenenderoutine */ 
int x,y,farbe; 
long routine;
{
    static long *s_abort(), con();

    linea->INTIN[0]=farbe;  /* Füllfarbe */
    linea->PTSIN[0]=x;      /* Startkoordinate X */
    linea->PTSIN[1]=y;      /* Startkoordinate Y */

    if (routine == 0L)
        linea->SEEDABORT=(long)s_abort; /* Adresse Zeilenenderoutine */ 
    else
        linea->SEEDABORT=routine;

    asm
    {
        dc.w    0xa00f      /* Füllen */
        jmp     con         /* über Zeilenenderoutine springen */
s_abort:                    /* Zeilenenderoutine */
        clr.w   D0          /* D0=0 heißt erfolgreich, kein Abbruch */
        rts
con:
    }
}

Aufruf:

void demo_a00f()
{
    int *gcur_x, *gcur_y, x, y, lx, ly; 
    char *cur_ms_stat; 
    int fill1=0xffff;

    set_line_a(&fill1,0,0,1,1,0,0,639,399); /* Vars setzen */

    gcur_x=(int*)((long)linea-0x25aL); /* Adresse X-Koordinate */
    gcur_y=(int*)((long)linea-0x258L); /* Adresse Y-Koordinate */
    cur_ms_stat=(char*)((long)linea-0x15cL); /* Adresse Mausstatus */

    a00a(0);                /* Maus aus */
    printf("\33E \n");      /* Bildschirm loschen */
    printf("\33HBitte zeichnen Sie eine geschlossene Kurve\n");
    a009(0);                /* Maus ein */

    while(!(*cur_ms_stat&1)); /* Warten, bis Maustaste gedrückt */
    while(*cur_ms_stat&1)   /* und losgelassen */
    {
        x=*gcur_x; 
        y=*gcur_y;
        if(!(x==lx && y==ly))        /* neue Koordinate ? */
        {
            a00a(0);                 /* Maus aus */
            a001(*gcur_x,*gcur_y,1); /* Solange zeichnen */
            a009(0);                 /* Maus ein */
        }
        lx=x;                        /* letzte Koordinate merken */
        ly=y;
    }
    printf("Bitte geben Sie einen Punkt in der Fläche an\n");

    while(!(*cur_ms_stat&1));   /* Warten, bis Maustaste gedrückt */

    x=*gcur_x;                  /* Startkoordinate zum Füllen */
    y=*gcur_y;

    a00a(0);                    /* Maus aus */
    a00f(x,y,1);                /* Füllen */
    a009(0);                    /* Maus ein */

    Crawcin();                  /* Tastendruck abwarten */
}

Listing 4: Seedfill mit A00F

Die Verknüpfungsart besteht aus vier Bits:

Bit 0:     Quellbit AND     Zielbit
Bit 1:     Quellbit AND NOT Zielbit
Bit 2: NOT Quellbit AND     Zielbit
Bit 3: NOT Quellbit AND NOT Zielbit

Aus den Kombinationen ergeben sich folgende Verknüpfungen: 
Verknüpfungsnummer  Verknüpfung

0                   Zielbit = 0
1                   Zielbit = Quellbit AND Zielbit
2                   Zielbit = Quellbit AND NOT Zielbit
3                   Zielbit = Quellbit
4                   Zielbit = NOT Quellbit AND Zielbit
5                   Zielbit = Zielbit
6                   Zielbit = Quellbit EXOR Zielbit
7                   Zielbit = Quellbit OR Zielbit
8                   Zielbit = NOT Quellbit AND NOT Zielbit
9                   Zielbit = NOT Quellbit XOR Zielbit
10                  Zielbit = NOT Zielbit
11                  Zielbit = Quellbit OR NOT Zielbit
12                  Zielbit = NOT Quellbit
13                  Zielbit = NOT Quellbit OR Zielbit
14                  Zielbit = NOT Quellbit OR NOT Zielbit
15                  Zielbit = 1

Listing 5: Die Blitter-Operationen

Abbruch

Seedfill bietet noch einen vierten Parameter, der sich als Adresse versteht, die auf eine Routine zeigt. Diese Routine wird während des Ablaufs der Seedfill-Routine immer dann angesprungen, wenn der Algorithmus am Zeilenende ankommt.

Dadurch können beliebige Zusatzabfragen am Ende einer jeden Füllzeile eingefügt werden. Allerdings ist mir nicht klar, wie man die Abfrage dieser Routine in bezug auf die momentan laufende Seedfill-Routine des Line A setzen soll, da ich nicht herausgefunden habe, ob in irgendeiner der Line A-Variablen oder der dazugehörigen Felder die aktuellen Zeilen stehen, die momentan bearbeitet werden. Sollte jemand von den Lesern wissen oder herausfinden, wie man an die aktuellen Zeilennummern der Füllroutine kommt oder für was man SEEDABORT sinnvoll einsetzen kann, so bitte ich ihn, mir dies mitzuteilen. Seedfill setzt seinen Fülldurchgang dann fort, solange Register DO gleich Null bleibt. Ist D0 ungleich Null, wird der Vorgang des Füllens sofort abgebrochen. In meinem Binding habe ich eine Routine eingefügt, die eine 0 in D0 zurückliefert und damit praktisch eine Dummy-Routine ist, die nichts tut, als eine OK-Signal zurückzuliefern. Dem Binding müssen Sie als viertem Parameter eine Adresse der Routine angeben, die Sie als SEEDABORT-Routine verwenden wollen. In den meisten Fällen wird man diese Option nicht nutzen wollen. Dann brauchen Sie nur eine 0L zu übergeben, worauf das Binding automatisch die Dummy-Routine s_abort als SEEDABORT-Routine installiert. Ich habe übrigens eine kleine Routine hallo() als SEEDABORT-Routine geschrieben, die einen Text auf dem Bildschirm ausgibt und auf Tastendruck wartet. Obwohl diese Routine das BIOS verwendet, funktioniert es einwandfrei:

hallo()
{
    printf("\33HTaste drücken:\n");
    Crawcin();
    return(0);
}

Ihren Aufruf müssen Sie dann in ‘a00f(x,y,1,hallo);'' ändern, damit die hallo()-Routine aufgerufen wird.

The final countdown

So, nun wären wir am Ende der gesamten Line A-Bibliothek angekommen und ich hoffe, ich habe Ihnen einen umfassenden und verständlichen Eindruck der Line A-Routinen geben können und damit ein kleines Nachschlagewerk in Sachen Line A geschaffen. Mich würde es freuen, wenn weitere Anregungen und Ergänzungen von Lesern kommen würden, die ich bei größerer Menge in der ST-Ecke oder bei kleinerer Anzahl wenigstens in den Leserbriefen anderen Lesern zugänglich machen könnte. Das gesamte Line A-Binding mit allen Demos und der Include -Datei line_a.h werden Sie auf der zu diesem Heft gehörenden Monatsdiskette finden. Allerdings möchte ich als Ausklang in der nächsten ST-Ecke ein nettes Programm vorstellen, an dem Sie ziemlich sicher Ihre Freude haben werden und das einiges mit Line A am Hut hat. Wenn Sie also wollen, warten Sie noch so lange, dann haben Sie alles beisammen, ohne viel tippen zu müssen. Freuen Sie sich also mit mir auf die nächste ST-Ecke, denn dann erfahren Sie endlich etwas mehr über die Adressen, mit denen man die Mauskoordinaten und vieles mehr auf legale Weise(!) erfragen kann. Außerdem erwartet Sie ein Feuerwerk...

SH

Literaturhinweise:

[1] David F. Rogers: Procedural Elements for Computer Graphics, McGraw Hill International Editions

[2] Jankowski, Rabich, Reschke: ATARI ST Profibuch, Sybex Verlag


/* Die folgende Routine dient dem Setzen von */ /* diversen LINE-A-Variablen. */ set_line_a(pattern, pat_nr, plane_flag, plane_no, clip_flag, cl_x1, cl_y1, cl_x2, cl_y2) int *pattern, pat_nr; /* Muster */ int plane_no; /* Anzahl der Planes */ int plane_flag, clip_flag; /* Farbflag, Clip-Flag */ int cl_x1, cl_y1, cl_x2, cl_y2; /* Clip-Bereich */ { linea->PATPTR = pattern; /* Muster-Adresse */ linea->PATMSK = pat_nr; /* Musterhöhe */ linea->MFILL = plane_flag; /* alle Farbbits benutzen ? */ if (plane_no>0) /* 0 nicht erlaubt !!!!! */ linea->PLANES= plane_no; linea->WIDTH = (plane_no>1) ? 160:80; /* <- Bytebreite des Bildes */ linea->CLIP = clip_flag; /* Clipping ein/ausschalten */ linea->XMINCL = cl_x1; /* Bereich setzen */ linea->YMINCL = cl_y1; linea->XMAXCL = cl_x2; linea->YMAXCL = cl_y2; } /*************************************************************/ /* */ /* Setzen der Farbe in unterschiedliche Variablen */ /* */ /*************************************************************/ set_line_color(color) /* Farbe in Line-A-Variablen schreiben */ int color; { int a,b,c,d; a=linea->COLBIT_0 = color&1; /* Bit 0 der Farbe */ b=linea->COLBIT_1 = (color&2)==2; /* <- Bit 1 der Farbe */ c=linea->COLBIT_2 = (color&4)==4; /* <- Bit 2 der Farbe */ d=linea->COLBIT_3 = (color&8)==8; /* <- Bit 3 der Farbe */ } int a008(ch, x, y, font) unsigned char ch; /* <- falsche Zeile */ FONT_HDR * font; { int back_x; /* nächste Zeichenausgabestelle */ if (!font) /* Fontheaderadresse = 0 ? */ return; /* Ja, dann zurück */ if (!font->flags&8) /* Proportionalschrift */ { linea->SOURCEX= font->off_table[ch-(font->first_ade)]; linea->DELX = font->off_table[ch+1-(font->first_ade)]-(linea->SOURCEX); } else /* Keine Offsettabelle verwenden */ { linea->SOURCEX=(ch-(font->first_ade))*(font->max_cell_width); linea->DELX = font->max_cell_width; } linea->SOURCEY= 0; /* meist 0 */ linea->DESTX= x; /* X-Koordinate */ linea->DESTY= y; /* Y-Koordinate */ /* altes X + Breite des Zeichens */ back_x=x+linea->DELX +((linea->STYLE&4)!=0)*(font->left_offset+font->right_offset) +((linea->STYLE&16)!=0)*2; asm { dc.w 0xa008 /* Textausgabe */ } return(back_x); /* nächste Koordinate */ }

Listing 6: Die berichtigten fehlenden drei Listings



Links

Copyright-Bestimmungen: siehe Über diese Seite