
// Scale 
// (C) 1996 Clemens Marschner
// $VER: Scale 2.4 (12-Apr-96; 13:36)
// Compiliert mit Maxon-C++ (makefile)

#include <intuition/intuition.h>
#include <graphics/gfx.h>
#include <graphics/rastport.h>
#include <pragma/intuition_lib.h>
#include <pragma/exec_lib.h>
#include <pragma/graphics_lib.h>
#include <pragma/dos_lib.h>
#include <stream.h>

// defines

#define FOREVER for(;;)

// Protos

// Assembler-Funktion: Schreibt die Pixel horizontal
extern "C" void WriteLine(register __a0 const char *src, register __a1 char *dest, 
					register __a2 const char *werte, register __d0 int breite);


void PollLoop();
void Scale(struct RastPort *rp, int dx, int dy, int x1, int y1);
void CalcProps(UBYTE *ziel, int b, int dx);

// Konstanten

const int b = 12*8, h = 12*9;

// externe und globale Variablen

extern const char bild[h][b];					
// die Bitmap. Der Demonstration halber als Chunky-Array (leichterer Zugriff!)
// siehe bild.c

struct Window *win; 
struct RastPort *rp;

void Example() {
	int mx = win->Width/2;
	int my = win->Height / 2;
	for(int i=0; i<40; i+=4) {
		Scale(rp, b+i*2+10, h+i+10, 5,15);
	}
}

void main() {
	win = OpenWindowTags(NULL, 
			WA_Flags, WFLG_ACTIVATE | WFLG_CLOSEGADGET | WFLG_DEPTHGADGET | 
			WFLG_DRAGBAR| WFLG_NOCAREREFRESH|WFLG_DEPTHGADGET|WFLG_SIZEGADGET,
			WA_Top, 11,				WA_Width, 480,			WA_Height, 300,
			WA_Left, 238,			WA_Top, 0,				WA_Title, "Scaling",
			WA_MinWidth, 50, WA_MinHeight, 25, WA_MaxWidth,~0,WA_MaxHeight,~0,
			WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE,
			WA_BlockPen, 4,			WA_DetailPen, 3,			TAG_END);
	if(!win) { cout << "Window offenbar nicht offenbar\n"; return; };
	rp = win->RPort;
	try {
		Delay(125); // warten, bis alle Files zu sind (bei Start aus dem Compiler heraus)
		Example();  // zeichnet ein paar Bitmaps
		PollLoop(); // paßt die Bitmap dem Window an
	} catch(...) { cout << "Fehler\n"; }
	CloseWindow(win);
}


inline void Clear(UBYTE*m,int sz) {
	while(sz--) *m++=0;
}

// zentrale Routine: Berechnet die Pixelweiten und legt sie im Array "ziel" ab.

void CalcProps(UBYTE *ziel, int qubreite, int zlbreite) {
	int x = 0, y = 0, error, dx=zlbreite, dy=qubreite;
	if(zlbreite > (qubreite<<8)) 	{
		cout << "Zu groß. Höchstens 25500% möglich\n";	return;
	}

	// double 	error = -(0.5 - 0.5*m) * 2*(dx+1) - (dx+1)
	//																	^ 0.5 * s (Prüfbedingung)
	//													^ Skalierung (Faktor s) -> ints
	//									^ error-Wert durch korrigierte Linie
	// 					= (-1 + m) * (dx+1) - (dx+1) mit m = (dy+1)/(dx+1)
	//	int 		error = (-(dx+1) + (dy+1)) - (dx+1)
	//						= -dx - 1 + dy + 1 - dx -1
	//						=  dy-dy - dx-1
	//						= dy-2*dx-1
	// bei Verkleinerung: dx und dy vertauscht
	Clear(ziel, qubreite);
	
	if(qubreite<=zlbreite) {				// vergrößern
		error= dy-2*dx-1;	   				// (dy-dx - (dx+1))	
		int sub=2*(dx+1), add=2*(dy+1), i = zlbreite;
		//			  ^ m*s			^ 1*s
		for(i=zlbreite; i; i--) {			// (i) prüfen ist schneller als (i<zlbreite)
			ziel[x]++;
			if(error>0) {
				x++;
				error-=sub;
			} 
			error+=add;
		} 
		return;
	} else {	// verkleinern: dx und dy vertauscht, um in 1. Oktanden zu kommen
		error= dx-2*dy-1;					   	
		int sub=2*(dy+1), add=2*(dx+1);		
		for (int i = 0; i< qubreite; i++) {	// hier die langsame Version 
			x++;
			if(error>0) {
				ziel[x]++;
				error-=sub;
			} 
			error+=add;
		}
		return;
	} 	
}

UBYTE breiteanz[2000];
UBYTE hoeheanz[2000];
struct RastPort trp;		// für WritePixelLine8
struct BitMap tbm;		//			""

// Scale: Skaliert Bild bild[][] mit Breite b und Hoehe h

void Scale(struct RastPort *rp, int dx, int dy, int x1, int y1) {
	// dx/dy: Zielbreite/Höhe. x1/y1: Linke obere Ecke der Ausgabe

	if(!(dx||dy))	return; 		// Eine Nullfläche brauchen wir nicht ausgeben
	
	// viel Administratives für WritePixelLine8	
	UBYTE *zbrpuffer = new char[((dx+1 + 15)>>4)<<4];	// auf 16 aufgerundet
	UBYTE *zhopuffer = new char[((dy+1 + 15)>>4)<<4];	
	int 	quelly = 0;

	// RastPort fertigmachen...
	trp = *rp;								trp.Layer = NULL;		tbm.Rows = 1;
	tbm.Depth = rp->BitMap->Depth;	trp.BitMap = &tbm;
	tbm.BytesPerRow = ((dx + 1 + 15) >> 4) << 1;		// Empfehlung des RKM
	
	struct BitMap *bm = trp.BitMap;
	
	// Eine Bitmap mit vier Zeilen, die danach als vier Bitmaps mit einer Zeile
	// aufgefaßt wird
	if(!(tbm.Planes[0] = AllocRaster(dx, rp->BitMap->Depth))) {
		cout << "AllocRaster returnierte 0\n";
		throw(int(1));
	}
	
	for(int m = 1; m < rp->BitMap->Depth; m++) {
		tbm.Planes[m] = tbm.Planes[0] + tbm.BytesPerRow * m * tbm.Rows;
	}
	
	// Hier werden die Pixelbreiten berechnet:	
	CalcProps(breiteanz, b, dx);
	// und hier die Höhen:
	CalcProps(hoeheanz, h, dy);
	// -> breiteanz[i]: die Breiten der Pixel mit x=i
	// -> hoeheanz[j]: die Höhen der Pixel mit y=j

	int dyc = dy+y1;
	while(y1 < dyc) {
		if(hoeheanz[quelly] > 0) 				// Wenn die Zeile überhaupt auftaucht...
		{
			WriteLine(bild[quelly],zbrpuffer,breiteanz,b);
				// Die Assembler-Version ist ein Ersatz für:
				//UBYTE *zbcp = zbrpuffer;
         	//for(int i=0; i < b; i++)
         	//	for(int j = 0; j < breiteanz[i]; j++)
         	//		*zbcp++ = bild[quelly][i];
				// ziemlich langsam
			UBYTE anz = hoeheanz[quelly];
			// die erste Zeile mit WritePixelLine8() ausgeben
			if(anz) {
				WritePixelLine8(rp, x1,y1++, dx, zbrpuffer, &trp);
				anz--;
			}
			// im Ggs. zu einigen Grafikkarten-Routinen löscht WritePixelLine8()
			// den Quellbereich zbrpuffer. Dafür steht eine Kopie der Zeile in bm
			// d.h.: jede weitere Zeile einfach kopieren
			while (anz--) {
				BltBitMapRastPort(bm, 0,0,rp,x1,y1++,dx,1,ABC|ABNC);
			}
		}
		quelly++; 
	}
		
	FreeRaster(tbm.Planes[0],dx, rp->BitMap->Depth);
	delete zbrpuffer;
	delete zhopuffer;
}

// PollLoop: Paßt das Bild immer auf Fensterbreite an

void PollLoop() {
	BOOL running = TRUE, mbpressed=FALSE;
	struct IntuiMessage *imsg, icpy;
	if(!win) return;
	Scale(rp, win->Width - win->BorderLeft - win->BorderRight, 
				win->Height - win->BorderTop - win->BorderBottom,
				win->BorderLeft+1, win->BorderTop+1);
	for(;;)
	{
		Wait(1 << win->UserPort->mp_SigBit);
		while(imsg = (struct IntuiMessage*)GetMsg(win->UserPort)) 
		{
			icpy = *imsg;	// C++-Kopie
			ReplyMsg((struct Message*)imsg);
			switch(icpy.Class) 
			{
				case IDCMP_CLOSEWINDOW:
					running = FALSE;
					break;
				case IDCMP_NEWSIZE:
					Scale(rp, win->Width - win->BorderLeft - win->BorderRight-1, 
								win->Height - win->BorderTop - win->BorderBottom,
								win->BorderLeft+1, win->BorderTop);
					break;
			}
		}
		if(!running) break;
	}
}

// i.F. einige Vorversionen von CalcProps() (Version 1 ist ganz interessant)

/****
void CalcProps(UBYTE *ziel, int qubreite, int zlbreite) {
	double x = 0, y = 0, error, dx, dy;
	int summe=0;
	if(zlbreite > (qubreite<<8)) 
	{
		cout << "Zu groß. Höchstens 25500% möglich\n";
		return;
	}
	BOOL kleiner = FALSE;
	
	if(qubreite >= zlbreite) {
		kleiner = TRUE;
	}
	
	dx = qubreite;			
	dy = zlbreite;
	
	error = -dx-1;

	if(kleiner) {
		for (int i = 0; i< zlbreite; i++) 
		{
			summe+=ziel[x];
			x++;
			if(error>=0) {
				ziel[x]++;
				//cout << "z["<<x<<"]="<<ziel[x]<<"; ";
				error-=dy+1;
			} 
			error+=2*dx+2;
		}
	if(summe != qubreite) cout << "Summe: "<<summe<<"; z: "<<zlbreite<<endl;
	}
	else 
	{
		for (int i = 0; i< qubreite; i++) {
			ziel[x]++;
			if(error>=0) {
				//cout << "z["<<x<<"]="<<ziel[x]<<"; ";
				summe+=ziel[x];
				x++;
				error-=dx+1;	// error-=0.5 skaliert mit 2*dy+1
			} 
			error+=2*dy+2;	// error+=m skaliert mit 2*(dx+1)
		}
	if(summe != zlbreite) cout << "Summe: "<<summe<<"; z: "<<zlbreite<<endl;
	}
}

// Version 1: mit doubles
void CalcProps(UBYTE *ziel, int qubreite, int zlbreite) {
	double x = 0, y = 0, error, dx=zlbreite, dy=qubreite, m;
	int summe=0;
	if(zlbreite > (qubreite<<8)) 
	{
		cout << "Zu groß. Höchstens 25500% möglich\n";
		return;
	}
	BOOL kleiner = (qubreite>=zlbreite);

	// nur Vergrößerung
	if(kleiner) {
		return;
	}	
	for(int i=0; i<qubreite; i++) ziel[i]=0;

	// Fehler: 1 bis zwei Pixel -- wegen double-Fehler???
	cout << "Vergrößerung von " << qubreite << " auf " << zlbreite << " Pixel\n";
	m = (dy+1)/(dx+1);
	error= 0.5 - 0.5*m;	// muss man sich mit einer Zeichnung klarmachen
	cout << "Steigung: "<<m<<"; error = " << error << "\n";
	for (i = 0; i< zlbreite; i++) {
		ziel[x]++;
		if(error>=0.5) {
			summe+=ziel[x];
			x++;
			error--;
		} 
		error+=m;
	}
	if(summe != zlbreite) cout << "Summe: "<<summe<<"; z: "<<zlbreite<<endl;

}

void CalcProps(UBYTE *ziel, int qubreite, int zlbreite) {
	int x = 0, y = 0, error, dx=zlbreite, dy=qubreite, m;
	int summe=0;
	if(zlbreite > (qubreite<<8)) 
	{
		cout << "Zu groß. Höchstens 25500% möglich\n";
		return;
	}
	BOOL kleiner = (qubreite>=zlbreite);

	// nur Vergrößerung
	if(kleiner) {
		return;
	}	
	
	// alle Werte mit 2*(dx+1) multipliziert
	error= dy-dx;	
	int sub=2*(dx+1);
	int add=2*(dy+1);
	for (i = 0; i< zlbreite; i++) {
		ziel[x]++;
		if(error>=dx+1) {
			summe+=ziel[x];
			x++;
			error-=sub;
		} 
		error+=add;
	}
	if(qubreite != x) cout << "->Qubreite: " << qubreite << "; x: "<<x<<" (Fehler: "<<qubreite-x<<")"<<endl; 
	if(summe != zlbreite) cout << "->Summe: "<<summe<<"; zlbreite: "<<zlbreite<<" (Fehler: "<<zlbreite-summe<<")"<<endl;
}

***/
