#include "stdafx.h"


#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"d3d8.lib")
#ifdef _DEBUG
#pragma comment(lib,"c:\\dxsdk\\lib\\d3dx8d.lib")
#else
#pragma comment(lib,"c:\\dxsdk\\lib\\d3dx8.lib")
#endif


#ifdef USEDJPEG
#include "djpeg\jpegdecoder.h"
#ifdef _DEBUG
#pragma comment( lib , "djpeg\\jpgdlib_d.lib")
#else
#pragma comment( lib , "djpeg\\jpgdlib.lib")
#endif
#else
#include "jpeglib\jpeglib.h"
#ifdef _DEBUG
#pragma comment( lib , "jpeglib\\debug\\jpeglib.lib")
#else
#pragma comment( lib , "jpeglib\\release\\jpeglib.lib")
#endif
#endif
int ownhwnd;

LRESULT WINAPI JATECHMsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
	{
		if (msg==WM_CLOSE)
		{
			return 0;
		}
		if (msg==WM_SETCURSOR)
		{
			SetCursor(LoadCursor(NULL,IDC_CROSS));
			return TRUE;
		}
		return DefWindowProc( hWnd, msg, wParam, lParam );
	}

  
//namespace D3D
//{

extern HWND thehwnd;
	HWND thehwnd;
	int inited=0;
	D3DDISPLAYMODE d3ddm;
	D3DPRESENT_PARAMETERS d3dpp;
	LPDIRECT3D8             d3d = NULL; // Used to create the D3DDevice
	LPDIRECT3DDEVICE8       d3ddev = NULL; // Our rendering device

	m44 cammat,projmat,worldmat,blobmat,worldviewmat;
	float camminz,cammaxz,camfov;
	v3 campos,camfoc,camup;

	Surface *Surface::backbufsurf=NULL;
	Surface *Surface::curtarget=NULL;

	void Flip()
	{
		d3ddev->Present(NULL,NULL,NULL,NULL);
		_sleep(0);
	}	

	void SetupPixelFog(DWORD Color, DWORD Mode, float start , float end)
	{
		float Start = start,    // For linear mode
			  End   = end,
			  Density = 0.66f;  // For exponential modes
 
		// Enable fog blending.
		d3ddev->SetRenderState(D3DRS_FOGENABLE, TRUE);
 
		// Set the fog color.
		d3ddev->SetRenderState(D3DRS_FOGCOLOR, Color);
    
		// Set fog parameters.

		
		if(D3DFOG_LINEAR == Mode)
		{
			d3ddev->SetRenderState(D3DRS_FOGTABLEMODE, Mode);
			d3ddev->SetRenderState(D3DRS_FOGSTART, *(DWORD *)(&Start));
			d3ddev->SetRenderState(D3DRS_FOGEND,   *(DWORD *)(&End));
		}
		else
		{
			d3ddev->SetRenderState(D3DRS_FOGTABLEMODE, Mode);
			d3ddev->SetRenderState(D3DRS_FOGDENSITY, *(DWORD *)(&Density));
		}
 
	}

	int Init(HWND hwnd, int xres, int yres)
	{
		if (inited) return 0;

		
		inited=1;

		Surface::InitOper();

		
			

		if (hwnd==NULL)
		{
		WNDCLASSEX wc2= { sizeof(WNDCLASSEX), CS_CLASSDC|CS_DBLCLKS, JATECHMsgProc, 0L, 0L,
								  GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
								  "alexjaakko", NULL };
				RegisterClassEx( &wc2 );
			hwnd = CreateWindowEx( 0,"alexjaakko", "tv",
				WS_OVERLAPPEDWINDOW | WS_CAPTION|WS_BORDER|WS_VISIBLE|WS_POPUP, 0, 0, 400, 300,
										  NULL, NULL, wc2.hInstance, NULL );
			ownhwnd=true;
		}
		else
		{
			ownhwnd=false;
		}

		if( NULL == ( d3d = Direct3DCreate8( D3D_SDK_VERSION ) ) )
        return E_FAIL;

		// Get the current desktop display mode, so we can set up a back
		// buffer of the same format
		
		if( FAILED( d3d->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
			return E_FAIL;

		// Set up the structure used to create the D3DDevice		
		ZeroMemory( &d3dpp, sizeof(d3dpp) );
		d3dpp.Windowed = TRUE;
		d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
		d3dpp.BackBufferFormat = d3ddm.Format;
		d3dpp.BackBufferWidth = xres;
		d3dpp.BackBufferHeight = yres;
		d3dpp.BackBufferCount = 0;	

		d3dpp.EnableAutoDepthStencil=true;
		d3dpp.AutoDepthStencilFormat=D3DFMT_D16;
		d3dpp.hDeviceWindow=hwnd;
		d3dpp.Flags=D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
		
		
		thehwnd=hwnd;

		// Create the D3DDevice
		if( FAILED( d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
										  D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED ,
										  &d3dpp, &d3ddev) ) )
		{
			return E_FAIL;
		}

		Surface::backbufsurf=new Surface();
		Surface::backbufsurf->CreateFromBackBuf();

		ResetState();

		return 0;
	}

	void Close()
	{
		if (!inited) return;

		inited=0;

		delete Surface::backbufsurf;
		Surface::backbufsurf=NULL;


		
		/*
		if( d3ddev!= NULL )
			d3ddev->Release();

		if( d3d!= NULL )
			d3d->Release();
	
		if (ownhwnd) DestroyWindow(thehwnd);
		*/
	}


	void ResetState()
	{
		d3ddev->SetRenderState(D3DRS_SPECULARENABLE,false);
		d3ddev->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);
		d3ddev->SetRenderState(D3DRS_ZFUNC,D3DCMP_LESSEQUAL);
		d3ddev->SetRenderState(D3DRS_LIGHTING,FALSE);
		d3ddev->SetRenderState(D3DRS_STENCILENABLE,FALSE);
		d3ddev->SetRenderState(D3DRS_CLIPPING,TRUE);
		d3ddev->SetRenderState(D3DRS_EDGEANTIALIAS,FALSE);
		d3ddev->SetRenderState(D3DRS_CLIPPLANEENABLE,FALSE);
		d3ddev->SetRenderState(D3DRS_VERTEXBLEND,FALSE);
		d3ddev->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
		d3ddev->SetRenderState(D3DRS_FOGENABLE,FALSE);

		d3ddev->SetRenderState(D3DRS_LIGHTING,FALSE);
        d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
        d3ddev->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
        d3ddev->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
        d3ddev->SetRenderState(D3DRS_ALPHATESTENABLE,FALSE);
        d3ddev->SetRenderState(D3DRS_ALPHAREF,0x80);
        d3ddev->SetRenderState(D3DRS_ALPHAFUNC,D3DCMP_GREATEREQUAL);
        d3ddev->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
        d3ddev->SetRenderState(D3DRS_ZENABLE,TRUE);
        d3ddev->SetRenderState(D3DRS_STENCILENABLE,FALSE);
        d3ddev->SetRenderState(D3DRS_CLIPPING,TRUE);
        d3ddev->SetRenderState(D3DRS_EDGEANTIALIAS,FALSE);
        d3ddev->SetRenderState(D3DRS_CLIPPLANEENABLE,FALSE);
        d3ddev->SetRenderState(D3DRS_VERTEXBLEND,FALSE);
        d3ddev->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
        d3ddev->SetRenderState(D3DRS_FOGENABLE,FALSE);
		d3ddev->SetRenderState(D3DRS_DITHERENABLE,true);

		//d3ddev->SetRenderState(D3DRS_WRAP0,D3DWRAP_U|D3DWRAP_V);
		
		d3ddev->SetRenderState(D3DRS_TEXTUREFACTOR,-1);

		d3ddev->SetTextureStageState(0,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_DISABLE);
		d3ddev->SetTextureStageState(1,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_DISABLE);
        
		d3ddev->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
        d3ddev->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
        d3ddev->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_DIFFUSE);

        d3ddev->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
        d3ddev->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
        d3ddev->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);

		for (int tss=0;tss<4;tss++)
        {
			d3ddev->SetTextureStageState(tss,D3DTSS_MINFILTER,D3DTEXF_LINEAR);
			d3ddev->SetTextureStageState(tss,D3DTSS_MAGFILTER,D3DTEXF_LINEAR);
			d3ddev->SetTextureStageState(tss,D3DTSS_MIPFILTER,D3DTEXF_POINT);
			d3ddev->SetTextureStageState(tss,D3DTSS_TEXCOORDINDEX,tss);
			d3ddev->SetTextureStageState(tss,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_DISABLE);
			if (tss>0)
			{
				d3ddev->SetTextureStageState(tss,D3DTSS_COLOROP,D3DTOP_DISABLE);
				d3ddev->SetTextureStageState(tss,D3DTSS_ALPHAOP,D3DTOP_DISABLE);
			}
		}

		D3DXMatrixIdentity((D3DXMATRIX*)&worldmat);
		D3DXMatrixIdentity((D3DXMATRIX*)&cammat);
		SetFOV(1.f);
		SetCam(v3(1,0,0),v3(0,0,0));
		SetWorldMat();

	}

	void SetCam()
	{
		
		d3ddev->SetTransform( D3DTS_VIEW, (D3DXMATRIX*)(&cammat) ); 
		worldviewmat = worldmat * cammat;
		D3DXMatrixInverse((D3DXMATRIX*)&blobmat,NULL,(D3DXMATRIX*)&worldviewmat);		
	}

	float jitterx=0;
	float jittery=0;
	
	
	void UpdateJitter(int jitterc)
	{
		jitterx = GaussRandom(0.1,0.4f);
		jittery = GaussRandom(0.1,0.4f);
		if (jitterc&1) jitterx+=0.5f;
		if (jitterc&2) jittery+=0.5f;
		jitterx-=0.5f;
		jittery-=0.5f;
		//jitterx*=0.6f;		jittery*=0.6f;
		SetFOV();
	}

	void SetFOV()
	{	
		D3DXMatrixPerspectiveFovLH((D3DXMATRIX*)&projmat,camfov*3.f/4.f,4.f/3.f,camminz,cammaxz);
		if (Surface::curtarget->isbackbuf)
			projmat.MultTranslate(jitterx*2 / Surface::curtarget->w, jittery*2 / Surface::curtarget->h, 0);
		d3ddev->SetTransform( D3DTS_PROJECTION, (D3DXMATRIX*)(&projmat) ); 
	}

	void SetWorldMat()
	{
		d3ddev->SetTransform( D3DTS_WORLD, (D3DXMATRIX*)(&worldmat) ); 
		worldviewmat = worldmat * cammat;
		D3DXMatrixInverse((D3DXMATRIX*)&blobmat,NULL,(D3DXMATRIX*)&worldviewmat);
	}

	
	void SetWorldMat(m44 mat)
	{
		worldmat=mat;
		SetWorldMat();
	}

	void SetFOV(float fov, float minz, float maxz)
	{
		camfov=fov;
		camminz=minz;
		cammaxz=maxz;
		SetFOV();		
	}

	void SetCam(m44 mat)
	{
		cammat=mat;
		SetCam();
	}

	void SetCam(v3 xcampos, v3 xcamfoc, v3 xcamup)
	{
		campos=xcampos;
		camfoc=xcamfoc;
		camup=xcamup;
		D3DXMatrixLookAtLH((D3DXMATRIX*)&cammat,(D3DXVECTOR3*)&campos,(D3DXVECTOR3*)&camfoc,(D3DXVECTOR3*)&camup);		
		SetCam();		
	}

	u8 Surface::opertable[OPER_LAST][256][256];

	void Surface::InitOper()
	{
		int x,y;
		// [operation][top][bottom] in the photoshop sense
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_SET][y][x]=pixel::clamp(y);
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_ADD][y][x]=pixel::clamp(x+y);
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_SUB][y][x]=pixel::clamp(x-y);
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_REVSUB][y][x]=pixel::clamp(y-x);
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_SIGNADD][y][x]=pixel::clamp(x+y-128);
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_MUL][y][x]=(x*y)>>8;
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_MUL2X][y][x]=pixel::clamp((x*y)>>7);
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_MUL4X][y][x]=pixel::clamp((x*y)>>6);
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_OVERLAY][y][x]=y<128 ? ((x*y)>>7) : (255-((255-x)*(255-y)>>7));
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_HARDLIGHT][y][x]=x<128 ? ((x*y)>>7) : (255-((255-x)*(255-y)>>7));
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_SCREEN][y][x]=255-((255-x)*(255-y)>>8);
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_MIN][y][x]=min(y,x);
		for (y=0;y<256;y++) for (x=0;x<256;x++) opertable[OPER_MAX][y][x]=max(y,x);
	}

	Surface::Surface(int ww, int hh)
	{
		w=h=0;;
		pix=pix2=NULL;
		tex=NULL;
		rtarget=zbuf=NULL;
		isbackbuf=false;
		SetSize(ww,hh);
	}

	void Surface::CopyFrom(const Surface &src)
	{
		SetSize(src.w,src.h);
		memcpy(pix,src.pix,w*h*4);
	}

	Surface::~Surface()
	{
		Reset();
	}

	void Surface::SetSize(int ww, int hh)
	{
		if (w==ww && h==h) return;
		Reset();
		w=ww;
		h=hh;
		CreatePix();
	}

	void Surface::Halve()
	{
		int w2=w/2;
		int h2=h/2;
		pixel *pix3=new pixel[w2*h2];
		pixel *src=pix;
		pixel *dst=pix3;
		for (int y=0;y<h2;y++)
		{		
			for (int x=0;x<w2;x++)
			{
				pixel d;
				d.r=(src[0].r+src[1].r+src[w].r+src[w+1].r)/4;
				d.g=(src[0].g+src[1].g+src[w].g+src[w+1].g)/4;
				d.b=(src[0].b+src[1].b+src[w].b+src[w+1].b)/4;
				d.a=(src[0].a+src[1].a+src[w].a+src[w+1].a)/4;
				*dst++=d;
				src+=2;
			}
			src+=w*2-w2*2;
		}
		SetSize(w2,h2);
		memcpy(pix,pix3,w2*h2*sizeof(pixel));
		delete [] pix3;
	}

	void Surface::LoadFromDisk(char *fname)
	{
		if (strstr(fname,".raw"))
		{
			SetSize(FULLXRES,FULLYRES);
			FILE *f=fopen(fname,"rb");
			if (f) fread(pix,4,FULLYRES*FULLXRES,f);
			if (f) fclose(f);
			for (int i =0; i<halfmode;i++) Halve();
			return;
		}

		D3DTexture *ntex=NULL;
		D3DXCreateTextureFromFileEx(d3ddev,fname,D3DX_DEFAULT,D3DX_DEFAULT,0,0,D3DFMT_A8R8G8B8,D3DPOOL_MANAGED,D3DX_DEFAULT,D3DX_DEFAULT,0,NULL,NULL,&ntex);
		if (ntex)
		{
			D3DSURFACE_DESC desc;
			ntex->GetLevelDesc(0,&desc);
			SetSize(desc.Width,desc.Height);
			RELEASE(tex);
			tex=ntex;
			UpdateFromTexture();
			for (int i =0; i<halfmode;i++) Halve();
			if (rtarget) UpdateToRendertarget();
		}
	}

	void Surface::UpdateFromTexture()
	{
		if (tex)
		{
			D3DLOCKED_RECT r;
			tex->LockRect(0,&r,NULL,D3DLOCK_READONLY);
			pixel *pp = (pixel*)r.pBits;
			int pitch = r.Pitch/4;
			pixel *ds = pix;
			for (int y=0;y<h;y++)
			{
				memcpy(ds,pp,w*4);
				ds+=w;
				pp+=pitch;
			}
			tex->UnlockRect(0);
		}
	}

	void Surface::UpdateFromRendertarget()
	{
		if (rtarget)
		{
			D3DLOCKED_RECT r;
			rtarget->LockRect(&r,NULL,D3DLOCK_READONLY);
			pixel *pp = (pixel*)r.pBits;
			int pitch = r.Pitch/4;
			pixel *ds = pix;
			for (int y=0;y<h;y++)
			{
				memcpy(ds,pp,w*4);
				ds+=w;
				pp+=pitch;
			}
			rtarget->UnlockRect();
		}
	}

	void Surface::UpdateToTexture()
	{
		if (!tex)
		{
			CreateTex();
		}
		if (tex)
		{
			D3DLOCKED_RECT r;
			tex->LockRect(0,&r,NULL,/*D3DLOCK_DISCARD*/0);
			pixel *pp = (pixel*)r.pBits;
			int pitch = r.Pitch/4;
			pixel *ds = pix;
			for (int y=0;y<h;y++)
			{
				memcpy(pp,ds,w*4);
				ds+=w;
				pp+=pitch;
			}
			tex->UnlockRect(0);
		}
	}
	void Surface::UpdateToRendertarget(Surface *sp)
	{
		if (!rtarget)
		{			
			CreateRenderTarget();
		}
		if (rtarget)
		{
			D3DLOCKED_RECT r;
			rtarget->LockRect(&r,NULL,0);
			pixel *pp = (pixel*)r.pBits;
			int pitch = r.Pitch/4;
			pixel *ds = sp ? sp->pix : pix;
			for (int y=0;y<h;y++)
			{
				memcpy(pp,ds,w*4);
				//memset(pp,y,w*4);
				ds+=w;
				pp+=pitch;
			}
			rtarget->UnlockRect();
		}
	}

	void Surface::ClearPix(int col)
	{
		for (int c1=0;c1<w*h;c1++) pix[c1]=col;
	}

	// operations
	void Surface::Blt(bool doalpha, OperType oper, int operalpha, Surface *src, int dstx, int dsty, int srcx, int srcy, int srcw, int srch)
	{
		if (operalpha<=0 || dstx>=w || dsty>=h) return;
		if (srcw==0) srcw=src->w;
		if (srch==0) srch=src->h;
		if (dstx<0)
		{
			srcx-=dstx; // gets bigger
			srcw+=dstx; // gets smaller
			dstx=0;
		}		
		if (dsty<0)
		{
			srcy-=dsty; // gets bigger
			srch+=dsty; // gets smaller
			dsty=0;
		}		
		if (srcx<0)
		{
			dstx-=srcx;
			srcw+=srcx;
			srcx=0;			
		}
		if (srcy<0)
		{
			dsty-=srcy;
			srch+=srcy;
			srcy=0;			
		}
		int sx2=srcx+srcw;
		int sy2=srcy+srch;
		if (sx2>=src->w)
		{
			srcw-=sx2-src->w;
		}
		if (sy2>=src->h)
		{
			srch-=sy2-src->h;
		}
		int dx2=dstx+srcw;		
		int dy2=dsty+srch;
		if (dx2>=w)
		{
			srcw-=dx2-w;
		}
		if (dy2>=h)
		{
			srch-=dy2-h;
		}
		if (srcw<=0 || srch<=0) return;

		pixel *srcptr = src->pix+srcx+srcy*src->w;
		pixel *dstptr = pix+dstx+dsty*w;
		for (int y=0;y<srch;y++)
		{
			//if (oper==OPER_SET && operalpha>=255) memcpy(dstptr,srcptr,4*srcw); else 
				if (doalpha) OperateAlpha(oper,operalpha,dstptr,dstptr,srcptr,srcw); else Operate(oper,operalpha,dstptr,dstptr,srcptr,srcw);
			
			srcptr+=src->w;
			dstptr+=w;
		}

	}

	void Surface::Tint(OperType oper, int operalpha, pixel col, int x, int y, int x2, int y2)
	{
		x2+=x;
		y2+=y;
		x=bound(0,x,w);
		y=bound(0,y,h);
		x2=bound(0,x2,w);
		y2=bound(0,y2,h);
		if (x<x2 && y<y2)
		{
			pixel *dstptr = pix+x+y*w;
			x2-=x;
			y2-=y;
			for (int y=0;y<y2;y++)
			{
				if (oper==OPER_SET && operalpha>=255) 
				{
					for (int x=0;x<x2;x++) *dstptr++ = col;
				}
				else
				{
					for (int x=0;x<x2;x++,dstptr++) Operate(oper,operalpha,dstptr,dstptr,&col);
				}
				dstptr+=w-x2;
			}
		}
	}
	
	void Surface::GaussBlur(pixel *dst, float k)
	{
		if (k<=1) return;
		GaussHBlur(pix,pix2,k); // pix2 is original, pix is blurred so...		
		GaussVBlur(pix2,dst,k); 
	}

	void Surface::BoxBlur(pixel *dst, int kx, int ky)
	{
		if (kx>1) BoxHBlur(pix,pix2,kx); // pix2 is original, pix is blurred so...
		//if (kx>1 && ky>1) SwapPix();
		if (ky>1) BoxVBlur(kx>1?pix2:pix,dst,ky); 
		if (kx<=1 && ky<=1) memcpy(dst,pix,sizeof(pixel)*w*h);
	}

	void Surface::BoxHBlur(pixel *ssrc, pixel *ddst, int k)
	{
#define MAXB 1024
#define MAXBSH 10
		if (k<=1) return;
		int rk = MAXB/k;
		pixel *src = ssrc;
		pixel *dst = ddst;
		int k2=(k+1)/2;
		if (k2>w/2) k2=w/2;
		int k3=k-k2;	
		if (k3>w/2) k3=w/2;
		for (int y=0;y<h;y++)
		{
			for (int c=0;c<4;c++) //if (chan&(1<<c))
			{
				int tot=0;
				// for the first k2 pixels, we just accumulate on the right
				for (int x=-k3;x<0;x++) tot+=(src[k3+x])[c];
				for (;x<k2;x++)
				{					
					(*dst)[c] = (tot*(MAXB/(x+k3))>>MAXBSH);
					tot+=(src[k3])[c];					
					src++;dst++;
				}
				for (;x<w-k3;x++)
				{										
					(*dst)[c] = (tot*rk>>MAXBSH);
					tot+=(src[k3])[c]-(src[-k2])[c];
					src++;dst++;
				}
				// for the final k3 pixels, we just decrement
				int k4=k+1;
				for (;x<w;x++)
				{										
					(*dst)[c] = (tot*(MAXB/(--k4))>>MAXBSH);
					tot-=(src[-k2])[c];
					src++;dst++;
				}

				src-=w;
				dst-=w;
			}
			src+=w;
			dst+=w;						
		}
		
	}

	void Surface::BoxVBlur(pixel *ssrc, pixel *ddst, int k)
	{
		if (k<=1) return;
		int rk = MAXB/k;
		pixel *src = ssrc;
		pixel *dst = ddst;
		int k2=(k+1)/2;
		if (k2>h/2) k2=h/2;
		int k3=k-k2;	
		if (k3>h/2) k3=h/2;
		for (int y=0;y<w;y++)
		{
			for (int c=0;c<4;c++)// if (chan&(1<<c))
			{
				int tot=0;
				// for the first k2 pixels, we just accumulate on the right
				for (int x=-k3;x<0;x++) tot+=(src[(k3+x)*w])[c];
				for (;x<k2;x++)
				{					
					(*dst)[c] = (tot*(MAXB/(x+k3))>>MAXBSH);
					tot+=(src[k3*w])[c];					
					src+=w;dst+=w;
				}
				for (;x<h-k3;x++)
				{					
					
					(*dst)[c] = (tot*rk>>MAXBSH);
					tot+=(src[k3*w])[c]-(src[-k2*w])[c];
					src+=w;dst+=w;
				}
				// for the final k3 pixels, we just decrement
				int k4=k+1;
				for (;x<h;x++)
				{										
					(*dst)[c] = (tot*(MAXB/(--k4))>>MAXBSH);
					tot-=(src[-k2*w])[c];
					src+=w;dst+=w;
				}

				src-=w*h;
				dst-=w*h;
			}
			src++;
			dst++;						
		}
		
	}
	void Surface::GaussHBlur(pixel *ssrc, pixel *ddst, float fk)
	{
		int k=int(fk+1);
		if (k<=1) return;
		if (k>256) k=512;
		static int ker[512];
		static int oker=-1;
		static int fac;
		if (k!=oker)
		{
			fac=0;
			for (int c1=0;c1<k;c1++) 
			{
				ker[c1] = int((1+sin(bound(0,c1*PI*2/(fk-1),PI*2)))*16384.f);
				fac+=ker[c1];
			}
			fac>>=8;
			for (c1=0;c1<k;c1++) 
			{
				ker[c1] /= (fac);
			}
			oker=k;
		}
		pixel *src = ssrc;
		pixel *dst = ddst;
		for (int y=0;y<h;y++)
		{
			for (int c=0;c<4;c++) //if (chan&(1<<c))
			{
				for (int x=0;x<w;x++)
				{
					int x1=x-k/2;
					int x2=x1+k;
					int *kp = ker;
					if (x1<0)
					{
						kp-=x1;
						x1=0;
					}
					if (x2>w) x2=w;
					x2-=x1;
					pixel *src2 = src+x1;
					int tot=0;
					for (int i=0;i<x2;i++)
					{
						tot+=*kp++ * (*src2)[c];
						src2++;
					}
					(*dst)[c] = tot>>8;
					dst++;
				}
				dst-=w;				
			}
			src+=w;
			dst+=w;						
		}
		
	}

	void Surface::GaussVBlur(pixel *ssrc, pixel *ddst, float fk)
	{
		int k=int(fk+1);
		if (k<=1) return;
		if (k>256) k=512;
		static int ker[512];
		static int oker=-1;
		static int fac;
		if (k!=oker)
		{
			fac=0;
			for (int c1=0;c1<k;c1++) 
			{
				ker[c1] = int((1+sin(bound(0,c1*PI*2/(fk-1),PI*2)))*16384.f);
				fac+=ker[c1];
			}
			fac>>=8;
			for (c1=0;c1<k;c1++) 
			{
				ker[c1] /= (fac);
			}
			oker=k;
		}
		pixel *src = ssrc;
		pixel *dst = ddst;
		for (int y=0;y<w;y++)
		{
			for (int c=0;c<4;c++) //if (chan&(1<<c))
			{
				for (int x=0;x<h;x++)
				{
					int x1=x-k/2;
					int x2=x1+k;
					int *kp = ker;
					if (x1<0)
					{
						kp-=x1;
						x1=0;
					}
					if (x2>h) x2=h;
					x2-=x1;
					pixel *src2 = src+x1*w;
					int tot=0;
					for (int i=0;i<x2;i++)
					{
						tot+=*kp++ * (*src2)[c];
						src2+=w;
					}					
					(*dst)[c] = tot >> 8;
					dst+=w;
				}
				dst-=w*h;				
			}
			src++;
			dst++;						
		}		
	}
	//void Surface::Median(OperType oper, int operalpha, int kx, int ky, int chan); TODO
	
	void Surface::Curves(pixel *curves)
	{
		int wh=w*h;
		pixel *p=pix;
		for (int c1=0;c1<wh;c1++)
		{
			p->r = curves[p->r].r;
			p->g = curves[p->g].g;
			p->b = curves[p->b].b;
			p->a = curves[p->a].a;
			p++;
		}
	}
	void Surface::BrightnessContrast(float brightness, float contrast)
	{
		static pixel curves[256];
		for (int c1=0;c1<256;c1++)
		{
			int k=int((c1-128)*contrast+brightness+128);
			curves[c1].r=curves[c1].g=curves[c1].b=curves[c1].a=bound(0,k,255);
		}
		Curves(curves);
	}

	float MIN(float r, float g, float b)
	{
		if (r<g && r<b) return r;
		if (g<b && g<r) return g;
		return b;
	}

	float MAX(float r, float g, float b)
	{
		if (r>g && r>b) return r;
		if (g>b && g>r) return g;
		return b;
	}

	void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v )
	{
		float min, max, delta;	
		min = MIN( r, g, b );
		max = MAX( r, g, b );
		*v = max;				// v	
		delta = max - min;	
		if( max != 0 )
			*s = delta / max;		// s
		else {
			// r = g = b = 0		// s = 0, v is undefined
			*s = 0;
			*h = -1;
			return;
		}	
		if( r == max )
			*h = ( g - b ) / delta;		// between yellow & magenta
		else if( g == max )
			*h = 2 + ( b - r ) / delta;	// between cyan & yellow
		else
			*h = 4 + ( r - g ) / delta;	// between magenta & cyan	
		*h *= 60;				// degrees
		if( *h < 0 )
			*h += 360;
	}


	void HSVtoRGB( float *r, float *g, float *b, float h, float s, float v )
	{
		int i;
		float f, p, q, t;	
		if( s == 0 ) 
		{
			// achromatic (grey)
			*r = *g = *b = v;
			return;
		}	h /= 60;			// sector 0 to 5
		i = int(floorf( h ));
		f = h - i;			// factorial part of h
		p = v * ( 1 - s );
		q = v * ( 1 - s * f );
		t = v * ( 1 - s * ( 1 - f ) );	
		switch( i ) 
		{
		case 0:
			*r = v;
			*g = t;
			*b = p;
			break;
		case 1:
			*r = q;
			*g = v;
			*b = p;
			break;
		case 2:
			*r = p;
			*g = v;
			*b = t;
			break;
		case 3:
			*r = p;
			*g = q;
			*b = v;
			break;
		case 4:
			*r = t;
			*g = p;
			*b = v;
			break;
		default:		// case 5:
			*r = v;
			*g = p;
			*b = q;
			break;
		}
	}


	void Surface::RGB2HSV()
	{
		int wh=w*h;
		for (int c1=0;c1<wh;c1++)
		{
			float r=pix[c1].r/255.f,g=pix[c1].g/255.f,b=pix[c1].b/255.f;
			float h,s,v;
			RGBtoHSV(r,g,b,&h,&s,&v);
			h=bound(0,h/360.f,1);
			s=bound(0,s,1);
			v=bound(0,v,1);
			pix[c1].r = u8(h*255.f);
			pix[c1].g = u8(s*255.f);
			pix[c1].b = u8(v*255.f);
		}
	}
	void Surface::HSV2RGB()
	{
		int wh=w*h;
		for (int c1=0;c1<wh;c1++)
		{
			float h=pix[c1].r/255.f*360.f,s=pix[c1].g/255.f,v=pix[c1].b/255.f;
			float r,g,b;
			HSVtoRGB(&r,&g,&b,h,s,v);
			r=bound(0,r,1);
			g=bound(0,g,1);
			b=bound(0,b,1);
			pix[c1].r = u8(r*255.f);
			pix[c1].g = u8(g*255.f);
			pix[c1].b = u8(b*255.f);
		}
	}
	
	// internal
	void Surface::Reset()
	{
		delete [] pix;
		delete [] pix2;
		pix=NULL;
		pix2=NULL;
		RELEASE(tex);
		RELEASE(rtarget);
		RELEASE(zbuf);
		w=h=0;
		isbackbuf=false;
	}
	void Surface::CreatePix()
	{
		delete [] pix;
		delete [] pix2;
		pix=new pixel[w*h];
		pix2=new pixel[w*h];
	}

	void Surface::CreateTex()
	{
		if (w!=h) return ;
		RELEASE(tex);
		D3DXCreateTexture(d3ddev,w,h,1,0,D3DFMT_A8R8G8B8,D3DPOOL_MANAGED,&tex);			
	}

	void Surface::CreateRenderTarget()
	{
		RELEASE(rtarget);
		RELEASE(zbuf);
		d3ddev->CreateRenderTarget(w,h,D3DFMT_A8R8G8B8,D3DMULTISAMPLE_NONE,true,&rtarget);
		d3ddev->CreateDepthStencilSurface(w,h,D3DFMT_D16,D3DMULTISAMPLE_NONE,&zbuf);
	}

	void Surface::CreateFromBackBuf()
	{
		D3DSurface *rtarget2;
		D3DSurface *zbuf2;
		
		d3ddev->GetRenderTarget(&rtarget2);
		d3ddev->GetDepthStencilSurface(&zbuf2);

		D3DSURFACE_DESC desc;		
		rtarget2->GetDesc(&desc);
		SetSize(desc.Width,desc.Height);

		rtarget=rtarget2;
		zbuf=zbuf2;
		isbackbuf=true;

		backbufsurf= curtarget=this;

	}

	void Surface::BeginFrame(bool clearz, bool clearback, int clearcol)
	{
		

		if (curtarget!=this)
		{
			if (!rtarget) CreateRenderTarget();
			d3ddev->SetRenderTarget(rtarget,zbuf);	
			lasttarget=curtarget;
			curtarget=this;			
		}

		D3DVIEWPORT8 vp;		
		float x1=0,y1=0,x2=1,y2=1;
		vp.MinZ=0;
		vp.MaxZ=1;
		vp.Width=w * (x2-x1);
		vp.Height=h * (y2-y1);
		vp.X=w * x1;
		vp.Y=h * y1;
		d3ddev->SetViewport(&vp);

		
		if (clearz || clearback)
		{
			d3ddev->Clear(0,NULL,(clearback?D3DCLEAR_TARGET:0)|(clearz?D3DCLEAR_ZBUFFER:0),clearcol,1.f,0);
		}		
		d3ddev->BeginScene();

		SetFOV();
	}

	void Surface::EndFrame(bool updatefromrendertarget, bool updatetotexture)
	{
		d3ddev->EndScene();
		if (curtarget==this && isbackbuf==false)
		{			
			curtarget=lasttarget;
			d3ddev->SetRenderTarget(curtarget->rtarget,curtarget->zbuf);	
		}
		if (updatefromrendertarget)
		{
			UpdateFromRendertarget();
		}
		if (updatetotexture)
		{
			UpdateToTexture();
		}
	}

	
	extern v3 sunpos,shadmin,shadmax;
	v3 sunpos(-64*10,164*10,482*10);
	v3 shadmin(0,0,0),shadmax(1,1,1);
	

	void ShadowProject(v3 &p, bool normalize)
	{
		v3 d= p - sunpos;
		float t = p.y / d.y;
		p = p - d * t;
		if (normalize)
		{
			p.x=(p.x-shadmin.x)/(shadmax.x-shadmin.x);
			p.y=(p.z-shadmin.z)/(shadmax.z-shadmin.z);
			p.z=0.5f;
		}
		else 
		{
			if (p.x<shadmin.x) shadmin.x=p.x;
			if (p.z<shadmin.z) shadmin.z=p.z;
			if (p.x>shadmax.x) shadmax.x=p.x;
			if (p.z>shadmax.z) shadmax.z=p.z;

		}
	}

//}