Extended Images: Das XIMG-Format

Auf dem ATARI-Sektor sind mittlerweile viele Grafikformate etabliert. Es stellt sich zwangsläufig die Frage, welches für ein Projekt geeignet ist und mit welchem Aufwand es implementiert werden kann. Seit langem wird das IMG-Format unter GEM als Austauschformat intensiv genutzt, es ist jedoch durch seine vordefinierte Farbpalette beschränkt. Der neue XIMG-Standard stellt die entsprechenden Erweiterungen zur Verfügung. Als Beispielanwendung wird zum Abschluß ein Screenshot-Programm der neuen Generation vorgestellt werden, wie es unter XWindow bereits benutzt wird.

Das bekannte IMG-Format stellt einen Kompromiß zwischen Informationsgehalt, Kompaktheit und Handhabbarkeit dar. Sieht man von der eingeschränkten Farbpalette ab, beinhaltet es alle wichtigen Informationen für die gespeicherte Rastergrafik. Damit die Dateien auch bei großen Bildern nicht den Massenspeicher über Gebühr in Anspruch nehmen, sind einige einfache Komprimieralgorithmen definiert. Diese lassen sich aber mit erträglichem Aufwand implementieren [1].

Nachdem nun der XIMG-Standard die Zuordnung einer Farbtabelle zu der IMG-Grafik gestattet, steht ein Format zur Verfügung, das die einheitliche Speicherung von Rastergrafiken unterschiedlicher Quellen mit allen wichtigen Daten in einem in jeder Beziehung handlichen Format ermöglicht.

Der (X)IMG-Standard

Wie ist also eine IMG-Datei aufgebaut? Sie besteht aus einem Header und den Bitplanes. Die Planes werden, ähnlich wie beim VDI-Standardformat, getrennt abgelegt. Die Aufspaltung erfolgt jedoch zeilenweise (siehe Kasten).

Im Header sind die wichtigsten Informationen über den Aufbau der Grafik in acht Wörtern abgelegt:

1 Version des benutzten IMG-Formats
2 Länge des Headers in Words
3 Anzahl der Bitplanes
4 Länge der Patterns in Bytes
5 Breite eines Pixels in Im
6 Höhe eines Pixels in Im
7 Breite der Grafik in Pixeln
8 Höhe der Grafik in Pixeln

Die aktuelle Version ist die Nummer 1. Die Länge des Headers beträgt natürlich 8, und die des Patterns ist von der gewünschten Komprimierung abhängig, auf die später eingegangen wird. Die übrigen Daten hängen von der zu speichernden Grafik ab. Sie sollten dementsprechend nicht durch „Default“-Werte ersetzt werden!

Die ursprüngliche IMG-Definition sieht für die Speicherung von Farbgrafiken die Zuordnung von Grundfarben zu den einzelnen Bitplanes vor:

Plane Farbe
0 rot
1 grün
2 blau
3 grau

Damit lassen sich also Grafiken mit bis zu 16 „Standardfarben“ speichern. TOS-Programme nutzen meist nur eine Bitplane und interpretieren diese als schwarzweiß, was gegen die ursprüngliche Definition verstößt. Für die viele Anwendungen ist diese aber eher zu eingeschränkt.

Die XIMG-Definition erweitert daher den Header um eine Farbpalette. Es sind drei Farbmodelle definiert, nämlich RGB, CMY und Pantone (siehe auch [2]). Im Header wird festgelegt, welches Modell Verwendung finden soll. Dann folgen für jede Farbe drei Wörter. Deren Bedeutung ist natürlich vom benutzten Farbmodell abhängig.

Bei Verwendung des RGB-Farbmodells werden die Rot-, Grün- und Blauanteile jeweils im Bereich zwischen 0 und 1000 angegeben und für jede Farbe ein solches Worttripel in der Tabelle abgelegt.

Die Länge des Headers erhöht sich dementsprechend auf 8 (für den Standard-Header) plus 3 (für das magische Lang-Word und das Farbmodell) plus 3k (für die k Farben), insgesamt 11+3k Wörter. Die Zahl der Farben wird nicht mitgesichert, sondern muß beim Laden aus der Länge des Headers rekonstruiert werden. Auf den Header, gleichgültig ob erweitert oder nicht, folgen dann die einzelnen Zeilen der Grafik. Jede Zeile ist in ihre Planes aufgespalten, und diese sind nacheinander abgespeichert (siehe oben). Jede dieser „Zeilen-Planes“ besteht aus einer Byte-Folge. Ist die Zeilenbreite nicht durch acht teilbar, muß mit einigen Dummybits aufgefüllt werden. Das Packen über Zeilen und Planes hinweg ist nicht erlaubt. Die Byte-Folge einer solchen „Zeilen-Plane“ läßt sich weiter unterteilen. Solche Teilstücke heißen Bitstrings, obwohl es sich immer noch um Byte-Folgen handelt. Sie können, mit einer Art Header versehen, unverändert in die IMG-Datei geschrieben werden.

So könnte man die gesamte Grafik als Bitstrings ablegen, doch das (X)IMG-Format stellt einige einfache Komprimierungen für die Bitstrings bereit. Haben in einem Bitstring entweder alle Bytes den Wert 0x00 oder alle den Wert 0xFF, kann er als Solidrun gespeichert werden. Der besteht nur aus einem einzigen Byte, dessen sieben niederwertige Bits die Anzahl der Bytes im Solidrun enthalten. Das achte Bit legt fest, ob es sich um einen 0x00-Run handelt (Bit 8 gleich 0) oder um einen 0xFF-Run (Bit 8 gleich 1).

Der Solidrun komprimiert insbesondere einfarbige Flächen. Andere regelmäßige Strukturen erfaßt der Patternrun. Die Länge der Patterns (engl.: Muster) ist im Header festgelegt. Stellt ein Bitstring die mehrfache Wiederholung eines Patterns dar, muß dieses nur einmal mit einer entsprechenden Struktur davor gespeichert werden.

Sind sogar mehrere Zeilen identisch, kann man, abweichend von der oben beschriebenen Grundstruktur des (X)IMG-Files, einer Zeile die Scanline-Struktur voranstellen. Der Inhalt der auf die Struktur folgenden Zeile muß also entsprechend oft wiederholt werden.

Rechteckig, praktisch, gut...

Das (X)IMG-Format läßt sich also einfach ex- und wieder importieren. Mit der Farbpalette zusammen ist eine vollständige Beschreibung jeder Grafik möglich. Für Truecolor-Bilder, bei denen jeder Bildpunkt seine eigene Farbe haben kann, ist eine Farbtabelle allerdings prinzipiell unsinnig, dafür gibt es andere Standards und Formate.

Bestimmte Optionen wie Scanlines und Patternruns lassen sich besonders dann nutzen, wenn ein Programm genauere Informationen über die Struktur der Grafik hat. Im folgenden wird ein Modul vorgestellt, das jede beliebige Grafik als (X)IMG-Datei abspeichert und dabei auf Scanlines und Patternruns verzichtet.

Schnittstelle

Eine Rastergrafik soll als Datei abgespeichert werden. Also stellt das Headerfile BITMFILE.H (Listing 1) eine Funktion bitmap_to_file zur Verfügung, die folgende lokalen Parameter übergeben bekommt:

1 typ: gewünschter File-Typ
2 ww, wh: Breite und Höhe der Grafik in Pixeln
3 pixw, pixh: Breite und Höhe eines Pixels in Im
4 planes, colors: Anzahl der Bitplanes und Farben in der Farbtabelle
5 filename: Name der Datei
6 get_color, get_pixel: zwei Funktionsparameter

Welche File-Typen zur Verfügung stehen, kann man dem Aufzählungstypen FILE_-TYP entnehmen. Die Funktion get_file_ext liefert übrigens zu jedem File-Typ die Standard-Fileextension zurück, z.B. IMG für den (X)IMG-Typ. Die Funktionsparameter können bei der Implementierung wie Funktionsaufrufe benutzt werden, es wird jedoch die vom aufrufenden Programm eingetragene Prozedur verwendet. Dieser Mechanismus ermöglicht es dem Modul, Daten vom aufrufenden Programm zu erfragen.

Beim Aufruf von get_color wird dem Modul im Parameter rgb die RGB-Repräsentation der durch colind spezifizierten Farbe übergeben. Die einzelnen RGB-Werte liegen dabei zwischen 0 und 1000. So kann die Farbtabelle nach und nach erfragt und abgespeichert werden. get_pixel liefert in colind den Farbindex des Punktes (x,y). Die Koordinaten beziehen sich auf den Ursprung in der linken oberen Ecke der Grafik.

bitm_to_file liefert eine 0 zurück, wenn die Grafik tatsächlich in einer Datei abgelegt werden konnte, sonst einen Fehlercode ungleich 0.

Die Implementation (BITMFILE. C, Listing 2) beschränkt sich zunächst auf eine Fallunterscheidung, in der überprüft wird, ob das gewünschte Format überhaupt unterstützt wird. Dann wird die entsprechende Funktion aufgerufen. Unbekannte Formate führen zur Rückgabe eines Fehlercodes durch bitm_to_file.

Im Moment werden natürlich nur IMG- und XIMG-Files erzeugt. Es ist aber leicht möglich, an dieser Stelle weitere Formate „einzuhängen“. Dann müssen auch FILE_TYP und get_file_ext angepaßt werden. So kann ein Anwendungsprogramm komfortabel das Format auswählen. Aufzählungstypen wie FILE_TYP sind zu den ganzen Zahlen äquivalent, d. h. das erste Element, hier also IMG, ist gleich 0, das zweite gleich 1 usw. In einer Schleife kann man über den File-Typ solange die zugehörige Fileextension erfragen, bis get_file_ext einen Leer-String zurück liefert. Dann hat man alle unterstützten Formate erfragt und kann beispielsweise ein Menü aufbauen, aus dem der Benutzer das gewünschte Format wählen kann. Dazu wäre es sinnvoll, noch eine weitere Funktion get_name einzuführen, die zu einem File-Typ den Namen des Formates im Klartext (GEM-Image, X-Image...) ausgibt.

Soll also ein (X)IMG-File erzeugt werden, übergibt bit_to_file die Parameter an bitm_to_img. Ausnahmsweise wird auch der Typ mit übergeben, da die Funktion ja mehrere Formate unterstützt. Zunächst werden nun die Strukturen header und xheader belegt, entsprechend dem beschriebenen Aufbau des (X)IMG-Headers. Dann wird die Länge line_len einer „Zeilen-Plane“ in Bytes berechnet und ein Puffer line_buf eingerichtet, der eine ganze Zeile aufnehmen kann. Nun muß die Datei geöffnet werden, und der Header wird schon mal geschrieben.

Für ein XIMG-File ist jetzt der Aufbau der Farbtabelle nötig. Also werden die Farben der Reihe nach per get_color beim Hauptprogramm erfragt. Die Struktur xrgb kann anschließend gleich in der Farbtabelle gespeichert werden.

Jetzt wird die Grafik Zeile für Zeile gepackt und in die Datei geschrieben. Das geschieht für jede Zeile in zwei Schritten. In einer ersten Schleife werden die Farbindizes der Pixel per get_pixel beim Hauptprogramm abgefragt und dann bitweise auf die Planes in line_buf verteilt. Die zweite Schleife packt die einzelnen Planes und speichert sie ab.

Dieser Vorgang wird von der Variablen mode gesteuert. Sie enthält den jeweils aktuellen Komprimiermodus. Zu Beginn der „Zeilen-Plane“ ist kein Modus eingestellt. Die „Zeilen-Plane“ soll nun in Bitstrings zerlegt werden, startpnt zeigt auf den Anfang des gerade bearbeiteten Bitstrings, byte enthält die Nummer des gerade bearbeiteten Bytes. Zuerst wird überprüft, ob sich das neue Byte mit dem aktuellen Komprimiermodus verträgt. Ist das nicht der Fall, wird der alte Bitstring in die Datei geschrieben, startpnt auf das neue Byte gesetzt und zum Schluß der neue Modus bestimmt. Außerdem ist zu beachten, daß die einzelnen Bitstrings nicht beliebig lang sein dürfen, daher werden sie auch bei Überschreitung der jeweiligen Maximallänge gespeichert.

Sind alle Zeilen bearbeitet, können die Datei geschlossen und der Puffer freigegeben werden. Eine 0 als Rückgabewert signalisiert, daß kein Fehler aufgetreten ist. Ansonsten wird der Fehlercode zurückgeliefert, den das Betriebssystem gemeldet hat.

Hardcopy wie noch nie...

Als Beispiel für die Benutzung dieser Bibliothek soll ein Screenshot-Utility dienen. Die Idee ist, nicht den ganzen Bildschirm zu speichern, sondern nur den Inhalt eines bestimmten Windows. Ein solches Utility existiert bereits für XWindow. In der Multitasking-Umgebung macht es meist keinen Sinn, den ganzen Bildschirm abzuspeichern, weil alle Programme in Windows laufen und der Rest des Bildschirms mit der festzuhaltenden Situation nichts zu tun hat. Mit der bevorstehenden Einführung von MultiTOS auf dem ATARI bietet sich eine Portierung des Utilities an.

Das Programm GEMWD läßt sich als Programm oder Accessory starten. Unter TOS verschwinden natürlich alle Fenster, wenn man GEMWD als Programm startet - also besser gleich als ACC installieren oder Chameleon benutzen. Es erscheint als Maus-Cursor ein Fadenkreuz, mit dem man das zu sichernde Fenster anklicken kann. Die rechte Maustaste bricht GEMWD ab. Bei einer monochromen Bildschirmauflösung wird man gefragt, ob aus Kompatibilitätsgründen nur das IMG-Format benutzt werden soll. Dann erscheint die Fileselectbox, und man kann den gewünschten File-Namen eingeben. Es werden auch Fenster mit gespeichert, die das ausgewählte überlagern. Klickt man auf den Desktop-Hintergrund, erhält man eine komplette Bildschirm-Hardcopy.

In GEMWD.C (Listing 3) meldet das Hauptprogramm die Applikation zunächst einmal bei AES und VDI an und bestimmt dann die Default-Belegung der Fileselectbox. Wurde GEMWD als Accessory installiert, wird do_windowdump nicht sofort aufgerufen, sondern erst nach Anklicken des Menüeintrages.

Die Prozedur do_window-dump wartet zunächst auf einen Mausklick. Nur wenn die rechte Maustaste nicht gedrückt wurde, wird mit der Hardcopy begonnen. Die Daten des Windows werden abgefragt. Dabei ist zu bemerken, daß legal auch auf solche Fenster zugegriffen werden kann, die dem Programm gar nicht gehören. Damit der Fensterinhalt durch die Fileselectbox nicht zerstört wird, muß er in einem Puffer gesichert werden. Sind File-Typ und -Name abgefragt, wird der Bildschirm wieder restauriert und bitm_to_file aufgerufen.

Die Parameter wind_w und windji hängen vom ausgewählten Window ab, VDI_PIXW, VDI_PIXH, VDI_PLANES und VDI_RESC ergeben sich aus den Daten, die das VDI beim Anmelden der Workstation zurückgegeben hat. Wichtig ist die Realisierung der Abfragefunktionen get_vdi_color und get_vdi_pixel. Sie lassen sich direkt mit Hilfe der entsprechenden VDI-Funktionen vq_color bzw. v_get_pixel implementieren. Trotzdem dürfen hier keine Makros verwendet werden, weil der Compiler die Anfangsadressen der Abfragefunktionen benötigt und Makros natürlich keine solche besitzen.

Damit keine Komplikationen durch parallellaufende Prozesse auftreten, ist do_window-dump durch wind_update-Aufrufe gekapselt. Und der allozierte Speicher wird sofort wieder freigegeben.

Was kann man noch machen?

Mit dem BITMFILE-Modul lassen sich die unterschiedlichsten Anwendungen erstellen. Ein Raytracer beispielsweise könnte zunächst anhand der Daten des abzubildenden Objekts eine Farbtabelle erstellen und als get_color-Parameter eine Zugriffsfunktion auf diese zur Verfügung stellen. Die get_pixel-Funktion würde durch die eigentliche Raytrace-Routine realisiert, die die Farbe des Punktes bestimmt. Ein solches Programm eignet sich hervorragend als Hintergrund-Task für ein Multitasking-System. Während man am Computer arbeitet, wird jede freie Millisekunde für die Berechnung eines hübschen Bildes für den Feierabend genutzt.

Gabriel Schmidt

Literatur:

[1] Jankowski, Rabich, Reschke: „ATARI ST/STE/TT Profibuch“ Sybex-Verlag

[2j J. Zettel: „Farbwahrnehmung und Farbmodelle" ST-Computer 7-8/92

/****************************\
* Bitmap mit Farbtabelle als *
* Graphik-Datei speichern    *
* Autor: Gabriel Schmidt     *
* (c)1992 by MAXON-Computer  *
*  -> Header-Datei           *
\*************************»**/

#ifndef H_TO_FILE 
#define H_TO_FILE

#include <portab.h>

typedef enum { IMG, XIMG } FILE_TYP;

const char *get_file_ext(FILE_TYP typ);

struct RGB 
{
    UWORD r, g, b;
};

int bitmap_to_file(FILE_TYP typ, 
    int ww, int wh,
    unsigned int pwx, unsigned int pwy, 
    unsigned int planes, unsigned int colors, 
    const char *filename, 
    void (*get_color)(unsigned int colind, struct RGB *rgb), 
    void (*get_pixel)(int x, int y, unsigned int *colind));

#endif

/****************************\
* Bitmap mit Farbtabelle als *
* Graphik-Datei speichern    *
* Autor: Gabriel Schmidt     *
* (c)1992 by MAXON-Computer  *
*  -> Programmcode           *
\****************************/

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#include "bitmfile.h"

/*---- (X)IMG-Implementation ------------------*/

ftdefine IMG_COMPRESSED

typedef struct
{
    UWORD img_version;
    UWORD img_headlen;
    UWORD img_nplanes;
    UWORD img_patlen;
    UWORD img_pixw;
    UWORD img_pixh;
    UWORD img_w;
    UWORD img_h;
} IMG_HEADER;

typedef enum
    {NONE, SOLID0, SOLID1, BITSTR} IMG_MODE;

typedef UBYTE IMG_SOLID;

typedef struct
{
    UBYTE bs_first;
    UBYTE bs_cnt;
} IMG_BITSTRING;

typedef enum
    {RGB=0, CMY=1, Pantone=2} XIMG_COLMODEL;

typedef struct 
{
    ULONG img_ximg;
    XIMG_COLMODEL img_colmodel;
} XIMG_HEADER;

typedef struct RGB XIMG_RGB;

int bitmap_to_img(FILE_TYP typ, 
    int ww, int wh,
    unsigned int pixw, unsigned int pixh, 
    unsigned int planes, unsigned int colors, 
    const char *filename, 
    void (*get_color)
        (unsigned int colind, struct RGB *rgb), 
    void (*get_pixel)
        (int x, int y, unsigned int *colind))
{
    int file, error, cnt;
    IMG_HEADER header;
    XIMG_HEADER xheader;
    XIMG_RGB xrgb;
    IMG_MODE mode;
    IMG_SOLID solid;
    IMG_BITSTRING bitstring;
    UBYTE *line_buf, *startpnt;
    unsigned int colind, line_len, line, byte, bit;

    /* (X)IMG-Header ausfüllen */ 
    header.img_version=1;
    header.img_headlen=(UWORD) sizeof(header)/2; 
    if (typ==XIMG)
        header.img_headlen+=(UWORD) (sizeof(xheader)+colors*sizeof(xrgb))/2; 
    header.img_nplanee=planes; 
    header.img_patlen=2; 
    header.img_pixw=pixw; 
    header.img_pixh=pixh; 
    header.img_w=ww; 
    header.img_h=wh; 
    xheader.img_ximg='XIMG'; 
    xheader.img_colmodel=RGB;
    /* Zeilenlänge best.. Puffer allozieren */ 
    line_len=(ww+7)/8;
    line_buf=malloc((size_t) planes*line_len); 
    if (line_buf==NULL)
    {
        return(ENOMEM);
    }
    /* Datei öffnen */
    file=open(filename,O_WRONLY|O_CREAT|O_TRUNC); 
    if (file<0)
    {
        error=errno; 
        free(line_buf); 
        return(error);
    }
    /* Header schreiben */
    if (write(file,&header,sizeof(header))!= sizeof(header) || 
        (typ==XIMG && write(file,&xheader,sizeof(xheader)) != sizeof(xheader)))
    {
        error=errno; 
        close(file); 
        free(line_buf); 
        return(error);
    }
    /* evtl. Farbtabelle speichern */ 
    if (typ==XIMG)
        for (cnt=0; cnt<colors; cnt++)
        {
            get_color(cnt,&xrgb); 
            if (write(file,&xrgb,sizeof(xrgb))! = sizeof(xrgb))
            {
                error=errno; 
                close(file); 
                free(line_buf); 
                return(error);
            }
        }
    bitstring.bs_first=0x80;
    /* Und nun Zeile für Zeile... */ 
    for (line=0; line<wh; line++)
    {
        /* Pixel abfragen, aufspalten und */
        /* als Planes im Puffer ablegen */ 
        for (byte=0; byte<line_len; byte++)
        {
            for (cnt=0; cnt<planes; cnt++)
                line_buf[cnt*line_len+byte]=0x00; 
            for (bit=0; bit<8; bit++)
            {
                if (8*byte+bit<ww)
                    get_pixel(8*byte+bit,line,&colind); 
                for (cnt=0; cnt<planes; cnt++)
                {
                    line_buf[cnt*line_len+byte]<<=1; 
                    line_buf[cnt*line_len+byte]|= colind&0x01; 
                    colind>>=1;
                }
            }
        }
        /* Bitstrings im Puffer komprimieren */
        /* und in die Datei schreiben */
        for (cnt=0; cnt<planes; cnt++)
        {
            /* Bitstringzeiger auf Anfang der Plane */ 
            startpnt=&line_buf[cnt*line_len];
            /* Keine Komprimierung aktiv */ 
            mode=NONE;
            /* Für jedes Byte der Plane... */ 
            for (byte=0; byte<=line_len; byte++)
            {
                /* Uberprüfen, ob Byte noch in den */
                /* aktuellen Bitstring passt, sonst */
                /* diesen speichern */
                switch (mode)
                {
                    case SOLID0:
                        if (line_buf[cnt*line_len+byte]!= 0x00 ||
                            &line_buf[cnt*line_len+byte]-startpnt==0x7F || 
                            byte==line_len)
                        {
                            solid=(unsigned int)(&line_buf[cnt*line_len+byte]-startpnt); 
                            if (write(file,&solid,1)!=1)
                            {
                                error=errno; 
                                close(file); 
                                free(line_buf); 
                                return(error);
                            }
                            startpnt=&line_buf[cnt*line_len+byte] ;
                        }
                        break; 
                    case SOLID1:
                        if (line_buf[cnt*line_len+byte]!= 0xFF ||
                            &line_buf[cnt*line_len+byte]-startpnt==0x7F || 
                            byte==line_len)
                        {
                            solid=0x80 | (unsigned int)(&line_buf[cnt*line_len+byte]-startpnt); 
                            if (write(file,&solid,1)!=1)
                            {
                                error=errno; 
                                close(file); 
                                free(line_buf); 
                                return(error);
                            }
                            startpnt=&line_buf[cnt*line_len+byte];
                        }
                        break; 
                    case BITSTR:
                        if (line_buf[cnt*line_len+byte]== 0x00 ||
                            line_buf[cnt*line_len+byte]== 0xFF ||
                            &line_buf[cnt*line len+byte]-startpnt==0xFF || byte==line_len)
                        {
                            bitstring.bs_cnt=(unsigned int) (&line_buf[cnt*line.len+byte]-startpnt); 
                            if (write(file,&bitstring,
                                sizeof(bitstring))!= sizeof(bitstring) || 
                                write(file,startpnt, bitstring.bs_cnt)!= bitstring.bs_cnt)
                            {
                                error=errno; 
                                close(file); 
                                free(line_buf);
                                return(error);
                            }
                            startpnt=&line_buf[cnt*line_len+byte];
                        }
                }
                /* Welcher Komprimiermodus "passt" */
                /* zum aktuellen Byte? */
                switch (line_buf[cnt*line_len+byte])
                {
                case 0x00:
                    mode=SOLID0; 
                    break; 
                case 0xFF:
                    mode=SOLID1: 
                    break; 
                default:
                    mode=BITSTR;
                }
            }
        }
    }
    /* Datei schließen, Puffer freigeben */ 
    close(file); 
    free(line_buf); 
    return(0);
}

/* ---Filetyp-Dispatcher ------------------------ */

const char *get_file_ext(FILE_TYP typ)
{
    switch (typ)
    {
        case IMG: 
        case XIMG:
            return("IMG"); 
        default:
            return("");
    }
}

int bitmap_to_file(FILE_TYP typ, 
    int ww, int wh,
    unsigned int pwx, unsigned int pwy, 
    unsigned int planes, unsigned int colors, 
    const char *filename,
    void (*get_color)(unsigned int colind, struct RGB *rgb), 
    void (*get_pixel)(int x, int y, unsigned int *colind))
{
    switch (typ)
    {
        case IMG: 
        case XIMG:
            return (bitmap_to_img(typ, ww, wh, pwx, pwy, 
                planes,colors,filename, 
                get_color,get_pixel)); 
        default:
            return (-1);
    }
}
/****************************\
* GEM-WindowDump             *
* Autor: Gabriel Schmidt     *
* (c)1992 by MAXON-Computer  *
* Läuft als PRG und als ACC  * 
\****************************/

#include <aes.h>
#include <vdi.h>
#include <tos.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include «string.h>

#include "bitmfile.h"

/* ---- Konstanten, Typen, Variablen ---------- */

#define ACC_NAME    "  GEM-WindowDump"
#define MONO_ALERT  \
    "[2][Diese Graphik|ist monochrom.|"\
    "Welches Format soll|erzeugt werden?]"\
    "[XIMG|IMG]"
#define FILE_ALERT \
    "[3][Datei konnte nicht|"\
    "geschrieben werden!][Abbruch]"
#define MEMORY ALERT \
    "[3][Der Speicher ist voll!][Abbruch]"

/* Applikationsflag */ 
extern int app;

/* AES- und VDI-Infos */
int work_in[11], work_out[57], ext_work_out[57]; 
int gl_wchar, gl_hchar, gl_wbox, gl_hbox; 
int phys_handle, handle; 
int appl_id;

#define VDI_RESX (work_out[0]+1)
#define VDI_RESY (work_out[1]+1)
#define VDI RESC (work out[13])
#define VDI_PIXW (work_out[3])
#define VDI_PIXH (work put[4])
#define VDI_PLANES (ext_work out[4])
#define VDI_LOOKOP (ext_work_out[5])
#define SCREENSIZE(sx,sy,sp) ((long) ((sx+15)/16)*2*sy*sp)

int wind_x, wind_y, wind_w, wind_h;

/* Pfade für Dateiauswahl */ 
char fsel_path[128], fsel_file[13]; 
char filename[128];

/* Dummy-Variable */ 
static int idum;

/* ----Hauptprogramm---------------------------- */

void get_vdi_color(unsigned int colind, struct RGB *rgb)
{
    vg_color(handle,colind,0, (int *) rgb);
}

void get_vdi_pixel(int x, int y, unsigned int *colind)
{
    v_get_pixel(handle, wind_x+x, wind_y+y, &idum, (int *) colind);
}

void do_windowdump(void)
{
    int error, mx, my, bstate, wind_handle; 
    int koord[8];
    MFDB screen, wind_dmp; 
    char *strpos; 
    int fsel_but;
    FILE_TYP typ;

    wind_update(BEG_UPDATE); 
    graf_mouse(THIN_CROSS,NULL); 
    wind_update(BEG_MCTRL);
    /* Bitte ein Window anklicken! */
    evnt_button(256|1,3,0,&mx,&my, &bstate, &idum);
    evnt_button(1,bstate,0, &idum, &idum, &idum, &idum); 
    wind_update (END_MCTRL);
    /* Rechte Maustaste nicht gedrückt? */ 
    if ((bstatea2)==0)
    {
        graf_mouse(BUSYBEE,NULL);
        /* Welches Window wurde ausgewählt? */ 
        wind_handle=wind_find(mx,my);
        /* Abfrage der Größe:               */
        /* WF_CURRXYWH = gesamtes Window    */
        /* WF_WORKXYWH = nur Windowinhalt   */ 
        wind_get(wind_handle, WF_CURRXYWH, &wind_x, &wind_y, &wind_w, &wind_h); 
        if (wind_x+wind_w>VDI_RESX) 
            wind_w=VDI_RESX-wind_x; 
        if (wind_y+wind_h>VDI_RESY) 
            wind_h=VDI_RESY-wind_y; 
        if (wind_w<=0 || wind h<=0)
        /* Kann auftreten bei WF_WORKXYWH */
        {
            wind_update(END_UPDATE); 
            graf_mouse(ARROW,NULL); 
            return;
        }

        /* Bildschirmausschnitt retten */ 
        screen.fd_addr=NULL; 
        wind_dmp.fd_addr=malloc (SCREENSIZE(wind_w,wind_h, VDI_PLANES)); 
        if (wind_dmp.fd_addr==NULL)
        {
            wind_update(END_UPDATE); 
            graf_mouse(ARROW,NULL); 
            form_alert(1,MEMORY_ALERT); 
            return;
        }
        wind_dmp.fd_w=wind_w; 
        wind_dmp.fd_h=wind_h; 
        wind_dmp.fd_wdwidth=(wind_w+15)/16; 
        wind_dmp.fd_stand=0; 
        wind_dmp.fd_nplanes=VDI_PLANES; 
        koord[0]=wind_x; 
        koord[1]=wind_y; 
        koord[2]=wind_x+wind_w-1; 
        koord[3]=wind_y+wind_h-1; 
        koord[4)=koord[5]=0; 
        koord[6]=wind_w-1; 
        koord[7]=wind_h-1; 
        graf_mouse(M_OFF,NULL); 
        vro_cpyfm(handle,S_ONLY,koord, &screen, &wind_dmp);
        /* Format & Namen vom Benutzer erfragen */
        graf_mouse(M_ON,NULL);
        typ=XIMG;
        if (VDI_PLANES==1 && form_alert(1, MONO_ALERT)==2) 
            typ=IMG;
        if (!fsel_input(fsel_path,fsel_file, &fsel_but) || !fsel_but)
        {
            free(wind_dmp.fd_addr); 
            wind_update(END_UPDATE); 
            graf_mouse(ARROW,NULL); 
            return;
        }
        /* Bildschirmausschnitt restaurieren */
        koord[4]=wind_x; 
        koord[5]=wind_y;
        koord[6]=wind_x+wind_w-1;
        koord[7]=wind_y+wind_h-1;
        koord[0]=koord[1]=0;
        koord[2]=wind_w-1;
        koord[3]=wind_h-1;
        graf_mouse(M_OFF, NULL);
        vro_cpyfm(handle, S_ONLY, koord, &wind_dmp,&screen); 
        graf_mouse(M_ON,NULL); 
        free(wind_dmp.fd_addr);
        /* Filenamen "berechnen" */ 
        strcpy(filename,fsel_path); 
        strpos=strrchr(filename,'\\'); 
        if (strpos==NULL)
            strpos=strrchr(filename,':'); 
        if (strpos==NULL) 
            strpos=filename; 
        else
            strpos++; 
        strcpy(strpos,fsel_file);

        /* Windowdump ausführen */ 
        graf_mouse(M_OFF,NULL);
        error=bitmap_to_file(typ,wind_w,wind_h, 
            VDI_PIXW, VDI_PIXH, VDI_PLANES, VDI_RESC, 
            filename, get_vdi_color, get_vdi_pixel); 
        if (error!=0)
        {
            switch (error)
            {
                case ENOMEM:
                    form_alert(1,MEMORY_ALERT); 
                    break; 
                default:
                    form_alert(1,FILE_ALERT);
            }
        }
        graf_mouse(M_ON,NULL);
    }
    wind_update(END_UPDATE); 
    graf_mouse(ARROW,NULL);
}

int main(void)
{
    int appl_id, menu_id, i, msgbuf[8];

    /* Applikation beim AES anmelden */ 
    appl_id=appl_init(); 
    if (appl_id!=-1)
    {
        /* Workstation beim VDI anmelden */ 
        for (i=0; i < 10; i++) 
            work_in[i]=1; 
        work_in[10]=2;
        phys_handle=graf_handle(&gl_wchar,&gl_hchar,&gl_wbox,&gl_hbox); 
        handle=phys_handle;
        v_opnvwk(work_in, &handle, work_out); 
        if (handle!=0)
        {
            vg_extnd(handle,1,ext_work_out);
            /* Defaultpfade für Fileselectbox best. */ 
            strcpy(fsel_path,"A:"); 
            fsel_path[0]='A'+Dgetdrv(); 
            if (Dgetpath(filename,0)==0) 
                strcat(fsel_path,filename); 
            strcat(fsel_path,"\\*."); 
            strcat(fsel_path,get_file_ext(XIMG)); 
            strcpy(fsel_file,"GEMWD    .");
            strcat(fsel_file,get_file_ext(XIMG));
            /* Läuft GEMWD als... */ 
            if (_app)
                /* ...Programm? */ 
                do_windowdump(); 
            else 
            {
                /* ...Accessory? */
                menu_id=menu_register(appl_id,ACC_NAME); 
                while (1)
                {
                    evnt_mesag(msgbuf); 
                    if (msgbuf[0]==AC_OPEN && msgbuf[4]==menu_id) 
                        do_windowdump();
                }
            }
            /* Workstation schließen */ 
            v_clsvwk(handle);
        }
        /* Applikation abmelden */ 
        appl_exit();
    }
    return(0);
}


Aus: ST-Computer 12 / 1992, Seite 103

Links

Copyright-Bestimmungen: siehe Über diese Seite