#include "AltiVecMerge.h"

#ifndef NO_ALTIVEC

static const vector unsigned char EMPTY8U = (vector unsigned char)(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
static const vector unsigned char FULL8U = (vector unsigned char)(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);

inline vector unsigned char specialMergeAV(int spp, vector unsigned char DEST, vector unsigned char SOURCE, vector unsigned char *OPACITY)
{
	vector unsigned char K1, K2;
	vector unsigned char MULTIPLY, INV_MULTIPLY;
	vector unsigned char INV_DEST, RSOURCE;
	vector unsigned char I1, I2, I3, I4, I5;
	int k, srcValue, destValue;
	
	if (spp == 2) {
		K1 = (vector unsigned char)(0x01, 0x01, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, 0x09, 0x09, 0x0B, 0x0B, 0x0D, 0x0D, 0x0F, 0x0F);
		K2 = (vector unsigned char)(0x00, 0x11, 0x02, 0x13, 0x04, 0x15, 0x06, 0x17, 0x08, 0x19, 0x0A, 0x1B, 0x0C, 0x1D, 0x0E, 0x1F);		
	}
	else {
		K1 = (vector unsigned char)(0x03, 0x03, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x0B, 0x0B, 0x0B, 0x0B, 0x0F, 0x0F, 0x0F, 0x0F);
		K2 = (vector unsigned char)(0x00, 0x01, 0x02, 0x13, 0x04, 0x05, 0x06, 0x17, 0x08, 0x09, 0x0A, 0x1B, 0x0C, 0x0D, 0x0E, 0x1F);
	}
	
	if (OPACITY) {
		RSOURCE = vec_perm(SOURCE, mvec_mult(SOURCE, *OPACITY), K2);
	}
	else {
		RSOURCE = SOURCE;
	}
	
	MULTIPLY = vec_perm(RSOURCE, EMPTY8U, K1);
	
	if (vec_all_eq(MULTIPLY, EMPTY8U))
		return DEST;
	
	for (k = spp - 1; k < 16; k += spp) {
		srcValue = mvec_get_uchar(RSOURCE, k);
		if (srcValue != 0) {
			destValue = mvec_get_uchar(DEST, k);
		 	if (srcValue + destValue < 255) {
				mvec_set_uchar(&MULTIPLY, k, (unsigned char)(((float)srcValue / (float)(srcValue + destValue)) * 255.0));
			}
		}
	}
	
	MULTIPLY = vec_perm(MULTIPLY, EMPTY8U, K1);
	
	INV_MULTIPLY = vec_andc(FULL8U, MULTIPLY);
	INV_DEST = vec_andc(FULL8U, DEST);
	
	I1 = mvec_mult(RSOURCE, MULTIPLY);
	I2 = mvec_mult(DEST, INV_MULTIPLY);
	I3 = mvec_mult(INV_DEST, RSOURCE);

	I4 = vec_add(I1, I2);
	I5 = vec_add(I3, DEST);
	
	return vec_perm(I4, I5, K2);
}

inline vector unsigned char normalMergeAV(int spp, vector unsigned char DEST, vector unsigned char SOURCE, vector unsigned char *OPACITY)
{
	vector unsigned char K1, K2;
	vector unsigned char ALPHA;
	vector unsigned char P1, P2;
	vector unsigned char I1, I2, I3, I4;
	
	if (spp == 2) {
		K1 = (vector unsigned char)(0x01, 0x01, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, 0x09, 0x09, 0x0B, 0x0B, 0x0D, 0x0D, 0x0F, 0x0F);
		K2 = (vector unsigned char)(0x00, 0x11, 0x02, 0x13, 0x04, 0x15, 0x06, 0x17, 0x08, 0x19, 0x0A, 0x1B, 0x0C, 0x1D, 0x0E, 0x1F);		
		
	}
	else {
		K1 = (vector unsigned char)(0x03, 0x03, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x0B, 0x0B, 0x0B, 0x0B, 0x0F, 0x0F, 0x0F, 0x0F);
		K2 = (vector unsigned char)(0x00, 0x01, 0x02, 0x13, 0x04, 0x05, 0x06, 0x17, 0x08, 0x09, 0x0A, 0x1B, 0x0C, 0x0D, 0x0E, 0x1F);
	}
	
	ALPHA = vec_perm(SOURCE, EMPTY8U, K1);
	
	if (vec_all_eq(ALPHA, EMPTY8U))
		return DEST;
	
	if (OPACITY)
		ALPHA = mvec_mult(ALPHA, *OPACITY);
	
	I1 = vec_perm(SOURCE, FULL8U, K2);
	I2 = ALPHA;
	I3 = DEST;
	I4 = vec_andc(FULL8U, ALPHA);
	
	P1 = mvec_mult(I1, I2);
	P2 = mvec_mult(I3, I4);
	
	return vec_add(P1, P2);
}

inline vector unsigned char eraseMergeAV(int spp, vector unsigned char DEST, vector unsigned char SOURCE, vector unsigned char *OPACITY)
{
	vector unsigned char K1;
	vector unsigned char ALPHA, INV_ALPHA;
	
	if (spp == 2)
		K1 = (vector unsigned char)(0x10, 0x01, 0x10, 0x03, 0x10, 0x05, 0x10, 0x07, 0x10, 0x09, 0x10, 0x0B, 0x10, 0x0D, 0x10, 0x0F);
	else
		K1 = (vector unsigned char)(0x10, 0x10, 0x10, 0x03, 0x10, 0x10, 0x10, 0x07, 0x10, 0x10, 0x10, 0x0B, 0x10, 0x10, 0x10, 0x0F);
		
	ALPHA = vec_perm(SOURCE, EMPTY8U, K1);
	
	if (vec_all_eq(ALPHA, EMPTY8U))
		return DEST;
	
	if (OPACITY)
		ALPHA = mvec_mult(ALPHA, *OPACITY);
	
	INV_ALPHA = vec_andc(FULL8U, ALPHA);
	
	return mvec_mult(DEST, INV_ALPHA);
}

inline vector unsigned char primaryMergeAV(int spp, vector unsigned char DEST, vector unsigned char SOURCE, vector unsigned char *OPACITY)
{
	vector unsigned char K1;
	vector unsigned char TDEST;
	
	if (spp == 2)
		K1 = (vector unsigned char)(0x00, 0x11, 0x02, 0x13, 0x04, 0x15, 0x06, 0x17, 0x08, 0x19, 0x0A, 0x1B, 0x0C, 0x1D, 0x0E, 0x1F);
	else
		K1 = (vector unsigned char)(0x00, 0x01, 0x02, 0x13, 0x04, 0x05, 0x06, 0x17, 0x08, 0x09, 0x0A, 0x1B, 0x0C, 0x0D, 0x0E, 0x1F);
	
	TDEST = vec_perm(DEST, FULL8U, K1);
	TDEST = normalMergeAV(spp, TDEST, SOURCE, OPACITY);
	
	return vec_perm(TDEST, DEST, K1);
}

inline vector unsigned char alphaMergeAV(int spp, vector unsigned char DEST, vector unsigned char SOURCE, vector unsigned char *OPACITY)
{
	vector unsigned char K1, K2;
	vector unsigned char TDEST;
	
	if (spp == 2) {
		K1 = (vector unsigned char)(0x01, 0x11, 0x03, 0x13, 0x05, 0x15, 0x07, 0x17, 0x09, 0x19, 0x0B, 0x1B, 0x0D, 0x1D, 0x0F, 0x1F);
		K2 = (vector unsigned char)(0x00, 0x10, 0x02, 0x12, 0x04, 0x14, 0x06, 0x16, 0x08, 0x18, 0x0A, 0x1A, 0x0C, 0x1C, 0x0E, 0x1E);
	}
	else {
		K1 = (vector unsigned char)(0x03, 0x03, 0x03, 0x13, 0x07, 0x07, 0x07, 0x17, 0x0B, 0x0B, 0x0B, 0x1B, 0x0F, 0x0F, 0x0F, 0x1F);
		K2 = (vector unsigned char)(0x00, 0x01, 0x02, 0x10, 0x04, 0x05, 0x06, 0x14, 0x08, 0x09, 0x0A, 0x18, 0x0C, 0x0D, 0x0E, 0x1C);
	}
	
	TDEST = vec_perm(DEST, FULL8U, K1);
	TDEST = normalMergeAV(spp, TDEST, SOURCE, OPACITY);
	
	return vec_perm(DEST, TDEST, K2);
}

#endif