/*
	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.
*/

#include <windows.h>
#include <stdio.h>

#include "AviSynth.h"
#include "KernelDeint.h"
#include "Utilities.h"


#define PITCH_WARNING "Pitches of fields differ - slower C++ implementation used."


#define COLORSPACE_YV12 1
#define COLORSPACE_YUY2 2
#define COLORSPACE_RGB32 3


#define KERNELDEINT_YV12
#define KERNELDEINT_YUY2
#define KERNELDEINT_RGB32


const __int64 qword_4354h=0x4354435443544354i64;
const __int64 qword_15c2h=0x15c215c215c215c2i64;
const __int64 qword_0ed9h=0x0ed90ed90ed90ed9i64;
const __int64 qword_0354h=0x0354035403540354i64;
const __int64 qword_03f8h=0x03f803f803f803f8i64;

const __int64 qword_0fh=0x0f0f0f0f0f0f0f0fi64;
const __int64 qword_14h=0x1414141414141414i64;
const __int64 qword_10h=0x1010101010101010i64;
const __int64 qword_140fh=0x140f140f140f140fi64;

const __int64 qword_01h=0x0101010101010101i64;
const __int64 qword_80h=0x8080808080808080i64;

const __int64 qword_ff00h=0xff00ff00ff00ff00i64;
const __int64 qword_000000ffh=0x000000ff000000ffi64;
const __int64 qword_00ff0000h=0x00ff000000ff0000i64;


extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env)
{
    env->AddFunction("LeakKernelDeint", "c[order]i[threshold]i[sharp]b[twoway]b[linked]b[map]b[debug]b[forceCPU]i", KernelDeint::CreateDeint, 0);
    env->AddFunction("LeakKernelBob", "c[order]i[threshold]i[sharp]b[twoway]b[linked]b[map]b[debug]b[forceCPU]i", KernelDeint::CreateBob, 0);

    return 0;
}


KernelDeint::KernelDeint(
	IScriptEnvironment* env,

	PClip child,

	int order, 
	int threshold, 

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

	bool bob,

	int forceCPU
	) :

	GenericVideoFilter(child),
	
	order(order),
	threshold(threshold),

	sharp(sharp),
	twoway(twoway),
	linked(linked),
	map(map),
	debug(debug),

	bob(bob),

	fullsizeMask(0),
	halfsizeMask(0),

	assumedInput(0),
	separatedInput(0),

	scratch(0),

	masksFilled(false),

	env(env),

	printedPitchWarning(false)
{
	child->SetCacheHints(CACHE_RANGE,2);

	cpuFlags=env->GetCPUFlags();

	if (forceCPU > 0)
	{
		long cpuFlagsMask=0;

		if (forceCPU >= 2) cpuFlagsMask|=CPUF_MMX;
		if (forceCPU >= 3) cpuFlagsMask|=CPUF_INTEGER_SSE;
		if (forceCPU >= 4) cpuFlagsMask|=CPUF_SSE;
		if (forceCPU >= 5) cpuFlagsMask|=CPUF_SSE2;

		cpuFlags&=cpuFlagsMask;
	}

	if (!vi.IsYUY2() && 
		!vi.IsRGB32() && 
		!vi.IsYV12())
		env->ThrowError("LeakKernelDeint: requires RGB32, YUY2, or YV12 data");

	if ((order < 0) ||
	    (order > 1))
		env->ThrowError("LeakKernelDeint: you must specify the order parameter (0=bff, 1=tff).\n"
						"Refer to the user manual for how to set it properly.\n"
						"An incorrect setting will result in incorrect rendering.");

	// Due to the vertical picture flip of RGB, the sense of the order parameter is reversed.
	if (vi.IsRGB32()) this->order=1-order;

	if (debug == true)
		OutputDebugString(KERNELDEINT_VERSION);

	if (vi.IsYV12())
	{
		fullsizeMask=new KernelDeintMask(vi.width,vi.height/2);
		halfsizeMask=new KernelDeintMask(vi.width/2,vi.height/4);
	}
	else if (vi.IsYUY2())
		fullsizeMask=new KernelDeintMask(vi.width,vi.height/2,2);
	else
		fullsizeMask=new KernelDeintMask(vi.width,vi.height/2,4);

	scratchPitch=(vi.RowSize()+15)&(-16);
	scratch=new unsigned char[scratchPitch*8*3];

	AVSValue args1[1] = { child };

	assumedInput=env->Invoke((vi.IsRGB32() ? "AssumeBFF" : "AssumeTFF"),AVSValue(args1,1)).AsClip();

	AVSValue args2[1] = { assumedInput };

	separatedInput=env->Invoke("SeparateFields",AVSValue(args2,1)).AsClip();

	if (bob)
	{
		vi.num_frames*=2;
		vi.SetFPS(vi.fps_numerator*2,vi.fps_denominator);
	}
}


AVSValue __cdecl KernelDeint::CreateDeint(AVSValue args, void* user_data, IScriptEnvironment* env)
{
    return new KernelDeint(
		env,

		args[0].AsClip(), // child

		args[1].AsInt(2), // order
		args[2].AsInt(10), // threshold

		args[3].AsBool(false), // sharp
		args[4].AsBool(false), // twoway
		args[5].AsBool(false), // linked
		args[6].AsBool(false), // map
		args[7].AsBool(false), // debug

		false, // bob

		args[8].AsInt(0) // forceCPU
		);
}


AVSValue __cdecl KernelDeint::CreateBob(AVSValue args, void* user_data, IScriptEnvironment* env)
{
    AVSValue bobber=new KernelDeint(
		env,

		args[0].AsClip(), // child

		args[1].AsInt(2), // order
		args[2].AsInt(10), // threshold

		args[3].AsBool(false), // sharp
		args[4].AsBool(false), // twoway
		args[5].AsBool(false), // linked
		args[6].AsBool(false), // map
		args[7].AsBool(false), // debug

		true, // bob

		args[8].AsInt(0) // forceCPU
		);

	AVSValue args1[1] = { bobber }; // Spurious uninitialized use warning when optimized - VC6SP5 must be on crack...

	return env->Invoke("AssumeFrameBased",AVSValue(args1,1));
}


KernelDeint::~KernelDeint()
{
	if (fullsizeMask) delete fullsizeMask;
	if (halfsizeMask) delete halfsizeMask;

	if (scratch) delete scratch;
}


PVideoFrame KernelDeint::GetField(int n, IScriptEnvironment* env)
{
	int frameCount=vi.num_frames * (bob ? 1 : 2);

	if (n < 0) n=0;
	else if (n >= frameCount) n=frameCount-1;

	return separatedInput->GetFrame(n^(1-order),env);
}


PVideoFrame __stdcall KernelDeint::GetFrame(int n, IScriptEnvironment* env)
{
	this->env=env;

	PVideoFrame cur=child->GetFrame((bob ? n/2 : n),env);

	unsigned int hint=0;

	if (!GetHintingData(cur->GetReadPtr(PLANAR_Y),&hint))
	{
		if (hint & HINT_PROGRESSIVE)
		{
			if (debug == true)
			{
				char buf[256];

				sprintf(buf,"KernelDeint: frame %d: progressive\n", n); 
				OutputDebugString(buf);
			}

			return cur;
		}
		else if (debug == true)
		{
			char buf[256];

			sprintf(buf,"KernelDeint: frame %d: interlaced\n", n); 
			OutputDebugString(buf);
		}
	}

    PVideoFrame dst=env->NewVideoFrame(vi);

	if (!bob) n*=2;

#if defined KERNELDEINT_YV12
	if (vi.IsYV12())
		KernelDeint_YV12(dst,n);
	else
#endif

#if	defined KERNELDEINT_YUY2
	if (vi.IsYUY2())
		KernelDeint_YUY2(dst,n);
	else
#endif

#if defined KERNELDEINT_RGB32
	if (vi.IsRGB32())
		KernelDeint_RGB32(dst,n);
	else
#endif
		;

	return dst;	
}


#if defined KERNELDEINT_YV12

void KernelDeint::KernelDeint_YV12(PVideoFrame& dst, int n)
{
	int usedOrder=order ^ (n & 1);

	if (threshold > 0)
		BuildMotionMask_YV12(fullsizeMask,halfsizeMask,usedOrder,n,linked,threshold);
	else if (!masksFilled)
	{
		memset(fullsizeMask->buffer,255,fullsizeMask->bytePitch*fullsizeMask->height);
		memset(halfsizeMask->buffer,255,halfsizeMask->bytePitch*halfsizeMask->height);

		masksFilled=true;
	}

	if (map)
	{
		PVideoFrame cur=child->GetFrame(n/2,env);

		CopyFrame(dst,cur,env);
		ShowMotionMask_YV12(dst,usedOrder,fullsizeMask);
	}
	else
		Deinterlace_YV12(dst,usedOrder,n,fullsizeMask,halfsizeMask,sharp,twoway);
}


inline void KernelDeint::BuildMotionMask_YV12(
	KernelDeintMask* fullsizeMask, 
	KernelDeintMask* halfsizeMask, 
	int order, 
	int n,
	bool linked,
	int threshold)
{
	if (linked)
	{
		BuildPlaneMotionMask_YV12(fullsizeMask,order,n,PLANAR_Y,true,threshold,true);
		BuildPlaneMotionMask_YV12(fullsizeMask,order,n,PLANAR_U,true,threshold,false);
		BuildPlaneMotionMask_YV12(fullsizeMask,order,n,PLANAR_V,true,threshold,false);

		HalveMotionMask_YV12(halfsizeMask,fullsizeMask);
	}
	else
	{
		BuildPlaneMotionMask_YV12(fullsizeMask,order,n,PLANAR_Y,false,threshold,true);
		BuildPlaneMotionMask_YV12(halfsizeMask,order,n,PLANAR_U,false,threshold,true);
		BuildPlaneMotionMask_YV12(halfsizeMask,order,n,PLANAR_V,false,threshold,false);
	}
}


void KernelDeint::BuildPlaneMotionMask_YV12(
	KernelDeintMask* mask, 
	int order, 
	int n,
	int plane, 
	bool linked,
	int threshold,
	bool overwrite
	)
{
	if (cpuFlags & CPUF_MMX)
		BuildPlaneMotionMask_YV12_MMX(mask,order,n,plane,linked,threshold,overwrite);
	else
		BuildPlaneMotionMask_YV12_C(mask,order,n,plane,linked,threshold,overwrite);
}


// Someone somewhere probably wants my head on a silver plate for this (mis-)usage
// of the preprocessor, but I regret nothing - at the very least it keeps the code 
// manageable and avoids branching in the inner loop... :P

void KernelDeint::BuildPlaneMotionMask_YV12_C(
	KernelDeintMask* mask, 
	int order, 
	int n,
	int plane, 
	bool linked,
	int threshold,
	bool overwrite
	)
{
	#define COLORSPACE COLORSPACE_YV12
	#define PITCH_MOD 1

	#define MOTIONMASK_STAGE1 "include/MotionMaskLine_YV12_1.cpp"
	#define MOTIONMASK_STAGE2 "include/MotionMaskLine_YV12_2_C.cpp"

	#include "include/MotionMask_YV12_0.cpp"
}


void KernelDeint::BuildPlaneMotionMask_YV12_MMX(
	KernelDeintMask* mask, 
	int order, 
	int n,
	int plane, 
	bool linked,
	int threshold,
	bool overwrite
	)
{
	int pitch1=GetField(n+1,env)->GetPitch(plane);
	int pitch2=GetField(n  ,env)->GetPitch(plane);
	int pitch3=GetField(n-1,env)->GetPitch(plane);
	int pitch4=GetField(n-2,env)->GetPitch(plane);

	if ((pitch1 != pitch2) ||
	    (pitch2 != pitch3) ||
		(pitch3 != pitch4))
	{
		if (!printedPitchWarning)
		{
			OutputDebugString(PITCH_WARNING);

			printedPitchWarning=true;
		}

		BuildPlaneMotionMask_YV12_C(mask,order,n,plane,linked,threshold,overwrite);

		return;
	}

	#define COLORSPACE COLORSPACE_YV12
	#define PITCH_MOD 8

	#define MOTIONMASK_STAGE1 "include/MotionMaskLine_YV12_1.cpp"
	#define MOTIONMASK_STAGE2 "include/MotionMaskLine_YV12_2_MMX.cpp"

	#include "include/MotionMask_YV12_0.cpp"

	__asm emms
}


void KernelDeint::HalveMotionMask_YV12(KernelDeintMask* halfsizeMask, KernelDeintMask* fullsizeMask)
{
	if (cpuFlags & CPUF_MMX)
		HalveMotionMask_YV12_MMX(halfsizeMask,fullsizeMask);
	else
		HalveMotionMask_YV12_C(halfsizeMask,fullsizeMask);
}


void KernelDeint::HalveMotionMask_YV12_C(KernelDeintMask* halfsizeMask, KernelDeintMask* fullsizeMask)
{
	unsigned char* halfMaskPtr=halfsizeMask->buffer;
	int halfMaskPitch=halfsizeMask->pitch;

	unsigned short* fullMaskPtr=(unsigned short*)fullsizeMask->buffer;
	int fullMaskPitch=fullsizeMask->pitch/2;

	int rowSize=halfsizeMask->width;

	for (int rowsLeft=halfsizeMask->height; rowsLeft > 0; rowsLeft--)
	{
		unsigned char* curHalfPtr=halfMaskPtr;
		unsigned short* curFullPtr=fullMaskPtr;

		for (int colsLeft=rowSize; colsLeft > 0; colsLeft--)
		{
			*curHalfPtr=(((*curFullPtr | *(curFullPtr+fullMaskPitch)) > 0) ? 255 : 0);

			*curHalfPtr++; curFullPtr++;
		}

		halfMaskPtr+=halfMaskPitch; fullMaskPtr+=2*fullMaskPitch;
	}
}


void KernelDeint::HalveMotionMask_YV12_MMX(KernelDeintMask* halfsizeMask, KernelDeintMask* fullsizeMask)
{
	unsigned char* halfMaskPtr=halfsizeMask->buffer;
	int halfMaskPitch=halfsizeMask->bytePitch;

	unsigned char* fullMaskPtr=fullsizeMask->buffer;
	int fullMaskPitch=fullsizeMask->bytePitch;

	int rowSize=halfsizeMask->width;
	int rowsLeft=halfsizeMask->height; 

	__asm
	{
		mov eax,080808080h
		movd mm2,eax
		punpcklbw mm2,mm2

		align 16

RowLoop:
		mov esi,fullMaskPtr
		mov edx,esi
		add edx,fullMaskPitch
		mov edi,halfMaskPtr

		mov ecx,rowSize
		add ecx,7
		and ecx,-8

		add esi,ecx
		add esi,ecx

		add edx,ecx
		add edx,ecx

		add edi,ecx

		neg ecx

		align 16

ColLoop:
		movq mm0,[esi+2*ecx  ]
		movq mm1,[esi+2*ecx+8]

		por mm0,[edx+2*ecx  ]
		por mm1,[edx+2*ecx+8]

		psrlw mm0,1
		psrlw mm1,1

		packuswb mm0,mm1
		pxor mm0,mm2
		pcmpgtb mm0,mm2

		movq [edi+ecx],mm0

		add ecx,8
		jnz ColLoop

		mov eax,halfMaskPitch
		add halfMaskPtr,eax

		mov eax,fullMaskPitch
		add eax,eax
		add fullMaskPtr,eax

		sub rowsLeft,1
		jnz RowLoop

		emms
	}
}


void KernelDeint::ShowMotionMask_YV12(PVideoFrame& dst,int order, KernelDeintMask* fullsizeMask)
{
	int maskPitch=fullsizeMask->pitch;
	unsigned char* maskPtr=fullsizeMask->buffer;

	int dstPitch=dst->GetPitch(PLANAR_Y);
	unsigned char* dstPtr=dst->GetWritePtr(PLANAR_Y)+order*dstPitch;

	int rowSize=dst->GetRowSize(PLANAR_Y);

	for (int rowsLeft=dst->GetHeight(PLANAR_Y)/2; rowsLeft > 0; rowsLeft--)
	{
		unsigned char* curMaskPtr=maskPtr;
		unsigned char* curDstPtr=dstPtr;

		for (int colsLeft=rowSize; colsLeft > 0; colsLeft--)
		{
			unsigned char curMask=*curMaskPtr;

			*curDstPtr|=curMask; curMask&=~235; *curDstPtr^=curMask;

			curMaskPtr++; curDstPtr++;
		}

		maskPtr+=maskPitch;
		dstPtr+=2*dstPitch;
	}
}

inline void KernelDeint::Deinterlace_YV12(
	PVideoFrame& dst,
	int order,
	int n,
	KernelDeintMask* fullsizeMask,
	KernelDeintMask* halfsizeMask,
	bool sharp,
	bool twoway
	)
{
	DeinterlacePlane_YV12(dst,order,n,fullsizeMask,sharp,twoway,PLANAR_Y);
	DeinterlacePlane_YV12(dst,order,n,halfsizeMask,sharp,twoway,PLANAR_U);
	DeinterlacePlane_YV12(dst,order,n,halfsizeMask,sharp,twoway,PLANAR_V);
}


inline void KernelDeint::DeinterlacePlane_YV12(
	PVideoFrame& dst,
	int order,
	int n,
	KernelDeintMask* mask,
	bool sharp,
	bool twoway,
	int plane
	)
{
	if (cpuFlags & CPUF_MMX)
		DeinterlacePlane_YV12_MMX(dst,order,n,mask,sharp,twoway,plane);
	else
		DeinterlacePlane_YV12_C(dst,order,n,mask,sharp,twoway,plane);
}


void KernelDeint::DeinterlacePlane_YV12_C(
	PVideoFrame& dst,
	int order,
	int n,
	KernelDeintMask* mask,
	bool sharp,
	bool twoway,
	int plane
	)
{
	#define COLORSPACE COLORSPACE_YV12
	#define PITCH_MOD 1

	#define DEINTERLACE_STAGE1 "include/DeinterlaceLine_1.cpp"
	#define DEINTERLACE_STAGE2 "include/DeinterlaceLine_2_C.cpp"
	#define COPY_LINE          "include/CopyLine_C.cpp"

	#include "include/Deinterlace_0.cpp"
}


void KernelDeint::DeinterlacePlane_YV12_MMX(
	PVideoFrame& dst,
	int order,
	int n,
	KernelDeintMask* mask,
	bool sharp,
	bool twoway,
	int plane
	)
{
	int pitch1=GetField(n+1,env)->GetPitch(plane);
	int pitch2=GetField(n  ,env)->GetPitch(plane);
	int pitch3=GetField(n-1,env)->GetPitch(plane);

	if ((pitch1 != pitch2) ||
	    (pitch2 != pitch3))
	{
		if (!printedPitchWarning)
		{
			OutputDebugString(PITCH_WARNING);

			printedPitchWarning=true;
		}

		DeinterlacePlane_YV12_C(dst,order,n,mask,sharp,twoway,plane);

		return;
	}

	#define COLORSPACE COLORSPACE_YV12
	#define PITCH_MOD 8

	#define DEINTERLACE_STAGE1 "include/DeinterlaceLine_1.cpp"
	#define DEINTERLACE_STAGE2 "include/DeinterlaceLine_2_MMX.cpp"
	#define COPY_LINE          "include/CopyLine_MMX.cpp"

	#include "include/Deinterlace_0.cpp"

	__asm emms
}

#endif


#if defined KERNELDEINT_YUY2

void KernelDeint::KernelDeint_YUY2(PVideoFrame& dst, int n)
{
	int usedOrder=order ^ (n & 1);

	if (threshold > 0)
		BuildMotionMask_YUY2(fullsizeMask,usedOrder,n,linked,threshold);
	else if (!masksFilled)
	{
		memset(fullsizeMask->buffer,255,fullsizeMask->bytePitch*fullsizeMask->height);

		masksFilled=true;
	}

	if (map)
	{
		PVideoFrame cur=child->GetFrame(n/2,env);

		CopyFrame(dst,cur,env);
		ShowMotionMask_YUY2(dst,usedOrder,fullsizeMask);
	}
	else
		Deinterlace_YUY2(dst,usedOrder,n,fullsizeMask,sharp,twoway);
}


inline void KernelDeint::BuildMotionMask_YUY2(
	KernelDeintMask* mask, 
	int order, 
	int n,
	bool linked,
	int threshold
	)
{
	if (cpuFlags & CPUF_MMX)
		BuildMotionMask_YUY2_MMX(mask,order,n,linked,threshold);
	else
		BuildMotionMask_YUY2_C(mask,order,n,linked,threshold);
}


void KernelDeint::BuildMotionMask_YUY2_C(
	KernelDeintMask* mask, 
	int order, 
	int n,
	bool linked,
	int threshold
	)
{
	#define COLORSPACE COLORSPACE_YUY2
	#define PITCH_MOD 1

	#define MOTIONMASK_STAGE1 "include/MotionMaskLine_YUY2+RGB_1.cpp"
	#define MOTIONMASK_STAGE2 "include/MotionMaskLine_YUY2_2_C.cpp"

	#include "include\MotionMask_YUY2+RGB_0.cpp"
}


void KernelDeint::BuildMotionMask_YUY2_MMX(
	KernelDeintMask* mask, 
	int order, 
	int n,
	bool linked,
	int threshold
	)
{
	int pitch1=GetField(n+1,env)->GetPitch();
	int pitch2=GetField(n  ,env)->GetPitch();
	int pitch3=GetField(n-1,env)->GetPitch();
	int pitch4=GetField(n-2,env)->GetPitch();

	if ((pitch1 != pitch2) ||
	    (pitch2 != pitch3) ||
		(pitch3 != pitch4))
	{
		if (!printedPitchWarning)
		{
			OutputDebugString(PITCH_WARNING);

			printedPitchWarning=true;
		}

		BuildMotionMask_YUY2_C(mask,order,n,linked,threshold);

		return;
	}

	#define COLORSPACE COLORSPACE_YUY2
	#define PITCH_MOD 8

	#define MOTIONMASK_STAGE1 "include/MotionMaskLine_YUY2+RGB_1.cpp"
	#define MOTIONMASK_STAGE2 "include/MotionMaskLine_YUY2_2_MMX.cpp"

	#include "include\MotionMask_YUY2+RGB_0.cpp"

	__asm emms
}


void KernelDeint::ShowMotionMask_YUY2(PVideoFrame& dst,int order, KernelDeintMask* mask)
{
	int maskPitch=mask->bytePitch;
	unsigned char* maskPtr=mask->buffer;

	int dstPitch=dst->GetPitch();
	unsigned char* dstPtr=dst->GetWritePtr()+order*dstPitch;

	int rowSize=dst->GetRowSize();

	for (int rowsLeft=dst->GetHeight()/2; rowsLeft > 0; rowsLeft--)
	{
		unsigned long* curMaskPtr=(unsigned long*)maskPtr;
		unsigned long* curDstPtr=(unsigned long*)dstPtr;

		for (int colsLeft=rowSize/4; colsLeft > 0; colsLeft--)
		{
			unsigned long curMask=(*curMaskPtr & 0x00ff00ff);

			*curDstPtr|=curMask; curMask&=~0x00eb00eb; *curDstPtr^=curMask;

			curMaskPtr++; curDstPtr++;
		}

		maskPtr+=maskPitch;
		dstPtr+=2*dstPitch;
	}
}


inline void KernelDeint::Deinterlace_YUY2(
	PVideoFrame& dst,
	int order,
	int n,
	KernelDeintMask* mask,
	bool sharp,
	bool twoway
	)
{
	if (cpuFlags & CPUF_MMX)
		Deinterlace_YUY2_MMX(dst,order,n,mask,sharp,twoway);
	else
		Deinterlace_YUY2_C(dst,order,n,mask,sharp,twoway);
}


void KernelDeint::Deinterlace_YUY2_C(
	PVideoFrame& dst,
	int order,
	int n,
	KernelDeintMask* mask,
	bool sharp,
	bool twoway
	)
{
	int plane=PLANAR_Y;

	#define COLORSPACE COLORSPACE_YUY2
	#define PITCH_MOD 1

	#define DEINTERLACE_STAGE1 "include/DeinterlaceLine_1.cpp"
	#define DEINTERLACE_STAGE2 "include/DeinterlaceLine_2_C.cpp"
	#define COPY_LINE          "include/CopyLine_C.cpp"

	#include "include/Deinterlace_0.cpp"
}


void KernelDeint::Deinterlace_YUY2_MMX(
	PVideoFrame& dst,
	int order,
	int n,
	KernelDeintMask* mask,
	bool sharp,
	bool twoway
	)
{
	int pitch1=GetField(n+1,env)->GetPitch();
	int pitch2=GetField(n  ,env)->GetPitch();
	int pitch3=GetField(n-1,env)->GetPitch();

	if ((pitch1 != pitch2) ||
	    (pitch2 != pitch3))
	{
		if (!printedPitchWarning)
		{
			OutputDebugString(PITCH_WARNING);

			printedPitchWarning=true;
		}

		Deinterlace_YUY2_C(dst,order,n,fullsizeMask,sharp,twoway);

		return;
	}

	int plane=PLANAR_Y;

	#define COLORSPACE COLORSPACE_YUY2
	#define PITCH_MOD 8

	#define DEINTERLACE_STAGE1 "include/DeinterlaceLine_1.cpp"
	#define DEINTERLACE_STAGE2 "include/DeinterlaceLine_2_MMX.cpp"
	#define COPY_LINE          "include/CopyLine_MMX.cpp"

	#include "include/Deinterlace_0.cpp"

	__asm emms
}

#endif


#if defined KERNELDEINT_RGB32

void KernelDeint::KernelDeint_RGB32(PVideoFrame& dst, int n)
{
	int usedOrder=order ^ (n & 1);

	if (threshold > 0)
		BuildMotionMask_RGB32(fullsizeMask,usedOrder,n,threshold);
	else if (!masksFilled)
	{
		memset(fullsizeMask->buffer,255,fullsizeMask->bytePitch*fullsizeMask->height);

		masksFilled=true;
	}

	if (map)
	{
		PVideoFrame cur=child->GetFrame(n/2,env);

		CopyFrame(dst,cur,env);
		ShowMotionMask_RGB32(dst,usedOrder,fullsizeMask);

		return;
	}

	Deinterlace_RGB32(dst,usedOrder,n,fullsizeMask,sharp,twoway);
}


inline void KernelDeint::BuildMotionMask_RGB32(
	KernelDeintMask* mask, 
	int order, 
	int n,
	int threshold
	)
{
	if (cpuFlags & CPUF_MMX)
		BuildMotionMask_RGB32_MMX(mask,order,n,threshold);
	else
		BuildMotionMask_RGB32_C(mask,order,n,threshold);
}


void KernelDeint::BuildMotionMask_RGB32_C(
	KernelDeintMask* mask, 
	int order, 
	int n,
	int threshold
	)
{
	#define COLORSPACE COLORSPACE_RGB32
	#define PITCH_MOD 1

	#define MOTIONMASK_STAGE1 "include/MotionMaskLine_YUY2+RGB_1.cpp"
	#define MOTIONMASK_STAGE2 "include/MotionMaskLine_RGB32_2_C.cpp"

	#include "include\MotionMask_YUY2+RGB_0.cpp"
}


void KernelDeint::BuildMotionMask_RGB32_MMX(
	KernelDeintMask* mask, 
	int order, 
	int n,
	int threshold
	)
{
	int pitch1=GetField(n+1,env)->GetPitch();
	int pitch2=GetField(n  ,env)->GetPitch();
	int pitch3=GetField(n-1,env)->GetPitch();
	int pitch4=GetField(n-2,env)->GetPitch();

	if ((pitch1 != pitch2) ||
	    (pitch2 != pitch3) ||
		(pitch3 != pitch4))
	{
		if (!printedPitchWarning)
		{
			OutputDebugString(PITCH_WARNING);

			printedPitchWarning=true;
		}

		BuildMotionMask_RGB32_C(mask,order,n,threshold);

		return;
	}

	#define COLORSPACE COLORSPACE_RGB32
	#define PITCH_MOD 8

	#define MOTIONMASK_STAGE1 "include/MotionMaskLine_YUY2+RGB_1.cpp"
	#define MOTIONMASK_STAGE2 "include/MotionMaskLine_RGB32_2_MMX.cpp"

	#include "include\MotionMask_YUY2+RGB_0.cpp"

	__asm emms
}


void KernelDeint::ShowMotionMask_RGB32(PVideoFrame& dst,int order, KernelDeintMask* mask)
{
	int maskPitch=mask->bytePitch;
	unsigned char* maskPtr=mask->buffer;

	int dstPitch=dst->GetPitch();
	unsigned char* dstPtr=dst->GetWritePtr()+order*dstPitch;

	int rowSize=dst->GetRowSize();

	for (int rowsLeft=dst->GetHeight()/2; rowsLeft > 0; rowsLeft--)
	{
		unsigned long* curMaskPtr=(unsigned long*)maskPtr;
		unsigned long* curDstPtr=(unsigned long*)dstPtr;

		for (int colsLeft=rowSize/4; colsLeft > 0; colsLeft--)
		{
			*curDstPtr|=*curMaskPtr;

			curMaskPtr++; curDstPtr++;
		}

		maskPtr+=maskPitch;
		dstPtr+=2*dstPitch;
	}
}


inline void KernelDeint::Deinterlace_RGB32(
	PVideoFrame& dst,
	int order,
	int n,
	KernelDeintMask* mask,
	bool sharp,
	bool twoway
	)
{
	if (cpuFlags & CPUF_MMX)
		Deinterlace_RGB32_MMX(dst,order,n,mask,sharp,twoway);
	else
		Deinterlace_RGB32_C(dst,order,n,mask,sharp,twoway);
}


void KernelDeint::Deinterlace_RGB32_C(
	PVideoFrame& dst,
	int order,
	int n,
	KernelDeintMask* mask,
	bool sharp,
	bool twoway
	)
{
	int plane=PLANAR_Y;

	#define COLORSPACE COLORSPACE_RGB32
	#define PITCH_MOD 1

	#define DEINTERLACE_STAGE1 "include/DeinterlaceLine_1.cpp"
	#define DEINTERLACE_STAGE2 "include/DeinterlaceLine_2_C.cpp"
	#define COPY_LINE          "include/CopyLine_C.cpp"

	#include "include/Deinterlace_0.cpp"
}


void KernelDeint::Deinterlace_RGB32_MMX(
	PVideoFrame& dst,
	int order,
	int n,
	KernelDeintMask* mask,
	bool sharp,
	bool twoway
	)
{
	int pitch1=GetField(n+1,env)->GetPitch();
	int pitch2=GetField(n  ,env)->GetPitch();
	int pitch3=GetField(n-1,env)->GetPitch();

	if ((pitch1 != pitch2) ||
	    (pitch2 != pitch3))
	{
		if (!printedPitchWarning)
		{
			OutputDebugString(PITCH_WARNING);

			printedPitchWarning=true;
		}

		Deinterlace_RGB32_C(dst,order,n,mask,sharp,twoway);

		return;
	}

	int plane=PLANAR_Y;

	#define COLORSPACE COLORSPACE_RGB32
	#define PITCH_MOD 8

	#define DEINTERLACE_STAGE1 "include/DeinterlaceLine_1.cpp"
	#define DEINTERLACE_STAGE2 "include/DeinterlaceLine_2_MMX.cpp"
	#define COPY_LINE          "include/CopyLine_MMX.cpp"

	#include "include/Deinterlace_0.cpp"

	__asm emms
}
#endif


inline void KernelDeint::CopyFrame(PVideoFrame& dst, PVideoFrame& src, IScriptEnvironment* env)
{
	env->BitBlt(dst->GetWritePtr(PLANAR_Y),
	            dst->GetPitch(PLANAR_Y),
				src->GetReadPtr(PLANAR_Y),
				src->GetPitch(PLANAR_Y),
				src->GetRowSize(PLANAR_Y),
				src->GetHeight(PLANAR_Y));

	env->BitBlt(dst->GetWritePtr(PLANAR_U),
	            dst->GetPitch(PLANAR_U),
				src->GetReadPtr(PLANAR_U),
				src->GetPitch(PLANAR_U),
				src->GetRowSize(PLANAR_U),
				src->GetHeight(PLANAR_U));

	env->BitBlt(dst->GetWritePtr(PLANAR_V),
	            dst->GetPitch(PLANAR_V),
				src->GetReadPtr(PLANAR_V),
				src->GetPitch(PLANAR_V),
				src->GetRowSize(PLANAR_V),
				src->GetHeight(PLANAR_V));
}
