/* 
 * rgstereo.c: red/green filter for stereo effect 
 * by strubi, Tue Oct 19 16:48:56 MEST 1999
 *
 * needs Blender version >= 1.69
 *
 * copy this code to $BLENDERDIR/plugins/
 * and type "./bmake rgstereo" 
 * 
 * copyright by NaN & The TuteBook group
 *
 */

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

/* -------------------------
 * defines 
 */

#define lout(x) (printf("%s\n", x))

/* -------------------------
 * global variables
 */

float g_cfra;				/* the current frame */
char g_name[24]= "RGStereo 1.0";	/* the name of the plugin */

/* -------------------------
 * structure for buttons, 
 */

VarStruct varstr[] = {
/*	  button code,   text,             default,     min,    max, tip */
	{ LABEL,         "Input: 2 strips",    0.0,     0.0,    0.0, ""},
	{ TOG|INT,       "Rev R/L ",           0.0,     0.0,    1.0, 
	   "Reverse Left/Right"},
	{ NUMSLI|FLO,    "Br Bal ",            0.0,    -0.5,    0.5, 
 	   "Brightness balance"},
	{ NUMSLI|FLO,    "R Gam ",             1.0,     0.5,    2.0, 
	   "Gamma for Red"},
	{ NUMSLI|FLO,    "G Gam ",             1.0,     0.5,    2.0, 
	   "Gamma for Green"},
	{ NUMSLI|FLO,    "G Bal ",             0.0,     0.0,    1.0, 
	   "Adjust green balance"}
};

typedef struct Cast {
	int dummy;			/* because of the 'label' button */
	int rev;			/* reverse flag l/r */
	float bri;			/* Brightness balance */
	float rgamma;			/* Gamma red */
	float ggamma;			/* Gamma green */
	float g_bal;			/* green balance */
} Cast;

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

/* -------------------------
 * Fixed functions
 * here you usually don't need to change anything
 */

int plugin_seq_getversion(void) 
{
	return B_PLUGIN_VERSION;
}

void plugin_but_changed(int but) 
{
}

void plugin_init()
{
}

/* this routine is called by blender first when loading the
 * plugin to get its info
 */
void plugin_getinfo(PluginInfo *info)
{
	info->name= g_name;
	info->nvars= sizeof(varstr)/sizeof(VarStruct);
	info->cfra= &g_cfra;

	info->varstr= varstr;

	info->init= plugin_init;
	info->seq_doit= (SeqDoit) plugin_seq_doit;
	info->callback= plugin_but_changed;
}

/* -------------------------
 * here come our own routines
 */

/* to make the code below more readable: */
#define RED 0
#define GREEN 1
#define BLUE 2
#define ALPHA 3

void plugin_seq_doit(Cast *cast, float facf0, float facf1, int x, int y, 
	ImBuf *in1, ImBuf *in2, ImBuf *out, ImBuf *use)
{
	int i;
	unsigned char *src1, *src2, *dest;	/* you may also use "uchar" 
						  - see include/utils.h */
	int red[4], green[4];			/* for RGBA values of the 
                                                   red/green parts */
	float lum1, lum2;			/* temporary variables to 
                                                   calculate luminance */
	float bal, bright;			/* balance, brightness */
	float gamma1, gamma2;			/* gamma values for red/green */

	
	/* get brightness balance value for R/G from the cast struct
	 * and decide whether brightness of red needs to be reduced 
	 * according to the value */
	bright = (cast->bri < 0.0) ? (1.0 + cast->bri) : 1.0;
	bright *= 255.0;
	
	red[RED] = bright;
	red[GREEN] = 0;
	red[BLUE] = 0;

	/* same as above, additionally, the balance value is being
	 * respected as the blue ratio of the green colour is being 
           affected by it */
	bright = (cast->bri > 0.0) ? (1.0 - cast->bri) : 1.0;
	bright *= 255.0;

	bal = cast->g_bal;
	green[RED] = 0;
	green[GREEN] = bright * (1.0 - bal);
	green[BLUE] = bal * bright;

	dest = (unsigned char *) out->rect;	/* pointer to the RGBA array 
                                                   of the output buffer */

	if (!in2)				/* check whether second input 
                                                   valid, if not, fill the 
                                                   output buffer red to 
                                                   indicate an error */
	{
		i = x * y;		
		while (i--)
		{
			dest[RED] = 255;
			dest[GREEN] = 0;
			dest[BLUE] = 0;
			dest[ALPHA] = 255;
			dest += 4;
		}

		lout("Error: second buffer missing !");
		return;
	}

	/* pointer to RGBA arrays of the input buffers depending on the 
           L/R reverse flag*/
	src1 = (unsigned char *) (cast->rev ? in2->rect : in1->rect);	
	src2 = (unsigned char *) (cast->rev ? in1->rect : in2->rect);	

	/* get the gamma values from the cast struct: */
	gamma1 = cast->rgamma;
	gamma2 = cast->ggamma;

	i = x * y;
	while (i--)
	{
		/* calculate the luminance: with the formula
		 * Y = 0.299 * R + 0.587 * G + 0.114 * B with normalization
		 * to 1.0, i.e. the factors were divided by 255.0
		 */
		lum1 =  0.00117 * src1[RED] +  0.0023 * src1[GREEN] + 
                   0.000447 * src1[BLUE];
		lum2 =  0.00117 * src2[RED] +  0.0023 * src2[GREEN] + 
                   0.000447 * src2[BLUE];

		lum1 = pow(lum1, gamma1);	/* here we do the gamma warp 
                                                with the slider values - 
                                                the reason why the luminances
						were normalized to 0.0 - 1.0 */
		lum2 = pow(lum2, gamma2);
		
		/* now we merge the red/green into the output. Maybe you want to
		 * look back at the balance calculation above, to make sure that
		 * there can be no overflow */
		dest[RED] = lum1 * (float) red[RED] + lum2 * (float) green[RED];
		dest[GREEN] = lum1 * (float) red[GREEN] + 
                   lum2 * (float) green[GREEN];
		dest[BLUE] = lum1 * (float) red[BLUE] + 
                   lum2 * (float) green[BLUE];
		dest += 4; src1 += 4; src2 += 4;

		/* take MAX(src1[ALPHA], src2[ALPHA]) : */
		dest[ALPHA] = (src1[ALPHA] > src2[ALPHA] ) ? 
                   src1[ALPHA] : src2[ALPHA];
	}
}
