
/*********************************************************************
----------------------------------------------------------------------

	picture key
	
----------------------------------------------------------------------
*********************************************************************/

#include <stdio.h>
#include <string.h>
#include <math.h>

#include <exec/types.h>
#include <guigfx/guigfx.h>
#include <proto/guigfx.h>

#include <clib/macros.h>

#include "global.h"
#include "picturekey.h"
#include "debug.h"

/*--------------------------------------------------------------------

	value = getbits(bitarray, num)
	setbits(bitarray, num, value)
	
	manipulate bitarrays

--------------------------------------------------------------------*/

ULONG __inline getbits(BOOL *array, int num)
{
	ULONG value = 0;
	int i;
	
	for (i = num - 1; i >= 0; --i)
	{
		if (*array++)
		{
			value += 1 << i;
		}
	}

	return value;
}


void __inline setbits(BOOL *array, int num, ULONG value)
{
	int i;
	
	for (i = num - 1; i >= 0; --i)
	{
		*array++ = (value & (1 << i)) ? TRUE : FALSE;
	}
}



/*--------------------------------------------------------------------

	rgb2yuv(rgb, &Y, &U, &V)
	
	convert a RGB value to YUV. 

--------------------------------------------------------------------*/

void __inline rgb2yuv(unsigned long rgb, float *Y, float *U, float *V)
{
	/*
	**	Y=0.299R+0.587G+0.114B 
	**	U=0.493(B-Y) 
	**	V=0.877(R-Y) 
	*/

	int r, g, b;
	float y, u, v;
	
	r = (rgb & 0xff0000) >> 16;
	g = (rgb & 0x00ff00) >> 8;
	b = (rgb & 0x0000ff);

	y = 0.299f * (float)r + 0.587f * (float)g + 0.114f * (float)b;
	u = 0.493f * ((float)b - y);
	v = 0.877f * ((float)r - y);
	
	*Y = y;
	*U = u;
	*V = v;
}





/*--------------------------------------------------------------------

	v' = BITNORMALIZE(v, min, max, maxbits)
	
	normalize a number to a specified range of bits

--------------------------------------------------------------------*/

#define BITNORMALIZE(v,min,max,bits) ( ((v)-(min)) * ((1L << (bits)) - 1) / ((max)-(min))  )




/*--------------------------------------------------------------------

	CalcFieldYUV (data, x,y,w,h, totalwidth, &Y, &U, &V)
	
	calc the average Y/U/V values of a picture segment

--------------------------------------------------------------------*/

void CalcFieldYUV(	unsigned long *data, 
				int sx, int sy, int width, int height, int totalwidth,
				double *avY, double *avU, double *avV)
{
	float Y, U, V;
	double R = 0.0, G = 0.0, B = 0.0;
	int x, y;
	double numpixel = (double)(width * height);
	unsigned char *p;
	
	p = (unsigned char *) data + sizeof(ULONG) * (totalwidth * sy + sx);
	

	for (y = 0; y < height; ++y)
	{
		for (x = 0; x < width; ++x)
		{
			p++;
			R += *p++;
			G += *p++;
			B += *p++; 
		}
		
		p += sizeof(ULONG) * (totalwidth - width);
	}


	rgb2yuv(	(((int) (R / numpixel)) << 16) +
				(((int) (G / numpixel)) << 8) +
				((int) (B / numpixel)),
				&Y, &U, &V);

	*avY = (double)Y;
	*avU = (double)U;
	*avV = (double)V;		

}




/*********************************************************************

	error = CompareKeys(key1, key2)

*********************************************************************/

double CompareKeys(PICTUREKEY *key1, PICTUREKEY *key2)
{
	int dY, dU, dV;
	double sum = 0.0;
	int n;

	for (n = 0; n < KEYHEIGHT * KEYWIDTH; ++n)
	{
		dY = key1->ykey[n] - key2->ykey[n];
		dU = key1->ukey[n] - key2->ukey[n];
		dV = key1->vkey[n] - key2->vkey[n];

//		sum += sqrt((double)(dY*dY + ABS(dU * dV)));		bessere KONTUR!
//		sum += sqrt((double)(dY*dY + dU*dU + dV*dV));		bessere FARBE!

		sum += sqrt((double)(dY*dY + ((dU*dU)>>2) + ((dV*dV)>>2)));		// (1, 2 oder 3) !
	}

	return sum;
}



/*********************************************************************

	error = CompareKeysX(key1, key2)
	
*********************************************************************/

double CompareKeysX(PICTUREKEY *key1, PICTUREKEY *key2)
{
	int dY, dU, dV;
	double sum = 0.0;

	int x, y, n = 0;

	for (y = 0; y < KEYHEIGHT; ++y)
	{
		for (x = 0; x < KEYWIDTH; ++x)
		{
			dY = key1->ykey[n] - key2->ykey[y * KEYWIDTH + (KEYWIDTH - x - 1)];
			dU = key1->ukey[n] - key2->ukey[y * KEYWIDTH + (KEYWIDTH - x - 1)];
			dV = key1->vkey[n] - key2->vkey[y * KEYWIDTH + (KEYWIDTH - x - 1)];
	
	//		sum += sqrt((double)(dY*dY + ABS(dU * dV)));		bessere KONTUR!
	//		sum += sqrt((double)(dY*dY + dU*dU + dV*dV));		bessere FARBE!
	
			sum += sqrt((double)(dY*dY + ((dU*dU)>>2) + ((dV*dV)>>2)));		// (1, 2 oder 3) !

			n++;
		}
	}

	return sum;
}

/*********************************************************************

	error = CompareKeysY(key1, key2)
	
*********************************************************************/

double CompareKeysY(PICTUREKEY *key1, PICTUREKEY *key2)
{
	int dY, dU, dV;
	double sum = 0.0;

	int x, y, n = 0;

	for (y = 0; y < KEYHEIGHT; ++y)
	{
		for (x = 0; x < KEYWIDTH; ++x)
		{
			dY = key1->ykey[n] - key2->ykey[(KEYHEIGHT - y - 1) * KEYWIDTH + x];
			dU = key1->ukey[n] - key2->ukey[(KEYHEIGHT - y - 1) * KEYWIDTH + x];
			dV = key1->vkey[n] - key2->vkey[(KEYHEIGHT - y - 1) * KEYWIDTH + x];
	
	//		sum += sqrt((double)(dY*dY + ABS(dU * dV)));		bessere KONTUR!
	//		sum += sqrt((double)(dY*dY + dU*dU + dV*dV));		bessere FARBE!
	
			sum += sqrt((double)(dY*dY + ((dU*dU)>>2) + ((dV*dV)>>2)));		// (1, 2 oder 3) !

			n++;
		}
	}

	return sum;
}


/*********************************************************************

	error = CompareKeysXY(key1, key2)
	
*********************************************************************/

double CompareKeysXY(PICTUREKEY *key1, PICTUREKEY *key2)
{
	int dY, dU, dV;
	double sum = 0.0;

	int x, y, n = 0;

	for (y = 0; y < KEYHEIGHT; ++y)
	{
		for (x = 0; x < KEYWIDTH; ++x)
		{
			dY = key1->ykey[n] - key2->ykey[(KEYHEIGHT - y - 1) * KEYWIDTH + (KEYWIDTH - x - 1)];
			dU = key1->ukey[n] - key2->ukey[(KEYHEIGHT - y - 1) * KEYWIDTH + (KEYWIDTH - x - 1)];
			dV = key1->vkey[n] - key2->vkey[(KEYHEIGHT - y - 1) * KEYWIDTH + (KEYWIDTH - x - 1)];
	
	//		sum += sqrt((double)(dY*dY + ABS(dU * dV)));		bessere KONTUR!
	//		sum += sqrt((double)(dY*dY + dU*dU + dV*dV));		bessere FARBE!
	
			sum += sqrt((double)(dY*dY + ((dU*dU)>>2) + ((dV*dV)>>2)));		// (1, 2 oder 3) !

			n++;
		}
	}

	return sum;
}








/*********************************************************************

	key = CalcPictureKeySegment(picture, x, y, width, height)
	
	calc a key for a picture segment

*********************************************************************/

PICTUREKEY *CalcPictureKeySegment(PICTURE *pic, int sx, int sy, int w, int h)
{
	PICTUREKEY *key = NULL;
	PICTURE *segpic;
	
	if (segpic = ClonePicture(pic, 
			GGFX_SourceX, sx, GGFX_SourceY, sy,
			GGFX_SourceWidth, w, GGFX_SourceHeight, h,
			GGFX_DestWidth, w, GGFX_DestHeight, h,
			TAG_DONE))
	{
		key = CalcPictureKey(segpic);
		DeletePicture(segpic);	
	}

	return key;
}




/*********************************************************************

	key = CalcPictureKey(picture)
	
	calc a key for a picture

*********************************************************************/

PICTUREKEY *CalcPictureKey(PICTURE *pic)
{
	BOOL success = FALSE;
	PICTUREKEY *key = NULL;



	if (DoPictureMethod(pic, PICMTHD_RENDER, PIXFMT_0RGB_32, NULL))
	{
		unsigned long width = 0, height = 0, *data;
		BOOL sizeOK = FALSE, error = FALSE;

	

		/*
		**	scale the image, if necessary
		*/

		do
		{
			if (GetPictureAttrs(pic, 
				PICATTR_Width, &width,
				PICATTR_Height, &height,
				PICATTR_RawData, &data,
				TAG_DONE) == 3)
			{
				if (width >= KEYWIDTH && height >= KEYHEIGHT)
				{
					sizeOK = TRUE;
				}
				else
				{
					error = !DoPictureMethod(pic, PICMTHD_SCALE, KEYWIDTH, KEYHEIGHT,
						TAG_DONE);
				}
			}
			else
			{
				error = TRUE;
			}

		} while (!sizeOK || error);


			
		if (!error && sizeOK)
		{

			if (key = Malloclear(sizeof(PICTUREKEY)))
			{
				double Y, U, V;
				int iY, iU, iV;
				
				int x, y, i;
				double cx, cy, dx, dy;

				dx = (double) width / (double) KEYWIDTH;
				dy = (double) height / (double) KEYHEIGHT;
				
				if ((int) dx > 0 && (int) dy > 0)
				{
					cy = 0.0;
					i = 0;
					for (y = 0; y < KEYHEIGHT; ++y)
					{
						cx = 0.0;
						for (x = 0; x < KEYWIDTH; ++x)
						{
							CalcFieldYUV(data, (int) cx, (int) cy, (int) dx, (int) dy,
								width, &Y, &U, &V);
				
							iY = BITNORMALIZE((int)Y, 0, 255, KEYDEPTH);
							iU = BITNORMALIZE((int)U, -111, 111, KEYDEPTH);
							iV = BITNORMALIZE((int)V, -156, 156, KEYDEPTH);
							
							key->ykey[i] = iY;
							key->ukey[i] = iU;
							key->vkey[i] = iV;
	
							cx += dx;
							i ++;
						}
						cy += dy;
					}
	
					success = TRUE;
				}
				else
				{
					printf("*** cannot create key\n");
				}

					
			}
			
		}
	}


	
	if (!success)
	{
		Free(key);
		key = NULL;
	}

	return key;
}






/*********************************************************************

	keystring = keytostring(PICTUREKEY)
	PICTUREKEY = stringtokey(char *keystring)
	
	translate a key to a string and vice versa

*********************************************************************/

static char conversionstring[(1 << BITSPERCHAR) + 1] =
	"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!$";

static BOOL bitarray[BITARRAYSIZE];

char *KeyToString(PICTUREKEY *key)
{
	char *keystring;

	assert (NUMCHARS <= 80);		// filecomment limit

	if (keystring = Malloc(NUMCHARS + 1))
	{
		int i, n;

		/*
		**	transfer key to bitarray
		*/
	
		for (n = 0, i = 0; n < KEYHEIGHT * KEYWIDTH; ++n)
		{
			setbits(bitarray + YKEY + i, KEYDEPTH, key->ykey[n]);
			setbits(bitarray + UKEY + i, KEYDEPTH, key->ukey[n]);
			setbits(bitarray + VKEY + i, KEYDEPTH, key->vkey[n]);
			i += KEYDEPTH;
		}		

		for (n = 0, i = 0; n < NUMCHARS; ++n)
		{
			keystring[n] = conversionstring[getbits(bitarray + i, BITSPERCHAR)];
			i += BITSPERCHAR;
		}
		keystring[NUMCHARS] = (char) 0;
	}

	return keystring;
}


PICTUREKEY *StringToKey(char *keystring)
{
	PICTUREKEY *key = NULL;

	if (keystring)
	{
		if (strlen(keystring) == NUMCHARS)
		{
			if (key = Malloc(sizeof(PICTUREKEY)))
			{
				static short inverseconversionstring[256];
				static BOOL inverseconversionstringinitialized = FALSE;
				BOOL *a = bitarray;
				int i, n, v;

				if (!inverseconversionstringinitialized)
				{
					for (i = 0; i < 256; ++i)
					{
						inverseconversionstring[i] = -1;		/* invalid */
					}
				
					for (i = 0; i < (1 << BITSPERCHAR); ++i)
					{
						inverseconversionstring[conversionstring[i]] = i;					
					}

					inverseconversionstringinitialized = TRUE;
				}
				
				for (i = 0; i < NUMCHARS; ++i)
				{
					if ((v = inverseconversionstring[keystring[i]]) == -1)
					{
						Free(key);
						key = NULL;
						break;
					}
					else
					{
						setbits(a, BITSPERCHAR, v);
						a += BITSPERCHAR;
					}						
				}

				if (key)
				{
					for (n = 0, i = 0; n < KEYHEIGHT * KEYWIDTH; ++n)
					{
						key->ykey[n] = getbits(bitarray + YKEY + i, KEYDEPTH);
						key->ukey[n] = getbits(bitarray + UKEY + i, KEYDEPTH);
						key->vkey[n] = getbits(bitarray + VKEY + i, KEYDEPTH);
						i += KEYDEPTH;
					}		
				}				
			}
		}
	}
	
	return key;
}





