/* 
This plugin Was a process developed by Mikado(Rmckay@chat.carleton.ca)
and was coded by: 

	SirDude		mein@cs.umn.edu
	http://www.cs.umn.edu/~mein/blender
  
	Reevan "Mikado" McKay	rmckay@chat.carleton.ca
	http://chat.carleton.ca/~rmckay/3d
	
	  *MIKADO:	Sat Mar 18 04:51:00 GMT 2000
	  Fixed the blur routine to be a lot faster.  It is stronger now, so
	  lower values produce larger blurs.  The maximum available blur radius
	  has been lowered to reflect this new behaviour.
	  Fixed a memory-zeroing omission in the IsolateHighlights routine
	  *MIKADO:	Fri Mar 17 18:10:00	GMT 2000
	  Changed the button layout.
	  Separated the elements of the effect to make it easier to optimize 
	  each one.  I apologize for the blur function; I had never written 
	  one before.  If someone can make it faster, please email me!
	  *SIRDUDE: Tue Mar 14 10:46:02 CST 2000
	  It basically blur's bright parts of an image and adds the results back
	  into the original image, to form a soft glow around the selected 
	  parts.  You can Modify the minimum and maximum intensity to use in 
	  the glow.
*/

#include <math.h>
#include "plugin.h"

enum {
	R=0,
	G=1,
	B=2,
	A=3
};

/* The cast struct is for input in the main doit function
Varstr and Cast must have the same variables in the same order */ 

typedef struct Cast {
	int dummy;	/* 	because of the 'label' button */
	float fMini;	/*	Minimum intensity to trigger a glow */
	float fClamp;
	float fBoost;	/*	Amount to multiply glow intensity */
    	float dDist;	/*	Radius of glow blurring */
	int	dQuality;
	int	bNoComp;/*	SHOW/HIDE glow buffer */
} Cast;


/*	Function prototypes */

/*	CORE FUNCTIONS */
void	RVBlurBitmap2( unsigned char* map, int width,int height,float blur,
        int quality);
void	RVIsolateHighlights (unsigned char* in,unsigned char* out,int width,
        int height,int threshold,float boost,float clamp);
void	RVAddBitmaps (unsigned char* a,unsigned char* b,unsigned char* c,
        int width,int height);

/*	OBSOLETE FUNCTIONS */
void	RVBlurBitmap ( unsigned char* map, int width,int height,int blur,
        int blurstep);

/*	Plugin function prototypes */
void plugin_seq_doit(Cast *, float, float, int, int, ImBuf *, ImBuf *, 
        ImBuf *, ImBuf *);
int plugin_seq_getversion(void);
void plugin_but_changed(int but);
void plugin_init(void);
void plugin_getinfo(PluginInfo *info);

/* ******************** GLOBAL VARIABLES ***************** */
char name[24]= "glow";	

/* structure for buttons, 
*  butcode      name           default  min  max  
*/

VarStruct varstr[]= {
	{ LABEL,	"In: 1 strip",		0.0,	0.0,	0.0, ""}, 
 	{ NUM|FLO,	"Threshold",	0.5,	0.0,	1.0, 
		"Trigger Intensity"},
	{ NUM|FLO,	"Clamp",		1.0,	0.0,	1.0,
		"Brightness limit of intensity"},
	{ NUM|FLO, "Boost Factor",	0.5,	0.0,	10.0,
		"Brightness multiplier"},
	{ NUM|FLO,"Blur Distance",3.0,	0.5,	20.0, 
		"Radius of glow effect"}, 
	{ NUM|INT,"Quality",			3.0,	1.0,	5.0,
		"Accuracy of the blur effect"},
	{ TOG|INT,"Only Boost"		,0.0,	0.0,	1.0,
		"Show the glow buffer only"},
};


/* cfra: the current frame */
float cfra;


/* ******************** Fixed functions ***************** */

/*=============================== */
int plugin_seq_getversion(void) {
	/*=============================== */
	return B_PLUGIN_VERSION;
}

/*=============================== */
void plugin_seq_doit(Cast *cast, float facf0, float facf1, int xo, int yo, 
	 ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *outbuf, ImBuf *use) 
/*=============================== */
{
	unsigned char *out=(unsigned char *)outbuf->rect;
	unsigned char *in=(unsigned char *)ibuf1->rect;
	
	RVIsolateHighlights	(in,out,xo,yo,cast->fMini*765,
           cast->fBoost,cast->fClamp);
	RVBlurBitmap2 (out,xo,yo,cast->dDist,cast->dQuality);
	if (!cast->bNoComp)
		RVAddBitmaps (in,out,out,xo,yo);
}

/*=============================== */
void plugin_but_changed(int but) {}
/*=============================== */

/*=============================== */
void plugin_init(void) {}
/*=============================== */

/*=============================== */
void plugin_getinfo(PluginInfo *info) {
	/*=============================== */
	info->name= name;
	info->nvars= sizeof(varstr)/sizeof(VarStruct);
	info->cfra= &cfra;
	
	info->varstr= varstr;
	
	info->init= plugin_init;
	info->seq_doit= (SeqDoit) plugin_seq_doit;
	info->callback= plugin_but_changed;
}

/*=============================== */
void RVBlurBitmap2 ( unsigned char* map, int width,int height,float blur,
   int quality)
/*	MUUUCCH better than the previous blur. */
/*	We do the blurring in two passes which is a whole lot faster. */
/*	I changed the math arount to implement an actual Gaussian */
/*	distribution. */
/* */
/*	Watch out though, it tends to misbehaven with large blur values on */
/*	a small bitmap.  Avoid avoid avoid. */
/*=============================== */
{
	unsigned char*	temp=NULL,*swap;
	float	*filter=NULL;
	int	x,y,i,fx,fy;
	int	index, ix, halfWidth;
	float	fval, k, weight, curColor[3], curColor2[3];
	
	/*	If we're not really blurring, bail out */
	if (blur<=0)
		return;
	
	/*	Allocate memory for the tempmap and the blur filter matrix */
	temp=malloc (width*height*4);
	if (!temp)
		return;

	/*	Allocate memory for the filter elements */
	halfWidth = ((quality+1)*blur);
	filter=(float *)malloc (sizeof(float)*halfWidth*2);
	if (!filter){
		free (temp);
		return;
	}		
	
	/*	Apparently we're calculating a bell curve */
	/*	based on the standard deviation (or radius) */
	/*	This code is based on an example */
	/*	posted to comp.graphics.algorithms by */
	/*	Blancmange (bmange@airdmhor.gen.nz) */
	
	k = -1.0/(2.0*3.14159*blur*blur);
	fval=0;
	for (ix = 0;ix< halfWidth;ix++){
          	weight = (float)exp(k*(ix*ix));
		filter[halfWidth - ix] = weight;
		filter[halfWidth + ix] = weight;
	}
	filter[0] = weight;
	
	/*	Normalize the array */
	fval=0;
	for (ix = 0;ix< halfWidth*2;ix++)
		fval+=filter[ix];
	
	for (ix = 0;ix< halfWidth*2;ix++)
		filter[ix]/=fval;
	
	/*	Blur the rows */
	for (y=0;y<height;y++){	
		/*	Do the left & right strips */
		for (x=0;x<halfWidth;x++){
			index=(x+y*width)*4;
			fx=0;
			curColor[0]=curColor[1]=curColor[2]=0;
			curColor2[0]=curColor2[1]=curColor2[2]=0;
			
			for (i=x-halfWidth;i<x+halfWidth;i++){
			   if ((i>=0)&&(i<width)){
				curColor[0]+=map[(i+y*width)*4+R]*filter[fx];
				curColor[1]+=map[(i+y*width)*4+G]*filter[fx];
				curColor[2]+=map[(i+y*width)*4+B]*filter[fx];

				curColor2[0]+=map[(width-1-i+y*width)*4+R] *
                                   filter[fx];
				curColor2[1]+=map[(width-1-i+y*width)*4+G] * 
                                   filter[fx];
				curColor2[2]+=map[(width-1-i+y*width)*4+B] *
                                   filter[fx];
				}				
				fx++;
			}
			temp[index+R]=curColor[0];
			temp[index+G]=curColor[1];
			temp[index+B]=curColor[2];
			
			temp[((width-1-x+y*width)*4)+R]=curColor2[0];
			temp[((width-1-x+y*width)*4)+G]=curColor2[1];
			temp[((width-1-x+y*width)*4)+B]=curColor2[2];
			
		}
		/*	Do the main body */
		for (x=halfWidth;x<width-halfWidth;x++){
			index=(x+y*width)*4;
			fx=0;
			curColor[0]=curColor[1]=curColor[2]=0;
			for (i=x-halfWidth;i<x+halfWidth;i++){
				curColor[0]+=map[(i+y*width)*4+R]*filter[fx];
				curColor[1]+=map[(i+y*width)*4+G]*filter[fx];
				curColor[2]+=map[(i+y*width)*4+B]*filter[fx];
				fx++;
			}
			temp[index+R]=curColor[0];
			temp[index+G]=curColor[1];
			temp[index+B]=curColor[2];
		}
	}

	/*	Swap buffers */
	swap=temp;temp=map;map=swap;
	

	/*	Blur the columns */
	for (x=0;x<width;x++){		
		/*	Do the top & bottom strips */
		for (y=0;y<halfWidth;y++){
			index=(x+y*width)*4;
			fy=0;
			curColor[0]=curColor[1]=curColor[2]=0;
			curColor2[0]=curColor2[1]=curColor2[2]=0;
			for (i=y-halfWidth;i<y+halfWidth;i++){
				if ((i>=0)&&(i<height)){
				   /*	Bottom */
				   curColor[0]+=map[(x+i*width)*4+R]*filter[fy];
				   curColor[1]+=map[(x+i*width)*4+G]*filter[fy];
				   curColor[2]+=map[(x+i*width)*4+B]*filter[fy];
					
				   /*	Top */
				   curColor2[0]+=map[(x+(height-1-i)*width) *
                                      4+R]*filter[fy];
				   curColor2[1]+=map[(x+(height-1-i)*width) *
                                      4+G]*filter[fy];
				   curColor2[2]+=map[(x+(height-1-i)*width) *
                                      4+B]*filter[fy];
				}
				fy++;
			}
			temp[index+R]=curColor[0];
			temp[index+G]=curColor[1];
			temp[index+B]=curColor[2];
			temp[((x+(height-1-y)*width)*4)+R]=curColor2[0];
			temp[((x+(height-1-y)*width)*4)+G]=curColor2[1];
			temp[((x+(height-1-y)*width)*4)+B]=curColor2[2];
		}
		/*	Do the main body */
		for (y=halfWidth;y<height-halfWidth;y++){
			index=(x+y*width)*4;
			fy=0;
			curColor[0]=curColor[1]=curColor[2]=0;
			for (i=y-halfWidth;i<y+halfWidth;i++){
				curColor[0]+=map[(x+i*width)*4+R]*filter[fy];
				curColor[1]+=map[(x+i*width)*4+G]*filter[fy];
				curColor[2]+=map[(x+i*width)*4+B]*filter[fy];
				fy++;
			}
			temp[index+R]=curColor[0];
			temp[index+G]=curColor[1];
			temp[index+B]=curColor[2];
		}
	}
	
		
	/*	Swap buffers */
	swap=temp;temp=map;map=swap;
		
	/*	Tidy up	 */
	free (filter);
	free (temp);
}

/*=============================== */
void	RVAddBitmaps (unsigned char* a,unsigned char* b,unsigned char* c,
   int width,int height)
/*	Adds two bitmaps and puts the results into a third map. */
/*	C must have been previously allocated but it may be A or B. */
/*	We clamp values to 255 to prevent weirdness */
/*=============================== */
{
	int	x,y,index;
	
	for (y=0;y<height;y++){
		for (x=0;x<width;x++){
			index=(x+y*width)*4;
			c[index+R]=MIN2(255,a[index+R]+b[index+R]);
			c[index+G]=MIN2(255,a[index+G]+b[index+G]);
			c[index+B]=MIN2(255,a[index+B]+b[index+B]);
			c[index+A]=MIN2(255,a[index+A]+b[index+A]);
		}
	}
}

/*=============================== */
void	RVIsolateHighlights (unsigned char* in,unsigned char* out,int width,
   int height,int threshold,float boost,float clamp)

/*	For each pixel whose total luminance exceeds the threshold, */
/*	Multiply it's value by BOOST and add it to the output map */
/*=============================== */
{
	int x,y,index;
	int	intensity;
	

	for(y=0;y< height;y++) {
		for (x=0;x< width;x++) {
	 	   index= (x+y*width)*4;
			
		   /*	Isolate the intensity */
		   intensity=(in[index+R]+in[index+G]+in[index+B]-threshold);
		   if (intensity>0){
			out[index+R]=MIN2(255*clamp,
                           (in[index+R]*boost*intensity)/255);
			out[index+G]=MIN2(255*clamp,
                           (in[index+G]*boost*intensity)/255);
			out[index+B]=MIN2(255*clamp,
                           (in[index+B]*boost*intensity)/255);
			}
			else{
				out[index+R]=0;
				out[index+G]=0;
				out[index+B]=0;
			}
			
		}
		
	}
	
}
