//---------------------------------------------------------------------------
//	Video editor
//  2004 - 2007 Stanislav Sumec <sumec@fit.vutbr.cz>
//  Brno University of Technology
//  Faculty of Information Technology
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#pragma hdrstop
#include "EffectPerspective.h"
#include "Tools.h"
#include "ImgTools.h"
#ifdef USE_OPENCV
#include "cv.h"
#endif

//---------------------------------------------------------------------------
#pragma package(smart_init)

//---------------------------------------------------------------------------
using namespace ee;

//---------------------------------------------------------------------------
TEffectPerspective::TEffectPerspective()
{
	Name = "Perspective";
	NeedFullFrame = false;
}

//---------------------------------------------------------------------------
TEffect* TEffectPerspective::New()
{
	return new TEffectPerspective();
}

//---------------------------------------------------------------------------
bool TEffectPerspective::SetupVideo(TParameters &Parameters, char *Name)
{
	ReadyVideo = GetParameterFloat(Parameters, "X1", RX1, Name) &&
		GetParameterFloat(Parameters, "Y1", RY1, Name) &&
		GetParameterFloat(Parameters, "X2", RX2, Name) &&
		GetParameterFloat(Parameters, "Y2", RY2, Name) &&
		GetParameterFloat(Parameters, "X3", RX3, Name) &&
		GetParameterFloat(Parameters, "Y3", RY3, Name) &&
		GetParameterFloat(Parameters, "X4", RX4, Name) &&
		GetParameterFloat(Parameters, "Y4", RY4, Name);
	if (!GetParameterFloat(Parameters, "AspectRatio", AspectRatio, Name))
	{
		AspectRatio = 0;
	}
	if (!GetParameterInt(Parameters, "Width", Width, Name))
	{
		Width = -1;
	}
	if (!GetParameterInt(Parameters, "Height", Height, Name))
	{
		Height = -1;
	}
	return ReadyVideo;
}

//---------------------------------------------------------------------------
void TEffectPerspective::DoVideo(TExtendedSources &Sources, TImg &Img)
{
	if (ReadyVideo)
	{
		float X1 = min(max(RX1 * (float)Img.GetWidth(), 0.0), (float)Img.GetWidth());
		float Y1 = min(max(RY1 * (float)Img.GetHeight(), 0.0), (float)Img.GetHeight());
		float X2 = min(max(RX2 * (float)Img.GetWidth(), 0.0), (float)Img.GetWidth());
		float Y2 = min(max(RY2 * (float)Img.GetHeight(), 0.0), (float)Img.GetHeight());
		float X3 = min(max(RX3 * (float)Img.GetWidth(), 0.0), (float)Img.GetWidth());
		float Y3 = min(max(RY3 * (float)Img.GetHeight(), 0.0), (float)Img.GetHeight());
		float X4 = min(max(RX4 * (float)Img.GetWidth(), 0.0), (float)Img.GetWidth());
		float Y4 = min(max(RY4 * (float)Img.GetHeight(), 0.0), (float)Img.GetHeight());

		if (Quality < 0)
		{
			Img.Clip(min(X1, X4), min(Y1, Y2), max(X3, X2), max(Y3, Y4));
			if (AspectRatio > 0)
			{
				int ViewWidth, ViewHeight;
				Img.GetViewSize(ViewWidth, ViewHeight);
				ViewWidth = TAVFile::Round(AspectRatio * ViewHeight);
				Img.SetViewSize(ViewWidth, ViewHeight);
			}
		}
		else
		{
			int DstWidth, DstHeight;
			DstHeight = max(Y4 - Y1, Y3 - Y2);
			DstWidth = max(X2 - X1, X3 - X4);

			int ViewWidth, ViewHeight;
			Img.GetViewSize(ViewWidth, ViewHeight);
			if (AspectRatio > 0)
			{
				ViewWidth = TAVFile::Round(AspectRatio * ViewHeight);
			}

			TImg TMP(DstWidth, DstHeight);
			//TImg TMP(ViewWidth, ViewHeight);

			TMP.SetViewSize(ViewWidth, ViewHeight);
#ifdef USE_OPENCV
			CvMat *map_matrix = cvCreateMat(3, 3, CV_32F);
			IplImage *Src = cvCreateImageHeader(cvSize(Img.GetWidth(), Img.GetHeight()), IPL_DEPTH_8U, 4);
			IplImage *Dst = cvCreateImageHeader(cvSize(TMP.GetWidth(), TMP.GetHeight()), IPL_DEPTH_8U, 4);
			if (map_matrix && Src && Dst)
			{
				cvSetImageData(Src, Img.GetBitmap(), Img.GetLineWidth());
				cvSetImageData(Dst, TMP.GetBitmap(), TMP.GetLineWidth());
				CvPoint2D32f src[4];
				CvPoint2D32f dst[4];
				src[0].x = X1;
				src[0].y = Y1;
				src[1].x = X2;
				src[1].y = Y2;
				src[2].x = X3;
				src[2].y = Y3;
				src[3].x = X4;
				src[3].y = Y4;
				dst[0].x = 0;
				dst[0].y = 0;
				dst[1].x = TMP.GetWidth();
				dst[1].y = 0;
				dst[2].x = TMP.GetWidth();
				dst[2].y = TMP.GetHeight();
				dst[3].x = 0;
				dst[3].y = TMP.GetHeight();
				cvGetPerspectiveTransform(src, dst, map_matrix);
				cvWarpPerspective(Src, Dst, map_matrix, max(Quality, 0) + CV_WARP_FILL_OUTLIERS);
			}
			cvReleaseMat(&map_matrix);
			cvReleaseImageHeader(&Dst);
			cvReleaseImageHeader(&Src);
#else
			int *Line = (int*)TMP.GetBitmap();
			BYTE *Src = Img.GetBitmap();

			float kx1 = (X4 - X1) / DstWidth;
			float ky1 = (Y4 - Y1) / DstHeight;
			float kx2 = (X3 - X2) / DstWidth;
			float ky2 = (Y3 - Y2) / DstHeight;

			float px1 = X1;
			float py1 = Y1;
			float px2 = X2;
			float py2 = Y2;

			int Width = Img.GetWidth();
			int Height = Img.GetHeight();
			int LineWidth = Img.GetLineWidth();

			for (int y = 0; y < DstHeight; y++)
			{
				float kx3 = (px2 - px1) / DstWidth;
				float ky3 = (py2 - py1) / DstWidth;

				float px3 = px1;
				float py3 = py1;

				for (int x = 0; x < DstWidth; x++)
				{
					if (Quality > 0)
					{
						Line[x] = Bilinear(Src, Width, Height, LineWidth, px3, py3);
					}
					else
					{
						int sx = int(px3);
						int sy = int(py3);
						if (sx >= 0 && sx < Width && sy >= 0 && sy < Height)
						{
							Line[x] = *((int*)(Src + 4 * sx + sy * LineWidth));
						}
						else
						{
							Line[x] = 0;
						}
					}
					px3 += kx3;
					py3 += ky3;
				}

				px1 += kx1;
				py1 += ky1;
				px2 += kx2;
				py2 += ky2;

				Line += TMP.GetWidth();
			}
#endif
			TMP.MoveTo(Img);
		}
	}
}

