/******************************************************************************
*
*			     IFF-Transformer V1.3
*			     --------------------
*	
*
*	Laedt ein IFF-Bild, stellt das Bild auf einer Kugeloberfaeche
*	dar, und speichert das transformierte Bild als IFF-File
*
*	(C) 11/87 Christian Moench
*
*		 Kellerbornstr.15
*	    6000 Frankfurt a. Main
*	
*******************************************************************************/

#include <exec/types.h>
#include <stdio.h>
#include <functions.h>
#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <math.h>

/* Macrodefinitionen zu den Rechenroutinen */

#define ABS(x) ((x) < 0.0) ? (-(x)) : (x)
#define PI 3.141592654
#define TOBOGEN(a) ((a)*PI/180.0)	/* wandelt Grad- in Bogenmass */
#define TOGRAD(a) ((a)*180.0/PI)	/* wandelt Bogen- in Gradmass */
/* maximal zulaessiger Abtastwinkel fuer
   eine gegebene Kugelgroesse */

#define MAXWIN(s) ((asin(.6/(s))<0) ? (PI+asin(0.6/(s))) : (asin(.6/(s))))

/* groesster Sinuswert im Bereich von 0-winkel */
#define BIGESTWIN(s) (double)((s >= 90.0) ? (1.0) : (sin(TOBOGEN(s))))
 
#define VOLL 1		/* Kugel undurchsichtig */
#define LEER 0		/* kugel durchsichtig   */
#define MONITOR 1.5	/* siehe getprop */
#define ASPECT aspects[(meinbmhd.w/meinbmhd.h)]

/* Definitionen zu IFF-Files 	
Macro  zum Errechnen der Id aus vier Buchstaben */

#define IDVAL(a,b,c,d) ((LONG)a << 24L | (LONG)b << 16L |  c << 8 | d)

/* Id's der einzelnen Chuncks */

#define ID_FORM  IDVAL('F','O','R','M')
#define ID_ILBM  IDVAL('I','L','B','M') /* Interleave Bitmap */
#define ID_BMHD  IDVAL('B','M','H','D') /* Bitmap Header */
#define ID_CMAP  IDVAL('C','M','A','P') /* Colormap */
#define ID_BODY  IDVAL('B','O','D','Y') /* Bilddaten */

/* BitMapHeader-Structure aufgebaut wie der "BMHD"-Chuncks. Dieser
Chunck enthaelt alle wichtigen Informationen ueber das zu ladende Bild  */

typedef struct {
	UWORD w,h;		/* Breite,Hoehe */
	WORD x,y;		/* x- und yoffset beim Laden */
	UBYTE nPlanes;		/* Anzahl der Bitmaps */
	UBYTE masking;		/* Maske ? (nicht beruecksichtigt) */
	UBYTE compression;	/* gepackt ? */
	UBYTE pad1;		/* die folgenden Werte */
	UWORD transcol;		/* werden nicht beruecksichtigt, */
	UBYTE xasp,yasp;	/* sind aber im BMHD-Chunck und  */
	WORD paw,pah;		/* werden gelesen		 */
	} BitMapHeader;


BitMapHeader meinbmhd;	/* Speicher fuer "BMHD"-Struktur belegen */

UBYTE farben[96];	/* Array zum Einlesen der Farben */
ULONG colors;		/* Anzahl der Farben des jeweiligen Bildes */
UBYTE *loc_bmhd;	/* Pointer auf "BMHD"-Struktur */
UBYTE *loc_cmap;	/* Pointer auf Farbarry */

struct GfxBase *GfxBase;		/* System pointer Grafik */
struct IntuitionBase *IntuitionBase;	/* System pointer Intuition (wird zum
					   Oeffnen der Screens benoetigt) */

/* Die folgenden Pointer sind jeweils fuer das Ausgangsbild und das trans-
formierte Bild definiert */

struct RastPort *rp,*rp2;
struct ViewPort *vp,*vp2;
struct Screen *s=NULL;
struct Screen *s2=NULL;

/* Newscreen Struktur wird vor dem Oeffnen der Screens vom Program an das zu
ladende Bild angepasst (Werte aus dem "BMHD"-Chunck) */

struct NewScreen ns = {0,0,320,200,5,
			0,1,
			0,
			CUSTOMSCREEN,
			NULL,
			NULL,
			NULL,
			NULL };

/* Speicherreservierung und Initialisierung der Rotationsmatrix zum Drehen der
Kugel */

double m[3][3] = {{1.0,1.0,1.0},
		  {1.0,1.0,1.0},
		  {1.0,1.0,1.0}};

double aspects[4] = {2.0 , 1.0 , 0.5 , 0.5 };

char zeichenpuffer[80]; /* Puffer fuer die Funktion "scanf" */
FILE *cleanup;		/* wird von "cleanexit()" benoetigt, um offene Files
			   zu schliesen */


/*---------------------------------------------------------------------------*/ 
/* 				Disk - Routinen 			     */
/*---------------------------------------------------------------------------*/

/*--------------- hole Byte von spezifiziertem File -------------------------*/

BYTE getbyte(fp)

	FILE *fp; /* Pointer auf File Struktur */

{
	BYTE erg;

    erg = getc(fp);	/* Lese Byte */
    if(feof(fp)) {	/* End of File ? */
	cleanexit("Unerwartetes Fileende !");
    }
    if(ferror(fp)) {	/* Lesefehler ? */
	cleanexit("Fehler beim Lesen aufgetreten !");
    }
    return(erg);	/* kein Fehler: gebe Byte zurueck */
}


/*------------- hole Langwort aus spezifiziertem File -----------------------*/

ULONG getlong(fp)

	FILE *fp;	/* Pointer auf File-Struktur */

{

	ULONG len;

	len = 0L;
	len = (ULONG)IDVAL(getbyte(fp),getbyte(fp),getbyte(fp),getbyte(fp));
	return(len);

}


/*--------------- schreibe Byte in spezifiziertes File ---------------------*/

void putbyte(fp,b)

	FILE *fp;	/* Pointer auf File-Struktur */
	BYTE b;		/* zu schreibendes Byte	     */

{

    putc(b,fp);
    if(ferror(fp)) {	/* Fehler beim Schreiben ? */
	cleanexit("Fehler beim Schreiben aufgetreten !");
    }
}


/*-------------- schreibe Langwort in spezifiziertes File -------------------*/

void putlong(fp,l)

	FILE *fp;	/* Pointer auf File-Struktur */
	LONG l;		/* zu schreibendes Langwort  */

{

    putbyte(fp,(UBYTE)((l >> 24L) & 0xff));		/* Byte Nr. 0 */
    putbyte(fp,(UBYTE)((l >> 16L) & 0xff));		/* Byte Nr. 1 */
    putbyte(fp,(UBYTE)((l >>   8) & 0xff));		/* Byte Nr. 2 */
    putbyte(fp,(UBYTE)( l         & 0xff));		/* Byte Nr. 3 */
	
}


/*-------------------------- Lade IFF-Bild ----------------------------------*/
loadpic(name)

	char *name;	/* Pointer auf Filenamen */
	
{

    FILE *fp;
    LONG id;
    void lookforpic();

    if((fp = fopen(name,"r")) == 0) {		/* File nicht geoffnet ? */
	cleanup = NULL;				/* kein File offen */
	cleanexit("File nicht gefunden !");	/* beenden	   */
    }
    cleanup = fp;		/* File "fp" ist offen */
    id = getlong(fp);		/* hole Id */
    if(id != ID_FORM) {		/* ist IFF-File ? */
	cleanexit("Kein IFF-File !");
    }
    lookforpic(fp);		/* hole Bild */
    fclose(fp);			/* schliese File */
    cleanup = NULL;		/* kein File offen */
}


/*----------------- Ueberlese unbekannten Chunck ----------------------------*/

overreadchunk(fp,l)

	FILE *fp;	/* Pointer auf File-Struktur */
	ULONG l;	/* Chuncklaenge	*/

{

	ULONG i;
	BYTE dummy;

    for(i=0;i<l;i++) {		/* ueber gesamte Laenge */
	dummy = getbyte(fp);	/* Byte wird nicht benoetigt */
    }
    if(l & 0x1L) {		/* Laenge ungerade ? */
	dummy = getbyte(fp);	/* ja,lese pad-Byte */
    }

}


/*------- prueft ob ein IFF-File vorliegt und liest ein vorhandenes Bild ----*/

void lookforpic(fp)

	FILE *fp;	/* Pointer auf File-Struktur	*/

{

	BOOL body;
	LONG length,id,i;
	BYTE dumm;

    body = FALSE;		/* Bilddaten noch nicht erreicht */
    length = getlong(fp);	/* hole Chuncklaenge */
    id = getlong(fp);		/* hole Chunckid */

    if(id != ID_ILBM) {		/* Bilddatei enthalten ? */
	cleanexit("Kein Bild gefunden !",fp);
    }

    while(!(body)) {		/* Solange keine Bilddaten gelesen wurden */
	id     = getlong(fp);	/* hole id */
	length = getlong(fp);	/* hole Laenge */

	switch(id) {		/* bearbeite Chunks */

	    case(ID_BMHD) : {	/* BitMapHeader ? */

		loc_bmhd = (UBYTE *)(&meinbmhd);
		for(i=0;i<length;i++) {
		    *loc_bmhd++ = getbyte(fp);
		}
		if(length&1)
		    dumm = getbyte(fp);
		computebmhd();
		break;
		}

		case(ID_CMAP) : {	/* colormap ? */
		    loc_cmap = farben;
		    for(i=0;i<length;i++) {
			*loc_cmap++ = getbyte(fp);
		    }
		    colors=length;
		    if(length&1) {
			dumm = getbyte(fp);
		    }
		    computecmap();
		    break;
		}

		case(ID_BODY) : {	/* body ? */
		    readbody(fp);
		    body = 1;		/* Bild ist gelesen */
		    break;
		}
		default	      : {	/* unbekannter Chunck */
		    overreadchunk(fp,length);
		    break;
		}

	}

    }

}


/*------------ Oeffnet Screen mit den gelesene Parametern ------------------*/

computebmhd()
{
	LONG i;


    /* Anpassen der Newscreen-Struktur an das glesene Bild */

    ns.ViewModes = 0;
    ns.Depth = meinbmhd.nPlanes;
    if(ns.Depth == 6) {
	ns.ViewModes |= HAM;
    }

    if(meinbmhd.w > 400) {
	ns.ViewModes |= HIRES;
    }
    ns.Width = meinbmhd.w;

    if(meinbmhd.h > 256) {
	ns.ViewModes |= LACE;
    }
    ns.Height = meinbmhd.h;

    /* Oeffne Screen 1, zum Laden des Bildes */

    if(s) {
	CloseScreen(s);
    }

    s = (struct Screen *) OpenScreen(&ns);
    if(s == NULL) {
	cleanexit("Screen no.1 kann nicht geoeffnet werden !");
    }

    /* Oeffne Screen 2, zum Transformieren des Bildes */

    if(s2) {
	CloseScreen(s2);
    }

    s2 = (struct Screen *) OpenScreen(&ns);
    if(s2 == NULL) {
	cleanexit("Screen no.2 kann nicht geoeffnet werden !");
    }

    /* Hole Viewports der beiden Screens, zum Setzen der Farben */
    /* Hole Rastports der beiden Screens, fuer Grafikfunktionen  */

    vp = &s -> ViewPort;
    rp = &s -> RastPort;

    vp2 = &s2 -> ViewPort;
    rp2 = &s2 -> RastPort;

    ScreenToFront(s);	/* Screen auf dem geladen wird kommt nach vorn */
    /* kann bei LATTICE "Screentofront" (mit kleinem t) heissemn */
	
}


/*---------------- setze Farben der gelesenen Colormap ----------------------*/

computecmap()

{
	LONG i,c,red,green,blue;

    loc_cmap = farben;		/* Pointer auf Array "farben" */
    c=colors/3L;		/* eine Farbe besteht aus drei Bytes */

    for(i=0;i<c;i++) {		/* ueber alle Farben */

	red = (LONG)(*loc_cmap++ >> 4);
	green = (LONG)(*loc_cmap++ >> 4);
	blue = (LONG)(*loc_cmap++ >> 4);

	SetRGB4(vp,i,red,green,blue);	/* setze Farben von Screen 1 */
	SetRGB4(vp2,i,red,green,blue);	/* setze Farben von Screen 2 */

    }

}


/*--------- dekommprimiert eine Bildzeile (Dpaint II und AegisImages) --------*/

UBYTE *unpackrow(bt,l,fp)

	UBYTE *bt;	/* Puffer fuer dekompriemierte Zeile, in diesem Fall
			   die Bitmap von Screen Nr. 1 */
	UWORD l;	/* Laenge der Zeile in Bytes */
	FILE *fp;	/* Pointer auf File-Struktur */

{

	BYTE n;
	UWORD i,j,k,count;

    count = 0;
    if(meinbmhd.compression == 1) {	/* ist komprimiert ? */
    	while(count < l) {		/* ueber alle Bytes */
	    n = getbyte(fp);
	    if(n<0) {			/* wiederholte Bytes ? */
		if(n > -128) {		/* kein Nooperation (-128) ? */
		    k = getbyte(fp);	/* hole zu wiederholendes Byte */
		    for(j=0;j<((-n)+1);j++) {
			*bt++ = k;	/* wiederhole Byte -n+1 mal */
			count ++;	/* schreibe Byte in Puffer
					   und erhoehe Zaehler */
		    }
		}
	    }
	    else {			/* naechsten n+1 Bytes Lesen */
		for(j=0;j<(n+1);j++) {	/* ueber n+1 */
		    *bt++ = getbyte(fp);	/* hole Byte */	
		    count++;			/* schreibe es  in Puffer und
						   erhoehe Zaehler */
		}
	    }

	}
    }
    else {				/* nicht kompriemiert */
	for(j=0;j<l;j++) {	 	/* lese l Bytes */
	    *bt++ = getbyte(fp);	/* und schreibe sie in Puffer */
	}
    }	
    return(bt);		/* gebe neue Position im Puffer zurueck */
}


/*------ holt Anzahl der gleichen Bytes eines Arrays (hier: Bitmap)---------*/

BYTE getsame(src,max)

	BYTE *src;	/* Array Position */
	WORD max;	/* maximale Anzahl der zu lesenden Bytes */

{

	BYTE l,c,f,*hsrc;

    c=0;
    f=0;
    hsrc = src+1;
    for(l=0;l<max;l++) {	/* ueber die maximale Anzahl */
	/* vergleiche Byte mit Nachfolger */
	if((*src == *hsrc) && (f==0)) {	/* ist gleich */
	    c++;			/* erhoehe Zaehler */
	    src++;			/* naechsten zwei Bytes */
	    hsrc++;
	}
	else {	/* ist ungleich */
	    f=1;	/* Flag setzen, so das keine weiteren Paare ge-
			   zaehlt werden */		
	}
    }
    if(c==0) {		/* kein Paar ? */
	c=-1;
    }
    return(c);
}


/*------------- hole Anzahl der verschieden Bytes eines Arrays -------------*/

BYTE getdifferent(src,max)

	BYTE *src;	/* Arbeitsweise analog zu getsame() */
	WORD max;

{

	BYTE l,c,f,*hsrc;

    c=0;
    f=0;
    hsrc = src+1;
    for(l=0;l<max;l++) {
	if((*src!=*hsrc) && (f==0)) {
	    c++;
	    src++;
	    hsrc++;
	}
	else {
	    f=1;
	}
    }
    if(c==0) {
	c=-1;
    }
    return(c);

}


/*- kommprimiert x Zeichen eines Arrays src, legt diese in das Array dest --*/

LONG packrow(src,dest,x)

	BYTE *src,*dest;	/* source- und destinationArrays */
	BYTE x;			/* maximale Bytezahl */

{

	BYTE c,dif,sam,i;
	LONG l;

    l=0;
    c=0;

    while(c<x) {		/* ueber alle Bytes */
	sam=getsame(src,x-c);		/* hole Anzahl gleicher und */
	dif=getdifferent(src,x-c);	/* ungleicher Bytes */
	if(sam!=-1) {	/* gleiche Bytes */
	    l+=2L;	/* Laenge des Ziel-Arrays wird um zwei groesser */ 
	    c+=sam;	/* Anzahl der gelesenen Bytes wird um die Anzahl
			   der gleichen Bytes groesser */
	    *dest++ = -(sam-1);  /* schreibe -Anzahl-1 in Ziel-Array */
	    *dest++ = *src;	 /* schreibe zu wiederholendes Byte */
	    src+=sam;	/* Position im Source Array wird um die Anzahl
			   der gleichen Bytes groesser */
	}
	if(dif!=-1) {
	    *dest++ = dif-1;	/* schreibe Anzahl-1 in Ziel-Array */
	    l++;		/* Laenge des Ziel-Arrays wird 
				   um eins groesser */
	    for(i=0;i<dif;i++) {	/* ueber ungleiche Bytes */
		c++;			/* gelesene Bytes wird
					   um "dif" groesser */
		l++;			/* Laenge des Zielarrays wird
					   um "dif" groesser */
		*dest++ = *src++;	/* schreibe die ungleichen Bytes ins
					   Ziel-Array */
	    }
			
	}

    }
    return(l);
}


/*--------------------- holt Bitmap-Adrressen -------------------------------*/

UBYTE *getbitmap(num)

	LONG num;	/* welche Bitmap */

{
	UBYTE *plane;

	plane = s->BitMap.Planes[num];	/* holt Adrresse aus Screen-Struktur */ 
	return(plane);
}


/*-------------------- liest die Bitmaps eines Bildes ------------------------*/
readbody(fp)

	FILE *fp;	/* Pointer auf File-Struktur */

{

	UWORD i,j,x,y;
	UBYTE *maps[7],*dummy;

    x = (meinbmhd.w >> 3);	/* Anzahl Bytes pro Zeile = 
				   Punkte pro Zeile / 3      */
    y = meinbmhd.h;

    for(j=0;j<meinbmhd.nPlanes;j++) {	/* hole Bitmapadrressen der maps */
	maps[j] = (UBYTE *)getbitmap((LONG)j);
    }

    for(i=0;i<y;i++) {			/* ueber alle Zeilen */
	for(j=0;j<meinbmhd.nPlanes;j++) {		/* ueber alle Bitplanes */
	    maps[j] = unpackrow(maps[j],x,fp);	/* dekommprimiere Zeile */
	}
	if(meinbmhd.masking == 1 || meinbmhd.masking == 3) { /* Maske ? */
	    dummy = unpackrow(farben,x,fp);	/* dekommprimiere Maske */
					/* Maske wird nicht beruecksichtigt */
	}
    }
}


/*------------- schreibt ein Bild das im Speicher steht auf Disk ------------*/

void savepic(name)

	char *name;	/* Name unter dem das Bild gespeichert wird */
{
	FILE *fip;
	LONG minlen,bodylen;
	UBYTE *maps[7];
	UWORD x,y,xw;
	COUNT i,j,k;
	BYTE odd,picbuff[90];

    if((fip=fopen(name,"w"))==NULL) {
	cleanup = NULL;
	cleanexit("File kann nicht geoeffnet werden !");
    }
    cleanup = fip;
    minlen = 48L+colors;
    bodylen = 0L;
    odd = 0;

/* minlen = 'ILBMBMHDCMAPBODY' + 12 ((lenght of chunks)*3) +
	     20 (BMHD) + (length of colormap(colors))
*/

    x = (meinbmhd.w >> 3);
    y = meinbmhd.h;

/* schreibe FORM auf Disk */
    putlong(fip,IDVAL('F','O','R','M'));

/* hole Laenge des BODY-Chunks damit die Laenge des FORM-Chuncks geschrieben
   werden kann */

    for(j=0;j<meinbmhd.h;j++) {
 	for(i=0;i<meinbmhd.nPlanes;i++) {
	    maps[i] = s2->BitMap.Planes[i];
	    bodylen += packrow(maps[i]+x*j,picbuff,(BYTE)x);
	}
    }
/* sollte die Laenge des BODY-Chuncks ungerade sein, muss ein pad-Byte ge-
   schrieben werden, da IFF-Chuncks nur gerade Laenge haben duerfen */

    odd=bodylen;
    if(odd&1) {
	putlong(fip,(minlen+bodylen+1));
    }
    else {
	putlong(fip,(minlen+bodylen));
    }
/* schreibe ILBM und BMHD auf Disk */

    putlong(fip,IDVAL('I','L','B','M'));
    meinbmhd.compression = 1;
    meinbmhd.masking = 0;
    putlong(fip,IDVAL('B','M','H','D'));
    putlong(fip,20L);
    loc_bmhd = (UBYTE *)(&meinbmhd);
    for(i=0;i<20;i++) {
	putbyte(fip,*loc_bmhd++);
    }
/* schreibe den CMAP-Chunck auf Disk */

    putlong(fip,IDVAL('C','M','A','P'));
    putlong(fip,colors);
    loc_cmap = farben;
    for(i=0;i<colors;i++) {
	putbyte(fip,*loc_cmap++);
    }	
/* schreibe BODY-Chunck
   das Bild wird immer kommprimiert und ohne Maske abgespeichert */

    putlong(fip,IDVAL('B','O','D','Y'));
    putlong(fip,bodylen);
    for(j=0;j<meinbmhd.h;j++) {
 	for(i=0;i<meinbmhd.nPlanes;i++) {
	    maps[i] = s2->BitMap.Planes[i];
	    bodylen = packrow(maps[i]+x*j,picbuff,(BYTE)x);
	    for(k=0;k<bodylen;k++) {
		putbyte(fip,picbuff[k]);
	    }
	}
    }

    if(odd) {	/* ungerade, dann schreibe pad-Byte */
	putbyte(fip,0);
    }

    fclose(fip);
    cleanup = NULL;

}


/*------- erstellt eine Rotationsmatrix mit den Winkeln x,y, und z ----------*/

void makerotmatrix(x,y,z)

	double x,y,z;

{

    m[0][0] =  cos(y)*cos(z);
    m[0][1] = -cos(y)*sin(z);
    m[0][2] =  sin(y);
    m[1][0] =  cos(x)*sin(z) + sin(x)*sin(y)*cos(z);
    m[1][1] =  cos(x)*cos(z) - sin(x)*sin(y)*sin(z);
    m[1][2] = -sin(x)*cos(y);
    m[2][0] =  sin(x)*sin(z) - cos(x)*sin(y)*cos(z);
    m[2][1] =  sin(x)*cos(z) + cos(x)*sin(y)*sin(z);
    m[2][2] =  cos(x)*cos(y);

}


/*------- multipliziert einen Vektor mit der Matrix m ----------------------*/

void rotatevec(v1,ve)

	double *v1,*ve;	/* v1 = Pointer auf den zu multiplizierenden Vektor */
			/* ve = Pointer auf Vektor in dem das Ergebnis steht */

{

	COUNT i,j;
	double h;

    for(i=0;i<3;i++) {
	h = 0.0;
	for(j=0;j<3;j++) {
	    h += (m[i][j] * v1[j]);
	}
	ve[i] = h;
    }

}


/*---------- Berechnung der y-Groesse fuer gleiche Breite und Hoehe ---------*/

double getyprop(xs,xw,yw)

	double xs,xw,yw;

{

	double ys,xh;

    xh = xs * BIGESTWIN(xw/2.0);
    ys = xh/BIGESTWIN(yw/2.0);

/* Der Wert "MONITOR" kann veraendert werden, und haengt von der Einstellung
   Ihres Monitors ab. Der Wert gibt an, um wieviel die einzelnen Pixel
   breiter als hoch sind; er muss im Zweifelsfall durch probieren er-
   mittelt werden. */

    /* vermeide Division durch Null */
    ys = (double)(ys == 0.0) ? (1.0) : (ys);
    return(ys);

}


/*---------- Berechnung der x-Groesse fuer gleiche Breite und Hoehe ---------*/

double getxprop(ys,xw,yw)

	double ys,xw,yw;

{

	double xs,yh;

    yh = ys * BIGESTWIN(yw/2.0);
    xs = yh/BIGESTWIN(xw/2.0);

    /* vermeide Division durch Null */
    xs = (double)(xs == 0.0) ? (1.0) : (xs);

    return(xs);

}


/*---------- Transformation eines Bildes ------------------------------------*/

void transformpic(src,dest,xa,xe,ya,ye,xwink,ywink,
		xoff,yoff,xsize,zsize,ref,xrot,yrot,zrot,mode)

	struct RastPort *src,*dest;
	LONG xa,xe,ya,ye;
	double xwink,ywink;
	LONG xoff,yoff;
	double xsize,zsize;
	BOOL ref;
	double xrot,yrot,zrot;
	LONG mode;

{

	double xstep,zstep;
	double xstart,ystart,xstop,ystop;
	double xpic,ypic,ysize;
	double pixstep,piystep;
	double x,z;
	double k[3],h[3],gs[2];
	LONG color,xclip,yclip;

    ScreenToFront(s2);	/* zeige Screen mit Transformiertem Bild */

    makerotmatrix(TOBOGEN(xrot),TOBOGEN(yrot),TOBOGEN(90.0+zrot));

    ysize = xsize;
    ystart = -TOBOGEN(ywink/2.0);
    ystop  = TOBOGEN(ywink/2.0);
    xstart = -TOBOGEN(xwink/2.0);
    xstop  = TOBOGEN(xwink/2.0);
    xstep = (MAXWIN(zsize));
    zstep = (MAXWIN(xsize));
    pixstep = ((double)(xe-xa))/(TOBOGEN(xwink)/zstep);
    piystep = ((double)(ye-ya))/(TOBOGEN(ywink)/xstep);

    SetRast(dest,0L);	/* loesche Screen Nr. 2 */

    ypic = (double)ya;

    for(x=ystart ;x<ystop ;x+=xstep) {	/* ueber alle Zeilen (y) */
	xpic = (double)xa;		/* setze x auf 0 */

	for(z=xstart; z<xstop;z+=zstep) {	/* ueber alle Spalten (x) */

	    color = ReadPixel(src,xa+(LONG)(xpic),ya+(LONG)(ypic));
	    if(color != 0L) {		/* wenn kein Punkt, weiter */

		/* errechne die Koordinaten der Kugeloberflaeche */
		h[0] = xsize*cos(x)*cos(z);
		h[1] = ysize*cos(x)*sin(z);
	    	h[2] = zsize*sin(x);
	    	rotatevec(h,k);		/* rotiere Punkt der Oberflaeche */

	    	SetAPen(dest,color);	/* uebertrage Farbe */
		switch(ref) {
		    case(0):{
			xclip = xoff-(LONG)k[0];
			yclip = yoff+(LONG)(k[2]*ASPECT);
			break;
		    }
		    case(1):{
			xclip = xoff-(LONG)(k[0]/ASPECT);
			yclip = yoff+(LONG)k[2];
			break;
		    }
		    default:{
			xclip = xoff-(LONG)k[0];
			yclip = yoff+(LONG)k[2];
			break;
		    }
		}
		xclip = ((xclip<0L)||(xclip>=(LONG)meinbmhd.w)) ? -1L : xclip;
		yclip = ((yclip<0L)||(yclip>=(LONG)meinbmhd.h)) ? -1L : yclip;

		if((xclip >= 0) && (yclip >= 0)) {
		    switch(mode) {

			case VOLL :{	/* undurchsichtig */
			    if(k[1]>0.0) {	/* zeigt Vektor nach vorn ? */
				WritePixel(dest,xclip,yclip);
			    }
			    break;
			}

			case LEER :{
			    if(k[1]<0.0) {	/* zeigt Vektor nach hinten ? */
			        /* dann setze Pixel wenn noch keiner ge-
				   setzt ist */	

				if(ReadPixel(dest,xclip,yclip)==0L)
				    WritePixel(dest,xclip,yclip);
			    }
			    else {
				WritePixel(dest,xclip,yclip);
			    }
			    break;
			}

	    	    }	/* ende switch */

		}	/* ende clipping */

	    } /* ist keine Farbe gesetzt, wird hier weitergemacht  */

	    /* naechste x-position */
	    xpic += pixstep;

	} /* ende ueber alle Spalten */ 

	if(s2->TopEdge >= 230) {	/* Screen Nr.2 unten ? */
	    WBenchToFront();
	    MoveScreen(s2,0L,-512L);
	    printf("IFF-Transformation unterbrochen.\n");
	    cont:
	    printf("Weiter (j,n) ? ");
	    scanf("%s",zeichenpuffer);
	    if(zeichenpuffer[0]=='n') {
		printf("IFF-Transformation abgebrochen .\n");
		return();
		} 
	    else {
	    	if(zeichenpuffer[0]!='j')
		goto cont;
	    }
	    WBenchToBack();	
	}

	/* nachste y-Position */
	ypic += piystep;

    }  /* ende ueber alle Zeilen  */

    printf("IFF-Transformation beendet.\n");

}


/*----------------------------------------------------------------------------*/

main()

{
	
    GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L);
    if(GfxBase == NULL)
	cleanexit("graphics library kann nicht geoeffnet werden !");

    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0L);
    if(IntuitionBase == NULL)
	cleanexit("intuition library kann nicht geoeffnet werden !",NULL);

    printf("\n\n        IFF-Transformer V1.3  \n");
    printf(" written in 1987 by Christian Moench.\n\n");
    printf("Name des zu transformierenden Bildes => ");
    scanf("%s",zeichenpuffer);
    printf("loading %s\n",zeichenpuffer);
    loadpic(zeichenpuffer);
    WBenchToFront();
    dotrans();
    getuser:
    WBenchToFront();
    printf("Speichern, Neustart, Laden oder Ende (s,n,l,e) ? ");
    scanf("%s",zeichenpuffer);
    if(zeichenpuffer[0]=='e') {
	cleanexit("IFF-Transformation beendet.\n");
    }
    if(zeichenpuffer[0]=='n') {
	dotrans();
	goto getuser;
    }
    if(zeichenpuffer[0]=='s') {
	name:
	printf("Name des Bildes => ");
	scanf("%s",zeichenpuffer);
	printf("saving %s.\n",zeichenpuffer);
	savepic(zeichenpuffer);
    }
    if(zeichenpuffer[0]=='l') {
	printf("Name des zu transformierenden Bildes => ");
	scanf("%s",zeichenpuffer);
	printf("loading %s\n",zeichenpuffer);
	loadpic(zeichenpuffer);
    }
    goto getuser;
}


/*--------------- hole Werte vom User, starte Transformation ---------------*/

dotrans()
{
	WORD xan,xen,yan,yen,xo,yo,mo;
	double xwin,ywin,xr,yr,zr,xsi,ysi;
	BOOL xprop,yprop,ref;

    ref = 2;
    printf("Parameter eingeben:\n");

    xa:
    printf("xstartpositon (0-%d) ? ",meinbmhd.w);
    scanf("%d",&xan);
    if(xan<0 || xan>meinbmhd.w) {
	printf("illegaler Wert.\n");
	goto xa;
    }

    xe:
    printf("xstopposition (0-%d) ? ",meinbmhd.w);
    scanf("%d",&xen);
    if(xen<0 || xen>meinbmhd.w) {
	printf("illegaler Wert.\n");
	goto xe;
    }

    ya:
    printf("ystartpositon (0-%d) ? ",meinbmhd.h);
    scanf("%d",&yan);
    if(yan<0 || yan>meinbmhd.h) {
	printf("illegaler Wert.\n");
	goto ya;
    }

    ye:
    printf("ystopposition (0-%d) ? ",meinbmhd.h);
    scanf("%d",&yen);
    if(yen<0 || yen>meinbmhd.h) {
	printf("illegaler Wert.\n");
	goto ye;
    }

    xwin:
    printf("xwinkel (1-360) ? ");
    scanf("%f",&xwin);
    if((xwin>360.0) || (xwin<1.0)) {
	printf("illegaler Wert.\n");
	goto xwin;
    }

    ywin:
    printf("ywinkel (1-180) ? ");
    scanf("%f",&ywin);
    if((ywin>180.0) || (ywin<1.0)){
	printf("illegaler Wert.\n");
	goto ywin;
    }

    rot:
    printf("xrot,yrot,zrot ? ");
    scanf("%f,%f,%f",&xr,&yr,&zr);

    xo:
    printf("xoffset,yoffset ? ");
    scanf("%d,%d",&xo,&yo);
    if((xo<0 || xo>meinbmhd.w) || (yo<0 || yo>meinbmhd.h)) {
	printf("illegaler Wert.\n");
	goto xo;
    }

    six:
    xprop = FALSE;
    printf("xradius (0 = X-Propmod.) ? ");
    scanf("%f",&xsi);
    if(xsi < 1.0) {
	if(xsi == 0.0) {
	    xprop = TRUE;
	    printf("  X-Propotionalmodus eingeschaltet.\n");
	}
	else {
	    printf("illegaler Wert.\n");
	    goto six;
	}
    }

    siy:
    yprop=FALSE;
    printf("yradius (0 = Y-Propmod.) ? ",meinbmhd.h/2);
    scanf("%f",&ysi);
    if(ysi < 1.0) {
	if(ysi == 0.0) {
	    if(xprop) {
		printf("Kein xradius vorhanden (X-Proportionalmodus) !\n");
		goto siy;
	    }
	    ysi = getyprop(xsi,xwin,ywin);
	    printf("  Y-Proportionalmodus eingeschaltet.\n");
	    printf("  yradius = %f\n",ysi);
	    yprop=TRUE;
	    goto rg;
	}
	printf("illegaler Wert.\n");
	goto siy;
    }
    if(xprop) {
	xsi = getxprop(ysi,xwin,ywin);
	printf("  xradius = %f\n",xsi);
    }

    rg:
    if((ASPECT != 1.0) && (xprop || yprop)) {
	printf("  Referenzgroesse eingeben (x oder y) ? ");
	scanf("%s",zeichenpuffer);
	if(zeichenpuffer[0]=='x') {
	    printf("  xradius ist Referenzgroesse.\n");
	    ref = 0;
	}
	else {
	    if(zeichenpuffer[0]!='y') {
		goto rg;
	    }
	    printf("  yradius ist Referenzgroesse.\n");
	    ref = 1;
	}
    }

    mo:
    if((xsi > meinbmhd.w/2) || (ysi > meinbmhd.h/2)) {
	if((xwin < 180.0) || (ywin < 180.0)) 
	    printf("  Achtung: Teile koennen unsichtbar sein !\n");
    }	
    printf("Modus (0 or 1) ? ");
    scanf("%d",&mo);
    if(mo!=0 && mo!=1) {
	printf("  unbekannter Modus.\n");
	goto mo;
    }
    WBenchToBack();
    transformpic(rp,rp2,
	(LONG)xan,(LONG)xen,(LONG)yan,(LONG)yen,
	xwin,ywin,
	(LONG)xo,(LONG)yo,
	xsi,ysi,ref,xr,yr,zr,
	(LONG)mo);

}


/*---- Verlassen des Programms und schliessen der Screen,Libraries,Files ----*/

cleanexit(errmes)

	char* errmes;

{
	int i;

    if(errmes != NULL) {
	printf("%s\n",errmes);
    }
    if(cleanup != NULL) {
	fclose(cleanup);
    }
    if(s != NULL) {
	CloseScreen(s);
    }
    if(s2 != NULL) {
	CloseScreen(s2);
    }
    if(IntuitionBase != NULL) {
	CloseLibrary(IntuitionBase);
    }
    if(GfxBase != NULL) {
	CloseLibrary(GfxBase);
    }

    exit(0L);

}
