/**
 * $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 "rectop"

void rectxor(drect,srect,x)
int *drect,*srect,x;
{
	for(;x>0;x--){
		*drect++ ^= *srect++;
	}
}

void rectmakepremul(drect,srect,x)
uchar *drect,*srect,x;
{
	uchar alpha;
	
	for(;x>0;x--){
		alpha = drect[0];
		drect[1] = (drect[1] * alpha ) / 255;
		drect[2] = (drect[2] * alpha ) / 255;
		drect[3] = (drect[3] * alpha ) / 255;
		drect += 4;
	}
}

void rectmakegenlock(drect,srect,x)
uchar *drect,*srect,x;
{
	int col;
	uchar alpha;
	
	for(;x>0;x--){
		alpha = drect[0];
		if (alpha) {
			col =  (drect[1] * 255 ) / alpha;
			if (col > 255) col = 255;
			drect[1] = col;

			col =  (drect[2] * 255 ) / alpha;
			if (col > 255) col = 255;
			drect[2] = col;

			col =  (drect[3] * 255 ) / alpha;
			if (col > 255) col = 255;
			drect[3] = col;
		}
		drect += 4;
	}
}

void rectsub(drect,srect,x)
int *drect,*srect,x;
{
	for(;x>0;x--){
		*drect++ -= *srect++;
	}
}

void rectaddchar(drect,srect,x)
uchar *drect,*srect;
int x;
{
	int col;
	
	for(;x>0;x--){
		col = drect[0] + srect[0];
		if (col > 255) drect[0] = 255;
		else drect[0] = col;

		col = drect[1] + srect[1];
		if (col > 255) drect[1] = 255;
		else drect[1] = col;
		
		col = drect[2] + srect[2];
		if (col > 255) drect[2] = 255;
		else drect[2] = col;

		col = drect[3] + srect[3];
		if (col > 255) drect[3] = 255;
		else drect[3] = col;

		drect += 4;
		srect += 4;
	}
}

void rectcpymask(drect, srect, x, mask)
uint *drect,*srect;
int x;
{
	int invmask = ~mask;
	
	for(; x > 0; x--){
		drect[0] = (drect[0] & invmask) | (srect[0] & mask);
		drect ++;
		srect ++;
	}
}


void rectsignedadd(drect,srect,x)
uchar *drect,*srect;
int x;
{
	int col;
	
	for(;x>0;x--){
		col = drect[0] + (srect[0] << 1) - 256;;
		if (col & 256) {
			if (col < 0) drect[0] = 0;
			else drect[0] = 255;
		} else drect[0] = col;

		col = drect[1] + (srect[1] << 1) - 256;
		if (col & 256) {
			if (col < 0) drect[1] = 0;
			else drect[1] = 255;
		} else drect[1] = col;
		
		col = drect[2] + (srect[2] << 1) - 256;
		if (col & 256) {
			if (col < 0) drect[2] = 0;
			else drect[2] = 255;
		} else drect[2] = col;

		col = drect[3] + (srect[3] << 1) - 256;
		if (col & 256) {
			if (col < 0) drect[3] = 0;
			else drect[3] = 255;
		} else drect[3] = col;

		drect += 4;
		srect += 4;
	}
}

void rectadd(drect,srect,x)
int *drect,*srect,x;
{
	for(;x>0;x--){
		*drect++ += *srect++;
	}
}

void rectsubchar(drect,srect,x)
uchar *drect,*srect;
int x;
{
	for(;x>0;x--){
		drect[0] -= srect[0];
		drect[1] -= srect[1];
		drect[2] -= srect[2];
		drect[3] -= srect[3];
		drect += 4;
		srect += 4;
	}
}

void rectsub2(drect, srect, x)
uchar *drect, *srect;
int x;
{
	short col;
	
	for (; x > 0; x--){
		col = *drect - *srect++;
		if (col < 0) *drect++ = ((col + 1) >> 1) + 128;
		else *drect++ = (col >> 1) + 128;
		
		col = *drect - *srect++;
		if (col < 0) *drect++ = ((col + 1) >> 1) + 128;
		else *drect++ = (col >> 1) + 128;
		
		col = *drect - *srect++;
		if (col < 0) *drect++ = ((col + 1) >> 1) + 128;
		else *drect++ = (col >> 1) + 128;
		
		col = *drect - *srect++;
		if (col < 0) *drect++ = ((col + 1) >> 1) + 128;
		else *drect++ = (col >> 1) + 128;
	}
}


void rectadd2(drect, srect, x)
uchar *drect, *srect;
int x;
{
	short col;
	
	for (; x > 0; x--){
		col = *drect + ((*srect++ - 128) << 1);
		if (col & 256) {
			if (col < 0) col = 0;
			else col = 255;
		}
		*drect++ = col;

		col = *drect + ((*srect++ - 128) << 1);
		if (col & 256) {
			if (col < 0) col = 0;
			else col = 255;
		}
		*drect++ = col;
		
		col = *drect + ((*srect++ - 128) << 1);
		if (col & 256) {
			if (col < 0) col = 0;
			else col = 255;
		}
		*drect++ = col;

		col = *drect + ((*srect++ - 128) << 1);
		if (col & 256) {
			if (col < 0) col = 0;
			else col = 255;
		}
		*drect++ = col;
	}
}


void rectorvalue(drect, srect, x, value)
uint *drect, *srect;
int x, value;
{
	for(;x > 0; x--)
		*drect++ |= value;
}

void rectandvalue(drect, srect, x, value)
uint *drect, *srect;
int x, value;
{
	for(;x > 0; x--)
		*drect++ &= value;
}

void rector(drect, srect, x)
uint *drect, *srect;
int x;
{
	for(;x > 0; x--)
		*drect++ |= *srect++;
}


void rectcpy(drect, srect, x, dummy)
uint *drect, *srect;
int x;
int dummy; /* unused */
{
	memcpy(drect,srect, x * sizeof(int));
}


void rectcpyalpha(drect, srect, x)
uchar *drect, *srect;
int x;
{
	for(;x > 0; x--) {
		drect[0] = srect[0];
		drect += 4;
		srect += 4;
	}
}


void rectfill(drect, srect, x, value)
uint *drect, *srect;
int x, value;
{
	for (;x > 0; x--) *drect++ = value;
}


void rectblend(drect, srect, x, value)
uchar *drect, *srect;
int x, value;
{
	short v1, v2;

	v1 = *((float *) &value) * 256;
	v2 = 256 - v1;

	if (srect != drect){
		for (;x > 0; x--){
			drect[0] = ((v2 * drect[0]) + (v1 * srect[0])) >> 8;
			drect[1] = ((v2 * drect[1]) + (v1 * srect[1])) >> 8;
			drect[2] = ((v2 * drect[2]) + (v1 * srect[2])) >> 8;
			drect[3] = ((v2 * drect[3]) + (v1 * srect[3])) >> 8;
			drect += 4; 
			srect += 4;
		}
	} else{
		for (;x > 0; x--){
			drect[0] = (v2 * drect[0]) >> 8;
			drect[1] = (v2 * drect[1]) >> 8;
			drect[2] = (v2 * drect[2]) >> 8;
			drect[3] = (v2 * drect[3]) >> 8;
			drect += 4;
		}
	}
}


void rectalphaover_oud(drect, srect, x)
uchar * drect, *srect;
int x;
{
	uchar alpha;

	for ( ; x > 0; x--){
		alpha = srect[0];
		if (alpha){
			if (alpha == 255){
				*((uint *) drect) = *((uint *) srect);
			} else{
				drect[0] += (alpha * (256 - drect[0])) >> 8;
				drect[1] += (alpha * (srect[1] - drect[1])) >> 8;
				drect[2] += (alpha * (srect[2] - drect[2])) >> 8;
				drect[3] += (alpha * (srect[3] - drect[3])) >> 8;
			}
		}
		drect += 4;
		srect += 4;
	}
}


void rectalphaover(drect, srect, x)
uchar * drect, *srect;
int x;
{
	ushort alpha;
	ushort col;

	for ( ; x > 0; x--){
		alpha = srect[0];
		if (alpha){
			if (alpha == 255){
				*((uint *) drect) = *((uint *) srect);
			} else{
				alpha++;
				drect[0] += (alpha * (255 - drect[0])) >> 8; 
				drect[1] += (alpha * (srect[1] - drect[1])) >> 8;
				drect[2] += (alpha * (srect[2] - drect[2])) >> 8;
				drect[3] += (alpha * (srect[3] - drect[3])) >> 8;
			}
		}
		drect += 4;
		srect += 4;
	}
}


void rectalphaunder(drect, srect, x)
uchar * drect, *srect;
int x;
{
	ushort alpha;
	ushort col;

	for ( ; x > 0; x--){
		alpha = drect[0];
		if (alpha != 255) {
			if (alpha == 0){
				*((uint *) drect) = *((uint *) srect);
			} else{
				alpha = 256 - alpha;
				drect[0] += (alpha * (255 - drect[0])) >> 8; 
				drect[1] += (alpha * (srect[1] - drect[1])) >> 8;
				drect[2] += (alpha * (srect[2] - drect[2])) >> 8;
				drect[3] += (alpha * (srect[3] - drect[3])) >> 8;
			}
		}
		drect += 4;
		srect += 4;
	}
}


void rectpremulalphaover(drect, srect, x)
uchar * drect, *srect;
int x;
{
	uchar alpha;
	ushort col;
	/* premulalpha ! */

	for ( ; x > 0; x--){
		alpha = srect[0];
		if (alpha){
			if (alpha == 255){
				*((uint *) drect) = *((uint *) srect);
			} else{
				alpha = 255 - alpha;

				col = ((alpha * drect[0]) / 255) + srect[0];
				if (col > 255) drect[0] = 255;
				else drect[0] = col;

				col = ((alpha * drect[1]) / 255) + srect[1];
				if (col > 255) drect[1] = 255;
				else drect[1] = col;

				col = ((alpha * drect[2]) / 255) + srect[2];
				if (col > 255) drect[2] = 255;
				else drect[2] = col;

				col = ((alpha * drect[3]) / 255) + srect[3];
				if (col > 255) drect[3] = 255;
				else drect[3] = col;
			}
		}
		drect += 4;
		srect += 4;
	}
}


void rectpremulalphaunder(drect, srect, x)
uchar * drect, *srect;
int x;
{
	uchar alpha;
	ushort col;
	/* premulalpha ! */

	for ( ; x > 0; x--){
		alpha = 255 - drect[0];
		
		if (alpha){
			if (alpha == 255){
				*((uint *) drect) = *((uint *) srect);
			} else{
				col = ((alpha * srect[0]) / 255) + drect[0];
				if (col > 255) drect[0] = 255;
				else drect[0] = col;

				col = ((alpha * srect[1]) / 255) + drect[1];
				if (col > 255) drect[1] = 255;
				else drect[1] = col;

				col = ((alpha * srect[2]) / 255) + drect[2];
				if (col > 255) drect[2] = 255;
				else drect[2] = col;

				col = ((alpha * srect[3]) / 255) + drect[3];
				if (col > 255) drect[3] = 255;
				else drect[3] = col;
			}
		}
		drect += 4;
		srect += 4;
	}
}


void rectop(dbuf,sbuf,destx,desty,srcx,srcy,width,height,operation, value)
struct ImBuf *dbuf,*sbuf;
int destx,desty,srcx,srcy,width,height, value;
void (*operation)(uint *, uint*, int, int); /* be careful here - nzc */
{
	uint *drect,*srect;

	if (dbuf == 0) return;
	if (operation == 0) return;

	if (destx < 0){
		srcx -= destx ;
		width += destx ;
		destx = 0;
	}
	if (srcx < 0){
		destx -= srcx ;
		width += destx ;
		srcx = 0;
	}
	if (desty < 0){
		srcy -= desty ;
		height += desty ;
		desty = 0;
	}
	if (srcy < 0){
		desty -= srcy ;
		height += desty ;
		srcy = 0;
	}

	if (width > dbuf->x - destx) width = dbuf->x - destx;
	if (height > dbuf->y - desty) height = dbuf->y - desty;
	if (sbuf){
		if (width > sbuf->x - srcx) width = sbuf->x - srcx;
		if (height > sbuf->y - srcy) height = sbuf->y - srcy;
	}

	if (width <= 0) return;
	if (height <= 0) return;

	drect = dbuf->rect;
	if (sbuf) srect = sbuf->rect;

	drect += desty * dbuf->x;
	drect += destx;
	destx = dbuf->x;

	if (sbuf) {
		srect += srcy * sbuf->x;
		srect += srcx;
		srcx = sbuf->x;
	} else{
		srect = drect;
		srcx = destx;
	}

	for (;height > 0; height--){
		operation(drect,srect,width, value);
		drect += destx;
		srect += srcx;
	}
}


void rectoptot(dbuf,sbuf,operation, value)
struct ImBuf *dbuf,*sbuf;
void (*operation)();
int value;
{
	rectop(dbuf,sbuf,0,0,0,0,32767,32767,operation, value);
}


void rectopeven(dbuf,sbuf,operation, value)
struct ImBuf *dbuf,*sbuf;
void (*operation)();
int value;
{
	int dx, dy, sx, sy;
	int max;

	if (dbuf) {
		dx = dbuf->x; 
		dy = dbuf->y;
		max = dx;
		dbuf->x <<= 1;
		dbuf->y = (dbuf->y + 1) >> 1;
	}
	if (sbuf) {
		sx = sbuf->x; 
		sy = sbuf->y;
		if (sx < max) max = sx;
		sbuf->x <<= 1;
		sbuf->y = (sbuf->y + 1) >> 1;
	}

	rectop(dbuf,sbuf,0,0,0,0,max,32767,operation, value);

	if (dbuf) { 
		dbuf->x = dx; 
		dbuf->y = dy;
	}
	if (sbuf) { 
		sbuf->x = sx; 
		sbuf->y = sy;
	}
}


void rectopodd(dbuf,sbuf,operation, value)
struct ImBuf *dbuf,*sbuf;
void (*operation)();
int value;
{
	int dx, dy, sx, sy;

	if (dbuf) {
		dx = dbuf->x; 
		dy = dbuf->y;
		dbuf->x <<= 1;
		dbuf->y >>= 1;
	}
	if (sbuf) {
		sx = sbuf->x; 
		sy = sbuf->y;
		sbuf->x <<= 1;
		sbuf->y >>= 1;
	} else {
		sx = dx;
		sy = dy;
	}

	rectop(dbuf,sbuf,dx,0,sx,0,32767,32767,operation, value);

	if (dbuf) { 
		dbuf->x = dx; 
		dbuf->y = dy;
	}
	if (sbuf) { 
		sbuf->x = sx; 
		sbuf->y = sy;
	}
}