#include "stdafx.h"
#include "basic.h"
#include "Textures.h"
#include "Eroc.h"
#include "time.h"
#include "OGLState.h"
#include <stdlib.h>

#ifdef import_globals
#undef import_globals
#endif
#include "globals.h"

// Framerate
int theNrFramesThisSec = 0;
int theFPSLastSec = 0;
time_t theLastTime = 0;

#define PI 3.141592654

/////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////
int gNrAnims = 0;
Animation gAnims[256];

void	LoadAnims()
{
	FILE* AnimList = fopen("AnimList.txt", "r");
	if (! AnimList )
		return;

	long int Duration;
	char Token[256];
	while (1 == fscanf(AnimList, " %s", Token ))
	{		
		if (strcmp(Token, "BEGIN") == 0)
		{		
			Animation& Anim = gAnims[gNrAnims];
			while(2 == fscanf(AnimList, " %s %ld", Token, &Duration ))
			{
				// Load the texture so it is cached
				CreateTextureFromFile(Token, false);
				Anim.mFrames[Anim.mNrFrames++] = new Frame(Token, Duration);
			}

			gNrAnims++;

			if (strcmp(Token, "END") != 0)
				break;			
		}
	}
	fclose(AnimList);
}

Animation*	GetAnim(int inNr)
{
	Animation* Result = 0;
	if (inNr >= 0 && inNr < gNrAnims)
		Result = &(gAnims[inNr]);
	return Result;
}

void	FreeAnims()
{
	for (int i = 0; i < gNrAnims; i++)
	{
		for ( int j = 0; j < gAnims[i].mNrFrames; j++)
			delete gAnims[i].mFrames[j];
	}
	gNrAnims = 0;
}

/////////////////////////////////////////////////////////////////////////////

EROC_API BOOL Clear ( void ) 
{
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	return true;
}

EROC_API BOOL BackgroundColor( signed long inRed, signed long inGreen, signed long inBlue)
{
	OGLState::PushBGColor(255, inRed, inGreen, inBlue);
	glClear( GL_COLOR_BUFFER_BIT);
	OGLState::PopBGColor();
	return true;
}


EROC_API BOOL Background ( signed long inImageNr ) 
{
	return BackgroundFade(inImageNr, 256);
}


EROC_API BOOL BackgroundFade ( signed long inImageNr, signed long inOpacity ) 
{
	OGLState::PushBlend(true);
	OGLState::PushBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	OGLState::PushTexture2D(true);

	Texture* Tex = GetTexture(inImageNr);
	OGLState::PushTextureUsed(Tex->mOpenGLTex);

	// sanity check
	if (inOpacity < 0)
		inOpacity = 0;
	else if (inOpacity > 256)
		inOpacity = 256;

	OGLState::PushMatrixMode(GL_MODELVIEW);
	glPushMatrix ( );
	
	float ScaleX = Tex->mScaleX;
	float ScaleY = Tex->mScaleY;
	glBegin ( GL_QUADS );
		glNormal3f( 0.0f, 0.0f, 1.0f);
		glColor4f ( 1, 1, 1, inOpacity/256.0f );
		glTexCoord2f(0, ScaleY);		glVertex3f (-1.0,-1.0, 0 );
		glTexCoord2f(ScaleX, ScaleY);	glVertex3f ( 1.0,-1.0, 0 );
		glTexCoord2f(ScaleX, 0);		glVertex3f ( 1.0, 1.0, 0 );
		glTexCoord2f(0, 0);				glVertex3f (-1.0, 1.0, 0 );
	glEnd ( );	
	glPopMatrix ( );
	OGLState::PopMatrixMode();

	OGLState::PopTextureUsed();
	OGLState::PopTexture2D();
	OGLState::PopBlendFunc();	
	OGLState::PopBlend();						
	return true;
}

EROC_API BOOL FastBackground ( signed long inImageNr, signed long inPosX, signed long inPosY,
							   signed long inSizeX, signed long inSizeY )
{
	OGLState::PushTexture2D(true);
	Texture* Tex = GetTexture(inImageNr);		
	OGLState::PushTextureUsed(Tex->mOpenGLTex);
	
	OGLState::PushMatrixMode(GL_MODELVIEW);
	glPushMatrix ( );
	
	float ScaleX = Tex->mScaleX;
	float ScaleY = Tex->mScaleY;

	float TexX1 = ScaleX*inPosX/1000.0f;
	float TexY1 = ScaleY*inPosY/1000.0f;
	float TexX2 = TexX1 + ScaleX*inSizeX/1000.0f;
	float TexY2 = TexY1 + ScaleY*inSizeY/1000.0f;
	glBegin ( GL_QUADS );
		glNormal3f( 0.0f, 0.0f, 1.0f);
		glColor3f ( 1, 1, 1 );
		glTexCoord2f(TexX1,TexY2);		glVertex3f ( -1.0, -1.0, 0 );
		glTexCoord2f(TexX2,TexY2);		glVertex3f (  1.0, -1.0, 0 );
		glTexCoord2f(TexX2,TexY1);		glVertex3f (  1.0,  1.0, 0 );
		glTexCoord2f(TexX1,TexY1);		glVertex3f ( -1.0,  1.0, 0 );
	glEnd ( );

	glPopMatrix ( );
	OGLState::PopMatrixMode();

	OGLState::PopTextureUsed();
	OGLState::PopTexture2D();

	return true;
}

// Doesn't set blending!
BOOL TexImage( const Texture* inTex, signed long inPosX, signed long inPosY,
					 signed long inScale, signed long inOpacity ) 
{
	OGLState::PushTexture2D(true);	
	OGLState::PushTextureUsed(inTex->mOpenGLTex);
	
	// sanity check
	if (inOpacity < 0)
		inOpacity = 0;
	else if (inOpacity > 256)
		inOpacity = 256;

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix ( );

	float TCenterX = inTex->mScaleX/2;
	float TCenterY = inTex->mScaleY/2;
	float THalfX =  inTex->mScaleX/2;
	float THalfY =  inTex->mScaleY/2;
	
	float CenterX = 2*inPosX/gVScreenX - 1;
	float CenterY = 1-2*inPosY/gVScreenY ;
	float HalfX = inScale *inTex->mSizeX/(gVScreenX*1000);
	float HalfY = inScale *inTex->mSizeY/(gVScreenY*1000);
	
	glBegin ( GL_QUADS );
		glNormal3f( 0.0f, 0.0f, 1.0f);
		glColor4f ( 1, 1, 1, inOpacity/256.0f );
		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (CenterX-HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (CenterX+HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (CenterX+HalfX,CenterY+HalfY, 0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (CenterX-HalfX,CenterY+HalfY, 0 );
	glEnd ( );

	glPopMatrix ( );
	OGLState::PopMatrixMode();
		
	OGLState::PopTextureUsed();
	OGLState::PopTexture2D();

	return true;
}

// This one sets blending!
EROC_API BOOL Image( signed long inImageNr, signed long inPosX, signed long inPosY,
					 signed long inScale, signed long inOpacity )
{
	OGLState::PushBlend(true);
	OGLState::PushBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	bool theResult = TexImage( GetTexture(inImageNr),inPosX, inPosY, inScale, inOpacity );
	OGLState::PopBlendFunc();	
	OGLState::PopBlend();
	return theResult;
}

EROC_API BOOL Animate( signed long inAnimNr, signed long inPosX, signed long inPosY,
					 signed long inScale, signed long inOpacity, signed long inTime ) 
{
	Animation* Anim = GetAnim(inAnimNr);
	if ( Anim )
	{
		int Frame = -1;
		while (inTime >= 0)
		{
			if ( ++Frame >= Anim->mNrFrames)
				Frame = 0;

			inTime -= Anim->mFrames[Frame]->mDuration;
		}
		// "load" cached texture
		int ImageNr = CreateTextureFromFile(Anim->mFrames[Frame]->mName, false);

		Image( ImageNr, inPosX, inPosY, inScale, inOpacity);
	}
	return true;
}

EROC_API BOOL PrintNr ( signed long inNr, signed long inPosX, signed long inPosY )
{
	int theX = inPosX;
	int theY = (int)(gVScreenY - inPosY);

	OGLState::PushBlend(false);

	char String[256];
	sprintf (String, "%li", inNr);
	glPrint(theX, theY,String, 0);

	OGLState::PopBlend();
	return true;
}

EROC_API BOOL ShowFPS ( signed long inPosX, signed long inPosY )
{
	OGLState::PushBlend(false);
	// Calculate FPS, should move to BeginNewFrame
	time_t theTime = time(0);
	if (theTime != theLastTime)
	{
		theLastTime = theTime;
		theFPSLastSec = theNrFramesThisSec;
		theNrFramesThisSec = 0;
	}
	theNrFramesThisSec++;

	int theX = inPosX;
	int theY = (int)(gVScreenY - inPosY);
	char theFPS[10];
	sprintf(theFPS, "%d", theFPSLastSec);
	glPrint(theX, theY, theFPS ,0);	// Print GL Text To The Screen

	OGLState::PopBlend();
	return true;
}

// Nr waves = x/1000 * up & down wave
// Size : 1000 = size image
// Offset: 1000 = 1 up/down sine wave
EROC_API BOOL Wave( signed long inImageNr, signed long inPosX, signed long inPosY,
					 signed long inScale, signed long inOpacity,
					 signed long inWavesX, signed long inWavesY, signed long inSizeX, signed long inSizeY,
					 signed long inOffsetX,signed long inOffsetY)
{
	Texture* Tex = GetTexture(inImageNr);

	int SectionsX = 1;
	int SectionsY = 1;
	if (inWavesX)
	{
		// for now, take about 15 sections per wave, regardless of magnitude
		SectionsX = 15*inWavesX;
	}
	if (inWavesY)
	{
		// for now, take about 15 sections per wave, regardless of magnitude
		SectionsY = 15*inWavesY;
	}

	// sanity check
	if (inOpacity < 0)
		inOpacity = 0;
	else if (inOpacity > 256)
		inOpacity = 256;

	GLboolean TextureEnable = glIsEnabled(GL_TEXTURE_2D);
	if ( !TextureEnable)
		glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix ( );

	float TCenterX = Tex->mScaleX/2;
	float TCenterY = Tex->mScaleY/2;
	float THalfX =  Tex->mScaleX/2;
	float THalfY =  Tex->mScaleY/2;
	
	float CenterX = 2*inPosX/gVScreenX - 1;
	float CenterY = 1-2*inPosY/gVScreenY ;
	float HalfX = inScale *Tex->mSizeX/(gVScreenX*1000);
	float HalfY = inScale *Tex->mSizeY/(gVScreenY*1000);

	glBindTexture(GL_TEXTURE_2D, Tex->mOpenGLTex);
	glNormal3f( 0.0f, 0.0f, 1.0f);
	glColor4f ( 1, 1, 1, inOpacity/256.0f );

	float StartX = CenterX - HalfX;
	float StartY = CenterY - HalfY;
	float IncX = 2*HalfX/SectionsX;
	float IncY = 2*HalfY/SectionsY;

	float TStartX = TCenterX - THalfX;
	float TStartY = TCenterY + THalfY;
	float TIncX = 2*THalfX/SectionsX;
	float TIncY = 2*THalfY/SectionsY;

	for (int j = 0; j < SectionsY; j++)
	{

		// X waves are based on y-coordinates and vice-versa
		float WaveX = HalfX*inSizeY/1000.0 * sin( 2.0*PI * inOffsetY/1000.0 +  (2.0*PI * j * inWavesX) / SectionsX );
		float WaveXP = HalfX*inSizeY/1000.0 * sin( 2.0*PI * inOffsetY/1000.0 + (2.0*PI * (j+1) * inWavesX) / SectionsX );

		for (int i = 0; i < SectionsX; i++)
		{
			float WaveY = HalfY*inSizeX/1000.0 * sin( 2.0*PI*inOffsetX/1000.0 + (2.0*PI * i * inWavesY) / SectionsY );
			float WaveYP = HalfY*inSizeX/1000.0 * sin( 2.0*PI*inOffsetX/1000.0 + (2.0*PI * (i+1) * inWavesY) / SectionsY );

			glBegin(GL_QUADS);
				glTexCoord2f(TStartX + i*TIncX, TStartY - j*TIncY);			glVertex3f (StartX + i*IncX +WaveX, StartY + j*IncY +WaveY, 0 );
				glTexCoord2f(TStartX + (i+1)*TIncX, TStartY - j*TIncY);		glVertex3f (StartX + (i+1)*IncX +WaveX, StartY + j*IncY +WaveYP, 0 );
				glTexCoord2f(TStartX + (i+1)*TIncX, TStartY - (j+1)*TIncY);	glVertex3f (StartX + (i+1)*IncX +WaveXP, StartY + (j+1)*IncY +WaveYP, 0 );
				glTexCoord2f(TStartX + i*TIncX, TStartY - (j+1)*TIncY);		glVertex3f (StartX + i*IncX +WaveXP, StartY + (j+1)*IncY +WaveY, 0 );
			glEnd();
		}
	}

	glPopMatrix ( );

	if (! TextureEnable)
		glDisable(GL_TEXTURE_2D);

	return true;
}

// 
void	
GreyTexture(unsigned int* inLFB, int inX, int inY)
{
	for (unsigned int y = 0; y < inY; y++)
		for (unsigned int x = 0; x < inX; x++)
		{		
			// Monochrome:  red		= 30% = 76/255;
			//				green	= 59% = 151/255
			//				blue	= 11% = 28/255

			unsigned int theColor = *(inLFB + y*inX + x);
			unsigned int theRed   = 76 * ((theColor >> 16 ) & 0xFF);
			unsigned int theGreen = 151 * ((theColor >> 8 ) & 0xFF);
			unsigned int theBlue  = 28 * ((theColor >> 0 ) & 0xFF);

			*(inLFB + y*inX + x) = 0x010101 * ((theRed + theGreen + theBlue) >> 8);
		}
}

int Clamp(int Value, int Upper)
{
	if (Value <=0)
		return 0;

	if (Value >= Upper)
		return Upper;

	return (Value + Upper) % Upper;
	
}

void
DeformTextureBlurHor(unsigned int* inLFB, int inX, int inY, int inAmount)
{
	unsigned int* theTempLFB = new unsigned int[inX*inY];

	for (int y = 0; y < inY; y++)
	{
		// Start cumulative total		
		unsigned int theTotalA = 0, theTotalR = 0, theTotalG = 0, theTotalB = 0;

		for (int i = - inAmount; i <= inAmount; i++) 
		{
			unsigned int* theCurPixel = inLFB + (y * inX) + Clamp(i,inX-1 );
			theTotalA += ((*theCurPixel) >> 24) & 0xFF;
			theTotalR += ((*theCurPixel) >> 16) & 0xFF;
			theTotalG += ((*theCurPixel) >> 8) & 0xFF;
			theTotalB += (*theCurPixel) & 0xFF;
		}
		
		for (int x = 0; x < inX; x++)
		{
			// Draw pixel
			unsigned int theA = (theTotalA*2 + 1) / (2 + (inAmount << 2));
			unsigned int theR = (theTotalR*2 + 1) / (2 + (inAmount << 2));
			unsigned int theG = (theTotalG*2 + 1) / (2 + (inAmount << 2));
			unsigned int theB = (theTotalB*2 + 1) / (2 + (inAmount << 2));
			*(theTempLFB + y * inX + x) = (theA << 24) + (theR << 16) + (theG << 8) + theB;

			// Update Total
			unsigned int* theNewPixel = inLFB + (y * inX) +  Clamp(x + inAmount + 1, inX-1);
			theTotalA += ((*theNewPixel) >> 24) & 0xFF;
			theTotalR += ((*theNewPixel) >> 16) & 0xFF;
			theTotalG += ((*theNewPixel) >> 8) & 0xFF;
			theTotalB += (*theNewPixel) & 0xFF;

			unsigned int* theOldPixel = inLFB + (y * inX) + Clamp(x - inAmount, inX-1);
			theTotalA -= ((*theOldPixel) >> 24) & 0xFF;
			theTotalR -= ((*theOldPixel) >> 16) & 0xFF;
			theTotalG -= ((*theOldPixel) >> 8) & 0xFF;
			theTotalB -= (*theOldPixel) & 0xFF;
		}	
	}
	for (int i = 0; i < inX*inY; i++)
		*(inLFB+i) = *(theTempLFB+i);
    
    delete[] theTempLFB;
}

void
DeformTextureBlurVer(unsigned int* inLFB, int inX, int inY, int inAmount)
{
	unsigned int* theTempLFB = new unsigned int[inX*inY];

	for (int x = 0; x < inX; x++)
	{
		// Start cumulative total	
		unsigned int theTotalA = 0, theTotalR = 0, theTotalG = 0, theTotalB = 0;

		for (int i = - inAmount; i <= inAmount; i++)
		{	
			unsigned int* theCurPixel = inLFB + (Clamp( i, inY-1) * inX) + x;
			theTotalA += ((*theCurPixel) >> 24) & 0xFF;
			theTotalR += ((*theCurPixel) >> 16) & 0xFF;
			theTotalG += ((*theCurPixel) >> 8) & 0xFF;
			theTotalB += (*theCurPixel) & 0xFF;			
		}
		
		for (int y = 0; y < inY; y++)
		{
			// Draw pixel
			unsigned int theA = (theTotalA*2 + 1) / (2 + (inAmount << 2));
			unsigned int theR = (theTotalR*2 + 1) / (2 + (inAmount << 2));
			unsigned int theG = (theTotalG*2 + 1) / (2 + (inAmount << 2));
			unsigned int theB = (theTotalB*2 + 1) / (2 + (inAmount << 2));
			*(theTempLFB + y * inX + x) = (theA << 24) + (theR << 16) + (theG << 8) + theB;

			// Update Total
			unsigned int* theNewPixel = inLFB + x + Clamp(y + inAmount + 1,inY-1) * inX;
			theTotalA += ((*theNewPixel) >> 24) & 0xFF;
			theTotalR += ((*theNewPixel) >> 16) & 0xFF;
			theTotalG += ((*theNewPixel) >> 8) & 0xFF;
			theTotalB += (*theNewPixel) & 0xFF;

			unsigned int* theOldPixel = inLFB + x + Clamp(y - inAmount, inY-1) * inX;
			theTotalA -= ((*theOldPixel) >> 24) & 0xFF;
			theTotalR -= ((*theOldPixel) >> 16) & 0xFF;
			theTotalG -= ((*theOldPixel) >> 8) & 0xFF;
			theTotalB -= (*theOldPixel) & 0xFF;
		}
	}
	for (int i = 0; i < inX*inY; i++)
		*(inLFB+i) = *(theTempLFB+i);
    
    delete[] theTempLFB;
}

// Add color to dest, saturated
void SaturatedAdd(unsigned int& inDest, unsigned int inColor)
{
	for (int i= 0; i < 4; i++)
	{
		unsigned int Component = ((inDest >> (i*8)) & 0xFF) +  ((inColor >> (i*8)) & 0xFF);
		if (Component > 255)
			Component = 255;
		inDest &= ~(0xFF << (i*8));
		inDest += Component << (i*8);
	}
}

// Make dest the maximum of color and dest 
void MaximumAdd(unsigned int& inDest, unsigned int inColor)
{
	for (int i= 0; i < 4; i++)
	{
		unsigned int Component = ((inColor >> (i*8)) & 0xFF);
		if (Component > ((inDest >> (i*8)) & 0xFF))
		{
			inDest &= ~(0xFF << (i*8));
			inDest += Component << (i*8);
		}
	}
}

void ExponentialColor(unsigned int& inColor)
{
	for (int i = 0; i <4; i++)
	{
		unsigned int Component = (inColor >> (i*8)) & 0xFF;
		Component = Component * Component >> 8;
		inColor &= ~(0xFF << (i*8));
		inColor += Component << (i*8);
	}
}


// Cutof: intensity below wich colors are cut of. Above it will be amplified
// ramp: How fast intensity changes. 256 = not at all, higher = faster
// Random: amount of noise   
void ModifyColor(unsigned int& inColor, int inCutoff, int inRamp, int inRandom)
{
	int Noise = rand() % inRandom;
	for (int i = 0; i <4; i++)
	{
		int Component = (inColor >> (i*8)) & 0xFF;
		Component =(((Component - inCutoff) * inRamp) >> 8);
		Component += Noise;
		if (Component < 0)
			Component = 0;
		else if (Component > 255)
			Component = 255;
		
		inColor &= ~(0xFF << (i*8));
		inColor += Component << (i*8);
	}
}

void RampLFB(unsigned int* inLFB, int inLevel, int inRamp, int inSize)
{
	unsigned char* thePixel = (unsigned char *)inLFB;
	for (int i = inSize*4; i ; i--)
	{
		int Component = *thePixel;
		Component = inLevel + (((Component - inLevel) * inRamp) >> 8);
		if (Component < 0)
			Component = 0;
		else if (Component > 255)
			Component = 255;
		
		*(thePixel++) = Component;
	}

}

//
// lines: segments, color = background color of midpoint
// intensity: determines how much 
// rest: alpha = blocks of RasterGap Size 
//
//
void CreateRaster(unsigned int* inLFB, int inSizeX, int inSizeY,
				  unsigned int* inBackGround, unsigned int* inAlphaLayer )
{
	const int RasterGap = 5;
	const int SegmentLength = 20;
	const int MinSegmentLength = 10;
	int i;

	// See ModifyColor
	int Cutoff = 128;
	int Ramp = 256+64;
	int Noise = 50;
	// chance the grid moves a pixel around. Higher = less chance
	int Rigidity = 16;
	// Amount of lines without secondary line. Higher = thin grid
	int DoubleLine = 3;

	// Clear
	for (i = 0; i < inSizeX*inSizeY; i++)
		inLFB[i] = inAlphaLayer[i];

	// Horizontal lines
	for (int y = rand() % RasterGap; y < inSizeY; y += RasterGap)
	{
		
		int StartX = 0;
		int Length = MinSegmentLength + rand() % SegmentLength;

		while (StartX < inSizeX )
		{
			if (StartX + Length > inSizeX)
				Length = inSizeX - StartX;

			// get the background color of the midde of the segment
			unsigned int Color = inBackGround[y*inSizeX + StartX + Length/2];
			ModifyColor(Color, Cutoff, Ramp, Noise);

			unsigned int Color2 = (Color >> 1) & 0x7F7F7F7F;
			for (int j = 0; j < Length; j++)
				MaximumAdd(inLFB[y*inSizeX + StartX + j], Color);

			// Secondary line
			if ((rand() % DoubleLine) && (Length > 4))
			{
				int Skip = rand() % (Length >> 1 );
				int SecLength = (Length >> 1) + rand() % ((Length>>1) - Skip);
				
				int SecY = y + (rand() % 2) * 2 - 1;
				if (SecY < 0)
					SecY = 0;
				else if (SecY >= inSizeY)
					SecY = inSizeY - 1;	

				for (int j = 0; j < SecLength; j++)
					MaximumAdd(inLFB[SecY*inSizeX + StartX +Skip + j], Color2);
			}

			StartX += Length;
			Length = MinSegmentLength + rand() % SegmentLength;

			//Chaos
			if (rand() % Rigidity == 0)
			{
				// Y +- 1
				y += (rand() % 2) * 2 - 1;
				if (y < 0)
					y = 0;
				else if (y >= inSizeY)
					y = inSizeY - 1;
				StartX -= Length;
				if (StartX < 0)
					StartX = 0;
				Length = MinSegmentLength + rand() % SegmentLength;
			}
		}
	}

	//Vertical lines
	for (int x = rand() % RasterGap; x < inSizeX; x += RasterGap)
	{	
		int StartY = 0;
		int Length = Length = MinSegmentLength + rand() % SegmentLength;
		
		while (StartY < inSizeY )
		{
			if (StartY + Length > inSizeY)
				Length = inSizeY - StartY;

			// get the background color of the midde of the segment
			unsigned int  Color = inBackGround[(StartY+Length/2)*inSizeX + x];
			ModifyColor(Color, Cutoff, Ramp, Noise);

			unsigned int Color2 = (Color >> 2) & 0x3F3F3F3F;
			for (int j = 0; j < Length; j++)
				MaximumAdd(inLFB[(StartY+j)*inSizeX + x], Color);
		
			// Secondary line
			if ((rand() % DoubleLine) && (Length > 4))
			{
				int Skip = rand() % (Length >> 1 );
				int SecLength = (Length >> 1) + rand() % ((Length>>1) - Skip);
				
				int SecX = x + (rand() % 2) * 2 - 1;
				if (SecX < 0)
					SecX = 0;
				else if (SecX >= inSizeX)
					SecX = inSizeX - 1;	

				for (int j = 0; j < SecLength; j++)
					MaximumAdd(inLFB[(StartY+j+Skip)*inSizeX + SecX], Color2);
			}

			StartY += Length;
			Length = Length = MinSegmentLength + rand() % SegmentLength;

			//Chaos
			if (rand() % Rigidity == 0)
			{
				// X +- 1
				x += (rand() % 2) * 2 - 1;
				if (x < 0)
					x = 0;
				else if (x >= inSizeX)
					x = inSizeX - 1;
				StartY -= Length;
				if (StartY < 0)
					StartY = 0;
				Length = MinSegmentLength + rand() % SegmentLength;
			}
		}
	}
}

EROC_API BOOL Rasterize(signed long inImageNr, signed long inPosX, signed long inPosY,
					  signed long inScale, signed long inOpacity )
{
	Texture* Tex = GetTexture(inImageNr);
	static int Raster = 0;
    static int AlphaLayer = 0;

	// bright layer one: merged with lines
	static unsigned int* theBrightAlphaLayerLFB = 0;
	//Dark layer: merged with background
	static unsigned int* theDarkAlphaLayerLFB = 0;

	// First create the alpha layer
	if (AlphaLayer == 0)
	{
		int i;
		SDL_Surface* Surface = GetSDLSurface(inImageNr);
		unsigned int* theSurfaceLFB = (unsigned int*)Surface->pixels;
		
		theBrightAlphaLayerLFB = new unsigned int[Tex->mSizeX*Tex->mSizeY];
		for (i = 0; i < Tex->mSizeX*Tex->mSizeY; i++)
		{
			
			
			if ( theSurfaceLFB[i] & 0xFFFFFF )
				 theBrightAlphaLayerLFB[i] = 0xFF000000;
			else theBrightAlphaLayerLFB[i] = 0x00000000;
		}

		theDarkAlphaLayerLFB = new unsigned int[Tex->mSizeX*Tex->mSizeY];
		for (i = 0; i < Tex->mSizeX*Tex->mSizeY; i++)
			theDarkAlphaLayerLFB[i] = theBrightAlphaLayerLFB[i];

		DeformTextureBlurHor(theBrightAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 125);
		DeformTextureBlurVer(theBrightAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 125);
		RampLFB(theBrightAlphaLayerLFB, 256,256*2, Tex->mSizeX*Tex->mSizeY);

		DeformTextureBlurHor(theDarkAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 125);
		DeformTextureBlurVer(theDarkAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 125);
		RampLFB(theDarkAlphaLayerLFB, 256, 256*5, Tex->mSizeX*Tex->mSizeY);

		DeformTextureBlurHor(theDarkAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 5);
		DeformTextureBlurVer(theDarkAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 5);

		AlphaLayer = CreateTextureFromMem(theBrightAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, false);
	}

	// Create new raster
	
	if (Raster == 0)
	{
		SDL_Surface* theBackGround = GetSDLSurface(inImageNr);
		unsigned int* LFB = new unsigned int[Tex->mSizeX*Tex->mSizeY];
		CreateRaster(LFB, Tex->mSizeX,Tex->mSizeY, (unsigned int*)theBackGround->pixels, theDarkAlphaLayerLFB);
		Raster = CreateTextureFromMem(LFB, Tex->mSizeX, Tex->mSizeY, false);	
		delete[] LFB;
	}
	else
	{
		static int FrameNr = 0;
		FrameNr++;
		FrameNr %= 5;
		if (FrameNr == 0)
		{
			SDL_Surface* theBackGround = GetSDLSurface(inImageNr);
			unsigned int* LFB = new unsigned int[Tex->mSizeX*Tex->mSizeY];
			CreateRaster(LFB, Tex->mSizeX,Tex->mSizeY, (unsigned int*)theBackGround->pixels,
								theDarkAlphaLayerLFB);

			ReplaceTextureFromMem(Raster, LFB, Tex->mSizeX, Tex->mSizeY, false);
			delete[] LFB;
		}
	}
	
	// Draw fuzzy alpha layer
	OGLState::PushColorMask(true, false, false, false);
	Image(AlphaLayer, inPosX, inPosY, inScale, inOpacity);
	OGLState::PopColorMask();

	// Now draw raster

	OGLState::PushBlend(true);
	OGLState::PushBlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA);
	TexImage(GetTexture(Raster), inPosX, inPosY, inScale, inOpacity);
	OGLState::PopBlendFunc();
	OGLState::PopBlend();

	return true;
}

///////////////////////////////////////////////////////////////////////////////
/// EXPERIMENTAL STUF
///////////////////////////////////////////////////////////////////////////////


int gCurrentCBlur = -1;
Texture gCBlurLowPlane, gCBlurHighPlane; // the bitplanes

void MakeCircularBlurTextures(int inImageNr)
{
	if (inImageNr != gCurrentCBlur)
	{
		SDL_Surface* theSurface = GetSDLSurface(inImageNr);

		gCurrentCBlur = inImageNr;
		LoadGLTextureBitPlane(theSurface, 0, gCBlurLowPlane );
		LoadGLTextureBitPlane(theSurface, 1, gCBlurHighPlane );
	}
}


//First: try: all images to 

EROC_API BOOL CircularBlur(signed long inImageNr, signed long inPosX, signed long inPosY,
					 signed long inScale, signed long inDegrees )
{	
	if (inDegrees <= 0)
		return true;

	CreatePBuffer(512, 512);

	StartRenderToTexture();

	///////////////////////////////////////////////

	MakeCircularBlurTextures(inImageNr);

	int i;
	
	Texture* inTex = GetTexture(inImageNr);
	
	// get the diagonal of the texture, and use this size to save a piece of the background
	int diameter = 2 * int( sqrt( (gCBlurLowPlane.mSizeX/2.0)*(gCBlurLowPlane.mSizeX/2.0) +
								(gCBlurLowPlane.mSizeY/2.0)*(gCBlurLowPlane.mSizeY/2.0) ) + 0.9999 );

	glClearColor(0.0, 0.0, 0, 0.0);
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glClearColor(0, 0, 0, 0);

	// sanity check
	if (inDegrees < 0)
		inDegrees = 0;
	else if (inDegrees > 3600)
		inDegrees = 3600;

	/////////////	

	OGLState::PushTexture2D(false);
	OGLState::SetTexture2D(true);

	OGLState::PushTextureUsed(inTex->mOpenGLTex);

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix ( );
	glShadeModel(GL_SMOOTH);

	float TCenterX = inTex->mScaleX/2;
	float TCenterY = inTex->mScaleY/2;
	float THalfX =  inTex->mScaleX/2;
	float THalfY =  inTex->mScaleY/2;
	
	float CenterX = 2*inPosX/gVScreenX - 1;
	float CenterY = 1-2*inPosY/gVScreenY ;
	float HalfX = inScale *inTex->mSizeX/(gVScreenX*1000);
	float HalfY = inScale *inTex->mSizeY/(gVScreenY*1000);

	glEnable(GL_TEXTURE_2D);	
	glBindTexture(GL_TEXTURE_2D, gCBlurLowPlane.mOpenGLTex);

	OGLState::PushBlend(true);
	OGLState::PushBlendFunc(GL_ONE, GL_ONE);
	
	glBegin ( GL_QUADS );
		glNormal3f( 0.0f, 0.0f, 1.0f);
		glColor4f ( 1, 1, 1, 1 );	

		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (CenterX-HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (CenterX+HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (CenterX+HalfX,CenterY+HalfY, 0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (CenterX-HalfX,CenterY+HalfY, 0 );

		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (CenterX-HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (CenterX+HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (CenterX+HalfX,CenterY+HalfY, 0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (CenterX-HalfX,CenterY+HalfY, 0 );

		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (CenterX-HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (CenterX+HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (CenterX+HalfX,CenterY+HalfY, 0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (CenterX-HalfX,CenterY+HalfY, 0 );

		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (CenterX-HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (CenterX+HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (CenterX+HalfX,CenterY+HalfY, 0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (CenterX-HalfX,CenterY+HalfY, 0 );

		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (CenterX-HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (CenterX+HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (CenterX+HalfX,CenterY+HalfY, 0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (CenterX-HalfX,CenterY+HalfY, 0 );

		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (CenterX-HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (CenterX+HalfX,CenterY-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (CenterX+HalfX,CenterY+HalfY, 0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (CenterX-HalfX,CenterY+HalfY, 0 );

	glEnd ( );

	OGLState::PopBlendFunc();
	OGLState::PopBlend();
	

	glPopMatrix ( );

	OGLState::PopTextureUsed();
	OGLState::PopTexture2D();

	/////////////////////////////////////////////////////

	const Texture * theTexture = EndRenderToTexture();

//Todo: slechts deel van PBuffer renderen dat gevraagd is, niet altijd 512*512?

	TexImage( theTexture,inPosX, inPosY, inScale, 256 );	

	//ReleasePBuffer();

	return true;
/*	

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix ( );
	glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping	

	// coords of texture
	float TCenterX = gCBlurLowPlane.mScaleX/2;
	float TCenterY = gCBlurLowPlane.mScaleY/2;
	float THalfX =  gCBlurLowPlane.mScaleX/2;
	float THalfY =  gCBlurLowPlane.mScaleY/2;
	
	float CenterX = -1 + (float)diameter/eScreenX;
	float CenterY = 1 - (float)diameter/eScreenY ;
	float HalfX = (float)gCBlurLowPlane.mSizeX/eScreenX;
	float HalfY = (float)gCBlurLowPlane.mSizeY/eScreenY;

	glTranslatef(CenterX, CenterY, 0);

	// If we have less then 16 degrees, we draw them all one-by-one. 
	// Else we have to draw degrees/16 in an accumulation buffer
	int Remainder = inDegrees % 16;
	int NrRepeats = inDegrees / 16;

	// make sure the brightness stays the same (256) even with rounding, repeats etc
	int BrightnessForRemain = 256 * Remainder/inDegrees; //rounded downward

	int BrightnessPerRepeat = NrRepeats ? (256 - BrightnessForRemain)/NrRepeats : 0; // also rounded downward
	BrightnessForRemain = 256 - BrightnessPerRepeat*NrRepeats; // rounded upwards

	// Quick and dirty version: draw low remainder first
	glBlendFunc(GL_ONE,GL_ONE);
	glBindTexture(GL_TEXTURE_2D, gCBlurLowPlane.mOpenGLTex);
	for (i = 0; i < Remainder; i++)
	{
		// If we use floats, we lose way too much brightness due to accumulated round-off errors.
		// Hence the baroque brightness setting method.
		int Brightness = ( (BrightnessForRemain*(i+1) +0.5f) /Remainder)
								-((BrightnessForRemain*i + 0.5f)/Remainder);
		if (! Brightness)
				continue;
		glColor4ub ( 255, 255, 255, Brightness ); // total alpha of all layers combined should always be 255!	
	
		glBegin ( GL_QUADS );
		glNormal3f( 0.0f, 0.0f, 1.0f);	
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (-HalfX,-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (+HalfX,-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (+HalfX,+HalfY, 0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (-HalfX,+HalfY, 0 );	
		glEnd ( );
		glRotatef ( 1, 0, 0, 1.0f ); // next layer 1 degree more rotated
	}
	// Save the low remainder
	CopyFromBackground(0, 0, diameter, diameter, gCBlurLowRemain);

	
	// draw the full low repeat
	if (NrRepeats)
	{	
		glBindTexture(GL_TEXTURE_2D, gCBlurLowPlane.mOpenGLTex);
		for (i = Remainder; i <= 16; i++)
		{
			// If we use floats, we lose way too much brightness due to accumulated round-off errors.
			// Hence the baroque brightness setting method.
			int Brightness = ( (BrightnessPerRepeat*(i+1) +0.5f) /16)
									-((BrightnessPerRepeat*i + 0.5f)/16);
			if (! Brightness)
					continue;
			glColor4ub ( 255, 255, 255, Brightness ); // total alpha of all layers combined should always be 255!	
		
			glBegin ( GL_QUADS );
			glNormal3f( 0.0f, 0.0f, 1.0f);	
			glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (-HalfX,-HalfY, 0 );
			glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (+HalfX,-HalfY, 0 );
			glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (+HalfX,+HalfY, 0 );
			glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (-HalfX,+HalfY, 0 );	
			glEnd ( );
			glRotatef ( 1, 0, 0, 1.0f ); // next layer 1 degree more rotated
		}
		// Save the low repeat
		CopyFromBackground(0, 0, diameter, diameter, gCBlurLowAccum);
	}

	glPopMatrix();

	//clear the temp buffer
	glDisable(GL_BLEND);
	glColor4f ( 0, 0, 0, 0 );	
	glRectf( -1.f, 1.f, -1 + TempX, 1 - TempY);
	glColor4f ( 1, 1, 1, 1 );
	glEnable(GL_BLEND);

	glPushMatrix();
	glTranslatef(CenterX, CenterY, 0);

	// now draw the high remainder
	glBindTexture(GL_TEXTURE_2D, gCBlurHighPlane.mOpenGLTex);
	for (i = 0; i < Remainder; i++)
	{
		// If we use floats, we lose way too much brightness due to accumulated round-off errors.
		// Hence the baroque brightness setting method.
		int Brightness = ( (BrightnessForRemain*(i+1) +0.5f) /Remainder)
								-((BrightnessForRemain*i + 0.5f)/Remainder);
		if (! Brightness)
				continue;
		glColor4ub ( 255, 255, 255, Brightness ); // total alpha of all layers combined should always be 255!	
	
		glBegin ( GL_QUADS );
		glNormal3f( 0.0f, 0.0f, 1.0f);	
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (-HalfX,-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (+HalfX,-HalfY, 0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (+HalfX,+HalfY, 0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (-HalfX,+HalfY, 0 );	
		glEnd ( );
		glRotatef ( 1, 0, 0, 1.0f ); // next layer 1 degree more rotated
	}
	// Save the high remainder
	CopyFromBackground(0, 0, diameter, diameter, gCBlurHighRemain);

	
	// draw the full high repeat
	if (NrRepeats)
	{	
		glBindTexture(GL_TEXTURE_2D, gCBlurHighPlane.mOpenGLTex);
		for (i = Remainder; i <= 16; i++)
		{
			// If we use floats, we lose way too much brightness due to accumulated round-off errors.
			// Hence the baroque brightness setting method.
			int Brightness = ( (BrightnessPerRepeat*(i+1) +0.5f) /16)
									-((BrightnessPerRepeat*i + 0.5f)/16);
			if (! Brightness)
					continue;
			glColor4ub ( 255, 255, 255, Brightness ); // total alpha of all layers combined should always be 255!	
		
			glBegin ( GL_QUADS );
			glNormal3f( 0.0f, 0.0f, 1.0f);	
			glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (-HalfX,-HalfY, 0 );
			glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (+HalfX,-HalfY, 0 );
			glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (+HalfX,+HalfY, 0 );
			glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (-HalfX,+HalfY, 0 );	
			glEnd ( );
			glRotatef ( 1, 0, 0, 1.0f ); // next layer 1 degree more rotated
		}
		// Save the High repeat
		CopyFromBackground(0, 0, diameter, diameter, gCBlurHighAccum);
	}
	
	glPopMatrix ( );

	CopyToBackground(0, 0, gSavedBG);

	// Now draw the final result (low + high, repeats + remainder) at the intended position

	glPushMatrix ( );
	// turn half the nr of degrees back, so the middle of the blur stays centered
	glRotatef ( -inDegrees/2.0f, 0, 0, 1.0f );

	TCenterX = Tex->mScaleX/2;
	TCenterY = Tex->mScaleY/2;
	THalfX =  Tex->mScaleX/2;
	THalfY =  Tex->mScaleY/2;
	
	CenterX = 2*inPosX/gVScreenX - 1;
	CenterY = 1-2*inPosY/gVScreenY ;
	HalfX = inScale *Tex->mSizeX/(gVScreenX*1000);
	HalfY = inScale *Tex->mSizeY/(gVScreenY*1000);
 
	glBindTexture(GL_TEXTURE_2D, gCBlurHighAccum.mOpenGLTex);

	glBegin ( GL_QUADS );
	glNormal3f( 0.0f, 0.0f, 1.0f);	
	glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (-HalfX,-HalfY, 0 );
	glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (+HalfX,-HalfY, 0 );
	glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (+HalfX,+HalfY, 0 );
	glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (-HalfX,+HalfY, 0 );	
	glEnd ( );	

	glPopMatrix ( );
	// set blending back as before.
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
*/

	return true;
}



