// OpenLuce.cpp by Antonino Perricone
// This is a open, didactic purpuose Luce version.
// This show the effect code and  it include comments to be
// understaind by reader.
// All commercial use of this code or its derivations must be
// autorized by Antonino Perricone.

// The windows parts is commented only superficially.
// All image are 24bit, with 8 bit for channel, and a BGR codec.

//! the width of image
unsigned short width;
//! the height of image
unsigned short height;
//! The source image <br>
//! it size will be #width * #height *3
unsigned char *sourceBits;
//! The final image<br>
//! it size will be #width * #height *3
unsigned char *endBits;
//! The does register<br>
//! It cues all does pixel. <br>
//! when the Luce effect make a line all pixel in this line will be
//! make. This buffer is useful for finding the pixel skipped by made lines.<br>
//! Is size are: #width * #height and all byte is used like a boolean.
unsigned char *does;
//! the coordinate x of light
unsigned short lx;
//! the coordinate y of light
unsigned short ly;
//! A switch for do not luce 2 times at the same time.
bool iDoingLuce;

#include <windows.h>
#include <stdio.h>
// Functions Declamation
bool OpenBitmap(HINSTANCE);
LRESULT CALLBACK WndFunc(HWND,UINT,WPARAM,LPARAM);
void Luce();
HWND hWnd;

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE,LPSTR,int)
{
	iDoingLuce=false;
	if(!OpenBitmap(hInstance)) return 0;
	WNDCLASS myClass={CS_DBLCLKS,WndFunc,0,0,hInstance,NULL,
		LoadCursor(NULL,IDC_ARROW),(HBRUSH)GetStockObject(BLACK_BRUSH),NULL,"OpenLuce"};
	RegisterClass(&myClass);
#define WINDOWSTILE WS_SYSMENU|WS_BORDER|WS_CAPTION
	RECT r;
	r.right=GetSystemMetrics(SM_CXSCREEN);
	r.bottom=GetSystemMetrics(SM_CYSCREEN);
	r.left=(r.right-width)/2;
	r.top=(r.bottom-height)/2;
	r.right=r.left+width;
	r.bottom=r.top+height;
	AdjustWindowRect(&r,WINDOWSTILE,FALSE);
	hWnd=CreateWindow("OpenLuce","OpenLuce",WINDOWSTILE,
		r.left,r.top,r.right-r.left,r.bottom-r.top,NULL,NULL,hInstance,NULL);
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
	//init the effect
	endBits=new unsigned char[width*height*3];
	does=new unsigned char[width*height];
	lx=width/2;
	ly=height/2;
	Luce();
	//***************
	MSG msg;
	while(GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	};
	delete [] sourceBits;
	delete [] endBits;
	delete [] does;
	return msg.wParam;
}

//! use windows functions for open the image source.bmp, and set #width , #height and #sourceBits
bool OpenBitmap(HINSTANCE hInstance)
{
	HBITMAP hBitmap=(HBITMAP)LoadImage(hInstance,"source.bmp",IMAGE_BITMAP,0,0,
		LR_LOADFROMFILE|LR_CREATEDIBSECTION);
	if(hBitmap==0)
	{
		MessageBox(0,"The file source.bmp don't found,\n the application will be closed.",
			"Error",MB_OK | MB_ICONERROR);
		return false;
	}
	BITMAPINFO bmi;
	memset(&bmi,0,sizeof(BITMAPINFO));
	bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
	HDC hDC=CreateCompatibleDC(0);
	SelectObject(hDC,hBitmap);
	GetDIBits(hDC,hBitmap,0,0,0,&bmi,DIB_RGB_COLORS);
	width=bmi.bmiHeader.biWidth; height=bmi.bmiHeader.biHeight;
	if((width<100)||(height<100))
	{
		DeleteObject(hBitmap);
		MessageBox(0,"The file source.bmp is invalid,\n or smaller.",
			"Error",MB_OK | MB_ICONERROR);
		return false;
	}
	if(bmi.bmiHeader.biBitCount<=16)
	{
		DeleteObject(hBitmap);
		MessageBox(0,"The format of source.bmp is not supported.",
			"Error",MB_OK | MB_ICONERROR);
		return false;
	}
	size_t dim=bmi.bmiHeader.biSizeImage;
	if(dim==0) dim=bmi.bmiHeader.biWidth*bmi.bmiHeader.biHeight*bmi.bmiHeader.biBitCount/8;
	unsigned char *tmp=new unsigned char[dim];
	sourceBits=new unsigned char[width*height*3];
	GetDIBits(hDC,hBitmap,0,height,tmp,&bmi,DIB_RGB_COLORS);
	unsigned short x,y;
	unsigned char *s=tmp;
	unsigned char *d=sourceBits;
	for(y=0;y<height;y++)
		for(x=0;x<width;x++)
		{
			d[0]=s[0];
			d[1]=s[1];
			d[2]=s[2];
			s+=bmi.bmiHeader.biBitCount/8;
			d+=3;
		}
	delete [] tmp;
	DeleteObject(hBitmap);
	return true;
}

//! the windows callback manager
LRESULT CALLBACK WndFunc(HWND hWnd, UINT MSG, WPARAM wParam, LPARAM lParam)
{
	switch (MSG)
	{
	case WM_PAINT:
		{
			BITMAPINFO bmi;
			memset(&bmi,0,sizeof(BITMAPINFO));
			bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
			bmi.bmiHeader.biWidth=width;
			bmi.bmiHeader.biHeight=height;
			bmi.bmiHeader.biBitCount=24;
			bmi.bmiHeader.biCompression=BI_RGB;
			bmi.bmiHeader.biPlanes=1;
			HDC hDC=GetDC(hWnd);
			SetDIBitsToDevice(hDC,0,0,width,height,0,0,0,height,endBits,&bmi,DIB_RGB_COLORS);
			ValidateRect(hWnd,0);
			return 1;
		};
	case WM_LBUTTONDOWN:
		{
			if(!iDoingLuce)
			{
				lx=LOWORD(lParam);
				ly=height-HIWORD(lParam);
				Luce();
			}
		}
		return 1;
	case WM_DESTROY:
		PostQuitMessage (0);
		return 0;
	}
	return DefWindowProc (hWnd,MSG,wParam,lParam);
}

// declamation
void Line(int x,int y);
//! The effect functions.
void Luce()
{
	DWORD time1=GetTickCount();
	iDoingLuce=true;
	//!* Phase A: reset buffer
	memset(does,FALSE,width*height);
	unsigned char *s=&endBits[(lx+ly*width)*3];
	s[0]=s[1]=s[2]=0;
	//!* Phase B: this is really important:<br>
	//!		From pixel upper left, trace a spiral that touch every pixel in image.
	//!		It means, we doing before the external pixel.<br>
	//! For every pixel we do the Line, see below for explation.
	RECT r; r.left=r.top=0; r.right=width-1; r.bottom=height-1;
	int x,y; x=-1; y=0;
	while ((r.right-r.left>2)&&(r.bottom-r.top>2))
	{
		while (x!=r.right)	{	x++; Line(x,y); }
		r.top++;
		while (y!=r.bottom)	{	y++; Line(x,y); }
		r.right--;
		while (x!=r.left)	{	x--; Line(x,y); }
		r.bottom--;
		while (y!=r.top)	{	y--; Line(x,y); }
		r.left++;
	}
	//! Phase C: Add Source (this is an option in the photoshop plug-in)
	s=sourceBits;
	unsigned char *d=endBits;
	unsigned char i;
	unsigned short sum;
	for(y=0;y<height;y++)
		for(x=0;x<width;x++)
		{
			for(i=0;i<3;i++)
			{
				sum=(unsigned short)(d[i])+(unsigned short)(s[i]);
				if(sum>255) sum=255;
				d[i]=sum;
			}
			s+=3; d+=3;
		}
	DWORD time2=GetTickCount();
	char buff[100];
	sprintf(buff,"OpenLuce - %i msec",time2-time1);
	SetWindowText(hWnd,buff);
	InvalidateRect(hWnd,0,FALSE);
	iDoingLuce=false;
}

//! This is the core of Luce.<br>
//! This function trace a line from the light and the given pixel.<br>
//! and for every pixel in line set the result as sum of previus process pixel
//! divide number of process pixel.<br>
//! Thus we made all pixel in line, and can set it does.
//! This is a cleary version, i hope :).
void _Line(int px,int py)
{
	// skip does pixel.
	if(does[py*width+px]) return;
	short dx=px-lx; //< delta x
	short dy=py-ly; //< delta y
	unsigned short nx=dx<0? -dx :dx; //< number of pixel along x
	unsigned short ny=dy<0? -dy :dy; //< number of pixel along y
	unsigned short np=nx>ny? nx :ny; //< number of pixel along the line
	unsigned short x,y; //< the current position (repeat: start from luce point and go to (px,py))
	unsigned long sums[3]={0,0,0}; //< they contain the sum of pixel processed in line.
	unsigned char *src; //< the source pixel
	unsigned char *dst; //< the destination pixel
	long idx; //< the offset of pixel
	unsigned char j;
	for(unsigned short i=1;i<=np;i++)
	{
		// Found pos on line
		x=lx+MulDiv(dx,i,np);
		y=ly+MulDiv(dy,i,np);
		// found pos on buffer
		idx=x+y*width;
		does[idx]=TRUE; //Set pixel as does.
		idx*=3;
		src=&sourceBits[idx];
		dst=&endBits[idx];
		for(j=0;j<3;j++)
		{
			// sum the pixel
			sums[j]+=src[j];
			// and set the destination
			dst[j]=sums[j]/i;
		}
	}
}

//! This is another Line function, very optimized but more complicated,
//! without MulDiv and with idx increment is two time more quickly.<br>
void Line(int px,int py)
{
	// skip does pixel.
	if(does[py*width+px]) return;
	short dx=px-lx; //< delta x
	short dy=py-ly; //< delta y
	unsigned short nx=dx<0? -dx :dx; //< number of pixel along x
	unsigned short ny=dy<0? -dy :dy; //< number of pixel along y
	long idx=lx+ly*width; //< the offset of light
	unsigned char *doesPtr=&does[idx];
	idx+=idx+idx;
	unsigned char *srcPtr=&sourceBits[idx];
	unsigned char *dstPtr=&endBits[idx];
	// we use a fraction number to calculate i*min(nx,ny)/max(nx,ny)
	// the numerator si fraction and denominator is costant np.
	long fraction=0;
	unsigned short increment; //min(nx,ny)
	unsigned short np; //< max(nx,ny) and number of pixel along the line
	long delta1; // delta1 will be used every pixel change
	long delta2; // delta2 will be used only when i*min(nx,ny)/max(nx,ny) change units
	long delta13,delta23; // delta13 is delta1*3 and delta23 is delta2*3 (3 is the number of components)
	if(nx>ny)
	{
		np=nx; //the number of pixel si nx
		increment=ny; // numerator
		delta1=dx<0? -1 : 1; //along x
		delta2=dy<0? -width : width; //along y
	} else
	{
		np=ny; //the number of pixel si ny
		increment=nx; // numerator
		delta1=dy<0? -width : width; //along y
		delta2=dx<0? -1 : 1; //along x
	}
	delta13=delta1+delta1+delta1;
	delta23=delta2+delta2+delta2;
	unsigned long sums[3]={0,0,0}; //< they contain the sum of pixel processed in line.
	unsigned char j;
	for(int i=1;i<=np;i++)
	{
		doesPtr+=delta1;
		srcPtr+=delta13;
		dstPtr+=delta13;
		fraction+=increment;
		if(fraction>=np)
		{
			doesPtr+=delta2;
			srcPtr+=delta23;
			dstPtr+=delta23;
			fraction-=np;
		}
		(*doesPtr)=TRUE;
		for(j=0;j<3;j++)
		{
			// sum the pixel
			sums[j]+=srcPtr[j];
			// and set the destination
			dstPtr[j]=sums[j]/i; //< this division is the core and the malediction of luce...
		}
	}
}


// I can make a multi-thread version of luce (every thread make a Line)
// i think it can be a good excercise for reader. <br>
// The Photoshop effect have the bound check and tons of other options.<br>
// For quadratic attenuation sum the source pixel scaled with distance of light and divide for the quadratic of distance.<br>

