/**
 * $Id:$
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * The contents of this file may be used under the terms of either the GNU
 * General Public License Version 2 or later (the "GPL", see
 * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or
 * later (the "BL", see http://www.blender.org/BL/ ) which has to be
 * bought from the Blender Foundation to become active, in which case the
 * above mentioned GPL option does not apply.
 *
 * The Original Code is Copyright (C) 2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

#include "imbuf.h" 


#define OBJECTBLOK "cspace"

/************************************************************************/
/*				COLORSPACE				*/
/************************************************************************/

extern short * safechromamap;
extern uchar * safelumamap;

int Cspace_err_diff = FALSE;

int cspace_err_diff(int new)
{
	int old;
	
	old = Cspace_err_diff;
	Cspace_err_diff = new;
	
	return(old);
}

int colcspace(int col,float mat[][4])
{
	float r,g,b;
	short x,y,z;

	b = (col & 0xff0000) >> 16;
	g = (col & 0xff00) >> 8;
	r = col & 0xff;

	x = b * mat[0][0] + g * mat[1][0] + r * mat[2][0] + mat[3][0] + .5;
	y = b * mat[0][1] + g * mat[1][1] + r * mat[2][1] + mat[3][1] + .5;
	z = b * mat[0][2] + g * mat[1][2] + r * mat[2][2] + mat[3][2] + .5;

	if (x & 0xFF00){
		if (x < 0) x=0;
		else x = 255;
	}
	if (y & 0xFF00){
		if (y < 0) y=0;
		else y = 255;
	}
	if (z & 0xFF00){
		if (z < 0) z=0;
		else z = 255;
	}

	col = (x << 16) + (y << 8) + z;
	return(col);
}


void fillmattab(val,mattab)
double val;
ushort *mattab;
{
	int tot,ival;
	int i;

	val *= (1 << 22);
	ival = val;
	tot = 32767; /* een half */

	for(i = 256; i > 0; i--){
		*(mattab) = (tot >> 16);
		mattab += 3;
		tot += ival;
	}
}


void cspfill(buf, fill, x)
short *buf,*fill;
int x;
{
	short r,g,b;

	b = fill[0];
	g = fill[1];
	r = fill[2];
	for (;x>0;x--){
		buf[0] = b;
		buf[1] = g;
		buf[2] = r;
		buf += 3;
	}
}


void cspadd(buf,cont,rect,x)
short *buf,cont[][3];
int x;
uchar *rect;
{
	short i;
	for (;x>0;x--){
		i = *(rect);
		rect += 4;
		buf[0] += cont[i][0];
		buf[1] += cont[i][1];
		buf[2] += cont[i][2];
		buf += 3;
	}
}


void cspret(buf,rect,x)
short *buf;
int x;
uchar *rect;
{
	int r,g,b;
	int dr = 0, dg = 0, db = 0;
	
	for(; x > 0; x--){
		b = buf[0];
		g = buf[1];
		r = buf[2];

		if (Cspace_err_diff) {
			r += dr;
			g += dg;
			b += db;
		}
		
		if (b & 0x4000){
			if (b<0) rect[2]=0;
			else rect[2]=255;
		} else rect[2] = b >> 6;

		if (g & 0x4000){
			if (g<0) rect[1]=0;
			else rect[1]=255;
		} else rect[1] = g >> 6;

		if (r & 0x4000){
			if (r<0) rect[0]=0;
			else rect[0]=255;
		} else rect[0] = r >> 6;

		if (Cspace_err_diff) {
			dr = r & 0x3f;
			dg = g & 0x3f;
			db = b & 0x3f;
		}

		buf += 3;
		rect += 4;
	}
}


void rotcspace(ibuf,cont_1,cont_2,cont_3,add)
struct ImBuf *ibuf;
short *cont_1,*cont_2,*cont_3,*add;
{
	short x,y,*buf;
	uchar *rect;

	x=ibuf->x;
	rect= (uchar *)ibuf->rect;

	buf=(short *)malloc(x*3*sizeof(short));
	if (buf){
		for(y=ibuf->y;y>0;y--){
			cspfill(buf,add,x);
			cspadd(buf,cont_1,rect+0,x);
			cspadd(buf,cont_2,rect+1,x);
			cspadd(buf,cont_3,rect+2,x);
			cspret(buf,rect,x);
			rect += x<<2;
		}
		free(buf);
	}
}


void cspace(ibuf,mat)
struct ImBuf *ibuf;
float mat[][4];
{
	short *cont_1,*cont_2,*cont_3,add[3];

	cont_1=(short *)malloc(256*3*sizeof(short));
	cont_2=(short *)malloc(256*3*sizeof(short));
	cont_3=(short *)malloc(256*3*sizeof(short));

	if (cont_1 && cont_2 && cont_3){

		fillmattab(mat[0][0],cont_1);
		fillmattab(mat[0][1],cont_1+1);
		fillmattab(mat[0][2],cont_1+2);

		fillmattab(mat[1][0],cont_2);
		fillmattab(mat[1][1],cont_2+1);
		fillmattab(mat[1][2],cont_2+2);

		fillmattab(mat[2][0],cont_3);
		fillmattab(mat[2][1],cont_3+1);
		fillmattab(mat[2][2],cont_3+2);

		add[0] = (mat[3][0] * 64.0) + .5;
		add[1] = (mat[3][1] * 64.0) + .5;
		add[2] = (mat[3][2] * 64.0) + .5;

		rotcspace(ibuf, cont_1, cont_2, cont_3, add);
	}

	if (cont_1) free(cont_1);
	if (cont_2) free(cont_2);
	if (cont_3) free(cont_3);
}


void init_safechromamap()
{
	float u, u2, v, len;
	short * map, safeu, safev;

	if (safechromamap) return;
	safechromamap = (short *) malloc(256 * 256 * sizeof(short));
	if (safechromamap == 0) return;

	map = safechromamap;

	for (u = -1.0; u < 1.0; u += 2.0 / 256.0){
		u2 = u * u;
		for (v = -1.0; v < 1.0; v += 2.0 / 256.0){
			len = u2 + v * v;
			if (len > 0.75 * 0.75){
				len = 128.0 * 0.75 / sqrt(len);
				safeu = (u * len) + 128.5;
				safev = (v * len) + 128.5;
				*map++ = (safeu << 8) + (safev & 0xff);
			} else{
				*map++ = 0;
			}
		}
	}
}

void init_safelumamap()
{
	int y, u, v;
	float fy, fu, fv;

	if (safelumamap) return;
	safelumamap = mallocstruct(uchar, 256 * 256);
	if (safelumamap == 0) return;

	for (u = 0; u < 256; u++){
		fu = (u - 128.0) / 128.0;
		for (v = 0; v < 256; v++){
			fv = (v - 128.0) / 128.0;
			fy = (fu * fu) + (fv * fv);
			fy *= 256.0;
			if (fy > 255.0) fy = 255.0;
			safelumamap[(u << 8) + v] = fy;
		}
	}
}


void safechroma(struct ImBuf * ibuf)
{
	uchar * rect;
	int i;
	ushort c;

	if (ibuf == 0) return;
	if (ibuf->rect == 0) return;

	if (safechromamap == 0) init_safechromamap();
	if (safechromamap == 0) return;

	rect = (uchar *) ibuf->rect;
	for (i = ibuf->x * ibuf->y; i > 0; i--){
		c = safechromamap[(rect[1] << 8) + rect[3]];
		if (c) {
			rect[1] = c >> 8;
			rect[3] = c;
		}
		rect += 4;
	}
}


void safeluma(struct ImBuf * ibuf)
{
	short x, y, t;
	float val, val2;
	uchar *rect;

	if (ibuf == 0) return;
	if (ibuf->rect == 0) return;

	if (safelumamap == 0) init_safelumamap();
	if (safelumamap == 0) return;

	rect = (uchar *) ibuf->rect;
	for(y = ibuf->y ; y > 0; y--) {
		for(x = ibuf->x ; x > 0; x--) {
			t = safelumamap[(rect[1] << 8) + rect[3]];
			if (t > rect[2]) {
				/* u en v naar beneden scalen */

				val = 1.0 - ((t - rect[2]) / (2.0 * t));
				val2 = (val + (val * val)) / 2.0;

				rect[1] = (val2 * (rect[1] - 128.0)) + 128.5;
				rect[3] = (val2 * (rect[3] - 128.0)) + 128.5;
				
				/* y middelen met 'veilige' waarde */
				
				rect[2] = t + 0.5 + val * (rect[2] - t);
			}
			rect += 4;
		}
	}
}


void gamwarp(ibuf,gamma)
struct ImBuf *ibuf;
double gamma;
{
	uchar gam[256];
	int i;
	uchar *rect;

	if (ibuf == 0) return;
	if (ibuf->rect == 0) return;
	if (gamma == 1.0) return;

	gamma = 1.0 / gamma;
	for (i = 255 ; i >= 0 ; i--) gam[i] = (255.0 * pow(i / 255.0 , gamma)) + 0.5;

	rect = (uchar *) ibuf->rect;
	for (i = ibuf->x * ibuf->y ; i>0 ; i--){
		rect ++;
		*rect ++ = gam[*rect];
		*rect ++ = gam[*rect];
		*rect ++ = gam[*rect];
	}
}