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

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

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

//---------------------------------------------------------------------------
TCustomMM *TImg::MemoryManager = NULL;
int TImg::Quality = 0;

//---------------------------------------------------------------------------
void TImg::SetupQuality(int _Quality)
{
	Quality = _Quality;
}

//---------------------------------------------------------------------------
TImg::TImg()
{
	Base = Bitmap = NULL;
	Width = Height = LineWidth = ViewWidth = ViewHeight = 0;
	Allocated = false;
}

//---------------------------------------------------------------------------
TImg::TImg(int _Width, int _Height)
{
	Base = Bitmap = NULL;
	Width = Height = LineWidth = ViewWidth = ViewHeight = 0;
	Allocated = false;
	Create(_Width, _Height);
}

//---------------------------------------------------------------------------
TImg::TImg(int _Width, int _Height, BYTE *_Bitmap, bool _Allocate)
{
	Base = Bitmap = NULL;
	Width = Height = LineWidth = ViewWidth = ViewHeight = 0;
	Allocated = false;
	Create(_Width, _Height, _Bitmap, _Allocate);
}

//---------------------------------------------------------------------------
TImg::~TImg()
{
	Free();
}

//---------------------------------------------------------------------------
void TImg::Create(int _Width, int _Height)
{
	Free();
	Width = _Width;
	Height = _Height;
	LineWidth = Width * 4;
	ViewWidth = Width;
	ViewHeight = Height;
	if (MemoryManager)
	{
		Base = (BYTE*)MemoryManager->GetMem(LineWidth * Height);
	}
	else
	{
		Base = new BYTE[LineWidth * Height];
	}
	Bitmap = Base;
	Allocated = true;
}

//---------------------------------------------------------------------------
void TImg::Create(int _Width, int _Height, BYTE *_Bitmap, bool _Allocate)
{
	Create(_Width, _Height, _Width * 4, _Bitmap, _Allocate);
}

//---------------------------------------------------------------------------
void TImg::Create(int _Width, int _Height, int _LineWidth, BYTE *_Bitmap, bool _Allocate)
{
	Free();
	Width = _Width;
	Height = _Height;
	ViewWidth = Width;
	ViewHeight = Height;
	LineWidth = _LineWidth;
	if (_Allocate)
	{
		if (MemoryManager)
		{
			Base = (BYTE*)MemoryManager->GetMem(LineWidth * Height);
		}
		else
		{
			Base = new BYTE[LineWidth * Height];
		}
		Bitmap = Base;
		memcpy(Bitmap, _Bitmap, LineWidth * Height);
	}
	else
	{
		Base = Bitmap = _Bitmap;
	}
	Allocated = _Allocate;
}

//---------------------------------------------------------------------------
void TImg::Free()
{
	if (Base && Allocated)
	{
		if (MemoryManager)
		{
			MemoryManager->FreeMem(Base);
		}
		else
		{
			delete[] Base;
		}
	}
	Base = Bitmap = NULL;
	Width = Height = LineWidth = 0;
	Allocated = false;
}

//---------------------------------------------------------------------------
void TImg::MoveTo(TImg &Img)
{
	Img.Free();
	Img.Width = Width;
	Img.Height = Height;
	Img.LineWidth = LineWidth;
	Img.ViewWidth = ViewWidth;
	Img.ViewHeight = ViewHeight;
	Img.Base = Base;
	Img.Bitmap = Bitmap;
	Img.Allocated = Allocated;
	Allocated = false;
	Free();
}

//---------------------------------------------------------------------------
void TImg::CopyFrom(TImg &Img)
{
	Create(Img.Width, Img.Height, Img.LineWidth, Img.Bitmap, true);
	ViewWidth = Img.ViewWidth;
	ViewHeight = Img.ViewHeight;
}

//---------------------------------------------------------------------------
void TImg::ResizeFrom(TImg &Img)
{
	if (Img.GetWidth() == GetWidth() && Img.GetHeight() == GetHeight())
	{
		BYTE* SrcLine = Img.GetBitmap();
		BYTE* DstLine = GetBitmap();
		for (int y = 0; y < GetHeight(); y++)
		{
			memcpy(DstLine, SrcLine, GetPixelWidth() * GetWidth());
			SrcLine += Img.GetLineWidth();
			DstLine += GetLineWidth();
		}
	}
	else
	{
#ifdef USE_OPENCV
		IplImage *Src = cvCreateImageHeader(cvSize(Img.GetWidth(), Img.GetHeight()), IPL_DEPTH_8U, 4);
		IplImage *Dst = cvCreateImageHeader(cvSize(Width, Height), IPL_DEPTH_8U, 4);
		if (Src && Dst)
		{
			cvSetImageData(Src, Img.GetBitmap(), Img.GetLineWidth());
			cvSetImageData(Dst, Bitmap, LineWidth);
			cvResize(Src, Dst, max(Quality, 0));
		}
		cvReleaseImageHeader(&Dst);
		cvReleaseImageHeader(&Src);
#else
		if (Quality > 0)
		{
			float dy = (float)Img.Height / (float)Height, dx = (float)Img.Width / (float)Width;
			int s = 16;
			int m = 1 << s;
			int idx = dx * m, idy = dy * m;
			int iry = 0;
			int *DstLine = (int*)Bitmap;

			if (idx < m && idy < m)
			{
				for (int y = 0; y < Height; y++)
				{
					float sy = (float)iry / m;
					int irx = 0;
					for (int x = 0; x < Width; x++)
					{
						float sx = (float)irx / m;
						*(DstLine++) = Bilinear(Img.Bitmap, Img.Width, Img.Height, Img.LineWidth, sx, sy);
						irx += idx;
					}
					iry += idy;
				}
			}
			else
			{
				for (int y = 0; y < Height; y++)
				{
					float sy = (float)iry / m;
					float ey = (float)(iry + idy) / m;
					int irx = 0;
					for (int x = 0; x < Width; x++)
					{
						float sx = (float)irx / m;
						float ex = (float)(irx + idx) / m;

						int r, g, b, a, c;
						r = g = b = a = c = 0;

						for (float j = sy; j < ey; j++)
						{
							for (float i = sx; i < ex; i++)
							{
								int ar, ag, ab, aa;
								if (int(i) < Img.Width && int(j) < Img.Height)
								{
									int z = *((int*)((Img.Bitmap + Img.LineWidth * int(j) + int(i) * 4)));
									ar = z & 0xff;
									ag = (z >> 8) & 0xff;
									ab = (z >> 16) & 0xff;
									aa = (z >> 24) & 0xff;
									r += ar;
									g += ag;
									b += ab;
									a += aa;
									c++;
								}
							}
						}
						*(DstLine++) = (int(float(r) / c)) | (int(float(g) / c) << 8) | (int(float(b) / c) << 16) | (int(float(a) / c) << 24);

						irx += idx;
					}
					iry += idy;
				}
			}
		}
		else
		{
			float dy = (float)Img.Height / (float)Height, dx = (float)Img.Width / (float)Width;
			int s = 16;
			int m = 1 << s;
			int idx = dx * m, idy = dy * m;
			int iry = 0;
			int *DstLine = (int*)Bitmap;

			for (int y = 0; y < Height; y++)
			{
				int sy = iry >> s;

				int *SrcLine;
				if (sy >= 0 && sy < (int)Img.Height)
				{
					SrcLine = (int*)(Img.Bitmap + sy * Img.LineWidth);
				}
				else
				{
					SrcLine = NULL;
				}
				int irx = 0;
				for (int x = 0; x < Width; x++)
				{
					int sx = irx >> s;
					if (SrcLine && sx >= 0 && sx < (int)Img.Width)
					{
						*(DstLine++) = *(SrcLine + sx);
					}
					else
					{
						DstLine += 1;
					}
					irx += idx;
				}
				iry += idy;
			}
		}
#endif
	}
}

//---------------------------------------------------------------------------
void TImg::CropFrom(TImg &Img)
{
	int w = min(Width, Img.Width);
	int h = min(Height, Img.Height);

	BYTE *Src = Img.Bitmap;
	BYTE *Dst = Bitmap;

	int lw = w * 4;
	int rw = (Width - w) * 4;

	for (int y = 0; y < h; y++)
	{
		memcpy(Dst, Src, lw);
		if (w < Width)
		{
			memset(Dst + lw, 0, rw);
		}
		Src += Img.LineWidth;
		Dst += LineWidth;
	}

	for (int y = h; y < Height; y++)
	{
		memset(Dst, 0, lw);
		Dst += LineWidth;
	}
}

//---------------------------------------------------------------------------
void TImg::CropResizeFrom(TImg &Img, int ResizeWidth, int ResizeHeight)
{
	if (Width == ResizeWidth && Height == ResizeHeight)
	{
		CropFrom(Img);
	}
	else
	{
#ifdef USE_OPENCV
		TImg TMP(ResizeWidth, ResizeHeight);
		TMP.ResizeFrom(Img);
		CropFrom(TMP);
#else
		if (Quality > 0)
		{
			TImg TMP(ResizeWidth, ResizeHeight);
			TMP.ResizeFrom(Img);
			CropFrom(TMP);
		}
		else
		{
			int w = min(Width, ResizeWidth);
			int h = min(Height, ResizeHeight);

			float dy = (float)Img.Height / (float)ResizeHeight, dx = (float)Img.Width / (float)ResizeWidth;
			int s = 16;
			int m = 1 << s;
			int idx = dx * m, idy = dy * m;
			int iry = 0;
			int *DstLine = (int*)Bitmap;

			for (int y = 0; y < h; y++)
			{
				int sy = iry >> s;

				int *SrcLine;
				if (sy >= 0 && sy < (int)Img.Height)
				{
					SrcLine = (int*)(Img.Bitmap + sy * Img.LineWidth);
				}
				else
				{
					SrcLine = NULL;
				}
				int irx = 0;
				for (int x = 0; x < w; x++)
				{
					int sx = irx >> s;
					if (SrcLine && sx >= 0 && sx < (int)Img.Width)
					{
						*(DstLine++) = *(SrcLine + sx);
					}
					else
					{
						*(DstLine++) = 0;
					}
					irx += idx;
				}
				DstLine += LineWidth / 4 - w;
				iry += idy;
			}
		}
#endif		
	}
}

//---------------------------------------------------------------------------
BYTE* TImg::GetBitmap()
{
	return Bitmap;
}

//---------------------------------------------------------------------------
int TImg::GetWidth()
{
	return Width;
}

//---------------------------------------------------------------------------
int TImg::GetHeight()
{
	return Height;
}

//---------------------------------------------------------------------------
void TImg::GetSize(int &Width, int &Height)
{
	Width = this->Width;
	Height = this->Height;
}

//---------------------------------------------------------------------------
int TImg::GetLineWidth()
{
	return LineWidth;
}

//---------------------------------------------------------------------------
int TImg::GetPixelWidth()
{
	return 4;
}

//---------------------------------------------------------------------------
TImg::EPixelFormat TImg::GetPixelFormat()
{
	return pfBGRA;
}

//---------------------------------------------------------------------------
void TImg::Clip(int _Left, int _Top, int _Right, int _Bottom)
{
	Bitmap += _Top * LineWidth + _Left * 4;
	Width = _Right - _Left;
	Height = _Bottom - _Top;
}

//---------------------------------------------------------------------------
void TImg::GetViewSize(int &_Width, int &_Height)
{
	_Width = ViewWidth;
	_Height = ViewHeight;
}

//---------------------------------------------------------------------------
void TImg::SetViewSize(int _Width, int _Height)
{
	ViewWidth = _Width;
	ViewHeight = _Height;
}

//---------------------------------------------------------------------------
void TImg::Clear()
{
	memset(Bitmap, 255, LineWidth * Height);
}

//---------------------------------------------------------------------------
void TImg::AutoAlpha()
{
	int TransparentColor = *((int*)Bitmap);
	for (int y = 0; y < Height; y++)
	{
		for (int x = 0; x < Width; x++)
		{
			BYTE *Pixel = Bitmap + x * 4 + y * LineWidth;
			if (*((int*)Pixel) == TransparentColor)
			{
				Pixel[3] = 255;
			}
			else
			{
				Pixel[3] = 0;
			}
		}
	}
}

