// MeshWarp.cpp: implementation of the CMeshWarp class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Morphing.h"
#include "MeshWarp.h"

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

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

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

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

void CMeshWarp::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 CMeshWarp::FantsResamplingHeight(CIMG *src, CIMG *dest, int srcStart, int srcEnd, int destStart, int destEnd, int w)
{
	int outlen = destEnd - destStart;
	int inlen = srcEnd - srcStart;
	float scale = (float)outlen / (float)inlen;
	float insfac = 1.0f / scale;
	float outseg = insfac;
	float inseg = 1.0f;
	float accumulatorR, accumulatorG, accumulatorB;
	int x = destStart;
	int u = srcStart;
	float intensityR, intensityG, intensityB;
	COLORREF color1, color2;

	accumulatorR = accumulatorG = accumulatorB = 0.0f;
	while (x <= destEnd && u <= srcEnd) {
		if (u < src->Height() - 1) {
			color1 = src->GetPixel(w, u);
			color2 = src->GetPixel(w, u + 1);
		}
		intensityR = (inseg * GetRValue(color1)) + ((1 - inseg) * GetRValue(color2));
		intensityG = (inseg * GetGValue(color1)) + ((1 - inseg) * GetGValue(color2));
		intensityB = (inseg * GetBValue(color1)) + ((1 - inseg) * GetBValue(color2));
		if (inseg < outseg) {
			accumulatorR += (intensityR * inseg);
			accumulatorG += (intensityG * inseg);
			accumulatorB += (intensityB * inseg);
			outseg -= inseg;
			inseg = 1.0f;
			u++;
		} else {
			accumulatorR += (intensityR * outseg);
			accumulatorG += (intensityG * outseg);
			accumulatorB += (intensityB * outseg);

			dest->SetPixel(w, x, RGB((float)accumulatorR * scale, (float)accumulatorG * scale, (float)accumulatorB * scale));
			accumulatorR = accumulatorG = accumulatorB = 0.0f;
			inseg -= outseg;
			outseg = insfac;
			x++;
		}
	}
}

void CMeshWarp::FantsResamplingWidth(CIMG *src, CIMG *dest, int srcStart, int srcEnd, int destStart, int destEnd, int h)
{
	int outlen = destEnd - destStart;
	int inlen = srcEnd - srcStart;
	float scale = (float)outlen / (float)inlen;
	float insfac = 1.0f / scale;
	float outseg = insfac;
	float inseg = 1.0f;
	float accumulatorR, accumulatorG, accumulatorB;
	int x = destStart;
	int u = srcStart;
	float intensityR, intensityG, intensityB;
	COLORREF color1, color2;

	accumulatorR = accumulatorG = accumulatorB = 0.0f;
	while (x <= destEnd && u <= srcEnd) {
		if (u < src->Width() - 1) {
			color1 = src->GetPixel(u, h);
			color2 = src->GetPixel(u + 1, h);
		}
		intensityR = (inseg * GetRValue(color1)) + ((1 - inseg) * GetRValue(color2));
		intensityG = (inseg * GetGValue(color1)) + ((1 - inseg) * GetGValue(color2));
		intensityB = (inseg * GetBValue(color1)) + ((1 - inseg) * GetBValue(color2));
		if (inseg < outseg) {
			accumulatorR += (intensityR * inseg);
			accumulatorG += (intensityG * inseg);
			accumulatorB += (intensityB * inseg);
			outseg -= inseg;
			inseg = 1.0f;
			u++;
		} else {
			accumulatorR += (intensityR * outseg);
			accumulatorG += (intensityG * outseg);
			accumulatorB += (intensityB * outseg);
			dest->SetPixel(x, h, RGB((float)accumulatorR * scale, (float)accumulatorG * scale, (float)accumulatorB * scale));
			accumulatorR = accumulatorG = accumulatorB = 0.0f;
			inseg -= outseg;
			outseg = insfac;
			x++;
		}
	}
}

void CMeshWarp::MeshWarp(CIMG *src, CIMG *dest, MESHPOINT *srcMesh, MESHPOINT *destMesh, int widthMesh, int heightMesh)
{
	int nWidth = src->Width();
	int nHeight = src->Height();
	int x, y, inx, iny;
	int **lineBuffer = NULL;
	int **srcBuffer = NULL;
	MESHPOINT *tempMesh = NULL;

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

	/****************************
		ONE PASS
	****************************/

	/* Create Linear function : Each row */

	/* Temporary Dest Mesh */
	tempMesh = (MESHPOINT *)malloc(sizeof(MESHPOINT) * widthMesh * heightMesh);
	for (x = 0; x < widthMesh * heightMesh; x++) {
		tempMesh[x].x = destMesh[x].x;
		tempMesh[x].y = srcMesh[x].y;
	}

	/* Allocate Linear function */
	lineBuffer = (int **)malloc(sizeof(int *) * widthMesh);
	srcBuffer = (int **)malloc(sizeof(int *) * widthMesh);
	for (x = 0; x < widthMesh; x++) {
		lineBuffer[x] = (int *)malloc(sizeof(int) * nHeight);
		srcBuffer[x] = (int *)malloc(sizeof(int) * nHeight);
	}

	for (x = 0; x < widthMesh; x++)
		for (y = 0; y < heightMesh - 1; y++) {
			int c = y * widthMesh + x;		/* Current Mesh Point */
			int b = c + widthMesh;			/* Bottom Mesh Point */

			/* Compute linear rate */
			float rate, srcrate;
			if (tempMesh[b].y - tempMesh[c].y != 0)
				rate = (float)(tempMesh[b].x - tempMesh[c].x) / (float)(tempMesh[b].y - tempMesh[c].y);
			else
				rate = 0.0f;
			if (srcMesh[b].y - srcMesh[c].y != 0)
				srcrate = (float)(srcMesh[b].x - srcMesh[c].x) / (float)(srcMesh[b].y - srcMesh[c].y);
			else
				srcrate = 0.0f;

			/* Compute linear buffer */
			for (iny = srcMesh[c].y; iny <= srcMesh[b].y; iny++) {
				lineBuffer[x][iny] = (int)((float)tempMesh[c].x + rate * ((float)iny - tempMesh[c].y));
				srcBuffer[x][iny] = (int)((float)srcMesh[c].x + srcrate * ((float)iny - srcMesh[c].y));
			}
		}

	/* Fan's Resampling : one pass (Each row) */
	for (x = 0; x < widthMesh - 1; x++)
		for (y = 0; y < heightMesh - 1; y++) {
			int lt = y * widthMesh + x;
			int rt = lt + 1;
			int lb = lt + widthMesh;
			int rb = lb + 1;
			for (iny = srcMesh[lt].y; iny <= srcMesh[lb].y; iny++)
				FantsResamplingWidth(src, 
									 dest, 
									 srcBuffer[x][iny],//srcMesh[lt].x,					/* Source Start */
									 srcBuffer[x + 1][iny],//srcMesh[rt].x,					/* Source End */ 
									 lineBuffer[x][iny],			/* Dest Start */
									 lineBuffer[x + 1][iny],		/* Dest End */
									 iny);							/* Height */
		}

	/* Memory Deallocation */
	for (x = 0; x < widthMesh; x++) {
		free(srcBuffer[x]);
		free(lineBuffer[x]);
	}
	free(srcBuffer);
	free(lineBuffer);

	src->CopyIMG(*dest);

	/****************************
		TWO PASS
	****************************/

	/* Create Linear function : Each col */

	/* Temporary Dest Mesh */
	for (x = 0; x < widthMesh * heightMesh; x++) {
		tempMesh[x].x = srcMesh[x].x;
		tempMesh[x].y = destMesh[x].y;
	}
	/* Allocate Linear function */
	lineBuffer = (int **)malloc(sizeof(int *) * heightMesh);
	srcBuffer = (int **)malloc(sizeof(int *) * heightMesh);
	for (x = 0; x < heightMesh; x++) {
		lineBuffer[x] = (int *)malloc(sizeof(int) * nWidth);
		srcBuffer[x] = (int *)malloc(sizeof(int) * nWidth);
	}

	for (y = 0; y < heightMesh; y++)
		for (x = 0; x < widthMesh - 1; x++) {
			int c = y * widthMesh + x;		/* Current Mesh Point */
			int r = c + 1;					/* Right Mesh Point */

			/* Compute linear rate */
			float rate, srcrate;
			if (tempMesh[r].x - tempMesh[c].x != 0)
				rate = (float)(tempMesh[r].y - tempMesh[c].y) / (float)(tempMesh[r].x - tempMesh[c].x);
			else
				rate = 0.0f;

			if (srcMesh[r].x - srcMesh[c].x != 0)
				srcrate = (float)(srcMesh[r].y - srcMesh[c].y) / (float)(srcMesh[r].x - srcMesh[c].x);
			else
				srcrate = 0.0f;

			/* Compute linear buffer */
			for (inx = srcMesh[c].x; inx <= srcMesh[r].x; inx++) {
				lineBuffer[y][inx] = (int)((float)tempMesh[c].y + rate * ((float)inx - tempMesh[c].x));
				srcBuffer[y][inx] = (int)((float)srcMesh[c].y + srcrate * ((float)inx - srcMesh[c].x));
			}
		}

	/* Fan's Resampling : two pass (Each col) */
	for (x = 0; x < widthMesh - 1; x++)
		for (y = 0; y < heightMesh - 1; y++) {
			int lt = y * widthMesh + x;
			int rt = lt + 1;
			int lb = lt + widthMesh;
			int rb = lb + 1;
			for (inx = srcMesh[lt].x; inx <= srcMesh[rt].x; inx++)
				FantsResamplingHeight(src, 
									  dest, 
									  srcBuffer[y][inx],//srcMesh[lt].y,					/* Source Start */
									  srcBuffer[y + 1][inx], //srcMesh[lb].y,					/* Source End */ 
									  lineBuffer[y][inx],			/* Dest Start */
									  lineBuffer[y + 1][inx],		/* Dest End */
									  inx);							/* Height */
		}

	for (x = 0; x < heightMesh; x++) {
		free(srcBuffer[x]);
		free(lineBuffer[x]);
	}
	free(srcBuffer);
	free(lineBuffer);
	free(tempMesh);
}

void CMeshWarp::Morphing(CIMG *src1, CIMG *src2, CIMG *dest, MESHPOINT *srcMesh, MESHPOINT *destMesh, int widthMesh, int heightMesh, 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);
	MESHPOINT *inter = (MESHPOINT *)malloc(sizeof(MESHPOINT) * widthMesh * heightMesh);
	for (i = 0; i < widthMesh * heightMesh; i++) {
		inter[i].x = (int)((float)(destMesh[i].x - srcMesh[i].x) * u + (float)srcMesh[i].x);
		inter[i].y = (int)((float)(destMesh[i].y - srcMesh[i].y) * u + (float)srcMesh[i].y);
	}

	MeshWarp(&copysrc1, &dest1, srcMesh, inter, widthMesh, heightMesh);
	MeshWarp(&copysrc2, &dest2, destMesh, inter, widthMesh, heightMesh);

	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);
}
