/*
	KernelDeint() deinterlacing plugin for Avisynth.

	Based on the original KernelDeint plugin (c) 2003 Donald A. Graft
	MMX optimizations + new motion mask code (c) 2004 Kurt B. Pruenner

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef __KERNELDEINT_H__
#define __KERNELDEINT_H__

#define KERNELDEINT_VERSION "LeakKernelDeint Version 1.5.4\n" \
		                    "Based on the original KernelDeint plugin (c) 2003 Donald A. Graft\n" \
						    "MMX optimizations + new motion mask code (c) 2004 Kurt B. Pruenner\n"


struct KernelDeintBuffer
{
	KernelDeintBuffer(int width, int height) : 
		width(width), byteWidth(width), height(height), size(1)
	{
		bytePitch=pitch=(width+15)&(-16);
		unalignedBuffer=new unsigned char[bytePitch*height+15];
		buffer=(unsigned char*)(((unsigned long)unalignedBuffer+15)&(-16));
	}

	KernelDeintBuffer(int width, int height, int size) : 
		width(width), height(height), size(size)
	{
		byteWidth=width*size;
		bytePitch=(byteWidth+15)&(-16);
		pitch=bytePitch/size;
		unalignedBuffer=new unsigned char[bytePitch*height+15];
		buffer=(unsigned char*)(((unsigned long)unalignedBuffer+15)&(-16));
	}

	virtual ~KernelDeintBuffer()
	{
		delete unalignedBuffer;
	}

	int width;
	int byteWidth;
	int height;
	int bytePitch;
	int pitch;
	int size;

	unsigned char* unalignedBuffer;
	unsigned char* buffer;
};

typedef struct KernelDeintBuffer KernelDeintMask;


class KernelDeint : public GenericVideoFilter
{
	PClip separatedInput;
	PClip assumedInput;

	int order;
	int threshold;

	bool sharp;
	bool twoway;
	bool linked;
	bool map;
	bool debug;

	bool bob;

	long cpuFlags;

	KernelDeintMask* fullsizeMask;
	KernelDeintMask* halfsizeMask;

	int scratchPitch;
	unsigned char* scratch;

	bool masksFilled;

	IScriptEnvironment* env;

	bool printedPitchWarning;

public:
	static AVSValue __cdecl CreateDeint(AVSValue args, void* user_data, IScriptEnvironment* env);
	static AVSValue __cdecl CreateBob(AVSValue args, void* user_data, IScriptEnvironment* env);

    KernelDeint(
		IScriptEnvironment* env,

	    PClip child,

	    int order, 
		int threshold, 

		bool sharp, 
		bool twoway, 
		bool linked,
		bool map, 
		bool debug,

		bool bob,

		int forceCPU
		);

	virtual ~KernelDeint();

    PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);

private:
	PVideoFrame GetField(int n, IScriptEnvironment* env);

	void KernelDeint_YV12(PVideoFrame& dst, int n);

	void BuildMotionMask_YV12(
		KernelDeintMask* fullsizeMask, 
		KernelDeintMask* halfsizeMask, 
		int order, 
		int n,
		bool linked,
		int threshold
		);

	void BuildPlaneMotionMask_YV12(
		KernelDeintMask* fullsizeMask, 
		int order, 
		int n,
		int plane, 
		bool linked,
		int threshold,
		bool overwrite
		);

	void BuildPlaneMotionMask_YV12_C(
		KernelDeintMask* fullsizeMask, 
		int order, 
		int n,
		int plane, 
		bool linked,
		int threshold,
		bool overwrite
		);

	void BuildPlaneMotionMask_YV12_MMX(
		KernelDeintMask* fullsizeMask, 
		int order, 
		int n,
		int plane, 
		bool linked,
		int threshold,
		bool overwrite
		);

	void HalveMotionMask_YV12(KernelDeintMask* halfsizeMask, KernelDeintMask* fullsizeMask);
	void HalveMotionMask_YV12_C(KernelDeintMask* halfsizeMask, KernelDeintMask* fullsizeMask);
	void HalveMotionMask_YV12_MMX(KernelDeintMask* halfsizeMask, KernelDeintMask* fullsizeMask);

	void ShowMotionMask_YV12(PVideoFrame& dst,int order, KernelDeintMask* fullsizeMask);

	void Deinterlace_YV12(
		PVideoFrame& dst,
		int order,
		int n,
		KernelDeintMask* fullsizeMask,
		KernelDeintMask* halfsizeMask,
		bool sharp,
		bool twoway
		);

	void DeinterlacePlane_YV12(
		PVideoFrame& dst,
		int order,
		int n,
		KernelDeintMask* mask,
		bool sharp,
		bool twoway,
		int plane
		);

	void DeinterlacePlane_YV12_C(
		PVideoFrame& dst,
		int order,
		int n,
		KernelDeintMask* mask,
		bool sharp,
		bool twoway,
		int plane
		);

	void DeinterlacePlane_YV12_MMX(
		PVideoFrame& dst,
		int order,
		int n,
		KernelDeintMask* mask,
		bool sharp,
		bool twoway,
		int plane
		);


	void KernelDeint_YUY2(PVideoFrame& dst, int n);

	void BuildMotionMask_YUY2(
		KernelDeintMask* mask, 
		int order, 
		int n,
		bool linked,
		int threshold
		);

	void BuildMotionMask_YUY2_C(
		KernelDeintMask* mask, 
		int order, 
		int n,
		bool linked,
		int threshold
		);

	void BuildMotionMask_YUY2_MMX(
		KernelDeintMask* mask, 
		int order, 
		int n,
		bool linked,
		int threshold
		);

	void ShowMotionMask_YUY2(PVideoFrame& dst,int order, KernelDeintMask* mask);

	void Deinterlace_YUY2(
		PVideoFrame& dst,
		int order,
		int n,
		KernelDeintMask* fullsizeMask,
		bool sharp,
		bool twoway
		);

	void Deinterlace_YUY2_C(
		PVideoFrame& dst,
		int order,
		int n,
		KernelDeintMask* fullsizeMask,
		bool sharp,
		bool twoway
		);

	void Deinterlace_YUY2_MMX(
		PVideoFrame& dst,
		int order,
		int n,
		KernelDeintMask* fullsizeMask,
		bool sharp,
		bool twoway
		);


	void KernelDeint_RGB32(PVideoFrame& dst, int n);

	void BuildMotionMask_RGB32(
		KernelDeintMask* mask, 
		int order, 
		int n,
		int threshold
		);

	void BuildMotionMask_RGB32_C(
		KernelDeintMask* mask, 
		int order, 
		int n,
		int threshold
		);

	void BuildMotionMask_RGB32_MMX(
		KernelDeintMask* mask, 
		int order, 
		int n,
		int threshold
		);

	void ShowMotionMask_RGB32(PVideoFrame& dst,int order, KernelDeintMask* mask);

	void Deinterlace_RGB32(
		PVideoFrame& dst,
		int order,
		int n,
		KernelDeintMask* mask,
		bool sharp,
		bool twoway
		);

	void Deinterlace_RGB32_C(
		PVideoFrame& dst,
		int order,
		int n,
		KernelDeintMask* mask,
		bool sharp,
		bool twoway
		);

	void Deinterlace_RGB32_MMX(
		PVideoFrame& dst,
		int order,
		int n,
		KernelDeintMask* mask,
		bool sharp,
		bool twoway
		);


	void CopyFrame(PVideoFrame& dst, PVideoFrame& src, IScriptEnvironment* env);
};

#endif