← ST-Computer 10 / 1988

ST-Ecke: WEITERBILDUNG - DEGAS komprimiert- und IFF-Format

Grundlagen

Nachdem wir uns in der letzten Ausgabe mit dem allgemeinen Aufbau eines Bildes und den einfachen (ungepackten) Bildformaten auseinandergesetzt haben, wollen wir weiter dieses Thema erforschen und die Geheimnisse der Packformate ergrĂŒnden. ZunĂ€chst betrachten wir allgemein, wie es ĂŒberhaupt möglich ist, Daten auf ein Minimum zu reduzieren, ohne daß tatsĂ€chlich Information verloren geht. Im weiteren Verlauf werden wir uns mit dem ersten Bildformat beschĂ€ftigen, das die Daten gepackt also komprimiert abspeichert- dem IFF-Format. Das Interessante des IFF-Formats ist, daß es nicht nur fĂŒr Bilddaten, sondern auch fĂŒr diverse andere Daten wie zum Beispiel Text verwendet werden kann.

ZunĂ€chst möchte ich mich dafĂŒr entschuldigen, daß in der letzten Ausgabe ein paar Zeilen im C-Listing gefehlt haben. Wahrscheinlich hat man beim Setzen das Wort Packen zu wörtlich genommen. Wir liefern die Zeilen in Listing 3 hiermit nach.

Weniger ist mehr

ZunĂ€chst wird sich der brave BĂŒrger (oder auch Programmierer ) fragen, wie es ĂŒberhaupt möglich sein kann, in weniger Daten genau die gleiche Information unterzubringen wie im Originaldatensatz - geht dabei nicht unausweichlich Information verloren ? Um so verblĂŒffender ist es, wenn relativ einfach gehaltenene Bilder sich nicht nur auf die HĂ€lfte, sondern vielleicht von 32000 Bytes auf knapp 1000 Bytes ohne jeglichen Informationsverlust verkĂŒrzen lassen. Zauberei? Wirklich nicht! Schauen wir uns dieses Wunder an einem alltĂ€glichen Beispiel an: Stellen Sie sich vor, Sie wĂŒrden ins Theater gehen, eine lange Schlange Menschen am Eingang sehen und wollten einem Bekannten nach selbigen Theaterbesuch ĂŒber diese Menschenkette erzĂ€hlen. Sicherlich kĂ€men Sie nicht auf die Idee, die Menschenkette auf folgende Art zu beschreiben: Vorne stand ein Mann, dann kam noch ein Mann und noch ein Mann, dann folgte eine Frau mit Kind und noch eine Frau mit Kind und so weiter. Vielmehr faßt man auch im normalen Sprachgebrauch mehrere gleiche Dinge durch AufzĂ€hlung zusammen. Sie werden mir recht geben, daß die Beschreibung “ Ich sah 3 MĂ€nner und 2 Frauen mit Kind in einer Schlange” eleganter klingt. Obwohl die Beschreibung kĂŒrzer ist, ist der Informationsgehalt der gleiche! Was spricht nun dagegen, Ă€hnliches auch auf die Beschreibung von Bilddaten anzuwenden? (Auch wenn es wahrscheinlich keinen Ihrer Bekannten interessieren wird, wie Ihre Bilder aussehen.)

Packen wir’s an

Normalerweise stehen in einer (nicht komprimierten) Datei hintereinander alle Bytes so, wie sie nachher auch im Bildschirmspeicher zu finden sind. Dies ist selbst dann der Fall, wenn hundertmal das gleiche Byte vorhanden ist. Viel schöner wĂ€re es doch, wenn folgende Beschreibung möglich wĂ€re: “Zuerst kommen 25 unzusammenhĂ€ngende Bytes, dann hundertmal das Byte X, dann zehnmal das Byte Y, dann wieder zwölf Bytes ohne Zusammenhang...”. Sie bemerken, daß wir keine Information beim Abspeichem der Daten verlieren, wenn wir spĂ€ter beim Laden die Datei wieder ‘dechiffrieren’. Die Kunst des Komprimierens ist es nun, ein Verfahren zu entwickeln, das möglichst viele wiederkehrende Muster erkennt und verschlĂŒsseln kann. Dabei ist die einfachste Art, den Bildspeicher in den aufeinanderfolgenden Bytes nach Bytefolgen abzusuchen. Bei Bildern, in denen aber hĂ€ufig senkrechte Linien Vorkommen, hilft das nicht. Man muß ein solches Bild senkrecht, nicht waagrecht durchsuchen. Auf diese Weise kann man sich die tollsten Suchalgorithmen ĂŒberlegen, um ein Bild zu packen.

Bei digitalisierten Bildern sollte dieser Algorithmus besonders ausgefuchst sein, weil hier kaum gleiche aufeinanderfolgende Bytes zu finden sind. Hier geht man meistens noch eine Ebene tiefer, indem man sogar Bitmuster zu finden versucht. Langsam wird auch deutlich, warum das Packen von (speziell digitalisierten ) Bildern teilweise so lange dauert, daß man “eine kleine Kaffeepause” entlegen kann - das Suchen von (Bit-)Mustem dauert relativ lange. Bereitet man ein solches Bild spĂ€ter wieder auf, kennt man aufgrund der Kodierung das Muster und kann die Daten im Bruchteil einer Sekunde wieder zurĂŒckgewinnen.

Die Qual der Wahl

Nun gibt es inzwischen auf dem ATARI ST eine Vielzahl von Zeichen- und Malprogrammen (gerade erreichte die Redaktion das 100undxte), und fast jedes Programm hat seine eigene Speicherroutine, will sagen sein eigenes Speicherformat. Unsere kleine Reihe soll dazu beitragen, daß Sie wenigstens die bekanntesten Formate, die Sie mit Ihrem gekauften Programm erstellen, auch in Ihren eigenen Programmen verwenden können. Bitte haben Sie VerstĂ€ndnis, daß wir nur den Auspacker des entsprechenden Datenformats und die Information ĂŒber das Format veröffentlichen. Das hat zwei GrĂŒnde: Erstens ist die eigentliche Komprimierroutine meist viel komplizierter als die Entkomprimierung, und zweitens steckt im Packer viel mehr Softwaregrips, was auf deutsch heißt, daß kaum ein Programmierer bereit ist, die Zustimmung zur Veröffentlichung des Packers zu geben.

DEGAS ELITE und das IFF-Format

Beim IFF-Format handelt es sich um ein Format, daß ursprĂŒnglich von der Firma Electronic Arts entwickelt und auf dem AMIGA (ATARI-Liebhaber werden mir die ErwĂ€hnung dieses Namens verzeihen) in Deluxe Paint zum ersten Mal eingesetzt wurde. SpĂ€ter wurde es auf dem ST in Tom Hudsons DEGAS ELITE in abgewandelter Form verwendet und ist auch auf vielen anderen Rechner verbreitet. Der Vorteil dieses Packformates ist, daß es so allgemein gehalten ist, daß man damit nicht nur Bilder, sondern beliebige andere Daten gepackt speichern kann - allerdings werden wir nur das Packformat fĂŒr Bilder erklĂ€ren.

Klumpen

Betrachtet man das IFF-Format, erkennt man, daß es aus einer Reihe von Blöcken besteht, die in IFF-Sprache unter dem aussagekrĂ€ftigen Namen CHUNK bekannt sind - fĂŒr alle, die dem AngelsĂ€chsischen nicht so zugetan sind: CHUNK bedeutet soviel wie großer Brocken oder Klumpen. Wie schon zu vermuten ist, gibt es Chunks unterschiedlicher Art.

Förmlich

ZunĂ€chst beginnt eine IFF-Datei mit einem sogenannten FORM-Chunk, der die Art der Datei angibt, denn, oben erwĂ€hnte ich es schon, man kann nicht nur Bild - sondern auch Text- oder Musikdaten mit IFF verschlĂŒsseln. Dieser FORM-Chunk hat die sagenhafte LĂ€nge von nur 8 Bytes, in denen die KĂŒrzel 8SVX (8-Bit-Sample-Voice), SMUS (Simple Music Score), FTXT (formatierter Text) oder -was uns am meisten interessiert - ILBM (Interleaved Bit Map ) stehen können. Jedem Name des Chunks folgt die LĂ€nge desselben, wobei die ID, also der Namen selbst, nicht miteinbezogen wird. Ein Wort noch zu dem KĂŒrzel ILBM: Wie Sie wahrscheinlich aus unserer letzten Folge noch wissen, wird ein Bild aus mehreren Planes aufgebaut, die auch noch wortweise (ATARI) oder zeilenweise (IFF-Format) ineinander verschachtelt sind, was man Interleaved-Bit-Planes (oder Map) nennt. Erkennt unser Entpacker am Form-Chunk, der ein ILBM enthĂ€lt, daß es sich um eine Grafik-Datei handelt, “weiß” er, daß auf einen FORM-Chunk ein BMHD-CHUNK folgt.

FORM-Chunk BMHD-Chunk CMAP-Chunk CRNG-Chunk BODY-Chunk
GesamtlÀnge - 8 Byte (L)
IFF-Art-Grafik (4B)
Chunk-LĂ€nge(L)
Breite (W)
Höhe (W)
X-Position (W)
Y-Position (W)
Bitplane-Anzahl (B)
Maskierung (B)
Komprimierung(B)
Null (B)
Transparenzfarbe (W)
X-Aspekt (B)
Y-Aspekt (B)
Seitenbreite (W)
Seitenhöhe (W)
Chunk-LĂ€nge (L)
Farbe 0 Rot (B)
Farbe 0 GrĂŒn (B)
Farbe 0 Blau (B)
Farbe 1 Rot (B)
Farbe 1 GrĂŒn (B)
Farbe 1 Blau (B)
Farbe 2 Rot (B)
Farbe 2 GrĂŒn (B)
Farbe 2 Blau (B)
Farbe 3 Rot (B)
Farbe 3 GrĂŒn (B)
Farbe 3 Blau (B)
Chunk-LĂ€nge (L)
Null (W)
Geschwindigkeit (W)
aktiviert (W)
untere Farbe (B)
obere Farbe (B)
Chunk-LĂ€nge (L)
Bilddaten (B)

Tabelle 1: Die unterschiedlichen Grafik-Chunks (B=Byte, W=Wort, L=Langwort)

Der Kopf der ganzen Sache

An AbkĂŒrzungen wurde nicht gespart: BMHD bedeutet Bit-Map-Header. Er enthĂ€lt allgemeine Informationen eines Bildes. Schaut man sich diesen Chunk an (Tabelle 1), erkennt man einige interessante Eigenschaften: ZunĂ€chst bietet die Angabe der Koordinaten sowie der Breite und Höhe die Möglichkeit, Teile oder sogar ÜbergrĂ¶ĂŸen von Bildern abzuspeichern, die spĂ€ter entsprechend verarbeitet werden. Weiter ist im BMHD-Chunk die Anzahl der Planes enthalten, bei der darauf hingewiesen werden soll, daß der AMIGA bis zu fĂŒnf, der ST aber nur vier Planes enthĂ€lt, so daß unwillkĂŒrlich bei der Darstellung Information verloren geht.

Maskenball

Ein weiterer Eintrag gibt die Art der Maskierung an. Dabei wird zwischen 0 (keiner Maskierung), 1 (in Bilddaten vorhandene Maske), 2 (Transparenz) und 3 (Lassotechnik) unterschieden. Eine Maske ist eine Bilddatenmenge, durch die man kennzeichnen kann, an welcher Stelle ein eventuell vorhandener Hintergrund durchscheinen (ein gelöschtes Bit) oder vom neuen Bild verdeckt (ein gesetztes Bit) sein soll. Bei transparenter Maskierung wird an der Stelle im Bild, wo sich eine (an anderer Stelle) definierte Transparenzfarbe befindet, der Hintergrund des schon vorhandenen Bildes durchgeblendet. Die Verarbeitung der Lassotechnik ist relativ aufwendig zu programmieren. Die Vorgehensweise: Die Daten der Lassomaske kennzeichnen eine sich irgendwann wieder schließende, im Bild befindliche Kurve. Der Bildbereich im Innern dieser FlĂ€che wird als transparent verarbeitet. Weiterhin sind im BMHD die Transparenzfarbe sowie das Flag vorhanden, das angibt, ob die Datei komprimiert (1) oder ungepackt (0) vorliegt. Zwei weitere EintrĂ€ge kennzeichnen das VerhĂ€ltnis zwischen Breite und Höhe eines Bildes. Vorhanden ist auch eine Information, wie groß der Arbeitsbildschirm ist, wobei diese Werte auch kleiner sein können als das eigentliche Bild - uns ist allerdings bisher kein Programm bekannt, daß dies unterstĂŒtzt. Die genaue Reihenfolge und den Speicherplatzbedarf entnehmen Sie bitte Tabelle 1.

# CHUNK Bemerkung

FORM: : kennzeichnet die IFF-Datei in ihrer Art und LĂ€nge, ILBM entspricht Grafikart

BMHD: : enthÀlt allgemeine Informationen der Grafik wie zum Beispiel Auflösung, Anzahl der Planes, Komprimierung etc.

CMAP: : enthĂ€lt die Werte fĂŒr die Farbpalette. Es können bis zu 8 Bits pro RGB-Wert Vorkommen, wobei der ATARI ST 3 Bits und der AMIGA 4 Bits unterstĂŒtzt.

CRNG: enthĂ€lt Daten ĂŒber Farbrotation und Shading, der CRNG ist meist vierfach vorhanden.

BODY: : eigentlicher Grafikdatenteil
Bilder sind zeilenweise und planeweise abgespeichert, meist innerhalb der Zeile komprimiert

Tabelle 2: Der Aufbau einer IFF-Grafik-Datei

DEGAS komprimiert:

Screenformat:

nach IFF-Format komprimierte Bilddaten,
ab Byte 35 .. xxx,

32 Bytes Farbpalette nicht nach IFF-Standard Bytes 3 .. 34,

32 Bytes Farbanimationsdaten, hinter den komprimierten Bilddaten

Auflösung:

LOW ( * PCI ), MED ( *.PC2 ), HIGH ( *.PC3 ),

DateilÀnge:

variabel, abhÀngig vom Bildinhalt, da Packformat

Kennung:

  1. Byte = 80, !!!
  2. Byte = Auflösung (0=LOW, 1=MED, 2=HIGH ).

Bemerkung:

IFF-Standard nur bei der Komprimierung eingehalten, die Definitionschunks des IFF sind nicht vorhanden.

AMIGA-IFF:

Screenformat:

nach IFF - Standard aufgebautes Datenfile,
Bilddaten durch ‘BMHD’- und ‘BODY’-chunk gekennzeichnet,

Farbpalette durch ‘BMHD’- und ‘CMAP’-chunk gekennzeichnet.

Farbanimationsdaten nach IFF-Format evtl, enthalten

Auflösung: frei wÀhlbar, im BMHD eingetragen

DateilÀnge: variabel, abhÀngig vom Bildinhalt

Kennung: FORM-Chunk am Dateianfang.

Dateinamen nicht festgelegt, Vorschlag (*.IFF)

Tabelle 3: Zusammenstellung der Bildformate

Noch mehr Information...

bietet der CMAP-Chunk, der die Farbpalette beschreibt. FĂŒr jede Farbe stehen drei Bytes zur VerfĂŒgung, die jeweils die Rot-, GrĂŒn- und Blauwerte der Farbe angeben. Wichtig hierbei ist folgendes: Der ATARI ST besitzt vier und der AMIGA bis zu fĂŒnf Planes. Möchte man Bilder ausnutzen, die fĂŒnf Planes enthalten, muß man aufwendige Farbumrechnungen durchfĂŒhren, um eine Farbpalette zu erstellen, die mit 16 Farben fast so gute Ergebnisse wie mit 32 liefert. Ein weiteres Problem ergibt sich dadurch, daß die RGB-Werte beim AMIGA 32 Stufen annehmen können, wĂ€hrend der ATARI nur 8 Stufen besitzt. Man hat die Werte des AMIGAs durch 2 zu teilen, andernfalls findet man eine völlig falsche Farbgebung vor!

Es geht rund

Bevor wir endlich zu den eigentlichen Grafikdaten kommen, fehlt noch eine zusĂ€tzliche Information, die sich mit Farbrotationen beschĂ€ftigt. Als Farbrotation bezeichnet man das Weitergeben einer Farbinformation von einem Register in das andere. Einige unter Ihnen werden vielleicht die Animation von ATARI kennen, in der ein Adler ĂŒber den Bildschirm fliegt, unter dem die Brandung bewegt zu sehen ist. Die Bewegung der Brandung ist nur durch Rotieren der einzelnen Farben erzeugt! Der Chunk, der uns Auskunft ĂŒber die Farbrolleigenschaften des Bildes gibt, hat den unaussprechlichen Namen CRNG, was ausfĂŒhrlich Color Range (Farbbereich) heißt. Außer der Möglichkeit die Farbrotation ein- und auszuschalten, kann man hier die Geschwindigkeit und die Anfangs- und Endfarbe angeben, zwischen denen die Inhalte gerollt werden sollen. Bei einer Geschwindigkeit von 2 hoch 14 also 16384, werden sechzig Rotationen pro Sekunde durchgefĂŒhrt. Daraus folgt, daß eine Rotation pro Sekunde einem Vert von 273 und ein Wert von 1 einer Rotation in etwa viereinhalb Minuten spricht.

Endlich eingepackt

Nach soviel Vorinformation folgen, lange erwartet, die Grafikdaten. Wie nicht anders zu vermuten, hat auch dieser Block einen Namen - BODY. Der BODY (Körper) enthĂ€lt die Daten eines Bildes in gepackter oder ungepackter Form, je nachdem, ob der Kompressionseintrag gesetzt ist oder nicht. Nun sind wir schon daran gewöhnt, daß die einzelnes Planes beim ATARI nicht hintereinander abgelegt, sondern ineinander als Worte verschachtelt sind. Bei IFF sind die Planes auch nicht vollstĂ€ndig hintereinander, sondern immer bildzeilen weise hintereinander gespeichert (gefolgt von einer eventuell vorhanden Maskenzeile). Dadurch hat man die Möglichkeit, Teile eines Bildes zu laden, ohne wie wild durch die Datei springen zu mĂŒssen. Innerhalb dieser Zeilen geschieht nun die Komprimierung, die (leider) nicht besonders effektiv ist. Der Packer sucht in einer Zeile nach n gleichen aufeinanderfolgende Bytes. Am Anfang eines Kodierungsteils steht immer ein sogenanntes Befehlsbyte. Liegt der Wert n zwischen 0 und 127, werden die nĂ€chsten n+1 Bytes unverĂ€ndert (praktisch ungepackt) ĂŒbernommen. Ist es ein Wert zwischen -1 und -128, wird das folgende Byte -n+1mal wiederholt in den Speicher geschrieben die Zahl Null wĂ€re sinnlos, da das zweifache Wiederholen zwei Bytes kosten wĂŒrde... Da das oberste Bit einschaltet, ob das folgende Byte, aufgrund des sich aus den unteren sieben Bits ergebenden Wertes, wiederholt werden soll, fĂŒhrt der Wert 128 (“Wiederhole nullmal !”) zu keinem Ergebnis.

Unterschiede zu DEGAS ELITE compressed

Wie oben schon angedeutet, unterscheiden sich IFF-Format und Pack-Format von DEGAS-ELITE, haben aber auch etwas gemeinsam, daher werden beide Formate in einer Folge der BILDUNG abgehandelt. Leider stimmen sie nur in der Art der Komprimierung ĂŒberein, so daß alle Header fehlen und auch die Farbpalette nicht im IFF-Format abgespeichert ist. Werden wir speziell: Das erste Byte enthĂ€lt eine Null, die wir auch besser so lassen, da sie wie viele, auch im IFF-Format vorhandene, Nullen Platzhalter fĂŒr spĂ€tere Erweiterungen darstellt. Im zweiten Byte findet man die Auflösung kodiert, wobei 0 niedrige, 1 mittlere und 2 hohe Auflösung bedeutet. Die folgenden 32 Bytes enthalten die Farbpalette, wie schon angesprochen, nicht als IFF-Format, sondern als ganz normale Palette des ST. Hinter der Farbpalette folgen die nach IFF komprimierten Daten, deren Anzahl der Zeilen sich aus der Auflösung ergibt. Wie wir es schon von der normalen DEGAS-Datei kennen, folgen am Ende noch 32 Bytes Farbanimationsdaten.

ResĂŒmee

Wir haben nun die ersten beiden Pack-Formate kennengelernt und dabei festgestellt, daß das IFF-Format hervorragend dazu geeignet ist, Daten unter Rechnern auszutauschen, zumal es auch noch andere Chunks fĂŒr andere Datenarten gibt, allerdings ist der Komprimieralgorithmus relativ mĂ€ĂŸig. Es wird immer nur eine Zeile untersucht und außerdem das Bild nur byteweise und waagrecht ĂŒberprĂŒft, was bei vielen Bildern zu kaum einem Erfolg fĂŒhrt. Leider hat sich DEGAS-ELITE, obwohl es die gleiche Komprimierung wie IFF benutzt, nicht an den IFF-Standard gehalten, obgleich das Programm die Möglichkeit bietet, IFF-Bilderzu laden.

Ausblick

In der nĂ€chsten Bildung werden wir uns mit einem weiteren Format beschĂ€ftigen: dem GEM-VDI- oder auch Image-Format. Viele unserer Leser haben danach gefragt, so daß wir auch dieses Pack-Format in unsere Bildungsreihe hineinnehmen. Packen wir’s also das nĂ€chste Mal an...

SH

Bild 1: Anfang einer IFF-Bilddatei
Bild 1: Anfang einer IFF-Bilddatei

Bild 2: Schema des Anfangs einer IFF-Bilddatei
Bild 2: Schema des Anfangs einer IFF-Bilddatei

*	modul AUSPACK.S 
*
*	Assemblermodul zum Auspacken von
*	verschiedenen gepackten Bildformaten.
*
*
*	Originalauszug aus dem Grafikpaket IMAGIC
*	von APPLICATION SYSTEMS /// HEIDELBERG.
*
*	Version 1.0
*
*	verfasst am 8-8-1988 von Jörg DrĂŒcker
*	Copyright (c) 1988 by IMAGIC GRAFIK.

*                             *
*	DEGAS ELITE BILDER        *
*	auspacken                 *
*                             *

		xdef DEC_ELIT

DEC_ELIT:	bsr	GET_PAR	*	Parameter holen
			cmpi.b #$80,(a0) * header byte "80' testen
			bne	ERR_DONE
			cmp.b	1(a0),d0	*	Bildauflösung testen
			bne	ERR_DONE
			lea	34(a0),a0	*	Farbinformation ĂŒberspringen

*                                   *
*   Register fĂŒr eine Vollbild      *
*   IFF-Dekomprimierung einrichten	*
*                                   *

INIT_LOW:	suba.l	a2,a2	* compressed lines
			move.w	#200,d6	*	200 scanlines
			moveq	#6,d5	*	plane byte offset (ATARI scheme) 
			moveq	#4,d4	*	plane count
			moveq	#40,d3	*	line byte count per plane
			moveq	#40,d7
			tst.w	d0	*	Niedere Auflösung?
			beq.s	ELIT_DECOMP	*	Ja...

INIT_MED:	moveq	#2,d5 * plane byte offset (ATARI scheme) 
			moveq	#2,d4	*	plane count
			moveq	#80, d3	*	line byte count per plane
			moveq	#80,d7
			subq.w	#1,d0 * Mittlere Auflösung? 
			beq.s	ELIT_DECOMP	*	Ja...

INIT_HIGH:	moveq	#0,d5	* plane	byte offset (ATARI scheme) 
			moveq	#1,d4	*	plane count
			add.w	d6,d6	*	400 scanlines

ELIT_DECOMP:	bsr	ERASE_PIC
			bsr	IFF_DECOMP * IFF-format decompression
			bra	ALL_DONE

*                       *
*   AMIGA IFF BILDER    *
*   auspacken           *
*                       *

		xdef DEC_IFF

DEC_IFF:	bsr	GET_PAR * Parameter holen
			bsr	ERASE_PIC * Bild löschen

*                                   *
*   Register fĂŒr eine Standard      *
*   IFF-Dekomprimierung einrichten  *
*                                   *
*                                   *
*   **EinschrÀnkungen:**                *
*   Anzahl Farbplanes nur 1/2/4     *
*                                   *
*   Keine Maskentechniken           *
*   zugelassen                      *
*                                   *


* NOTE: data Starts direct with 'BMHD' (Bitmap-Header) chunk ...

			cmpi.1	#'BMHD',(a0)	* Bitmap-Header chunk found? 
			bne		ERR_DONE	* no, abort
			movem.w	8(a0),d1-d2	* picture width & height in pixel
			move.w	d2,d6	*	number	of scanlines
			addq.w	#7, d1
			lsr.w	#3,d1 * plane bytes per line (pix+7) div 8

			moveq	#0,d3 
			move.b	16(a0),d3 * number of planes

* NOTE: only 1/2/4 planes can be handled.

			move.w	d3,d2	*	d2	nur zum Testen (1/2/4)
			subq.w	#1,d2 
			beq.s	PLANES_OK 
			subq.w	#1,d2 
			beq.s	PLANES_OK 
			subq.w	#2,d2
			bne		ERR_DONE * otherwise abort

PLANES_OK:	move.b	17(a0),d2

* masking technique, 0	=	mskNone,
*	1	=	mskHasMask, 2 = mskHasTranspcolor,
*	3	=	mskLasso

			cmp.b	#1, d2
			beq		ERR_DONE * cannot handle mask in data.

			suba.1	a2,a2	* "compressed"
			tst.b	18(a0) * compression flag, 1=compressed 
			bne.s	COMPRESSED

			move.w	d1,a2 * number of bytes per line (uncomp)

COMPRESSED:	move.w	d3,d4 * number of planes
			move.w	d1,d3 * number of plane bytes per line
			subq.w	#1,d0 * res = low? 
			bmi.s	RES_LOW 
			beq.s	RES_MED
			
			cmpi.w	#1,d4	*	Anzahl planes testen
			bne		ERR_DONE
			moveq	#0,d5	* plane byte offset HIGH = 0
			moveq	#80, d7 * screen bytes per line
			cmpi.w	#400,d6 * max. 400 scanlines
			ble.s	GET_BODY
			move.w	#400, d6
			bra.s	GET_BODY

RES MED:	cmpi.w	#2,d4	* Anzahl planes testen

			bne		ERR_DONE

			moveq	#2,d5	*	plane byte offset MED = 2

			moveq	#80,d7
			cmpi.w	#200,d6 *	max. 200 scanlines
			ble.s	GET_BODY

			move.w	#200,d6
			bra.s	GET_BODY

RES_LOW:	cmpi.w	#4,d4	* Anzahl planes testen
			bne		ERR_DONE

			moveq	#6,d5	*	plane	byte offset LOW = 6

			moveq	#40,d7
			cmpi.w	#200, d6	*	max. 200 scanlines
			ble.s	GET_BODY

			move.w	#200,d6

GET_BODY:	move.1	4(a0),d0 * chunk length
			addq.1	#1,d0
			bclr	#0,d0	*	even length
			lea		8(a0,d0.1),a0 * skip chunk
			cmpi.1	#'BODY',(a0) * body chunk found?
			bne.s	GET_BODY
			addq.l	#8,a0 * Start of body chunk
			bsr		IFF_DECOMP	* IFF-format decompression
			bra		ALL_DONE

*                           *
*   AMIGA IFF BILDER        *
*   Farbpalette umrechnen   *
*                           *

		xdef COL_IFF

COL_IFF:	movea.l	4(sp),a1 * COLOR data pointer
			movea.l 8(sp),a0 * IFF	DATA - Start of	BMHD chunk

*                                  *
*    Standard IFF Farbdaten holen  *
*                                  *
*    **EinschrÀnkungen:**              *
*    ----------------              *
*    Anzahl Farbregister == 16     *
*                                  *

* NOTE: data Starts direct with 'BMHD' (Bitmap-Header) chunk ...

			moveq	#0,d0
			cmpi.l #'BMHD',(a0) * Bitmap-Header chunk found? 
			bne.s	RETURN_COL	* no,	abort

GET_COLR:	move.1	4(a0),d0	*	chunk length
			addq.l	#1,d0
			bclr	#0,d0	*	even length
			lea	8 	(a0, d0.1),a0 * skip chunk

			cmpi.l	#'CMAP',(a0) * CMAP chunk found?
			bne.s	GET_COLR

			addq.l	#8,a0 *	Start of color chunk
			moveq	#15, d6	* 16 color registers 
			moveq	#4,d4	* #4

MAKE_COLR:	moveq	#0,d0	*	color register
			moveq	#2,d1	* 3 values

MAKE_1:		lsl.w	d4,d0	*	next nibble
			move.b	(a0)+,d0	*	IFF RGB value ( upper nibble )

			lsr.b	#1,d0	* convert IFF RGB range 0..15
							* to ATARI RGB range 0..7
			dbra	d1, MAKE_1
			lsr.w	d4,d0	* position d0 to: "0rgb" 
			move.w	d0,(a1)+ * save color value

			dbra	d6,MAKE_COLR
			moveq	#1,d0 *	return	"OK"

RETURN_COL:	movea.1	(sp)+,a0
			addq.l	#8,sp	*	cleanup	stack
			jmp		(a0)	*	rts

*                          *
*    GEM VDI BILDER        *
*    auspacken             *
*                          *

		xdef DEC_VDI

DEC_VDI:	bsr	GET_PAR * Parameter holen
			bsr	ERASE_PIC	*	erase picture

* vorlĂ€ufig nicht verfĂŒgbar, wird spĂ€ter erweitert:

bra	ERR_DONE	*	Fehlermeldung

*                         *
*   IMAGIC BILDER         *
*   auspacken             *
*                         *

		xdef DEC_IMAG

DEC_IMAG:	bsr	GET_PAR * Parameter holen
			bsr	ERASE_PIC	*	erase picture

* vorlĂ€ufig nicht verfĂŒgbar, wird spĂ€ter erweitert:

			bra	ERR_DONE	*	Fehlermeldung

*                                 *
*   Fehlerausgang                 *
*   Booleanwert FALSE zurĂŒckgeben *
*                                 *

ERR_DONE:	moveq	#0,d0	* "error"
			bra.s	RETURN

*                                *
*   Normalausgang                *
*   Booleanwert TRUE zurĂŒckgeben *
*                                *

ALL_DONE:	moveq	#1,d0	* "no error"

RETURN:	movem.l	(sp)+,d7/a6	*	restore d7 / a6

			movea.1 (sp)+,a0
			lea		12(sp),sp	* cleanup stack
			jmp		(a0)	*	rts

*                               *
*   Hilfsfunktion               *
*   Parameter vom Stack holen   *
*   Register korrekt setzen     *
*                               *

GET_PAR:	lea	8(sp),a4	* Zeiger auf Parameter

			move.w	(a4)+,d0 * RESOLUTION 
			move.w	(a4)+,d6 * PICLEN 
			movea.1	(a4)+,a1	* PICTURE
			movea.1	(a4)+,a0	* COMPRESSED DATA

			move.1	(sp)+,a5	* Return Adresse
			movem.l	d7/a6,-(sp)	* Register retten

			jmp	(a5)

*                       *
*   Hilfsfunktion       *
*   Bildinhalt löschen	*
*                       *

*	a1 = picture
*	d6 = picturelen (32000 or more, but always a multiple of 64 bytes !)

ERASE_PIC:	movem.l	d0-d7/al-a2,- (sp)

			moveq	#0,d0
			moveq	#0,d1
			moveq	#0,d2
			moveq	#0,d3
			moveq	#0,d4
			moveq	#0,d5
			moveq	#0,d7

			move.l	d0,a2	* "0" ins a2

			adda.w	d6,a1 * upper border
			lsr.w	#6,d6	* picturelen div 64
			subq.w	#1,d6 * ZĂ€hler -1

clrpic:		movem.l	d0-d5/d7/a2,-(a1) * 32 bytes
			movem.l d0-d5/d7/a2,-(a1) * 32 bytes 
			dbra	d6,clrpic

			movem.l	(sp)+,d0-d7/a1-a2
			rts

*=========================================*
*                                         *
*    Routinen zur Dekomprimierung         *
*                                         *
*=========================================*

*                                   *
*   Standard IFF Dekomprimierung    *

* a0 = compr. source
* a1 = pic destination
* a2 = uncompressed linelength in bytes /0 = compressed lines
*
* d3 = max destination line byte count per plane
* d4 = number of planes
* d5 = byte offset from plane-to-plane (ATARI scheme)
* d6 = total number of destination lines (ysize)
* d7 = number of destination plane bytes per line (80/80/40)

IFF_DECOMP:	move.1 d4,a4
			subq.l	#1,a4 * plane count -1

			move.1	d3,a3	*	line byte count
			bclr	#0,d3	*	only even byte counts
			move.w	d3,d0
			neg.w	d0
			muls	d4,d0	*	-(byte count) x (plane count)+2
			addq.1	#2,d0
			movea.1	d0,a5	*	next plane offset
			move.w	d7,d2	* destination bytes per line

			mulu	d4,d7	*	source bytes per line (even)
			sub.l	d5,d7
			subq.l	#2,d7	*	(bytes_per_line) - (byte offset)-2 
			movea.1	d7,a6	*	next line offset

			subq.w	#1,d6	*	"dbra" ylines counter
			move.w	a4,d4	*	"dbra" planes counter
			clr.w	d3	*	plane byte counter
			moveq	#0,d7	*	destination offset (Start = 0)

MAIN_LOOP:	cmp.w a3,d3	*	one plane written?
			bpl.s NEXT_PLANE

			move.w	a2,d0	*	packed data?
			bne.s COPY_LOOP * take line-length as byte count
							*	for	uncompressed data
			moveq	#0,d0
			move.b	(a0)+,d0 * compression byte
			bmi.s	MULTIPLE * negative: multiple byte count
			*	positive: single bytes following

COPY_LOOP:	addq.w	#1,d3
			cmp.w	d3, d2 * check line clipping
			bmi.s	COPY_CLIP
			move.b	(a0),0(a1,d7.w) * copy <d0> bytes

COPY_CLIP:	addq.1	#1,a0
			* position in destination screen according to the
			* ATARIs special colorplane encoding scheme:

			addq.w	#1,d7
			btst	#0,d7	* second byte written?
			bne.s	COPY_1
			add.w	d5,d7 * add plane byte offset

COPY_1:		dbra	d0,COPY_LOOP
			bra.s	MAIN_LOOP * all singles written

MULTIPLE:	neg.b	d0 * multiple counter
			bmi.s	MAIN_LOOP	* Byte was $80: "no Operation byte"
			move.b	(a0)+,d1	* multiple byte

MULT_LOOP:	addq.w	#1, d3
			cmp.w	d3, d2
			bmi.s	MULT_CLIP

			move.b d1,0(a1,d7.w) * write <d0> multiple bytes

MULT_CLIP:
* position in destination screen according to the
* ATARIs special colorplane encoding scheme:

			addq.w	# 1, d7
			btst	#0,d7 * second byte written?
			bne.s	MULT_1
			add.w	d5,d7 * add plane byte offset

MULT_1:		dbra	d0,MULT_LOOP
			bra.s	MAIN_LOOP * all multiples written

NEXT_PLANE:	clr.w d3	* clear line byte counter
			bclr	#0,d7	* re-position from even position 
			add.w	a5,d7	* re-position on next plane
			dbra	d4,MAIN_LOOP * plane counter
			move.w	a4,d4	* re-load counter
			add.w	a6,d7	* position on next line

			dbra	d6,MAIN_LOOP

END_IFF:	rts	* all lines done, end

*=====================================*

		end

Listing 1: Benötigte Assemblerroutinen

{ Erweiterung zu BILDEINLESEN aus ST-Computer 8/9 88 ST-Ecke }
{ Diese Zeilen sind allein nicht lauffÀhig ! ! }

program BILDEINLESEN ( input, output ) ;

{

Demonstrationsprogramm zum Einlesen von 
verschiedenen Bildformaten.
->
Erweiterung zu BILDEINLESEN aus ST-Computer 8/9 88 ST-Ecke

Version 1.0

verfasst am 7-6-1988 von Jörg DrĂŒcker 

erweitert am 8-8-1988 neue Bildformate.

Copyright (c) 1988 by IMAGIC GRAFIK.

}

{...}

PICTURE_TYPES = ( P_DEGAS,	{	Degas unkomprimiert }
				P_DOODLE, { Doodle / Screenformat } 
				P_NEO, { Neochrome Format } 
				P_ART, { ART Director }
				P_DEGCOM, { Degas komprimiert } 
				P_IFF, { AMIGA IFF }
				P_GEMVDI, { GEM VDI Format } 
				P_IMAGIC, { IMAGIC komprimiert } 
				P_UNDEF ); { unbekanntes Format }

{...}

{ globale Variablen }

		AUFLOESUNG : integer;

{ Systemfunktionen:
}

function getrez : integer;

{ Aktuell eingestellte Bildschirmauf lösung holen } 

		xbios ( 4 );

{...}

{ Assemblerroutinen zur EntschlĂŒsselung der gepackten Bildformate:
}

function DEC_ELIT ( COMP, PICTURE : DATA_POINTER;
					PICSIZE, RESOLUTION : integer ): boolean;
		externa1;

function DEC_IFF ( COMP, PICTURE : DATA_POINTER;
					PICSIZE, RESOLUTION : integer ): boolean;
		external;

function DEC_VDI ( COMP, PICTURE : DATA_POINTER;
					PICSIZE, RESOLUTION : integer ): boolean;
		external;

function DEC_IMAG ( COMP, PICTURE : DATA_POINTER;
					PICSIZE, RESOLUTION : integer ): boolean;
		external;

{ Assemblerroutinen zur EntschlĂŒsselung der der Farbpalette:
}

function COL_IFF ( IFF_DATA, COLOR_DATA : DATA_POINTER): boolean;
		external;

{ folgende Routine wird insgesamt ausgetauscht }

procedure HOLE_BILDDATEN ( READBUF, DEST : DATA_POINTER;
					PIC_TYPE : PICTURE_TYPES; 
					RESOLUTION : integer );

{ Hole Bildinhalt, je nach Bildformat }

var OK : boolean;

begin
	OK := true; { O.K. fĂŒr alle nicht komprimierten Formate )

	case PIC_TYPE of

{...}
	P_DEGCOM : OK := DEC_ELIT ( READBUF,
					DEST,
					PICTURELEN,
					RESOLUTION );

	P_IFF : OK := DEC_IFF ( ADDR_OFFSET
					( READBUF, 12 )
					
					{ FORM chunk ĂŒberspringen }
					
					DEST,
					PICTURELEN,
					RESOLUTION );

	P_GEMVDI : OK := DEC_VDI ( READBUF,
					DEST,
					PICTURELEN,
					RESOLUTION ) ;

	P_IMAGIC : OK := DEC_IMAG ( READBUF,
					DEST,
					PICTURELEN,
					RESOLUTION )

end; { case }

if not OK then begin

	writeln ( chr ( 27 ), 'E', chr ( 7 ) ) ;
					{ Bildschirm löschen, Warnton }

	writeln ( 'Fehler beim EntschlĂŒsseln des Bildformats !' ) ;
	writeln;
	writeln ( 'Weiter mit <RETURN>' ) 
	
end

end; { HOLE_BILDDATEN }

procedure HOLE_FARBEN ( READBUF : DATA_POINTER;
					FARBEN : COLOUR_PTR;
					PIC_TYPE : PICTURE_TYPES );

{ Hole Farbpalette, je nach Bildformat }
{...}

	OK : boolean;

begin 
{...}
	OK := true; { O.K. fĂŒr alle nicht komprimierten Formate }

{...}
	case PIC_TYPE of 
			P_DEGAS,
			P_DEGCOM : TRANSFER ( ADDR_OFFSET 
						(READBUF,2 ),
						COL.DATA,
						32 ) ;

	{...}
			P_DOODLE,
			P_GEMVDI : SYSPALET ( COL.CPTR );
			{ aktuelle Palette holen }

			P_IFF : OK := COL_IFF ( ADDR_OFFSET
						( READBUF, 12 ) ,

					{ FORM chunk ĂŒberspringen }

						COL.DATA );

			P_IMAGIC : TRANSFER ( ADDR_OFFSET 
						( READBUF,6 ),
						COL.DATA,
						32 )
	end; { case }

	if not OK then
	{ im Falle eines Fehlers: Systempalette einstellen ! }

		SYSPALET ( COL.CPTR ) ;

end; { HOLE_FARBEN }

{ Hauptprogramm:
}

begin

	AUFLOESUNG := getrez; { Bildschirmauflösung ? } 
	
		loop

{...}

			repeat

				writeln;
				writeln ('Bildtyp eingeben:' ); 
				writeln (' A = DEGAS Normal' ); 
				writeln (' B = DOODLE' ); 
				writeln (' C = NEO Chrome' ); 
				writeln (' D = ART Director' ); 
				writeln (' E = DEGAS komprimiert' ); 
				writeln (' F = AMIGA IFF' ); 
				writeln (' G = GEM VDI' ); 
				writeln ( ' H = IMAGIC komprimiert' ) ;
				write ( '> ' ); 
				readln ( BILDTYP ) ;

				case BILDTYP of
				'A','a' : PIC_TYPE :=P_DEGAS;
				'B','b' : PIC_TYPE :=P_DOODLE;
				'C','c' : PIC_TYPE :=P_NEO; 
				'D','d' : PIC_TYPE :=P_ART;
				'E','e'	: PIC_TYPE :=P_DEGCOM;
				'F','f'	: PIC_TYPE :=P_IFF;
				'G','g' : PIC_TYPE :=P_GEMVDI; 
				'H','h' : PIC_TYPE :=P_IMAGIC;

				otherwise : PIC_TYPE := P_UNDEF
{...}
				
			HOLE_BILDDATEN ( LESEBUFFER.DATA,
			BILDSCHIRM, PIC_TYPE, 
			AUFLOESUNG );

end.

Listing 2: Erweiterungslisting in PASCAL

case P_DOODLE:
syspalet(farben);	/*	aktuelle Palette	holen	*/
break;

case P_NEO: 
transfer(readbuf+4,farben,32L); 
break;

case P_ART: 
transfer(readbuf+32000L,farben,32L); 
break;
}

}

Listing 3: Fehlende Zeilen aus Listing 3 der letzten ST-Ecke