Teil 18 „Graphics“

Der C-Kurs
Antworten
Benutzeravatar
bodo
User
Beiträge: 319
Registriert: 14.02.2007, 17:21
Kontaktdaten:

Teil 18 „Graphics“

Beitrag von bodo » 02.01.2011, 12:03

C für BASIC-Programmierer

Arbeiten mit dem z88dk Cross-Compiler

von Jens Sommerfeld und Bodo Wenzel

Teil 18

In diesem Teil wird es richtig spannend! Denn wie im BASIC-Buch heißt es „Graphics“! In diesem Teil wird es 'mal wieder keine neuen Dinge zu C geben, sondern wir stellen euch die HRG-Bibliotheken des z88dk vor. Mit diesen Sammlungen von Unterprogrammen könnt ihr HRG-Anwendungen programmieren. Die Bibliotheken sind im Wikiwiki (Zum Beispiel http://www.z88dk.org/wiki/doku.php/library:monographics) des z88dk dokumentiert, aber auch die Headerdateien (Dies sind graphics.h und gray.h, wie in den Beispielprogrammen zu sehen.) geben erhellende Einblicke. ;-)

Das z88dk bietet uns verschiedene Bildschirmmodi an, die durch die Option „-startup“ auf der Kommandozeile ausgewählt werden. Wir benutzen ja bereits seit geraumer Zeit „-startup=2“, um das C-Programm in SLOW arbeiten zu lassen. Mit „-startup=1“ würden wir nichts sehen, weil das C-Programm in FAST arbeitet. Dieser Modus ist aber offenbar auch der Default. Die Modi 3 bis 7 sind nun HRG-Modi:
  • „-startup=3“ verwendet 256×192 Pixel, am Programmende muss SPACE gedrückt werden, um wieder in den Textmodus zu kommen. Das ist auch praktisch für unsere Testprogramme.
  • „-startup=4“ verwendet auch 256×192 Pixel, aber bei der Rückkehr ins BASIC wird der Textmodus automatisch wieder eingeschaltet.
  • „-startup=5“ ist wie „-startup=3“, aber erzeugt einen Modus mit 256×64 Pixeln, der VIEL Zeit für den Programmlauf lässt.
  • „-startup=6“ ist wie „-startup=4“, aber auch mit 256×64 Pixeln.
  • „-startup=7“ erzeugt einen 256×64 Pixel großen Schirm, der dafür 4 verschiedene Graustufen besitzt. Bei der Rückkehr ins BASIC wird der Textmodus automatisch wieder eingeschaltet.
Damit beim Linken auch die HRG-Funktionen hinzugebunden werden, muss noch die entsprechende Bibliothek mit der Option „-lgfx81hr192“ (bei 192 Pixeln Höhe) bzw. „-lgfx81hr64“ (bei 64 Pixeln Höhe) angegeben werden.

Grafikmodus 256×192

Zum Test dieses Modus' haben wir das folgende einfache Beispielprogramm verwendet:

Code: Alles auswählen

#include <graphics.h>
#include <stdio.h>
#include <zx81.h>

#define WIDTH 256
#define HEIGHT 192

int main(void) {
    int x;
    int y;

    printf("\n GRAFIK MIT %d X %d PIXELN\n", WIDTH, HEIGHT);
    clg();
    copytxt(txt_or);

    drawb(0, 0, WIDTH, HEIGHT);

    for (x = 0; x < WIDTH; x++) {
        y = (HEIGHT * x) / WIDTH;
        plot(x, y);
        plot(x, HEIGHT - 1 - y);
    }

    return 0;
}
Es löscht den HRG-Schirm, kopiert den Text vom Textbildschirm hinein, malt dann ein Rechteck ganz außen und plottet schließlich die zwei Diagonalen. Die Ausgabe sieht so aus wie im nächsten Modus, nur halt 192 Pixel hoch...

Grafikmodus 256×64

Zum Test dieses Modus' haben wir dasselbe Testprogramm verwendet, nur wurde die Bildhöhe angepasst:

Code: Alles auswählen

#define HEIGHT 64
Und so sieht es auf dem Bildschirm aus:
CfBASIC_18-2-klein.gif
CfBASIC_18-2-klein.gif (787 Bytes) 2625 mal betrachtet
Grafikmodus 256×64 in Graustufen

Auf diesen Modus war Bodo besonders gespannt, hat er doch etwas ähnliches in seinem Demo PUZZLE99 gemacht. Und auf dem FPGA-Prototypen sieht es auch ziemlich gut aus, im Gegensatz zur Darstellung in Emulatoren (Kann EightyOne das inzwischen eigentlich? Antwortet bitte im Forum...). Bei Röhrenschirmen dürften die grauen Flächen deutlich flimmern!

Dies ist das Testprogramm zu diesem Modus, es zeigt, dass die grauen Varianten der HRG-Funktionen einen Präfix „g_“ besitzen:

Code: Alles auswählen

#include <gray.h>
#include <stdio.h>
#include <zx81.h>

#define WIDTH 256
#define HEIGHT 64
#define LEVELS (G_WHITE + 1)
#define PAGES 2

int main(void) {
    int g;
    int p;
    int x;
    int y;
    char eingabe;

    for (g = 0; g < LEVELS; g++) {
        for (x = (g * WIDTH) / LEVELS;
             x < ((g + 1) * WIDTH) / LEVELS;
             x++) {
            g_draw(x, 0, x, HEIGHT - 1, g);
        }
    }

    printf("\n %d GRAUSTUFEN AUF %d SEITEN\n"
           " MIT %d X %d PIXELN\n", LEVELS, PAGES, WIDTH, HEIGHT);
    for (p = 0; p < PAGES; p++) {
        g_page(p);
        copytxt(txt_xor);
    }

    g = 0;
    for (x = 0; x < WIDTH; x++) {
        g_plot(x, 0, g);
        g_plot(x, HEIGHT - 1, g);
        g++;
        if (g >= LEVELS) {
            g = 0;
        }
    }
    for (y = 0; y < HEIGHT; y++) {
        g_plot(0, y, g);
        g_plot(WIDTH - 1, y, g);
        g++;
        if (g >= LEVELS) {
            g = 0;
        }
    }

    for (x = 0; x < WIDTH; x++) {
        y = (HEIGHT * x) / WIDTH;
        g = LEVELS - 1 - (LEVELS * x) / WIDTH;
        g_plot(x, y, g);
        g_plot(x, HEIGHT - 1 - y, g);
    }

    gets(&eingabe); /* nur ENTER eingeben! */

    return 0;
}
Die Graustufen werden durch entsprechende Umschaltung von zwei HRG-Bildspeichern erzeugt. Der eine wird offenbar doppelt so häufig benutzt wie der andere, dadurch ergeben sich die verschiedenen Zwischenstufen. Leider hat der Treiber einen kleinen Fehler: die beiden „Zwischengraus“ werden umgekehrt erzeugt...

Das Testprogramm offenbarte noch einen weiteren Fehler: die Funktion g_draw(), wie sie dort aufgerufen wird, beginnt erst mit dem zweiten Pixel tatsächlich zu zeichnen. Das Bildschirmfoto wurde mit dem Grafikprogramm GIMP nachbearbeitet, um die Graustufen zu zeigen:
CfBASIC_18-3-klein.gif
CfBASIC_18-3-klein.gif (1.4 KiB) 2625 mal betrachtet
Wir sind sehr gespannt, was für Programme euch für diesen Modus einfallen!

Beispiele von Grafikfunktionen

Die HRG-Bibliotheken stellen die wichtigsten Funktionen zur Verfügung. Einige davon haben wir im folgenden Testprogramm ausprobiert:

Code: Alles auswählen

#include <graphics.h>
#include <stdio.h>

int x[] = {
      0,  32,  65, 100, 100,  85, 120, 135, 130, 196, 141, 168,
    168, 200, 250, 139, 108,  44,  60,  30,   0,
    -1, 120 };
int y[] = {
    162, 130, 145, 145, 130, 114, 100, 128, 108, 108, 141, 141,
    133, 126, 151, 170, 170, 165, 182, 190, 162,
    -1, 144 };

int main(void) {
    int koordinate;
    int groesse;
    int index;

    clg();

    draw(8, 8, 92, 8);
    draw(92, 8, 92, 92);
    draw(92, 92, 8, 92);
    draw(8, 92, 8, 8);
    koordinate = 10;
    for (groesse = 81; groesse > 0; groesse -= 2) {
        drawb(koordinate, koordinate, groesse, groesse);
        koordinate++;
    }
    uncircle(50, 50, 20, 1);
    uncircle(50, 50, 25, 2);
    uncircle(50, 50, 30, 10);
    uncircle(50, 50, 35, 90);

    plot(108, 8);
    drawr(84, 0);
    drawr(0, 84);
    drawr(-84, 0);
    drawr(0, -84);
    xorborder(110, 10, 81, 81);
    xorborder(113, 13, 31, 31);
    circle(150, 50, 20, 1);
    circle(150, 50, 25, 2);
    circle(150, 50, 30, 10);
    circle(150, 50, 35, 90);

    for (index = 1; x[index] >= 0; index++) {
        draw(x[index - 1], y[index - 1], x[index], y[index]);
    }
    fill(x[index + 1], y[index + 1]);

    return 0;
}
Dieses Programm enthält wieder ein typisches Konstrukt: initialisierte Arrays. Wer von euch schon über den Tellerrand des ZX81-BASICs hinausgeschaut hat, wird ähnliches mit DATA-Zeilen erzeugt haben. Die beiden Arrays enthalten die Koordinaten für ein ziemlich fieses Polygon, das zum Test der fill()-Funktion dient. Es ist sehr spannend, dieser Funktion zuzusehen!
CfBASIC_18-4-klein.gif
CfBASIC_18-4-klein.gif (1.82 KiB) 2625 mal betrachtet
Der Schnappschuss wurde während des Füllens gemacht: ihr könnt ahnen, nach welchem Algorithmus gefüllt wird... Und wir erkennen einen Fehler in der Funktion xorborder(), die ab einer bestimmten Größe den unteren Teil nicht mehr zeichnet.

Spezielle ZX81-Funktion für die HRG-Modi

Auch die Bibliothek für den ZX81 besitzt Funktionen für die Grafikmodi. Das folgende Beispiel untersucht die möglichen Arten des Kopierens des Textbildschirms, die die Funktion copytxt() beherrscht:

Code: Alles auswählen

#include <graphics.h>
#include <stdio.h>
#include <zx81.h>

#define BREITE 256
#define HOEHE 64
#define ABSTAND 32

void gibaus(int zeile, int spalte, char *text) {
    printf("\f");
    while (zeile > 0) {
        printf("\n");
        zeile--;
    }
    while (spalte > 0) {
        printf(" ");
        spalte--;
    }
    printf("%s", text);
}

void kopieretext(int zeile, char *text, int modus) {
    gibaus(zeile, 0, text);
    copytxt(txt_or);
    gibaus(zeile, 16, "0123456789ABCDEF");
    copytxt(modus);
}

int main(void) {
    int x;
    int y;

    clg();
    for (x = BREITE / 2; x < BREITE; x++) {
        if ((x & (ABSTAND / 2)) == 0) {
            draw(x, 0, x, HOEHE - 1);
        }
    }

    kopieretext(0, "TXT_AND",     txt_and);
    kopieretext(1, "TXT_AND_CPL", txt_and_cpl);
    kopieretext(2, "TXT_OR",      txt_or);
    kopieretext(3, "TXT_XOR",     txt_xor);
    kopieretext(4, "TXT_OR_R",    txt_or_r);
    kopieretext(5, "TXT_OR_L",    txt_or_l);
    kopieretext(6, "TXT_AND_R",   txt_and_r);
    kopieretext(7, "TXT_AND_L",   txt_and_l);

    return 0;
}
Die Funktion kopiert immer den ganzen Textschirm, aber nur dort, wo Zeichen stehen, wird überhaupt etwas ausgeführt. Um trotzdem die gewünschte Ausgabe hinzubekommen, sind ein paar Klimmzüge nötig. Versucht, das im Quelltext nachzuvollziehen. Experimentieren ist ausdrücklich gewünscht! Auch hier wieder der obligatorische Schnappschuss:
CfBASIC_18-5-klein.gif
CfBASIC_18-5-klein.gif (1.56 KiB) 2625 mal betrachtet
Sonstiges

Es gibt dann noch die Funktionen hrg_off() und hrg_on(), aber deren Untersuchung bleibt den Neugierigen von euch überlassen – wir wollen euch ja nicht immer alles vorkauen! :-P
B0D0: Real programmers do it in hex.

Antworten