IFF - nicht nur für den Amiga, Teil 2

Im ersten Teil gingen wir auf das Konzept und den Dateiaufbau des IFF ein. Heute beschäftigen wir uns mit dem IFF für Bilder, das nahezu alle gängigen Malprogramme auf nahezu allen Heimcomputern unterstützen. Damit steht Ihnen ein reichhaltiges Spektrum an fertigen Bildern zur Verfügung. Um nun dieses Angebot für eigene Anwendungen zu nutzen und eigene Programme IFF-kompatibel zu gestalten, ist es nötig zu wissen, wie das Bild-IFF aufgebaut ist.

Wie wir bereits im Teil 1 erwähnten, besteht eine IFF-Datei aus den sogenannten »Chunks«. Dies sind Daten- oder Informationsblöcke. Eine 4-Byte-Kennung (etwa »FORM« oder »ILBM«) leitet einen Chunk ein. Damit erkennen Sie den Sinn und den Inhalt des Chunks und können ihn ignorieren oder verarbeiten. Es folgen die Chunkdatenlänge und die Daten des Chunks. Der wichtigste Chunk-Typ ist der »FORM«-Chunk. In seinem Datenteil enthält er weitere, ihm untergeordnete Chunks. Um zu erkennen, welche Art von Chunks darin zusammengefaßt sind, bilden die ersten 4 Byte des Datenteils wiederum eine Kennung, die den Typ der untergeordneten Chunks angibt. Bilder haben die Kennung »ILBM«, die eine Abkürzung für »InterLeaved Bit-Map« (etwa zeilenorientiertes Bild) ist. Eine IFF-Bilddatei beginnt demnach mit der 4-Byte-ID »FORM«, der Längenangabe der folgenden Daten und der Kennung »ILBM«. Mit der im ersten Teil eingeführten Schreibweise läßt sich eine Bilddatei folgendermaßen beschreiben:

ILBM := ‚FORM‘ # {‚ILBM‘ BMHD CMAP [GRAB] [DEST] [SPRT] [CAMG] [CRNG]* [CCRT]* BODY}

In der folgenden Übersicht finden Sie alle gängigen Bild-Chunk-Typen. Diese Chunks folgen im Datenteil des »FORM«-Chunks nach der Kennung »ILBM«. Fast alle Chunk-Typen enthalten nur Zusatzinformationen (in der obigen Schreibweise in [] gesetzt), wie etwa Animationsanweisungen und die Bildposition. Es gibt nur drei Chunks, die in jeder Bilddatei vorkommen müssen: der Chunk »BMHD«, der die Bildausmaße beschreibt, »CMAP«, der die Farbeinstellungen enthält, und »BODY« mit den Bilddaten.

BMHD:
Dieser Chunk sollte am Anfang stehen, da er die Größe und Form der Bilddaten beschreibt und außerdem angibt, wie das Objekt zu interpretieren ist. Die C-Struktur ist

typedef UBYTE Masking;
#define mskNone 0	/* Werte des masking-Flags */
#define mskHasMask 1
#define mskHasTransparentColor 2
#define mskLasso 3
typedef UBYTE Compression;
#define cmpNone 0	/* Werte des compression-Flags */
#define cmpByteRunl 1
typedef struct
{
	UWORD w, h;	/* Breite, Höhe */
	WORD x,y;	/* Bildschirmposition */
	UBYTE nPlanes;	/* Anzahl der Bitplanes - > Tiefe */
	Masking masking;	/* Maskenflag */
	Compression compression;	/* Compressionsflag */
	UBYTE padl;	/* Füllbyte */
	UWORD transparentColor;	/* Nummer der transp. Farbe */
	UBYTE xAspect, yAspect;	/* Seitenverhältnis */
	WORD pageWidth, pageHeight;	/* Breite und Höhe des original Bildschirms */
} BitMapHeader;

»w« und »h« bestimmen die Ausmaße des Grafikobjekts. Ein komplettes Farbbild des ST hat eine Breite von »w = 320« und eine Höhe von »h=200«. Das Amiga-Standardmalprogramm »Deluxe Paint« sichert eine Grafikseite von 1024 x 1024 tatsächlich als ein solch großes Grafikobjekt, obwohl auf dem Bildschirm nur 640 x 512 Pixel erscheinen. Sie müssen also damit rechnen, beim Einlesen von überlangen und überbreiten Bildern den rechten, nicht sichtbaren Bereich der Grafik zu überlesen bzw. zu clippen. Mit der Bildschirmposition »x« und »y« merken Sie sich z. B. eine Pinsel- oder Spriteposition und stellen somit sicher, daß beim Einlesen diese Position wieder eingenommen wird. Bei Grafikseiten ist »x« und »y« immer 0, 0. Die Anzahl der Farben errechnen Sie aus der Anzahl der Bitplanes (»nPlanes«). Dazu verwenden Sie die Formel »AnzCol = 2^nPlanes«.

Ein Monochrombild hat eine Bitplane (2^1 = 2 Farben), ein ST-Low-Resolution-Bild vier Bitplanes (2^4 = 16 Farben). Mit dem Maskenflag können Sie eine zusätzliche Maskenbitplane verwenden. Das Kompressionsflag gibt an, ob die Bilddaten komprimiert sind oder nicht. Hierzu gibt es den Standard-Komprimier- und Dekomprimier-Algorithmus, den das Listing »IFFLOAD.H« verwendet. Es ist ein einfacher Algorithmus, der zeilenorientiert arbeitet und nur wiederholte Byte im Format [-Anzahl-1].b[Byte].b zusammenfaßt. Mit den Einstellungen »xAspect« und »yAspect« geben Sie die Seitenverhältnisse des Bildes an. Zuletzt bestimmen »pageWidth« und »pageHeight« die Breite und Höhe des Bildschirms. Speichern Sie in Deluxe Paint eine Grafikseite mit den Maßen 1024 x 1024 in der Auflösung 640 x 512, so setzt das Programm »pageWidth« und »page Height« auf die Werte 640 und 512. Damit finden Sie schnell heraus, in welcher Auflösung ein Bild vorliegt.

CMAP:
Dieser Chunk enthält die verwendeten Farben. Die Anzahl der Farben ist variabel, enthalten sind fast immer 2^nPlanes. Eine Farbe besteht aus 3 Byte. Im ersten steht der Rot-, im zweiten der Grün- und im dritten der Blauanteil. Die Anteile reichen von 0 (Anteil nicht vorhanden) bis 255 (intensiv). Da der ST pro RGB-Anteil nur acht Stufen kennt (3 statt 8 Bit), ignoriert man von den 8 Bit einfach die unteren 5 (ST-Rotanteil = Rotanteil»5). In C läßt sich die Farbwertstruktur beschreiben als

typdef struct
{
	UBYTE red,green,blue;
} ColorRegister;

Bitte vergessen Sie nie, durch ein Füllbyte eine gerade Länge zu erzwingen, falls die Chunkdaten eine ungerade Länge haben (z. B. fünf Farben - > 5 x 3 = 15 Byte).

CRNG:
Dieser Chunk bestimmt einen Farbwechsel. Speichern Sie mehrere Farbanimationen, so müssen Sie auch mehrere CRNG-Chunks hintereinander schreiben.

typedef struct
{
	WORD padl;	/* für Erweiterungen */
	WORD rate;	/* 16384 = 60 Wechsel/sec, 8192 = 30/sec */
	WORD active;	/* TRUE oder FALSE */
	UBYTE low, high;	/* die untere und obere zu beeinflussende Farbnummer */
} CRange;

Mit »rate« geben Sie die Geschwindigkeit der Farbenwechsel an. Diese errechnet sich nach folgendem Schema: 16384 bedeutet 60 Wechsel pro Sekunde, 8192 die Hälfte (30), 4096 nur noch 15 usw. Die Formel lautet: x Wechsel/sec = rate/273. Mit dem Flag »active« stellen Sie fest, ob der Farbwechsel momentan aktiv ist. Abschließend bestimmen Sie mit »low« und »high« den Bereich der Farben, in dem gespult wird.

GRAB:
Mit diesem Chunk bestimmen Sie die relative Position des Cursors, den die Grafik enthält. Geben Sie z. B. (100,10) an, so ist die linke obere Ecke des Bildes auf dem Bildschirm (100,10). Dies dient hauptsächlich dazu, die Bildschirmpositionen von Pinsel und Sprites festzuhalten.

typedef struct
{
	WORD x, y;
} Point2D;

DEST:
Mit den Informationen aus diesem Chunk bestimmen Sie, wie die Bild-Bitplanes auf die Bildschirm-Bitplanes aufgeteilt werden, und wie unbenutzte Bildschirm-Bitplanes zu bearbeiten sind. Haben Sie z. B. nur eine Bitplane im Bild definiert, der Bildschirm hat jedoch vier Bitplanes, so legen Sie in diesem Chunk fest, in welche der vier Bitplanes die Bild-Bitplane kopiert wird und ob die restlichen drei gelöscht oder gesetzt werden.

typedef struct
{
	UBYTE depth;	/* Bitplanetiefe, für die der Chunk definiert ist */
	UBYTE padl;	/* Füllbyte */
	UWORD planePick;	/* Welche Bild-Bitplane in welche Bildschirm-Bitplane */
	UWORD planeOnOff;	/* Undef. Bildschirmbpl. löschen o. setzen? */
	UWORD planeMask;	/* Welche Bildschirmbpl. dürfen beschrieben werden */
} Destmerge;

Die Strukturkomponente »planePick« betrachten Sie nach ihren gesetzten Bits. Jedes gesetzte Bit besagt soviel wie »Packe die nächste vorhandene Bitplane aus der Datei in die Bildschirm-Bitplane mit der Nummer des gesetzten Bits«. Ist ein Bit nicht gesetzt, so betrachten Sie das gleiche Bit unter »planeOnOff«. Ist dieses Bit gesetzt, füllen Sie die gesamte Bitplane auf, ansonsten löschen Sie sie. Mit »planeMask« unterbinden Sie das Schreiben in eine Bildschirm-Bitplane. Ein gelöschtes Bit verhindert das Schreiben, ein gesetztes erlaubt es. Im Normalfall sollten »planePick« und »planeMask« den Wert (2^nPlanes)-1 haben. So sind alle Bits für jede Bitplane definiert, und alle vorhandenen Bitplanes werden benutzt.
Bei Bitplane-Manipulationen auf dem ST ist jedoch zu beachten, daß der ST die Bitplanes nach folgendem Schema im Speicher verzahnt:

640 x 400:	00000000 00000000
	00000000 00000000 ...
640 x 200:	00000000 00000000
	11111111 11111111
	00000000 00000000 ...
320 x 200:	00000000 00000000
	11111111 11111111
	22222222 22222222
	33333333 33333333
	00000000 00000000...

In diesem Schema stellt die 0 ein Bit aus der Bitplane # 0, die 1 ein Bit aus Bitplane # 1 usw. dar. Der ST faßt also immer 16 Pixel zu einem Speicherwort zusammen. Im Farbmodus kommt ein Speicherwort der Bitplane # 0, dann der Bitplane # 1 usw. Wie man die zeilenorientiert abgelegten Bitplanes des ILBM-Formats in die verzahnten des ST kopiert, finden Sie im Listing IFFSHOW.C beschrieben.

SPRT:
In einer ILBM-Datei lassen sich neben Bildern auch Sprites ablegen. Ob die Bilddaten zu einem Sprite gehören, erkennen Sie daran, daß der Chunk »SPRT« vorkommt. Dieser enthält die Variable »SpritePrecedence«, welche die Priorität des Sprites auf dem Bildschirm angibt. Eine Null besagt, das Sprite hat höchste Priorität.

typedef UWORD Sprite Precedence;

BODY:
Der wichtigste Teil des ILBM-Bildformats ist natürlich der, der die Bilddaten enthält. Hier liegen alle Bitplanes in der durch den »BMHD«-Chunk festgelegten Auflösung vor. Ein Bild mit 200 Zeilen, 4 Bitplanes und einer wahlfreien Maskenbitplane sieht folgendermaßen aus:

Zeile   1 Bitplane 0,
Zeile   1 Bitplane 1,
Zeile   1 Bitplane 2,
Zeile   1 Bitplane 3[,
Zeile   1 Maske]
Zeile 200 Bitplane 0,
Zeile 200 Bitplane 1,
Zeile 200 Bitplane 2,
Zeile 200 Bitplane 3[,
Zeile 200 Maske]

Sind die Bitplanes komprimiert, bleibt die zeilenorientierte Struktur trotzdem bestehen, da der Komprimieralgorithmus nur eine Bitplanezeile bearbeitet.

Programmbeschreibung

Das Programm »IFFSHOW« wurde mit dem Mark Williams C-Compiler entwickelt, sollte sich jedoch mit jedem C-Compiler übersetzen lassen. Compilieren Sie zuerst das Listing »IFFSHOW.C«, dann »IFFLOAD.C«. Beide greifen auf die Headerdatei »IFF.H« zu, welche die wichtigsten IFF-Strukturen enthält. Linken Sie »IFFSHOW.O« und »IFFLOAD.O«.
Unter Mark Williams C sieht die Kommandozeile folgendermaßen aus: »cc IFFSHOW.C IFFLOAD.C«. Der Compiler lädt den Linker automatisch nach. »IFFSHOW« rufen Sie aus einem Kommandointerpreter oder als »IFFSHOW.TTP« mit »IFFSHOW [IFF-Datei1] [IFF-Datei2] [...]« auf. Das Programm zeigt Ihnen die Bilder in der vorgegebenen Reihenfolge an. Drücken Sie die Leertaste, um zum nächsten Bild zu kommen. Das Programm gliedert sich intern in zwei Teile, das Verwaltungs- und ST-spezielle Aufrufunterprogramm »IFFSHOW.C« und den rechnerunabhängigen standardmäßigen IFF-Bildlader »IFFLOAD.C«. Zunächst stellt »IFFSHOW.C« fest, wie viele Dateien zu bearbeiten sind. Eine Schleife lädt alle Bilder nacheinander und zeigt sie an. Die Laderoutine in »IFFLOAD.C« testet erst, ob die Datei ein IFFLOAD-kompatibles ILBM-Format enthält. Wir nehmen an, daß ILBM-Bilder im Normalfall in einem FORM-Chunk des Typs ILBM vorliegen, der die Definitions-Chunks BMHD und CMAP, sowie die Bilddaten in BODY enthält. Alle anderen Chunks sind für die Laderoutine unwichtig. Wir erwarten also eine ILBM-Datei, wie »Deluxe Paint« sie z. B. speichert:

FORM len ILBM
BMHD len BitMapHead
CMAP len ColorRegisters
BODY len Data

Als Erweiterung der Laderoutine könnten Sie noch die CRNG-Chunks, die das Color-Cycling beschreiben, beachten und verarbeiten. Hat sich die Laderoutine bis zum BODY-Chunk durchgearbeitet, kopiert sie die Bitplanes unter Beachtung des Kompressionsflags in den Speicher. Die Laderoutine gibt einen Zeiger auf die im »IFF.H« definierte ILBM-info-Struktur zurück. Darin enthalten sind unter anderem die Zeiger auf die Bitplanes im Speicher, die Objektbeschreibung (BMHD) und die Farbwerte. Diese Struktur erhält die Anzeigeroutine, die entscheidet, welche Auflösung das Programm verwendet. Nun kopiert das Programm die Bitplanes in die Bildschirm-Bitplanes. Dabei erkennt es zu große und zu kleine Bilder und verarbeitet sie richtig (Clipping). In der Routine »show_16col« sehen Sie zudem, wie man die verzahnten Bitplanes des ST bearbeitet. Auf unserer Leserservice-Diskette finden Sie neben dem Source-Code und dem lauffähigen Programm auch eine Reihe ansprechender IFF-Bilder. (uh) Literaturverweis: Amiga ROM Kernal Reference Manual: Exec, Addison Wesley, ISBN 0-201-11099-7

Listing 1: IFF.H, die Kopfdatei für Bildlader

/* *********************************************************************************
   IFF. H
   (c) Jim Kent
   Auf den Atari ST portiert, erweitert und eingedeutscht für das ST-Magazin:
   Martin Backschat
   Laut Jim Kent sind diese Routinen Public Domain und können für eigene Programme
   verwendet werden.
************************************************************************************* 	*/

typedef char BYTE;	/* signed 8 bit integer */
typedef unsigned char UBYTE;	/* unsigned 8 bit int */
typedef short WORD;	/* signed 16 bit int */
typedef unsigned short UWORD;	/* unsigned 16 bit int */
typedef long LONG;	/* signed 32 bit int */
typedef unsigned long ULONG;	/* unsigned 32 bit int */

#define REG register
#define TRUE 1
#define FALSE 0

#include <osbind.h>	/* GEMDOS-Aufrufe */
#include <stdio.h>	/* Strukturen, wie FILE holen */

/* Erleichterung, die 4-Byte-ID zu beschreiben */
#define MAKE_ID (a, b, c, d) (((long)(a)<<24)+((long)(b)<<16)+((long)(c)<<8)+(long)(d))

#define MAXCOL 16	/* ST kann nur 16 Farben anzeigen */

/* Die vow Programm beachteten Chunk-Typen */
#define FORM MAKE_ID(‚F‘, ‚O‘, ‚R‘, ‚M‘)
#define ILBM MAKE_ID(‚U‘, ‚L‘, ‚B‘, ‚M‘)
#define BMHD MAKE_ID(‚B‘, ‚M‘, ‚H‘, ‚D‘)
#define CMAP MAKE_ID(‚C‘, ‚M‘, ‚A‘, ‚P‘)
#define BODY MAKE_ID(‚B‘, ‚O‘, ‚D‘, ‚Y‘)/* Chunk-Typen, die vom Programm ignoriert werden */
#define GRAB MAKE_ID(‚G‘, ‚R‘, ‚A‘, ‚B‘)
#define DEST MAKE_ID(‚D‘, ‚E‘, ‚S‘, ‚T‘)
#define SPRT MAKE_ID(‚S‘, ‚P‘, ‚R‘, ‚T‘)
#define CAMG MAKE_ID(‚C‘, ‚A‘, ‚M‘, ‚G,)
#define CRNG MAKE_ID(‚C‘, ‚R‘, ‚N‘, ‚G‘)
#define CCRT MAKE_ID(‚C‘, ‚C‘, ‚R‘, ‚T‘)

/* line_bytes = die Word-Anzahl * 2 (für Bytes), die eine Rasterzeile benötigt */
#define line_bytes(width) (((width+15)>>4)<<1)

/* psizd - Bitplanegröße in bytes, wobei Breite und Höhe gegeben sind */
#define psize(width, height) (line_bytes (width) *height)

/* eine bestimmte Anzahl Bytes überlesen */
#define bit_bucket(file, length) fseek(file, (long)length, 1)

/* Es folgen die eigentlichen Chunk-Strukturen */
union bytes4
{						/* Struktur für Chunk- */
	char b4_name[4];	/* Name und Chunk-Typ  */
	long b4_type;
};

struct iff_chunk
{						/* Struktur for */
	union bytes4 iff_type;	/* Chunk-Header */
	ULONG iff_length;	/* und Daten */
};

/* Struktur für ein FORM-Chunk (enhält Unterchunks!) */
struct form_chunk
{
	union bytes4 fc_type;	/* == FORM */
	ULONG fc_length;
	union bytes4 fc_subtype;
};

/ Ls folgen die wichtigsten Merkmal- und Datentypenstrukturen */
/* BMHD - beschreibt das in BODY enthaltene Bitimage */
typedef struct
{
	UWORD w, h;		/* Breite und Höhe des Bitimage */
	UWORD X, y;		/* Position am Bildschirm */
	UBYTE nPlanes;	/* Anzahl der Bitplanes
	UBYTE masking;	/* Masking-Flag (0:kein, */
						/* 1:Mask, 2:Transparente Farbe. 3:Lasso) */
	UBYTE compression;	/* Compressionsflag (0:keine, 1:vorhanden) */
	UBYTE padl;
	UWORD transparentColor;	/* Farbnummer der transparenten Farbe */
	UBYTE xAspect, yAspect;	/* Seitenverhältnis */
	WORD pageWidth, pageHeight;	/* Seitenhöhe und - breite */
} BitMapHeader;

typedef struct		/* CMAP-Struktur */
{
	UBYTE red,green,blue;	/* Farbanteile (0,0,0)-(255,255,255) */
} ColorRegister;typedef struct		/* CRNG-Struktur */
{
	WORD padl;		/* Erweiterung */
	WORD rate;		/* 16384 für 60 Wechsel/Sek */
	WORD active;
	UBYTE low, high;	/* Farbnummergrenzen */
} CRange;

typedef struct		/* CCRT */
{
	WORD dircection;	/* -1 rückwarts, 0, 1 vorwärts */
	UBYTE start, end;
	LONG seconds;
	LONG microseconds;
	WORD pad;
} CycleInfo;

112: typedef struct	/* GRAB */
{
	WORD x, y;
} Point2D;

typedef struct		/* DEST */
{
	UBYTE depth;
	UBYTE padl;
	UWORD planePick;
	UWORD planeOnOff;
	UWORD planeMask;
} DestMerge;

typedef struct		/* CAMG */
{
	ULONG ViewModes;	/* Amiga Viewmode Variables */
} CamgChunk;

/* ILBM info: Struktur, die readiff zurückgibt */
typedef struct		/* Besser, beim Einlesen Amiga simulieren! */
{
	UWORD BytesPerRow;	/* wir können 80 Bytes pro Zeile verwenden! */
	UWORD Rows;		/* Anzahl Zeilen */
	UBYTE Flags;
	UBYTE Depth;		/* Anzahl Bitplanes */
	UWORD pad;
	UBYTE *Planes[8];	/* Bitplanezeiger */
} AmigaBitMap;

struct ILBM_info
{
	BitMapHeader header;	/* Kopie der BMHD-Chunk-Definitionen */
	UBYTE emap[MAXCOL*31;	/* Farben (RGB = 3 Bytes je Farbe)
	AmigaBitMap bitmap;	/* Amiga-spezielle Struktur: wird emuliert */
};

Listing 2: IFFSHOW.C zeigt IFF-Bilder an

/* ************************************************************************************
   IFFSHOW.C
   (c) Martin Backschat
   Dieses Listing verwendet die im File IFFLOAD.C dekarierten Routinen und liest
   über sie ein Bild ein. Dieses wird entsprechend seiner Auflösung weiterverarbeitet.
*/

#include „iff.h“

extern struct ILBM_info *read_iff();

UWORD *farbreg = Oxff82401;	/* Hier hat der ST seine Farbpalette! */
UWORD *screen;

main(argc, argv)
WORD argc;
BYTE **argv;
{
	LONG spvi;
	UWORD i, rez, palette[16];

	rez = (int) Getrez();	/* Die ursprüngliche Auflösung merken */
	spvi = Super(0L);
	for (i = 0; i < 16; i++)	/* Farbpalette retten */
		palette[i] = farbreg[i];
	Super(spvi);
	for (i = 2; i <= argc; i++)
	{
		show_iff(argv[i-1]);	/* Bild laden & zeigen */
		while ((int)Crawcin() != ‚ ‚);	/* Auf taste warten */
	}
	Setpallette (palette);
	Setscreen(-1L, -1L, rez);
	exit();
}

show_iff(name)
char *name;
{
	REG struct ILBM_info *info;
	REG UWORD depth;
	if (!(info = read_iff(name,0)))	/* Das Bild in den Speicher laden */
		return;
	screen = (UWORD *) Logbase();	/* Adresse des Bildschirmspeichers des ST */

	depth m info->bitmap.Depth;	/* Anzahl Bitplanes (Farben: 1<<depth) */
	if ( depth == 1 )
		showmono(info);	/* Monochrombild anzeigen */
	else
	if ( depth >= 4 )
		show_16col(info);		/* 16-Farben-Bild anzeigen */
	else
		printf(„Ich brauche 2 oder >=16 Farben!“);
}show_mono(info)
struct ILBM_info *info;
{
	REG WORD offs;
	REG UWORD i, j, *scr;
	UWORD *planeptr0;
	UWORD rows;

	planeptr0 = (UWORD* ) info->bitmap.Planes[0];
	/* Zeiger in Bitplane */
	scr = screen;
	rows = info->bitmap.Rows;	/* Anzahl der Objektzeilen.
							   Um kleinere bzw. größere Bildobjekte
							   darzustellen, ist es nötig, herauszufinden,
							   um wieviel Wörter das Objekt breiter ist als
							   eine ST-Bildschirmzeile in Lo-Rez. So kommt
							   es zu der Formel offs = [Bytes pro Bitplane
							   einer Zeile]-[Bytes pro Bitplane einer
							   ST-Zeile]/2 */

	offs = ((int)info->bitmap.BytesPerRow-80)/2;
	/* 12 wegen UWORD-Addressierung */
	for (i = 0; (i < rows) && (i < 400); i++)
		if (offs < 0)	/* Objekt nicht so breit wie */
		{					/* Bildschirm (z.B. Sprites) */
			for (j = -offs; i < 40; j++)	/* 40 Wörter pro Bitplane einer Zeile */
				*scr++ = *planeptr0++;	/* Bitplane 1 kopieren */
			scr -= offs;	/* An das Ende des Bildschirms positionieren */
		}
		else
		{
			for (j = 0; j < 40; j++)	/* 40 Wörter pro Bitplane einer Zeile */
				*scr++ = *planeptr0++;	/* Bitplane 1 kopieren */
			planeptr0 += offs;	/* Ans Ende der IFF-Bildzeile positionieren */
		}
}

show_16col(info)
struct ILBM_info *info;
{
	REG WORD offs;
	REG UWORD i,j,*scr;
	UWORD *planeptr0, *planeptr1, *planeptr2, *planeptr3;
	UWORD red, green, blue, rows;
	LONG spvi;

	spvi = Super(0L);	/* Wenn man die Farbregister */
							/* direkt beschreibt, ist Super nötig!*/
	for (i = 0; i < 16; i++)
	{
		red   = info->cmap[3*i]>>5;	/* die obersten 3 Bits nehmen */
		green = info->cmap[3*i+1]>>5;
		blue  = info->cmap[3*i+2]>>5;
		farbreg[i] = (red<<8)|(green<<4)|(blue);
	}
	Super (spvi);
	Setscreen(-1L,-1L, 0);	/* Auf niedrige Auflösung schalten */
	planeptr0 = (UWORD* ) info->bitmap.Planes[0];
	/*  Zeiger in Bitplane */
	planeptr1 = (UWORD* info->bitmap.Planes[1];
	planeptr2 = (UWORD* info->bitmap.Planes[2];
	planeptr3 = (UWORD* info->bitmap.Planes[3];
	scr = screen;
	rows = info->bitmap.Rows;	/* Anzahl der Objektzeilen */
							/* um kleinere bzw. größere Bildobjekte
							   darzustellen, ist es nötig, herauszufinden,
							   um wieviel Wörter das Objekt breiter ist als
							   eine ST-Bildschirmzeile in Lo-Rez. So kommt
							   es zu der Formel offs = [Bytes pro Bitplane
							   einer Zeile]-[Bytes pro Bitplane einer ST-
							   Zeile]/2 */
	offs = ((int)info->bitmap.BytesPerRow-40)/2;
	/*  12 wegen UWORD-Addressierung */
	for (1 = 0; (i < rows) && (i < 200); i++)
		if (offs < 0)
		{		/* Objekt nicht so breit wie Bildschirm (z.B. Sprites) */
			for (j = -offs; j < 20; j++)	/* 20 Wörter pro Bitplane einer Zeile */
			{
				*scr++ = *planeptr0++;	/* Bitplane 1 kopieren */
				*scr++ = *planeptr1++;	/* 2, 3, 4 */
				*scr++ = *planeptr2++;
				*scr++ = *planeptr3++;
			}
			scr -= offs;	/* An das Ende des Bildschirms positionieren */
			scr -= offs;
			scr -= offs;
			scr -= offs;
		}
		else
		{
			for (j = 0; j < 20; j++)	/* 20 Wörter pro Bitplane einer Zeile */
			{
				*scr++ = *planeptr0++;	/* Bitplane 1 kopieren */
				*scr++ = *planeptr1++;	/* 2, 3, 4 */
				*scr++ = *planeptr2++;
				*scr++ = *planeptr3++;
			}
			planeptr0 += offs;	/* Ans Ende der IFF-Bildzeile positionieren */
			planeptr1 += offs;
			planeptr2 += offs;
			planeptr3 += offs;
		}
}

Listing 3: IFFLOAD.C lädt IFF-Bilder

/* *********************************************************************************
   IFFLOAD.C
   (c) Jim Kent
   weiterentwickelt und umgesetzt auf den ST von Martin Backschat für das ST-Magazin
   Mit der Routine read_iff( (char *)name, int just_colors ) können Sie ein Bild
   einlesen bzw. mit just_colors = TRUE nur dessen Farben. Zurück bekommen Sie eine
   ILBM-info-Struktur, die im IFF.H definiert ist.
************************************************************************************* */

#include „iff.h“

static struct ILBM_info *read-ilbm();
static struct ILBM_info *read body();

static struct ILBM_info root_info;

struct ILBM_info *read_iff(name, just_colors)
BYTE *name;
WORD just_colors;
{
	struct ILBM_info *info = &root_info;
	struct form chunk chunk;
	FILE *file;
	REG UWORD i;

	/* Wurden schon mal Bitplanes im Speicher reserviert, dann freigeben! */
	if (info->bitmap.Planes[0] != NULL)
		for (i = 0: i < info->bitmap.Depth; i++)
		{
			free(info->bitmap.Planes[i]);
			info->bitmap.Planes[i] = NULL;
		}

	if (!(file = fopen(name, „rb“)))	/* Öffne zum binären lesen! */
	{
		printf(„Kann Datei %s nicht öffnen\n“, name);
		return(NULL);
	}

	if ( fread(&chunk, (UWORD) sizeof(struct formchunk), 1, file) != 1)
	{
		printf(„Komischer Dateiinhalt!\n“);
		fclose(file);
		return(NULL);
	}

	if (chunk.fc_type.b4_type != FORM)
	{
		printf(„Datei %s enthält keine FORM-ID\n“, name);
		fclose (file);
		return(NULL);
	}

	if (chunk.fc_subtype.b4-type != ILBM)
	{
		printf(„FORM-Typ in der Datei %s ist nicht ILBM\n“, name);
		fclose(file);
		return(NULL);
	}	info = read_ilbm(file, info, chunk.fc_length - (UWORD)sizeof(chunk), justcolors);
	fclose(file);
	return(info);
}

/* Liest den eigentlichen ILBM-Chunk ein */
static struct ILBM_info *read_ilbm(file, info, length, justcolors)
REG FILE *file;
REG struct ILBM_info *info;
ULONG length;
WORD justcolors;
{
	struct iff chunk chunk;
	ULONG readin = 0;
	WORD got_header = FALSE;	/* BMHD vor CMAP */
	WORD got_cmap = FALSE;	/* CMAP vor BODY */
	LONG chunktype;		/* enthält 4-Byte IDs */

	while (read_in < leng%)
	{
		/* Gesamten Chunk durchlesen */
		if (fread(&chunk, (LIWORD) sizeof(chunk), 1, file) != 1)
			return(NULL);
		chunktype = chunk.iff_type.b4_type;
		if (chunktype == BMHD)
		{
			/* BMHD - Bitmapheader mit Definitionen? */
			if (fread(&info->header, (UWORD) sizeof(info->header), 1, file) != 1)
				return(NULL);
			got_header TRUE;
		} /* if BMHD */
		else if (chunktype == CMAP)
		{
			/* CMAP - Colormap mit Farbtabelle? */
			if (!got_header)
			{
				printf(„CMAP vor BMHD nicht erlaubt!\n“);
				return(NULL);
			}
			if (chunk.iff_length <= 3*MAXCOL)
			{
				/* Zu viele Farben definiert? */
				if (fread(info->cmap, (UWORD) chunk.iff_length, 1, file) != 1)
					return(NULL);
			}
			else
			{	/* wenn zuviele, dann die ersten lesen, die restl. ignorieren */
				if (fread(info->cmap, (UWORD)3*MAXCOL, 1, file) != 1)
					return(NULL);
				bit_bucket (file, chunk.iff_length - (UWORD)(3*MAXCOL));
			}
			got_cmap = TRUE;
			if (justcolors)	/* Braucht man nur d. Farben, so abbrechen! */
				return(info);	/* ILBM-info-Struktur mit Farben zurück */
		}					/* if CMAP */
		else if (chunktype == BODY)
		{
			/* Der eigentliche Bilddatenteil? */
			if (!got_cmap)
				return(NULL);
			return( read_body(file, info, chunk.iff_length));
		}					/*if BODY */		else				/* keiner der obigen Chunks,
							   so den unbekannten einfach überlesen! */
			bit_bucket(file, chunk.iff_length);

		read_in += chunk.iff_length - (UWORD)sizeof(chunk);
	}
	return(NULL);
}

/* Liest den eigentlichen Bilddatenteil, (BODY) ein */
static struct ILBM_info *read_body(file, info, length)
FILE *file;
REG struct ILBM_info *info;
ULONG length;
{
	struct ILBM_header *header;
	UWORD i, j, plane_offset;
	UWORD rlength;

	/* unbekannte Kompressionsart? */
	if (info->header.compression != 0 && info->header.compression != 1)
		return(NULL);
	/* Die Amiga-BitMap-Werte setzen. */
	info->bitmap.BytesPerRow = line_bytes(info->header.w); /* Breite */
	info->bitmap.Rows = info->header.h;	/* Höhe des Objektes */
	info->bitmap.Depth z info->header.nPlanes;
	info->bitmap.Flags = info->bitmap.pad = 0;
	/* Anzahl Bytes für eine Plane */
	rlength = info->bitmap.Rows * info->bitmap.BytesPerRow;
	for (i = 0; 1 < info->header.nPlanes; i++)
		if ((info->bitmap.Planes[i] = malloc(rlength) ) == NULL)
			return(NULL);

	plane_offset = 0;

	for ( i=0; i < info->bitmap.Rows; i++)
	{
		/* Zeilenweise vorgehen */
		if (!info->header.compression)
		{
			/* keine Kompression? */
			for (j = 0; j < info->bitmap.Depth; j++)
				/* Zeile einlesen */
				if ( fread(info->bitmap.Planes[j] + plane_offset, info->bitmap.BytesPerRow, 1, file) != 1)
					return(NULL);
		}
		else
		{	/* ansonsten Dekompacken */
			REG BYTE *dest, value;
			REG UWORD so_far, count;

			for (i < info->bitmap.Depth; j++)
			{
				so_far = info->bitwap.BytesPerRow;
				dest = (char *)info->bitmap.Planes[j] + planeoffset;
				while (so_far > 0)	/* Zeile einlesen */
				{
					if ((value = getc(file)) == 128);
						/* ist value >= 0, dann ist es Länge-1 der folgenden Daten */
					else if (value >= 0)
					{
						count = ((int)value) + 1;
						so_far -= count;
						if (fread (dest, count, 1, file) != 1)
							return(NULL);
						dest += count;
					}
					/* ist value < 0, dann folgt weiteres Byte,
					   das value+1 mal geschrieben wird */
					else
					{
						count = ((int)-value) + 1;
						so_far -= count;
						value = getc(file);
						while (--count >= 0)
							*dest++ = value;
					}
				}			/* Zeileneinlese-Schleife */
				if (sofar != 0)	/* Ist Zeile zu lang, dann Fehler! */
					return(NULL);
			}  /* Schleife zum Einlesen der einzelnen BitMaps */
		} /* end of else */
		Planeoffset += info->bitmap.BytesPerRow;
	}
	return(info);
}

Martin Backschat
Aus: ST-Magazin 11 / 1989, Seite 71

Links

Copyright-Bestimmungen: siehe Über diese Seite