// FieldWarp.cpp: implementation of the CFieldWarp class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Morphing.h"
#include "FieldWarp.h"
#include "math.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define CLIP(val, low, high) {if(val<low) val=low; if(val>high) val=high;}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CFieldWarp::CFieldWarp()
{
	m_pSrcIMG = new CIMG;
	m_pDestIMG = new CIMG;
	m_bLoaded = FALSE;
}

CFieldWarp::~CFieldWarp()
{
	delete m_pSrcIMG;
	delete m_pDestIMG;
}

void CFieldWarp::LoadSrcIMG(CString filename)
{
	if (m_pSrcIMG->OpenIMG(filename)) {
		m_bLoaded = TRUE;
		// Set size
		m_nWidth = m_pSrcIMG->Width();
		m_nHeight = m_pSrcIMG->Height();

		// Create destination canvas
		m_pDestIMG->Create(m_nWidth, m_nHeight, RGB(255, 255, 255));
	} else {	// Failed
		m_bLoaded = FALSE;
		MessageBox(NULL, "Supported Only 24 Bit Bitmap", "Failed", MB_OK);
	}
}

 void CFieldWarp::FieldWarp(CIMG *src, CIMG *dest, FIELDLINE *srcField, FIELDLINE *destField, int numField)
{
	int nWidth = src->Width();
	int nHeight = src->Height();
	int x, y;
	int line;  
	int dx, dy;
	int last_row, last_col;     /* last valid row and column */
	int source_x, source_y;     /* source coordinate indices */
	int indx, indy;
	int indx1, indy1;
	long inlength_square;
	long inlength_square1;
	float inlength;
	float inlength1;
	float fraction, distance;   /* fraction along and distance from control line */
    float fdist;                /* intermediate variable used to compute distance */
	float a = 0.000001f;           /* small number used to avoid divide by 0 */
    float b = 2.0f;             /* exponent used in line weight calculation */
    float p = 0.75f;            /* exponent used in line weight calculation */
	float weight_sum;           /* accumulated weight */
    float weight;               /* line weight */
	float sum_x, sum_y;         /* delta sum X and Y */
	float u, v;                 /* intermediate source coordinate indices */
	double numerator;           /* numerator used to compute line weight */
    double denominator;         /* denominator used to compute line weight */
	unsigned long source_index; /* index into linear addressed source image */
    unsigned long dest_index;   /* index into linear addressed destination */
	
	last_row = nHeight - 1;          /* initialize last row */
    last_col = nWidth - 1;          /* initialize last column */

	/* New canvas */
	dest->Create(nWidth, nHeight, RGB(255, 255, 255));

	for(y = 0; y < nHeight; y++)
	{
		for(x = 0; x < nWidth; x++)
		{
			weight_sum = 0.0f;
			sum_x = 0.0f;
			sum_y = 0.0f;

			for (line = 0; line < numField; line++)
			{
				indx = destField[line].end.x - destField[line].start.x;
				indy = destField[line].end.y - destField[line].start.y;
				inlength_square = indx * indx + indy * indy;
				inlength = (float)sqrt(inlength_square);
				indx1 = srcField[line].end.x - srcField[line].start.x;
				indy1 = srcField[line].end.y - srcField[line].start.y;
				inlength_square1 = indx * indx + indy * indy;
				inlength1 = (float)sqrt(inlength_square1);

				dx = x - destField[line].start.x;
				dy = y - destField[line].start.y;
				fraction = (dx * (long)indx + dy * (long)indy) / (float)inlength_square;
				fdist = (dy * (long)indx - dx * (long)indy) / inlength;

				if (fraction <= 0 )
					distance = sqrt(dx * (long)dx + dy * (long)dy);
				else if (fraction >= 1)
				{
					dx = x - destField[line].end.x;
					dy = y - destField[line].end.y;
					distance = sqrt(dx * (long)dx + dy * (long)dy);
				}
				else if (fdist >= 0)
					distance =  fdist;
				else
					distance = -1.0 * fdist;

				u = srcField[line].start.x + fraction * indx1 - fdist * indy1 / inlength1;
				v = srcField[line].start.y + fraction * indy1 + fdist * indx1 / inlength1;
			
				/* calculate line weight */
				numerator = pow((double)(inlength), p);
				denominator = a + distance;
				weight = pow((numerator / denominator) , b);

				sum_x += (u - x) * weight;
				sum_y += (v - y) * weight;
				weight_sum += weight;
			}

			source_x = x + sum_x / weight_sum + 0.5;
			source_y = y + sum_y / weight_sum + 0.5;
			CLIP(source_x, 0, last_col);
			CLIP(source_y, 0, last_row);
			dest->SetPixel(x, y, src->GetPixel(source_x, source_y));
		}
	}
}

void CFieldWarp::Morphing(CIMG *src1, CIMG *src2, CIMG *dest, FIELDLINE *srcField, FIELDLINE *destField, int numField, float u)
{
	int nWidth = src1->Width();
	int nHeight = src1->Height();
	int x, y, i;
	CIMG dest1, dest2;
	CIMG copysrc1, copysrc2;
	BYTE srcR, srcG, srcB, destR, destG, destB;
	COLORREF color;
	copysrc1.CopyIMG(*src1);
	copysrc2.CopyIMG(*src2);
	dest1.Create(nWidth, nHeight);
	dest2.Create(nWidth, nHeight);
	// º¸°£ --------------
	FIELDLINE *inter = (FIELDLINE *)malloc(sizeof(FIELDLINE) * numField);
	for (i = 0; i < numField; i++) {
		inter[i].start.x = (int)((float)(destField[i].start.x - srcField[i].start.x) * u + (float)srcField[i].start.x);
		inter[i].start.y = (int)((float)(destField[i].start.y - srcField[i].start.y) * u + (float)srcField[i].start.y);
		inter[i].end.x = (int)((float)(destField[i].end.x - srcField[i].end.x) * u + (float)srcField[i].end.x);
		inter[i].end.y = (int)((float)(destField[i].end.y - srcField[i].end.y) * u + (float)srcField[i].end.y);
	}
	//-------------------------------------

	FieldWarp(&copysrc1, &dest1, srcField, inter, numField);
	FieldWarp(&copysrc2, &dest2, destField, inter, numField);

	dest->Create(nWidth, nHeight);
	for (y = 0; y < nHeight; y++)
		for (x = 0; x < nWidth; x++) {
			color = dest1.GetPixel(x, y);
			srcR = GetRValue(color);
			srcG = GetGValue(color);
			srcB = GetBValue(color);
			color = dest2.GetPixel(x, y);
			destR = GetRValue(color);
			destG = GetGValue(color);
			destB = GetBValue(color);
			dest->SetPixel(x, y, RGB((1.0f - u) * (float)srcR + u * (float)destR,
									 (1.0f - u) * (float)srcG + u * (float)destG,
									 (1.0f - u) * (float)srcB + u * (float)destB));
		}
	
	free(inter);
}

