/*
Zutilz-v.2a  -a sequence plugin with output based on the zbuffer
	console window gives information about the zbuffer and plugin settings
	produces 8bit grayscale depth maps of the selected Z values
	extracts entire zbuffer to rbga image for 32 bit depth file
	inserts zbuffer image or scene zbuffer to zbuffer of input if no zbuf present for composting
	DOF grayscale gradient mask output with the selected Z values all black or white-clipping avail
	a "green screen" effect that masks output by z-buffer values instead of color

	3\25\2005    paprmh   Email me at gmail,com			GPL of course
*/

#include "plugin.h"
#include "stdio.h"
#include "floatpatch.h"
#include "string.h"

/*for the TOG3 buttons*/

#define TOG3 (7<<9)
#define SHO  64

#define minZ -2147.483648
#define maxZ 2147.483647
/* ******************** GLOBAL VARIABLES ***************** */
char *butstr;
char buttext;

char name[24]= "Zutilz v.2a";

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

VarStruct varstr[]= {
	{TOG3|SHO,	"Depth/dumpZ",		0.0,	0.0,	1.0,		"grayscale from start-z to end-z, mask outside selection || dump Z to rgba image"},
	{TOG3|SHO,	"viewZ/DOFmask",	1.0,	0.0,	1.0,		"show z as 3 gradients: Red<start-z, Gray=select z, Blue>end-z || DOF mask"},
	{TOG3|SHO,	"maskZ/invmask",	0.0,	0.0,	1.0,		"mask outside || inside selection"},
	{LABEL,		"maskcol+alpha",	0.0,	0.0,	0.0,		"color and alpha used for masking output"},
	{COL|CHA,		"color",			0.0,	0.0,	255.0,	"mask color"},
	{NUM|INT,		"Alpha: ",		0.0,	0.0,	255.0,	"alpha component of mask color"},
	{TOG|INT,		"auto select",		1.0,	0.0,	1.0,		"set start-z to closest and end-z to farthest nontransparent pixel in frame"},
	{NUM|FLO,		">Z ",			minZ,minZ,2147.483,	"start-z value of selection (in millions) -auto range must be off to change"},
	{NUM|FLO,		"<Z ",			maxZ,minZ,maxZ, 	"end-z value of selection (in millions) -auto range must be off to change"},
	{TOG|INT,		"DOF clip",		0.0,	0.0,	1.0,		"clip near/far gradients for DOF mask"},
	{NUM|INT,		"front",			255.0,	0.0,	255.0,	"clipping value for near DOF mask"},
	{NUM|INT,		"rear",			255.0,	0.0,	255.0,	"clipping value for far DOF mask"},
	{LABEL,		"Zutilz v.2a",		0.0,	0.0,	0.0,		"if attached to only one strip, it must be either scene or IRIZ"},
  	{TOG|INT,		"output zbuf",		0.0,	0.0,	1.0,		"insert zbuf into output image for composting"},
	{TOG|INT,		"use ibuf2",		0.0,	0.0,	1.0,		"use ibuf2 (if present) for zbuf(only if no zbuf in ibuf1)"},
	{TOG|INT,		"inv gradient",	0.0,	0.0,	1.0,		"invert to white closest for gradient"},
	{TOG3|SHO,	"verbose / TMI",	1.0,	0.0,	256.0,	"output info to the console or (probably)Too Much Info"},

};

/* 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 grayZ;
	int viewZ;
	int maskZ;
	int onetime;			/*might as well use to preset mask color & other settings first time the plugin runs*/
	char maskcol[3];
	int maskalpha;
	int autoselect;
	float startz;
	float endz;
	int uclip;
	int nclip;
	int fclip;
	int mode;				/*might as well use to store mode*/
	int opZ;
	int use2;
	int invgradient;
	int talk;


} Cast;

/* cfra: the current frame */

float cfra;

void plugin_seq_doit(Cast *, float, float, int, int, ImBuf *, ImBuf *, ImBuf *, ImBuf *);

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

int plugin_seq_getversion(void)
{
	return B_PLUGIN_VERSION;
}

void plugin_but_changed(int but)
{
}

void plugin_init()
{
}

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;
}

/* ************************************************************
	zutilz - working code


		functions
   ************************************************************ */


/*move buffer function*/
void bufmov( int *out,int *in, int tb,int bts){
				int bn;

/*if bit shift*/
	if(!bts)memcpy(out, in, tb);
	else if(bts>0){
		for(bn=0;bn<tb/4; bn++){
			out[bn]=in[bn]>>bts;
		}
	}
	else {
		for(bn=0;bn<tb/4; bn++){
			out[bn]=in[bn]<<(-bts);
		}
	}

				printf("buffer moved...w/bitshift %d\n",bts);
			}

/*invert colors->warning image*/

void invcol(int *buf, int tpx ){
	int i;
	for(i=0; i<tpx; i++){
		buf[i]=((~buf[i])&0xffffff)|(buf[i]&0xff000000);
	}
}





/*******************************main function******************************************/

void plugin_seq_doit(Cast *cast, float facf0, float facf1, int sx, int sy, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *out, ImBuf *use)
{

	unsigned int tpix= sx*sy,totbytes=tpix*4, pixnum;
	VarStruct *infbut;
	char *dv;
	unsigned char mv[4];
	unsigned char tv[4];
	int *rectz1= (int *)ibuf1->zbuf;
	char *rectout= (char *)out->rect;
	char *irect1 = (char *)ibuf1->rect;
	unsigned char xv=0;
	float closeZ=maxZ,farZ=minZ,thisZ, startZ, endZ,autZ=minZ;
	char modestr[8][25]= {"lurking","view Z","DOF mask","8bit gray depth Z","dump Z to image buffer","mask outside selected Z","mask selected Z",""};
	char invstr[2][25]= {"normal","inverted"};
	char errstr[]="\n**********Zutil WARNING**********\n";
	char z2[2][5]={"rect","zbuf"};
	int z2p=0;
	int nc=255,fc=255;

/*set front/rear clipping values for gradients*/
	if(cast->uclip){
		fc=(cast->fclip);
		nc=(cast->nclip);
	}

/*	set default mask color first time plugin runs
		if user has not aready set it
	print opening message
*/

	if(!cast->onetime){
		if(cast->maskcol[0]==0&&cast->maskcol[1]==0&&cast->maskcol[2]==0&&cast->maskcol[3]==0)cast->maskcol[1]=255;
		cast->talk=1;
		cast->viewZ=1;
		cast->onetime=1;
		printf("\nThe zbuffer has 32 bits with a range of 4 gig [-2147483648 -> +2147483647]\n");
		printf("            The z-values shown  are\n    divided by 1 million to make adjustment easier\n");
		}



/* experiment*/

	infbut= &varstr[12];
	butstr=(char*)infbut->name;
	printf("\n%s...frame #%d...(%dx%d)...\nibuf1: ",butstr,(int)cfra,sx,sy);
	if(cast->talk){
		if(ibuf1->name[0])	printf("%s...",ibuf1->name);
		else printf("must be scene strip...");
	}



/*if no zbuffer load one if available
****************************************/

	if(!ibuf1->zbuf) {
		memcpy((int *)out->rect, (int *)ibuf1->rect, totbytes);
		printf("No zbuf...\n");
		if(ibuf2 && ibuf2!=ibuf1){
			printf("ibuf2: ");
			if(cast->talk){
				if(ibuf2->name[0])	printf("%s...",ibuf2->name);
				else printf("must be scene strip...");
			}
			if(ibuf2->zbuf){
				printf("with zbuf...\n...strip 2 zbuffer(ibuf2->zbuf) is avalable for import to zbuf...\n");
				z2p=1;
			}
			else printf("no zbuf...\n...strip 2 image(ibuf2->rect) is avalable for import to zbuf...\n");

/*if [use ibuf2] and ibuf2 has zbuffer use it else use image as zbuf*/
			if(cast->use2){
				rectz1=(ibuf2->zbuf)?	(int *)ibuf2->zbuf:(int *)ibuf2->rect;
				printf("Z-buffer imported from ibuf2->%s...",z2[z2p]);
			}
			else{
				printf("\n     ->toggle [use ibuf2] to use ibuf2->%s for working zbuffer\n",z2[z2p]);
				cast->opZ=0;
/*output err screen*/
				invcol((int *)rectout,tpix);
				return;
			}
		}
		else {
			printf("\n     ->and no second strip to use for one\n...you need to add a rgba depth image or\n...the origional scene strip to plugin\n");
/*output err screen*/
			invcol((int *)rectout,tpix);
			cast->opZ=0;
			return;
		}
	}
	else cast->use2=0;

/********************************
************now work with zbuffer*/




	if(cast->talk) printf("working zbuf detected...\n");

/*output zbuf if present*/
	if(cast->opZ){
		out->zbuf=rectz1;
		if(cast->talk) printf("zbuf exported to output image(out->zbuf linked to working zbuf) ...\n");
	}


/*dump z buffer into output image*/
	if(cast->grayZ && cast->grayZ!=1){
		memcpy((int *)out->rect, (int *)rectz1, totbytes);
	}




/* find closest & farthest non transparent pixel's z value in the frame for auto select*/

	for(pixnum=0; pixnum<tpix; pixnum++) {
		thisZ=(float)rectz1[pixnum]/1000000;
		if(thisZ>farZ)farZ=thisZ;
		if(thisZ<closeZ)closeZ=thisZ;
		if(thisZ>autZ){
			xv=irect1[(pixnum*4)+3];
			if(xv==255)autZ=thisZ;
		}
	}

/*****************************************************************************************************/



/*radio button effect*/

	if(cast->viewZ && cast->mode>2){
		if(cast->grayZ)cast->grayZ=0;
		if(cast->maskZ)cast->maskZ=0;

	}
	else if(cast->grayZ && (cast->mode<3 || cast->mode>4)){
		if(cast->viewZ)cast->viewZ=0;
		if(cast->maskZ)cast->maskZ=0;
	}
	else if(cast->maskZ && cast->mode<5){
		if(cast->viewZ)cast->viewZ=0;
		if(cast->grayZ)cast->grayZ=0;
	}

/*new set mode*/

	if(cast->viewZ){
		cast->mode=(cast->viewZ==1) ? 1:2;
	}
	else if(cast->grayZ){
		cast->mode=(cast->grayZ==1) ? 3:4;
	}
	else if(cast->maskZ){
		cast->mode=(cast->maskZ==1) ? 5:6;
	}
	else cast->mode=0;




/* set up and check for valid selection values */

	if(cast->autoselect){

		cast->startz=startZ=closeZ;
		cast->endz=endZ=autZ;
	}
	else {
		startZ= (cast->startz);
		endZ=(cast->endz);
	}




/*get mask color and alpha*/
	mv[0]=cast->maskcol[0];
	mv[1]=cast->maskcol[1];
	mv[2]=cast->maskcol[2];
	mv[3]=(char) cast->maskalpha;

/*if start selec > end select then switch*/
	if(startZ>endZ){
		thisZ=endZ;				/*might as well use thisZ as temp variable*/
		cast->endz=endZ=startZ;
		cast->startz=startZ=thisZ;

		printf("\n%s\n* start z must be smaller than end z\n* start z and end z have been switched\n%s\n",errstr,errstr);
	}


	if((cast->mode==2 || cast->mode>4) && cast->autoselect){
		printf("\n%s\n* Turn off autoselect\n* Then adjust start-z and end-Z for proper effect\n%s\n",errstr,errstr);
	}

/* print info to the console if verbose or TMI*/

	if(cast->talk){
		if(cast->talk!=1){
		printf("\nThe zbuffer has 32 bits with a range of 4 gig [-2147483648 -> +2147483647]\n");
		printf("            The z-values shown  are\n    divided by 1 million to make adjustment easier\n");
		thisZ=(farZ-closeZ);
		printf("This frame's closest and farthest pixels are %f million Z units apart\n",thisZ);
		pixnum=(unsigned int)(thisZ*1000000);
		for(xv=0;pixnum;xv++)pixnum>>=1;
		printf("     and uses %d of the available 32 bits\n",xv);
		}

		printf("\nZutil mode: %s",modestr[cast->mode]);
		if(cast->mode==4){
			printf("\n\nibuf1->zbuf dumped into out->rect...\n");
			printf("     output is now 32bit RGBA snapshot of zbuffer\n     save this z-buffer image as a rgba for depth file\n");
			return;
		}
		if(cast->mode && cast->mode<4)printf(", %s gradient",invstr[cast->invgradient]);
		printf("\n");
		if(cast->uclip)printf("DOF gradient clip active: near clip=%d, far clip=%d\n",nc,fc);
		if(cast->mode && cast->mode!=2)printf("    mask color: red %u; green %u; blue %u; alpha %u\n",mv[0],mv[1],mv[2],mv[3]);
		if(cast->autoselect)printf("auto select on [toggle off to change selected z-values]\n");
		printf("   Selected range start Z: %12.6f;     end Z: %12.6f\n",(startZ),(endZ));
		printf("    This frame's lowest Z: %12.6f; highest Z: %12.6f\n",(closeZ),(farZ));


	}

/* loop through the zbuffer */

	for(pixnum=0; pixnum<tpix; pixnum++) {
		thisZ=(float)rectz1[pixnum]/1000000;

		if(!cast->mode) dv=(char*)irect1;
		else if(startZ<=thisZ && thisZ<=endZ){

			if(cast->mode<4){
				if(cast->mode==2)xv=0;	/*if dof all black in selected*/
				else xv=(unsigned char)(((thisZ-startZ)/(endZ-startZ))*255);
				if(cast->invgradient) xv=255-xv;
				tv[0]=xv;
				tv[1]=xv;
				tv[2]=xv;
				tv[3]=255;
				dv=(char*)tv;
			}

			else if(cast->mode==5) dv=(char*)mv;

			else dv=(char*)irect1;

		}

		else {

			if(cast->viewZ){			/*either ziewZ or dof*/
				if (thisZ<startZ){
					xv=(unsigned char)((startZ-thisZ)/(startZ-closeZ)*nc);
					if(cast->invgradient) xv=255-xv;
					tv[0]=(cast->mode==2) ? xv:255;
					tv[1]=(cast->mode==2) ? xv:255-xv;		/*mode 2 dof so do grayscale else red->yellow*/
					tv[2]=(cast->mode==2) ? xv:0;
					tv[3]=(cast->mode==2) ? 255:mv[3];
					dv=(char*)tv;
				}
				else {
					xv=(unsigned char)((thisZ-endZ)/(farZ-endZ)*fc);
					if(cast->invgradient) xv=255-xv;
					tv[0]=(cast->mode==2) ? xv:0;		/*mode 2 dof so do grayscale else */
					tv[1]=(cast->mode==2) ? xv:xv;
					tv[2]=(cast->mode==2) ? xv:255;
					tv[3]=(cast->mode==2) ? 255:mv[3];
					dv=(char*)tv;

				}
			}
			else if(cast->mode==6 || cast->mode==3 || cast->mode==3) dv=(char*)mv;	/*output mask color*/
			else dv=(char*)irect1;							/*ouput origional pixel*/


		}

/*send to output buffer*/

		rectout[0]=dv[0];
		rectout[1]=dv[1];
		rectout[2]=dv[2];
		rectout[3]=dv[3];
		rectout += 4;
		irect1 += 4;

	}

}

