/*
 * Chroma Keying Plugin (YUV Version) 0.9
 *
 * Copyright (c) 1999 Daniel Dunbar <zr@blender.nl>, Stefan Gartner <Stefan.Gartner@fhs-hagenberg.ac.at>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include "math.h"
#include "plugin.h"

char name[]= "Chroma Key";

VarStruct varstr[]= {
	{ NUMSLI|FLO, "Y:",	0.5,	0.0,	1.0,	""}, 
	{ NUMSLI|FLO, "U:",	0.5,	0.0,	1.0,	""},
	{ NUMSLI|FLO, "V:",	0.5,	0.0,	1.0,	""},

	{ NUMSLI|FLO, "TolY:",0.1,	0.0001,	1.0,	"Tolerance(Y)"}, 
	{ NUMSLI|FLO, "TolU:",0.1,	0.0001,	1.0,	"Tolerance(U)"},
	{ NUMSLI|FLO, "TolV:",0.1,	0.0001,	1.0,	"Tolerance(V)"},

	{ TOG|INT,	"Y On", 1.0,	0.0,	1.0,	""}, 
	{ TOG|INT,	"U On", 1.0,	0.0,	1.0,	""},
	{ TOG|INT,	"V On", 1.0,	0.0,	1.0,	""},
	
	{ NUMSLI|FLO,	"Blend: ", 0.1,	0.0,	1.0,	""},
	{ NUMSLI|INT,	"Debug: ", -1.0,	-1.0,	2.0,	""},
};

typedef struct Cast {
	float y, u, v;
	float ty, tu, tv;
	int y_on, u_on, v_on;
	float blend;
	int debug;
} Cast;

float cfra;

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

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

static void rgb_to_yuv(float rgb[3], float yuv[3]) {
	yuv[0]= 0.299*rgb[0] + 0.587*rgb[1] + 0.114*rgb[2];
	yuv[1]= 0.492*(rgb[2] - yuv[0]);
	yuv[2]= 0.877*(rgb[0] - yuv[0]);
	
	/* Normalize */
	yuv[1]*= 255.0/(122*2.0);
	yuv[1]+= 0.5;
	
	yuv[2]*= 255.0/(157*2.0);
	yuv[2]+= 0.5;
}

void plugin_seq_doit(Cast *cast, float facf0, float facf1, int width, 
	int height, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *out, ImBuf *use) {
	char *dest, *src1, *src2;
	float yc= cast->y, uc= cast->u, vc= cast->v;
	float ty= cast->ty, tu= cast->tu, tv= cast->tv;
	int y_on= cast->y_on, u_on= cast->u_on, v_on= cast->v_on;
	float fac_y, fac_u, fac_v, fac, mfac;
	float rgb[3], yuv[3];
	int debug= cast->debug;
	float blend= cast->blend;
	int x, y, c;

	if (!ibuf1 || !ibuf2 || ibuf2==ibuf1) return;

	dest= (char *) out->rect;
	src1= (char *) ibuf1->rect;
	src2= (char *) ibuf2->rect;

	y= height;
	while (y--) {
	   x= width;
	   while (x--) {
		rgb[0]= (float)src1[0]/255.0;
		rgb[1]= (float)src1[1]/255.0;
		rgb[2]= (float)src1[2]/255.0;
		rgb_to_yuv(rgb, yuv);

		if (debug!=-1) {
			*(dest++)= yuv[debug]*255.0;
			*(dest++)= yuv[debug]*255.0;
			*(dest++)= yuv[debug]*255.0;
			*(dest++)= 1.0;
			src1 +=4; src2 +=4;
		} else {
			fac_y= fabs(yuv[0]-yc);
			fac_u= fabs(yuv[1]-uc);
			fac_v= fabs(yuv[2]-vc);
	 		if ((y_on && fac_y<=ty) || (u_on && fac_u<=tu)  || 
			   (v_on && fac_v<=tv)) {
				mfac= 0.0; c= 0;
				if (y_on && fac_y<=ty) { 
					mfac+= 1.0-(fac_y/ty);
					c++; 
				}
				if (u_on && fac_u<=tu) { 
					mfac+= 1.0-(fac_u/tu);
					c++; 
				}
				if (v_on && fac_v<=tv) { 
					mfac+= 1.0-(fac_v/tv);
					c++; 
				}
				mfac= mfac/c;

				if(mfac>blend) mfac= 1.0;
				else mfac/= blend;

				fac= 1.0-mfac;

				*(dest++)= *(src2++)*mfac + *(src1++)*fac;
				*(dest++)= *(src2++)*mfac + *(src1++)*fac;
				*(dest++)= *(src2++)*mfac + *(src1++)*fac;
				*(dest++)= *(src2++)*mfac + *(src1++)*fac;
			} else {
				*(dest++)= *(src1++);
				*(dest++)= *(src1++);
				*(dest++)= *(src1++);
				*(dest++)= *(src1++);
				src2 +=4;
			}
		}
	   }
	}
}
