/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: deintl.cpp,v 1.7.32.1 2004/07/09 01:55:14 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */


////////////////////////////////////////////////////////
//	defines
////////////////////////////////////////////////////////

// Defineing TIME_DEINTERLACE will time the deinterlacer
// and write a file with filename TIME_DEINTERLACE_FILENAME
// with the times.

//#define TIME_DEINTERLACE
//#define TIME_DEINTERLACE_FILENAME "d:\\dintl.log"

////////////////////////////////////////////////////////
//	include files
////////////////////////////////////////////////////////

#include "hlxclib/stdlib.h"
#include "hlxclib/string.h"

#define INTL_I420_CODE

#ifdef TIME_DEINTERLACE
#include "hlxclib/stdio.h"
#endif

#include "mmx_util.h"
#include "deintl.h"


////////////////////////////////////////////////////////
//	internal prototypes
////////////////////////////////////////////////////////

static void
Deinterlace_RGB24(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format);

static void
Deinterlace_I420(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format);

static void
Deinterlace_RGB24_Fast(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format);

static void
Deinterlace_I420_Fast(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format);

static void
Deinterlace_I420_Advanced(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format);

static void
Deinterlace_I420_EdgeInterp(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format);

static void
Deinterlace_RGB24_Field(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format, int field);

static void
Deinterlace_I420_Field(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format, int field);

static void
Deinterlace_I420_FlipFlop(unsigned char *frame, unsigned char *temp_frame, int pels, int lines, int pitch, int format);

#ifdef _M_IX86

static void
Deinterlace_I420_MMX(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format);

#endif

int
C_DetectInterlace_I420(unsigned char *frame, unsigned char *prev_frame, int first_frame, int pels, int lines, int pitch);


////////////////////////////////////////////////////////
//	macros
////////////////////////////////////////////////////////

#define MEDIAN_3(a,b,c)			\
	((a > b)?					\
		(						\
			(b > c)?			\
			(b):				\
			(					\
				(a > c)?		\
				(c):			\
				(a)				\
			)					\
		):						\
		(						\
			(a > c)?			\
			(a):				\
			(					\
				(b > c)?		\
				(c):			\
				(b)				\
			)					\
		))						\

#define MABS(v) (tmp=(v),((tmp)^(tmp>>31)))
#define ABS(a)  (((a) < 0) ? (-(a)) : (a))
#define DIFF_FCN(v) ((v)*(v))		// Squared difference

static const int SQUARED_TAB[255+255+1] = {
    65025,  64516,  64009,  63504,  63001,  62500,
    62001,  61504,  61009,  60516,  60025,  59536,  59049,  58564,  58081,  57600,
    57121,  56644,  56169,  55696,  55225,  54756,  54289,  53824,  53361,  52900,
    52441,  51984,  51529,  51076,  50625,  50176,  49729,  49284,  48841,  48400,
    47961,  47524,  47089,  46656,  46225,  45796,  45369,  44944,  44521,  44100,
    43681,  43264,  42849,  42436,  42025,  41616,  41209,  40804,  40401,  40000,
    39601,  39204,  38809,  38416,  38025,  37636,  37249,  36864,  36481,  36100,
    35721,  35344,  34969,  34596,  34225,  33856,  33489,  33124,  32761,  32400,
    32041,  31684,  31329,  30976,  30625,  30276,  29929,  29584,  29241,  28900,
    28561,  28224,  27889,  27556,  27225,  26896,  26569,  26244,  25921,  25600,
    25281,  24964,  24649,  24336,  24025,  23716,  23409,  23104,  22801,  22500,
    22201,  21904,  21609,  21316,  21025,  20736,  20449,  20164,  19881,  19600,
    19321,  19044,  18769,  18496,  18225,  17956,  17689,  17424,  17161,  16900,
    16641,  16384,  16129,  15876,  15625,  15376,  15129,  14884,  14641,  14400,
    14161,  13924,  13689,  13456,  13225,  12996,  12769,  12544,  12321,  12100,
    11881,  11664,  11449,  11236,  11025,  10816,  10609,  10404,  10201,  10000,
    9801,   9604,   9409,   9216,   9025,   8836,   8649,   8464,   8281,   8100,
    7921,   7744,   7569,   7396,   7225,   7056,   6889,   6724,   6561,   6400,
    6241,   6084,   5929,   5776,   5625,   5476,   5329,   5184,   5041,   4900,
    4761,   4624,   4489,   4356,   4225,   4096,   3969,   3844,   3721,   3600,
    3481,   3364,   3249,   3136,   3025,   2916,   2809,   2704,   2601,   2500,
    2401,   2304,   2209,   2116,   2025,   1936,   1849,   1764,   1681,   1600,
    1521,   1444,   1369,   1296,   1225,   1156,   1089,   1024,   961,    900,
    841,    784,    729,    676,    625,    576,    529,    484,    441,    400,
    361,    324,    289,    256,    225,    196,    169,    144,    121,    100,
    81,     64,     49,     36,     25,     16,     9,      4,      1,      0,
    1,      4,      9,      16,     25,     36,     49,     64,     81,     100,
    121,    144,    169,    196,    225,    256,    289,    324,    361,    400,
    441,    484,    529,    576,    625,    676,    729,    784,    841,    900,
    961,    1024,   1089,   1156,   1225,   1296,   1369,   1444,   1521,   1600,
    1681,   1764,   1849,   1936,   2025,   2116,   2209,   2304,   2401,   2500,
    2601,   2704,   2809,   2916,   3025,   3136,   3249,   3364,   3481,   3600,
    3721,   3844,   3969,   4096,   4225,   4356,   4489,   4624,   4761,   4900,
    5041,   5184,   5329,   5476,   5625,   5776,   5929,   6084,   6241,   6400,
    6561,   6724,   6889,   7056,   7225,   7396,   7569,   7744,   7921,   8100,
    8281,   8464,   8649,   8836,   9025,   9216,   9409,   9604,   9801,   10000,
    10201,  10404,  10609,  10816,  11025,  11236,  11449,  11664,  11881,  12100,
    12321,  12544,  12769,  12996,  13225,  13456,  13689,  13924,  14161,  14400,
    14641,  14884,  15129,  15376,  15625,  15876,  16129,  16384,  16641,  16900,
    17161,  17424,  17689,  17956,  18225,  18496,  18769,  19044,  19321,  19600,
    19881,  20164,  20449,  20736,  21025,  21316,  21609,  21904,  22201,  22500,
    22801,  23104,  23409,  23716,  24025,  24336,  24649,  24964,  25281,  25600,
    25921,  26244,  26569,  26896,  27225,  27556,  27889,  28224,  28561,  28900,
    29241,  29584,  29929,  30276,  30625,  30976,  31329,  31684,  32041,  32400,
    32761,  33124,  33489,  33856,  34225,  34596,  34969,  35344,  35721,  36100,
    36481,  36864,  37249,  37636,  38025,  38416,  38809,  39204,  39601,  40000,
    40401,  40804,  41209,  41616,  42025,  42436,  42849,  43264,  43681,  44100,
    44521,  44944,  45369,  45796,  46225,  46656,  47089,  47524,  47961,  48400,
    48841,  49284,  49729,  50176,  50625,  51076,  51529,  51984,  52441,  52900,
    53361,  53824,  54289,  54756,  55225,  55696,  56169,  56644,  57121,  57600,
    58081,  58564,  59049,  59536,  60025,  60516,  61009,  61504,  62001,  62500,
    63001,  63504,  64009,  64516,  65025
};

#define MSQUARED(v) (SQUARED_TAB[(v) + 255])

// Some thresholds
#define INTL_DIFF_THRESH	40000
#define INTL_HORIZ_THRESH	80

#ifdef TIME_DEINTERLACE
static unsigned int num_avg = 0;
static unsigned int num_med = 0;
#endif


int
InitDeinterlace (
	int pels, int lines, int pitch, 
	int format, 
	INTL_MODE mode, 
	T_DEINTL_STATE **state)
{
	T_DEINTL_STATE *new_state = 0;

	if (*state == 0)
	{
		new_state = (T_DEINTL_STATE *)malloc(sizeof(T_DEINTL_STATE));
	}

	if (new_state == 0)
		return 1;

	new_state->pels = pels;
	new_state->lines = lines;
	new_state->pitch = pitch;
	new_state->format = format;
	new_state->detection_measure = (lines > 242)?(16):(0);
	new_state->commit_deinterlace = FALSE;

	*state = new_state;

	return 0;
}

void
FreeDeinterlace (T_DEINTL_STATE **state)
{
	if (*state != 0)
		free(*state);
	*state = 0;
}

BOOL
IsContentInterlaced (T_DEINTL_STATE *state)
{
	if (state != 0)
		return (state->commit_deinterlace == 1)?(TRUE):(FALSE);

	return FALSE;
}

void
ResetDeinterlace (T_DEINTL_STATE *state)
{
	state->detection_measure = 0;
	state->commit_deinterlace = FALSE;
}


////////////////////////////////////////////////////////
//
//	Deinterlace
//
//	Top level function to deinterlace a video frame
//
//	Parameters:
//		frame:		Pointer to the frame to deinterlace
//		prev_frame:	Pointer to the previous frame.
//					Better results are achieved by giving
//					the previous *interlaced* frame
//		pels,
//		lines,
//		pitch:		Frame dimensions
//		format:		INTL_FORMAT_RGB24 or INTL_FORMAT_I420
//		mode:		Choose either using both fields or
//					to remove one field entirely
//					(see 'deintl.h')
//
////////////////////////////////////////////////////////

int
Deinterlace(
	unsigned char *frame, 
	unsigned char *prev_frame,
	int pels, int lines, int pitch,
	int format,
	int first_frame, 
	INTL_MODE mode,
	T_DEINTL_STATE *state)
{
	if (pels == 0)
		pels = state->pels;
	if (lines == 0)
		lines = state->lines;
	if (pitch == 0)
		pitch = state->pitch;
	if (format == 0)
		format = state->format;

	unsigned char *pfp = (first_frame == TRUE)?(NULL):(prev_frame);

	// Unless were going to do detection first (i.e. in INTL_MODE_SMART_AUTO mode),
	// don't de-interlace when there is less than 242 lines.  This is a little
	// check to prevent unintentional de-interlacing of probable progressive frames.
	if (lines < 242 && mode != INTL_MODE_SMART_AUTO)
		return 0;

	// Run detection
	if (mode == INTL_MODE_SMART_AUTO)
	{
		unsigned char *detection_frame;
		unsigned char *detection_prev_frame;
		int res;

		if (format == INTL_FORMAT_I420)
		{
			// point to the Y-Plane
			detection_frame = frame;
			detection_prev_frame = prev_frame;
		}
		if (format == INTL_FORMAT_RGB24)
		{
			// point to the G-Plane
			detection_frame = frame + pels * lines;
			detection_prev_frame = prev_frame + pels * lines;
		}

		// Perform detection
		res = C_DetectInterlace_I420(
			detection_frame, 
			detection_prev_frame, 
			first_frame, 
			pels, lines, pitch);

		// Update the "detection measure" based on result of detection
		switch (res)
		{
		case INTL_STRONG_INTERLACE:
			{
				state->detection_measure = (31 * state->detection_measure + 256) >> 5;
			}
			break;

		case INTL_WEAK_INTERLACE:
			{
				state->detection_measure = (31 * state->detection_measure + 256) >> 5;
			}
			break;

		case INTL_WEAK_PROGRESSIVE:
			{
				state->detection_measure = (31 * state->detection_measure + 0) >> 5;
			}
			break;

		case INTL_STRONG_PROGRESSIVE:
			{
				state->detection_measure = 0;
			}
			break;
		}

		// If the detection measure is above a threshold, set this flag used
		// to indicate we are confident the content is interlaced.
		if (state->detection_measure > 128)
		{
			state->commit_deinterlace = TRUE;
		}

		// If the detection measure is above this threshold, we should
		// de-interlace this frame. Otherwise, return without de-interlacing
		if (state->detection_measure > 16)
		{
			mode = INTL_MODE_SMART;
		}
		else
		{
			return 0;
		}
	}

#ifdef TIME_DEINTERLACE
	FILE *fp = NULL;
	double proc_time;

	USE_CODEC_TIMER;

	num_avg = 0;
	num_med = 0;

	START_CODEC_TIMER;
#endif

	switch (format)
	{

#ifdef INTL_RBG24_CODE
	case INTL_FORMAT_RGB24:
		Deinterlace_RGB24_Fast(frame, pfp, pels, lines, pitch, format);
		break;
#endif

#ifdef INTL_I420_CODE

	case INTL_FORMAT_I420:

#ifdef USE_MMX_DEINTERLACING		//_M_IX86
        if (checkMmxAvailablity() & CPU_HAS_MMX)
			Deinterlace_I420_MMX(frame, pfp, pels, lines, pitch, format);
		else
#endif
		// Neelesh's new deinterlacer.
		Deinterlace_I420_Advanced(frame, pfp, pels, lines, pitch, format);

		break;

#endif
	}

#ifdef TIME_DEINTERLACE
	STOP_CODEC_TIMER(proc_time);
	
	fp = fopen(TIME_DEINTERLACE_FILENAME,"a+");
	if (fp != NULL)
	{
		fprintf(fp,"deinterlace time\t%f\t%d\t%d\n",proc_time,num_avg,num_med);
		fclose(fp);
	}
#endif

	return 1;
}


////////////////////////////////////////////////////////
//
//	C_DetectInterlace_I420
//
//	Top level function to detect the presense of 
//	interlaced video content
//
//	Parameters:
//		frame:		Pointer to the frame to deinterlace
//		prev_frame:	Pointer to the previous frame.
//		pels,
//		lines,
//		pitch:		Frame dimensions
//
////////////////////////////////////////////////////////

// Threshold: If 8x8 SAD is above MOVEMENT_THRESH then
// we'll decide that the block is in motion
#define MOVEMENT_THRESH (400)

// This is the factor by which 8x8 are skipped (not checked).
// SKIP_FACTOR = 1 means no skipping.
#define SKIP_FACTOR 4

#ifdef DEBUG
// This increments with each call, 
// and is useful for debugging a specific frame.
static unsigned int call_count = 0;
#endif

int
C_DetectInterlace_I420(
	unsigned char *frame, 
	unsigned char *prev_frame, 
	int first_frame, 
	int pels, int lines, int pitch)
{
	unsigned int image_size = (unsigned int)(pels * lines);
		// pre-calculated image size used for determining thresholds

	unsigned int interlaced_blocks = 0;			
		// count of the number of 8x8 blocks that look to be interlaced

	unsigned int progressive_blocks = 0;
		// count of the number of 8x8 blocks that look to be progressive

	unsigned int step1v_tot = 0;
		// running total of the 1-step squared vertical difference of pixels

	unsigned int step2v_tot = 0;
		// running total of the 2-step squared vertical difference of pixels

	unsigned int step1h_tot = 0;
		// running total of the 1-step squared horizontal difference of pixels

	unsigned int step2h_tot = 0;
		// running total of the 2-step squared horizontal difference of pixels

	unsigned char *fp, *pp;
	unsigned char *f0, *f1, *f2, *f3;
		// various temporary frame pointers

	int i, j, k;	// loop counter	
	int tmp;		// intermediate values for correlation and SAD macro
	int d0, d1;		// SAD counters

#ifdef DEBUG
	call_count++;
#endif

	// We have no detection scheme for the first frame yet.
	if (first_frame)
	{
		return INTL_NO_DETECTION;
	}

	// Loop through tiled 8x8 blocks of the image
	for (i = 8; i < lines - 8; i += 8)
	{
		// Start pointer at new line
		fp = frame + i * pitch + (i & (8*(SKIP_FACTOR - 1))) + 8;
		pp = prev_frame + i * pitch + (i &  (8*(SKIP_FACTOR - 1))) + 8;

		for (j = 8; j < pels - 8; j += 8 * SKIP_FACTOR)
		{
			f0 = fp;
			f1 = f0 + pitch;
			f2 = pp;
			f3 = f2 + pitch;

			d0 = d1 = 0;

			// Calculate SAD for even and odd lines of 8x8 block.
			// We're doing a partial SAD here for speed.
			for (k = 0; k < 4; k++)
			{
				d0 += MABS(f0[0] - f2[0]);
				d0 += MABS(f0[2] - f2[2]);
				d0 += MABS(f0[4] - f2[4]);
				d0 += MABS(f0[6] - f2[6]);
				d1 += MABS(f1[1] - f3[1]);
				d1 += MABS(f1[3] - f3[3]);
				d1 += MABS(f1[5] - f3[5]);
				d1 += MABS(f1[7] - f3[7]);

				f0 += 2 * pitch;
				f1 += 2 * pitch;
				f2 += 2 * pitch;
				f3 += 2 * pitch;
			}
			d0 <<= 1;
			d1 <<= 1;
			
			// If there is enough difference to determine movement,
			// check this region for indications of interlaced artifacts
			if (d0 > MOVEMENT_THRESH || d1 > MOVEMENT_THRESH)
			{
				int step1v_corr = 0;
				int step2v_corr = 0;
				int step1h_corr = 0;
				int step2h_corr = 0;

				f0 = fp;
				f1 = f0 + pitch;
				f2 = f1 + pitch;

				// Determine 1 and 2 step pixel differences for 8x8 region
				for (k = 0; k < 4; k++)
				{
					tmp = f0[1] - f1[1];
					step1v_corr += MSQUARED(tmp);
					tmp = f0[1] - f2[1];
					step2v_corr += MSQUARED(tmp);
					tmp = f1[0] - f1[1];
					step1h_corr += MSQUARED(tmp);
					tmp = f1[0] - f1[2];
					step2h_corr += MSQUARED(tmp);

					tmp = f0[3] - f1[3];
					step1v_corr += MSQUARED(tmp);
					tmp = f0[3] - f2[3];
					step2v_corr += MSQUARED(tmp);
					tmp = f1[2] - f1[3];
					step1h_corr += MSQUARED(tmp);
					tmp = f1[2] - f1[4];
					step2h_corr += MSQUARED(tmp);

					tmp = f0[5] - f1[5];
					step1v_corr += MSQUARED(tmp);
					tmp = f0[5] - f2[5];
					step2v_corr += MSQUARED(tmp);
					tmp = f1[4] - f1[5];
					step1h_corr += MSQUARED(tmp);
					tmp = f1[4] - f1[6];
					step2h_corr += MSQUARED(tmp);

					tmp = f0[7] - f1[7];
					step1v_corr += MSQUARED(tmp);
					tmp = f0[7] - f2[7];
					step2v_corr += MSQUARED(tmp);
					tmp = f1[6] - f1[7];
					step1h_corr += MSQUARED(tmp);
					tmp = f1[6] - f1[8];
					step2h_corr += MSQUARED(tmp);

					f0 += 2 * pitch;
					f1 += 2 * pitch;
					f2 += 2 * pitch;
				}

				// These conditions indicate the block 
				// is very likely interlaced.
				if (step1v_corr > step2v_corr && 
					step1h_corr < (40*40*4*4) &&
					step2v_corr < (40*40*4*4))
				{
					interlaced_blocks++;
				}

				// These conditions indicate the block 
				// is very likely progressive.
				if (step1v_corr < 2 * step2v_corr && 
					step1h_corr < (40*40*4*4) &&
					step1v_corr < (40*40*4*4))
				{
					progressive_blocks++;
				}

				// Maintain totals
				step1v_tot += step1v_corr;
				step2v_tot += step2v_corr;
				step1h_tot += step1h_corr;
				step2h_tot += step2h_corr;
			}

			// Jump to the next 8x8 block
			fp += 8 * SKIP_FACTOR;
			pp += 8 * SKIP_FACTOR;
		}
	}

#if (SKIP_FACTOR != 1)
	// If we're skipping some 8x8 block, 
	// acount for this by multiplying by skip factor

	interlaced_blocks *= SKIP_FACTOR;
	progressive_blocks *= SKIP_FACTOR;

	step1v_tot *= SKIP_FACTOR;
	step2v_tot *= SKIP_FACTOR;
	step1h_tot *= SKIP_FACTOR;
	step2h_tot *= SKIP_FACTOR;

#endif

	// Scale these values. 
	// (Done to prevent overflow during later multiplies)
	step1v_tot /= image_size;
	step2v_tot /= image_size;
	step1h_tot /= image_size;
	step2h_tot /= image_size;

	// Interlaced Tests
	if (interlaced_blocks > progressive_blocks &&
		interlaced_blocks > (image_size >> 12))
	{
		return INTL_WEAK_INTERLACE;
	}

	if (step1v_tot * step2h_tot > step1h_tot * step2v_tot &&
		step1v_tot > step2v_tot &&
		2 * interlaced_blocks > progressive_blocks &&
		interlaced_blocks > (image_size >> 10))
	{
		return INTL_WEAK_INTERLACE;
	}

	// Progressive Tests
	if (progressive_blocks > (image_size >> 10) &&
		progressive_blocks > (interlaced_blocks << 4))
	{
		return INTL_WEAK_PROGRESSIVE;
	}

	return INTL_NO_DETECTION;

}

////////////////////////////////////////////////////////
//
//	Deinterlace_RGB24_Fast
//
//	Fast deinterlacing of RGB24 data, using both fields
//
////////////////////////////////////////////////////////

static void
Deinterlace_RGB24_Fast(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format)
{
	int line, pel;
	unsigned char *fp1,*pfp1;
	unsigned char *fp2,*pfp2;
	unsigned char *fp3,*pfp3;

	unsigned char *fpr;
	unsigned char *fpb;

	// Only handle RGB for now...
	if (format != INTL_FORMAT_RGB24)
		return;

	for (line = 1; line < lines - 2; line += 2)
	{
		unsigned int a,b,c,d,e,f;
		int c_nw,c_ne,c_se,c_sw;
		int diff,tmp;
		int prev_mode = INTL_LINE_REMOVE_AVG;
		int mode = INTL_LINE_REMOVE_AVG;
		int next_mode = INTL_LINE_REMOVE_AVG;

		// current frame
		fpr = frame + line * pitch;
		fp2 = fpr + pels * lines;
		fp1 = fp2 - pitch;
		fp3 = fp2 + pitch;
		fpb = fp2 + pels * lines;

		// previous frame
		if (prev_frame != 0)
		{
			pfp2 = prev_frame + pels * lines + line * pitch;
			pfp1 = pfp2 - pitch;
			pfp3 = pfp2 + pitch;
		}
		else
		{
			pfp2 = fp2;
			pfp1 = fp1;
			pfp3 = fp3;
		}

		// initialize
		for (pel = 0; pel < pels - 4; pel += 4)
		{
			// Load *next* 4 pels
			a = ((unsigned int *)fp1)[1];
			b = ((unsigned int *)fp2)[1];
			c = ((unsigned int *)fp3)[1];

			// Get corners
			c_nw = (a >> 24);
			c_ne = (a & 0xff);
			c_sw = (c >> 24);
			c_se = (c & 0xff);

			// Edge detect
			tmp = c_nw + c_ne - c_sw - c_se;
			if ((tmp < 100) && (tmp > -100))
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}

			tmp = c_ne + c_se - c_nw - c_sw;
			if (tmp > 100)
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}
			if (tmp < -100)
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}

			// Load previous pels
			d = ((unsigned int *)pfp1)[1];
			e = ((unsigned int *)pfp2)[1];
			f = ((unsigned int *)pfp3)[1];

			// Diff with previous pels
			tmp = c_nw;
			tmp -= (d >> 24);
			diff = tmp ^ (tmp >> 31);

			tmp = c_ne;
			tmp -= (d & 0xff);
			diff += tmp ^ (tmp >> 31);

			if (diff > 100)
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}

			tmp = c_sw;
			tmp -= (f >> 24);
			diff += tmp ^ (tmp >> 31);

			tmp = c_se;
			tmp -= (f & 0xff);
			diff += tmp ^ (tmp >> 31);

			if (diff > 200)
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}

			tmp = (b >> 24);
			tmp -= (e >> 24);
			diff += tmp ^ (tmp >> 31);
			tmp = (b & 0xff);
			tmp -= (e & 0xff);
			diff += tmp ^ (tmp >> 31);

			if (diff > 300)
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}

			next_mode = INTL_LINE_REMOVE_MEDIAN;

proc:
			if (mode == INTL_LINE_REMOVE_MEDIAN || prev_mode == INTL_LINE_REMOVE_MEDIAN || next_mode == INTL_LINE_REMOVE_MEDIAN)
			{
				// median
				fp2[0] = MEDIAN_3(fp1[0],fp2[0],fp3[0]);
				fp2[1] = MEDIAN_3(fp1[1],fp2[1],fp3[1]);
				fp2[2] = MEDIAN_3(fp1[2],fp2[2],fp3[2]);
				fp2[3] = MEDIAN_3(fp1[3],fp2[3],fp3[3]);

				fpr[0] = MEDIAN_3(fpr[-pitch],fpr[0],fpr[pitch]);
				fpr[1] = MEDIAN_3(fpr[-pitch+1],fpr[1],fpr[pitch+1]);
				fpr[2] = MEDIAN_3(fpr[-pitch+2],fpr[2],fpr[pitch+2]);
				fpr[3] = MEDIAN_3(fpr[-pitch+3],fpr[3],fpr[pitch+3]);

				fpb[0] = MEDIAN_3(fpb[-pitch],fpb[0],fpb[pitch]);
				fpb[1] = MEDIAN_3(fpb[-pitch+1],fpb[1],fpb[pitch+1]);
				fpb[2] = MEDIAN_3(fpb[-pitch+2],fpb[2],fpb[pitch+2]);
				fpb[3] = MEDIAN_3(fpb[-pitch+3],fpb[3],fpb[pitch+3]);

#ifdef TIME_DEINTERLACE
				num_med++;
#endif

			}
			else
			{
				// average
				a = ((unsigned int *)fp1)[0];
				c = ((unsigned int *)fp3)[0];
				((unsigned int*)fp2)[0] = (((a^c)>>1) & 0x7F7F7F7F) + (a&c);

				a = ((unsigned int *)(fpr - pitch))[0];
				c = ((unsigned int *)(fpr + pitch))[0];
				((unsigned int*)fpr)[0] = (((a^c)>>1) & 0x7F7F7F7F) + (a&c);

				a = ((unsigned int *)(fpb - pitch))[0];
				c = ((unsigned int *)(fpb + pitch))[0];
				((unsigned int*)fpb)[0] = (((a^c)>>1) & 0x7F7F7F7F) + (a&c);

#ifdef TIME_DEINTERLACE
				num_avg++;
#endif
			}

			prev_mode = mode;
			mode = next_mode;

			fp1 += 4;
			fp2 += 4;
			fp3 += 4;

			pfp1 += 4;
			pfp2 += 4;
			pfp3 += 4;

			fpr += 4;
			fpb += 4;
		}

		// last 4 pels
		if (mode == INTL_LINE_REMOVE_MEDIAN || prev_mode == INTL_LINE_REMOVE_MEDIAN)
		{
			// median
			fp2[0] = MEDIAN_3(fp1[0],fp2[0],fp3[0]);
			fp2[1] = MEDIAN_3(fp1[1],fp2[1],fp3[1]);
			fp2[2] = MEDIAN_3(fp1[2],fp2[2],fp3[2]);
			fp2[3] = MEDIAN_3(fp1[3],fp2[3],fp3[3]);

			fpr[0] = MEDIAN_3(fpr[-pitch],fpr[0],fpr[pitch]);
			fpr[1] = MEDIAN_3(fpr[-pitch+1],fpr[1],fpr[pitch+1]);
			fpr[2] = MEDIAN_3(fpr[-pitch+2],fpr[2],fpr[pitch+2]);
			fpr[3] = MEDIAN_3(fpr[-pitch+3],fpr[3],fpr[pitch+3]);

			fpb[0] = MEDIAN_3(fpb[-pitch],fpb[0],fpb[pitch]);
			fpb[1] = MEDIAN_3(fpb[-pitch+1],fpb[1],fpb[pitch+1]);
			fpb[2] = MEDIAN_3(fpb[-pitch+2],fpb[2],fpb[pitch+2]);
			fpb[3] = MEDIAN_3(fpb[-pitch+3],fpb[3],fpb[pitch+3]);

#ifdef TIME_DEINTERLACE
			num_med++;
#endif

		}
		else
		{
			// average
			a = ((unsigned int *)fp1)[0];
			c = ((unsigned int *)fp3)[0];
			((unsigned int*)fp2)[0] = (((a^c)>>1) & 0x7F7F7F7F) + (a&c);

			a = ((unsigned int *)(fpr - pitch))[0];
			c = ((unsigned int *)(fpr + pitch))[0];
			((unsigned int*)fpr)[0] = (((a^c)>>1) & 0x7F7F7F7F) + (a&c);

			a = ((unsigned int *)(fpb - pitch))[0];
			c = ((unsigned int *)(fpb + pitch))[0];
			((unsigned int*)fpb)[0] = (((a^c)>>1) & 0x7F7F7F7F) + (a&c);

#ifdef TIME_DEINTERLACE
			num_avg++;
#endif
		}
	}

}


////////////////////////////////////////////////////////
//
//	Deinterlace_RGB24
//
//	Slow deinterlacing of RGB24 data, using both fields
//
////////////////////////////////////////////////////////

static void
Deinterlace_RGB24(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format)
{
	int line, pel;
	unsigned char *fp1,*pfp1;
	unsigned char *fp2,*pfp2;
	unsigned char *fp3,*pfp3;
	int tmp;

	// Only handle RGB for now...
	if (format != INTL_FORMAT_RGB24)
		return;

	for (line = 1; line < lines - 2; line += 2)
	{
		int a,b,c,d,e;
		int mode = INTL_LINE_REMOVE_AVG;

		fp1 = frame + line * pitch;
		fp2 = frame + pels*lines + line * pitch;
		fp3 = frame + 2 * pels*lines + line * pitch;

		if (prev_frame != 0)
		{
			pfp1 = prev_frame + line * pitch;
			pfp2 = prev_frame + pels*lines + line * pitch;
			pfp3 = prev_frame + 2 * pels*lines + line * pitch;
		}
		else
		{
			pfp1 = fp1;
			pfp2 = fp2;
			pfp3 = fp3;
		}

		// initialize
		tmp = (int)(fp1[-pitch]) + (int)(fp2[-pitch]) + (int)(fp3[-pitch]) - (int)(pfp1[-pitch]) - (int)(pfp2[-pitch]) - (int)(pfp3[-pitch]);
		a = DIFF_FCN(tmp);
		tmp = (int)(fp1[0]) + (int)(fp2[0]) + (int)(fp3[0]) - (int)(pfp1[0]) - (int)(pfp2[0]) - (int)(pfp3[0]);
		a += DIFF_FCN(tmp);
		tmp = (int)(fp1[pitch]) + (int)(fp2[pitch]) + (int)(fp3[pitch]) - (int)(pfp1[pitch]) - (int)(pfp2[pitch]) - (int)(pfp3[pitch]);
		a += DIFF_FCN(tmp);

		fp1++;pfp1++;
		fp2++;pfp2++;
		fp3++;pfp3++;

		tmp = (int)(fp1[-pitch]) + (int)(fp2[-pitch]) + (int)(fp3[-pitch]) - (int)(pfp1[-pitch]) - (int)(pfp2[-pitch]) - (int)(pfp3[-pitch]);
		b = DIFF_FCN(tmp);
		tmp = (int)(fp1[0]) + (int)(fp2[0]) + (int)(fp3[0]) - (int)(pfp1[0]) - (int)(pfp2[0]) - (int)(pfp3[0]);
		b += DIFF_FCN(tmp);
		tmp = (int)(fp1[pitch]) + (int)(fp2[pitch]) + (int)(fp3[pitch]) - (int)(pfp1[pitch]) - (int)(pfp2[pitch]) - (int)(pfp3[pitch]);
		b += DIFF_FCN(tmp);

		fp1++;pfp1++;
		fp2++;pfp2++;
		fp3++;pfp3++;

		tmp = (int)(fp1[-pitch]) + (int)(fp2[-pitch]) + (int)(fp3[-pitch]) - (int)(pfp1[-pitch]) - (int)(pfp2[-pitch]) - (int)(pfp3[-pitch]);
		c = DIFF_FCN(tmp);
		tmp = (int)(fp1[0]) + (int)(fp2[0]) + (int)(fp3[0]) - (int)(pfp1[0]) - (int)(pfp2[0]) - (int)(pfp3[0]);
		c += DIFF_FCN(tmp);
		tmp = (int)(fp1[pitch]) + (int)(fp2[pitch]) + (int)(fp3[pitch]) - (int)(pfp1[pitch]) - (int)(pfp2[pitch]) - (int)(pfp3[pitch]);
		c += DIFF_FCN(tmp);

		tmp = (int)(fp1[-pitch+1]) + (int)(fp2[-pitch+1]) + (int)(fp3[-pitch+1]) - (int)(pfp1[-pitch+1]) - (int)(pfp2[-pitch+1]) - (int)(pfp3[-pitch+1]);
		d = DIFF_FCN(tmp);
		tmp = (int)(fp1[1]) + (int)(fp2[1]) + (int)(fp3[1]) - (int)(pfp1[1]) - (int)(pfp2[1]) - (int)(pfp3[1]);
		d += DIFF_FCN(tmp);
		tmp = (int)(fp1[pitch+1]) + (int)(fp2[pitch+1]) + (int)(fp3[pitch+1]) - (int)(pfp1[pitch+1]) - (int)(pfp2[pitch+1]) - (int)(pfp3[pitch+1]);
		d += DIFF_FCN(tmp);

		for (pel = 2; pel < pels - 2; pel++)
		{
			tmp = (int)(fp1[-pitch+2]) + (int)(fp2[-pitch+2]) + (int)(fp3[-pitch+2]) - (int)(pfp1[-pitch+2]) - (int)(pfp2[-pitch+2]) - (int)(pfp3[-pitch+2]);
			e = DIFF_FCN(tmp);
			tmp = (int)(fp1[2]) + (int)(fp2[2]) + (int)(fp3[2]) - (int)(pfp1[2]) - (int)(pfp2[2]) - (int)(pfp3[2]);
			e += DIFF_FCN(tmp);
			tmp = (int)(fp1[pitch+2]) + (int)(fp2[pitch+2]) + (int)(fp3[pitch+2]) - (int)(pfp1[pitch+2]) - (int)(pfp2[pitch+2]) - (int)(pfp3[pitch+2]);
			e += DIFF_FCN(tmp);

			if (mode == INTL_LINE_REMOVE_MEDIAN)
			{
				if (a + b + c + d + e > INTL_DIFF_THRESH)
				{
					a =  MABS(fp2[-pitch-2] - fp2[-pitch+1]);
					a += MABS(fp2[pitch-2] - fp2[pitch+1]);
					a += MABS(fp2[-pitch-1] - fp2[-pitch+2]);
					a += MABS(fp2[pitch-1] - fp2[pitch+2]);
					a <<= 1;
					a -= MABS(fp2[-pitch-2] - fp2[pitch-2]);
					a -= MABS(fp2[-pitch-1] - fp2[pitch-1]);
					a -= MABS(fp2[-pitch+1] - fp2[pitch+1]);
					a -= MABS(fp2[-pitch+2] - fp2[pitch+2]);

					if (a > 0)
					{
						mode = INTL_LINE_REMOVE_AVG;
					}
				}
			}
			else
			{
				if (a + b + c + d + e < INTL_DIFF_THRESH)
				{
					a =  MABS(fp2[-pitch-2] - fp2[-pitch+1]);
					a += MABS(fp2[pitch-2] - fp2[pitch+1]);
					a += MABS(fp2[-pitch-1] - fp2[-pitch+2]);
					a += MABS(fp2[pitch-1] - fp2[pitch+2]);
					a <<= 1;
					a -= MABS(fp2[-pitch-2] - fp2[pitch-2]);
					a -= MABS(fp2[-pitch-1] - fp2[pitch-1]);
					a -= MABS(fp2[-pitch+1] - fp2[pitch+1]);
					a -= MABS(fp2[-pitch+2] - fp2[pitch+2]);

					if (a < 0)
					{
						mode = INTL_LINE_REMOVE_MEDIAN;
					}
				}
			}
				
			if (mode == INTL_LINE_REMOVE_MEDIAN)
			{
				// median
				fp1[0] = MEDIAN_3(fp1[-pitch],fp1[0],fp1[pitch]);
				fp2[0] = MEDIAN_3(fp2[-pitch],fp2[0],fp2[pitch]);
				fp3[0] = MEDIAN_3(fp3[-pitch],fp3[0],fp3[pitch]);
			}
			else
			{
				// average
				tmp = fp1[-pitch];
				tmp += fp1[pitch];
				fp1[0] = (tmp >> 1);
				tmp = fp2[-pitch];
				tmp += fp2[pitch];
				fp2[0] = (tmp >> 1);
				tmp = fp3[-pitch];
				tmp += fp3[pitch];
				fp3[0] = (tmp >> 1);
			}
				
			a = b;
			b = c;
			c = d;
			d = e;

			fp1++;pfp1++;
			fp2++;pfp2++;
			fp3++;pfp3++;

		}
	}
}


////////////////////////////////////////////////////////
//
//	Deinterlace_I420_Fast
//
//	Fast deinterlacing of I420 data, using both fields
//
////////////////////////////////////////////////////////

static void
Deinterlace_I420_Fast(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format)
{
	int line, pel;
	unsigned char *fp1,*pfp1;
	unsigned char *fp2,*pfp2;
	unsigned char *fp3,*pfp3;

	// Only handle RGB for now...
	if (format != INTL_FORMAT_I420)
		return;

	for (line = 1; line < lines - 2; line += 2)
	{
		unsigned int a,b,c,d,e,f;
		int c_nw,c_ne,c_se,c_sw;
		int diff,tmp;
		int prev_mode = INTL_LINE_REMOVE_AVG;
		int mode = INTL_LINE_REMOVE_AVG;
		int next_mode = INTL_LINE_REMOVE_AVG;

		// current frame
		fp2 = frame + line * pitch;
		fp1 = fp2 - pitch;
		fp3 = fp2 + pitch;

		// previous frame
		if (prev_frame != 0)
		{
			pfp2 = prev_frame + line * pitch;
			pfp1 = pfp2 - pitch;
			pfp3 = pfp2 + pitch;
		}
		else
		{
			pfp2 = fp2;
			pfp1 = fp1;
			pfp3 = fp3;
		}

		// initialize
		for (pel = 0; pel < pels - 4; pel += 4)
		{
			// Load *next* 4 pels
			a = ((unsigned int *)fp1)[1];
			b = ((unsigned int *)fp2)[1];
			c = ((unsigned int *)fp3)[1];

			// Get corners
			c_nw = (a >> 24);
			c_ne = (a & 0xff);
			c_sw = (c >> 24);
			c_se = (c & 0xff);

			// Edge detect
			tmp = c_nw + c_ne - c_sw - c_se;
			if ((tmp < 100) && (tmp > -100))
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}

			tmp = c_ne + c_se - c_nw - c_sw;
			if (tmp > 100)
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}
			if (tmp < -100)
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}

			// Load previous pels
			d = ((unsigned int *)pfp1)[1];
			e = ((unsigned int *)pfp2)[1];
			f = ((unsigned int *)pfp3)[1];

			// Diff with previous pels
			tmp = c_nw;
			tmp -= (d >> 24);
			diff = tmp ^ (tmp >> 31);

			tmp = c_ne;
			tmp -= (d & 0xff);
			diff += tmp ^ (tmp >> 31);

			if (diff > 100)
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}

			tmp = c_sw;
			tmp -= (f >> 24);
			diff += tmp ^ (tmp >> 31);

			tmp = c_se;
			tmp -= (f & 0xff);
			diff += tmp ^ (tmp >> 31);

			if (diff > 200)
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}

			tmp = (b >> 24);
			tmp -= (e >> 24);
			diff += tmp ^ (tmp >> 31);
			tmp = (b & 0xff);
			tmp -= (e & 0xff);
			diff += tmp ^ (tmp >> 31);

			if (diff > 300)
			{
				next_mode = INTL_LINE_REMOVE_AVG;
				goto proc;
			}

			next_mode = INTL_LINE_REMOVE_MEDIAN;

proc:
			if (mode == INTL_LINE_REMOVE_MEDIAN || prev_mode == INTL_LINE_REMOVE_MEDIAN || next_mode == INTL_LINE_REMOVE_MEDIAN)
			{
				// median
				fp2[0] = MEDIAN_3(fp1[0],fp2[0],fp3[0]);
				fp2[1] = MEDIAN_3(fp1[1],fp2[1],fp3[1]);
				fp2[2] = MEDIAN_3(fp1[2],fp2[2],fp3[2]);
				fp2[3] = MEDIAN_3(fp1[3],fp2[3],fp3[3]);

#ifdef TIME_DEINTERLACE
				num_med++;
#endif

			}
			else
			{
				// average
				a = ((unsigned int *)fp1)[0];
				c = ((unsigned int *)fp3)[0];
				((unsigned int*)fp2)[0] = (((a^c)>>1) & 0x7F7F7F7F) + (a&c);

#ifdef TIME_DEINTERLACE
				num_avg++;
#endif
			}

			prev_mode = mode;
			mode = next_mode;

			fp1 += 4;
			fp2 += 4;
			fp3 += 4;

			pfp1 += 4;
			pfp2 += 4;
			pfp3 += 4;
		}

		// last 4 pels
		if (mode == INTL_LINE_REMOVE_MEDIAN || prev_mode == INTL_LINE_REMOVE_MEDIAN)
		{
			// median
			fp2[0] = MEDIAN_3(fp1[0],fp2[0],fp3[0]);
			fp2[1] = MEDIAN_3(fp1[1],fp2[1],fp3[1]);
			fp2[2] = MEDIAN_3(fp1[2],fp2[2],fp3[2]);
			fp2[3] = MEDIAN_3(fp1[3],fp2[3],fp3[3]);

#ifdef TIME_DEINTERLACE
			num_med++;
#endif

		}
		else
		{
			// average
			a = ((unsigned int *)fp1)[0];
			c = ((unsigned int *)fp3)[0];
			((unsigned int*)fp2)[0] = (((a^c)>>1) & 0x7F7F7F7F) + (a&c);

#ifdef TIME_DEINTERLACE
			num_avg++;
#endif
		}
	}
}

////////////////////////////////////////////////////////
//
//	Deinterlace_I420_Advanced
//
//	Slow deinterlacing of I420 data, using Edge Interpolation
//
////////////////////////////////////////////////////////

void
Deinterlace_I420_Advanced(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format)
{
	int line, pel;
	unsigned char *fpy, *pfpy;
	unsigned char *fp1, *pfp1;
	unsigned char *fp2, *pfp2;
	unsigned char *fp3, *pfp3;
	
	int edge1, edge2, pedge1, pedge2, diff1, diff2;
	unsigned int a, c;

	int tmp, pitch2;
	pitch2= pitch<<1;

	for (line = 1; line < lines - 2; line += 2)
	{
	
		fpy = frame + line*pitch;				
		
		// current frame
		// current line
		fp2 = fpy;
		// prev line
		fp1 = fp2 - pitch;
		// next line
		fp3 = fp2 + pitch;
		// current frame

		// previous frame
		if (prev_frame != 0)
		{
			pfpy = prev_frame + line * pitch;
			pfp2 = pfpy;
			pfp1 = pfp2 - pitch;
			pfp3 = pfp2 + pitch;
		}
		else
		{			
			pfp2 = fp2;
			pfp1 = fp1;
			pfp3 = fp3;
		}
				
		// handle first pel
		tmp = (*fp1 + *fp3) >> 1;
		*fp2 = (unsigned char) tmp;
		fp1++;
		fp2++;
		fp3++;
		pfp1++;

		edge1 = (*fp1) + *(fp1+1) +*(fp1+2);
		edge2=  (*fp3) + *(fp3+1) +*(fp3+2);
				
		for (pel = 1; pel < pels-4; pel += 1)
		{					
			edge1+=*(fp1+3);
			edge2+=*(fp3+3);
			tmp = (edge1 - edge2);

			if( tmp > 160 || tmp < -160) {
				// horiz
				pfp3 = pfp1 + pitch2;
				pedge1= *pfp1 + *(pfp1+1) + *(pfp1+2) + *(pfp1+3);
				pedge2= *pfp3 + *(pfp3+1) + *(pfp3+2) + *(pfp3+3);				
				
				diff1 = (edge1 - pedge1);				
				if (diff1 < 0) diff1=-(diff1);
				//diff1 = ABS(diff1);
				diff2 = (edge2  - pedge2);
				if (diff2 < 0) diff2=-(diff2);
				//diff2 = ABS(diff2);

				if ((diff1 + diff2) < 200) {
					// not moving					
					*fp2 = MEDIAN_3(*fp1,*fp2,*fp3);
					*(fp2+1) = MEDIAN_3(*(fp1+1),*(fp2+1),*(fp3+1));
					
#ifdef TIME_DEINTERLACE
						num_med++;
#endif				
					edge1 -= *fp1;
					edge2 -= *fp3;					
					fp1++;
					fp2++;
					fp3++;
					pfp1++;					
					pel++;
					edge1+=*(fp1+3);
					edge2+=*(fp3+3);					
				} else {		
					tmp = *(fp2-1) + *fp1 + *fp3;	
					tmp *= 21845;
					*fp2 = 	(unsigned char) (tmp >> 16);
					//tmp = (*(fp2-1)<<1) + *fp1 + *fp3;	
					//*fp2 = 	(unsigned char) (tmp >> 2);
#ifdef TIME_DEINTERLACE
						num_avg++;
#endif									
				}	
			} else {
				// not horiz				
				// average
				tmp = (*fp1 + *fp3) >> 1;
				*fp2 = (unsigned char) tmp;
				
#ifdef TIME_DEINTERLACE
					num_avg++;
#endif				
			}			
			edge1 -= (*fp1);
			edge2 -= (*fp3);					
			fp1++;
			fp2++;
			fp3++;
			pfp1++;			
		}
		// handle last four pels
		// last 4 pels
		pfp3 = pfp1 + pitch2;
		pedge1= *pfp1 + *(pfp1+1) + *(pfp1+2) + *(pfp1+3);
		pedge2= *pfp3 + *(pfp3+1) + *(pfp3+2) + *(pfp3+3);		
			
		diff1 = (edge1 - pedge1);
		diff1 = ABS(diff1);
		diff2 = (edge2  - pedge2);
		diff2 = ABS(diff2);
		if ((diff1 + diff2) < 200) {
					// not moving					
					// median
				fp2[0] = MEDIAN_3(fp1[0],fp2[0],fp3[0]);
				fp2[1] = MEDIAN_3(fp1[1],fp2[1],fp3[1]);
				fp2[2] = MEDIAN_3(fp1[2],fp2[2],fp3[2]);
				fp2[3] = MEDIAN_3(fp1[3],fp2[3],fp3[3]);

#ifdef TIME_DEINTERLACE
					num_med++;
#endif
		}
		else
		{
			// average
			a = ((unsigned int *)fp1)[0];
			c = ((unsigned int *)fp3)[0];
			((unsigned int*)fp2)[0] = (((a^c)>>1) & 0x7F7F7F7F) + (a&c);

#ifdef TIME_DEINTERLACE
				num_avg++;
#endif
		}
	}
	
}	


////////////////////////////////////////////////////////
//
//	Deinterlace_I420
//
//	Slow deinterlacing of I420 data, using both fields
//
////////////////////////////////////////////////////////

static void
Deinterlace_I420(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format)
{
	int line, pel;
	unsigned char *fp1,*pfp1;			// line above
	unsigned char *fp2,*pfp2;			// current line
	unsigned char *fp3,*pfp3;			// line below
	int tmp;

	// Only handle RGB for now...
	if (format != INTL_FORMAT_I420)
		return;

	for (line = 1; line < lines - 2; line += 2)
	{
		int a,b,c,d,e;
		int mode = INTL_LINE_REMOVE_AVG;

		// set pointers
		fp1 = frame + (line - 1) * pitch;
		fp2 = fp1 + pitch;
		fp3 = fp2 + pitch;

		if (prev_frame != 0)
		{
			pfp2 = prev_frame + line * pitch;
			pfp1 = pfp1 - pitch;
			pfp3 = pfp2 + pitch;
		}
		else
		{
			pfp2 = fp2;
			pfp1 = fp1;
			pfp3 = fp3;
		}

		// initialize
		// a
		tmp = (int)(*fp1++);
		tmp -= (int)(*pfp1++);
		a = DIFF_FCN(tmp);
		tmp = (int)(*fp2++);
		tmp -= (int)(*pfp2++);
		a += DIFF_FCN(tmp);
		tmp = (int)(*fp3++);
		tmp -= (int)(*pfp3++);
		a += DIFF_FCN(tmp);

		// b
		tmp = (int)(*fp1++);
		tmp -= (int)(*pfp1++);
		b = DIFF_FCN(tmp);
		tmp = (int)(*fp2++);
		tmp -= (int)(*pfp2++);
		b += DIFF_FCN(tmp);
		tmp = (int)(*fp3++);
		tmp -= (int)(*pfp3++);
		b += DIFF_FCN(tmp);

		// c
		tmp = (int)(*fp1);
		tmp -= (int)(*pfp1);
		c = DIFF_FCN(tmp);
		tmp = (int)(*fp2);
		tmp -= (int)(*pfp2);
		c += DIFF_FCN(tmp);
		tmp = (int)(*fp3);
		tmp -= (int)(*pfp3);
		c += DIFF_FCN(tmp);

		// d
		tmp = (int)(fp1[1]);
		tmp -= (int)(pfp1[1]);
		d = DIFF_FCN(tmp);
		tmp = (int)(fp2[1]);
		tmp -= (int)(pfp2[1]);
		d += DIFF_FCN(tmp);
		tmp = (int)(fp3[1]);
		tmp -= (int)(pfp3[1]);
		d += DIFF_FCN(tmp);

		for (pel = 2; pel < pels - 2; pel++)
		{
			// e
			tmp = (int)(fp1[2]);
			tmp -= (int)(pfp1[2]);
			e = DIFF_FCN(tmp);
			tmp = (int)(fp2[2]);
			tmp -= (int)(pfp2[2]);
			e += DIFF_FCN(tmp);
			tmp = (int)(fp3[2]);
			tmp -= (int)(pfp3[2]);
			e += DIFF_FCN(tmp);

			// should we switch line removal mode?
			if (mode == INTL_LINE_REMOVE_MEDIAN)
			{
				if (a + b + c + d + e > INTL_DIFF_THRESH)
				{
					a =  MABS(fp1[-2] - fp1[+1]);
					a += MABS(fp3[-2] - fp3[+1]);
					a += MABS(fp1[-1] - fp1[+2]);
					a += MABS(fp3[-1] - fp3[+2]);
					a <<= 1;
					a += MABS(fp1[-2] - fp3[-2]);
					a += MABS(fp1[-1] - fp3[-1]);
					a += MABS(fp1[+1] - fp3[+1]);
					a += MABS(fp1[+2] - fp3[+2]);
					if (a > 0)
					{
						mode = INTL_LINE_REMOVE_AVG;
					}
				}
			}
			else
			{
				if (a + b + c + d + e < INTL_DIFF_THRESH)
				{
					a =  MABS(fp1[-2] - fp1[+1]);
					a += MABS(fp3[-2] - fp3[+1]);
					a += MABS(fp1[-1] - fp1[+2]);
					a += MABS(fp3[-1] - fp3[+2]);
					a <<= 1;
					a += MABS(fp1[-2] - fp3[-2]);
					a += MABS(fp1[-1] - fp3[-1]);
					a += MABS(fp1[+1] - fp3[+1]);
					a += MABS(fp1[+2] - fp3[+2]);
					if (a < 0)
					{
						mode = INTL_LINE_REMOVE_MEDIAN;
					}
				}
			}
				
			if (mode == INTL_LINE_REMOVE_MEDIAN)
			{
				// median
				fp2[0] = MEDIAN_3(fp1[0],fp2[0],fp3[0]);
			}
			else
			{
				// average
				tmp = fp1[0];
				tmp += fp3[0];
				fp2[0] = (tmp >> 1);
			}
			
			// shift difference measures
			a = b;
			b = c;
			c = d;
			d = e;

			// move pointers
			fp1++;pfp1++;
			fp2++;pfp2++;
			fp3++;pfp3++;

		}
	}
}



////////////////////////////////////////////////////////
//
//	Deinterlace_RGB24_Field
//
//	Replaces 'frame' with only one field.
//  The result is an image with dimesions pels x lines/2
//
////////////////////////////////////////////////////////

static void
Deinterlace_RGB24_Field(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format, int field)
{
	int line;
	unsigned char *in,*out;

	lines >>= 1;

	// R
	in  = frame + field * pitch;
	out = frame;
	for (line = field; line < lines; line++)
	{
		memcpy(out, in, pels); /* Flawfinder: ignore */
		out += pitch;
		in  += (pitch << 1);
	}

	// G
	in  = frame + pels * (lines << 1);
	out = frame + pels * lines;
	for (line = 0; line < lines; line++)
	{
		memcpy(out, in, pels); /* Flawfinder: ignore */
		out += pitch;
		in  += (pitch << 1);
	}

	// B
	in  = frame + pels * (lines << 1) * 2;
	out = frame + pels * lines * 2;
	for (line = 0; line < lines; line++)
	{
		memcpy(out, in, pels); /* Flawfinder: ignore */
		out += pitch;
		in  += (pitch << 1);
	}
}



////////////////////////////////////////////////////////
//
//	Deinterlace_I420_Field
//
//	Replaces 'frame' with only one field.
//  The result is an image with dimesions pels x lines/2
//
////////////////////////////////////////////////////////

static void
Deinterlace_I420_Field(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format, int field)
{
	register int line;
	register unsigned char *in,*out;
	int in_pitch;
	int out_pitch;
	int out_lines;

	// Y
	in_pitch = (pitch << 1);
	out_pitch = pitch;
	out_lines = (lines >> 1) & ~3;

	in  = frame + field * pitch;
	out = frame;
	for (line = 0; line < out_lines; line++)
	{
		memcpy(out, in, pels); /* Flawfinder: ignore */
		out += out_pitch;
		in  += in_pitch;
	}

	pels >>= 1;
	lines >>= 1;
	in_pitch >>= 1;
	out_pitch >>= 1;
	out_lines >>= 1;

	// U
	in  = frame + ((pels * lines) << 2);
	out = frame + ((pels * out_lines) << 2);
	for (line = 0; line < out_lines; line++)
	{
		memcpy(out, in, pels); /* Flawfinder: ignore */
		out += out_pitch;
		in  += in_pitch;
	}

	// V
	in  = frame + pels * lines * 5;
	out = frame + pels * out_lines * 5;
	for (line = 0; line < out_lines; line++)
	{
		memcpy(out, in, pels); /* Flawfinder: ignore */
		out += out_pitch;
		in  += in_pitch;
	}
}



////////////////////////////////////////////////////////
//
//	Deinterlace_I420_FlipFlop
//
//	The Odd field is put at the top of the frame,
//  the Even field is put up-side-down at the bottom.
//
////////////////////////////////////////////////////////

static void
Deinterlace_I420_FlipFlop(unsigned char *frame, unsigned char *tempFrame, int pels, int lines, int pitch, int format)
{
	int lineCounter;
	
	//  reorder the frame, even field on top, Y, Cr then Cb
	for(lineCounter=0;lineCounter<lines;lineCounter+=2)
	{
		memcpy(tempFrame+((lineCounter>>1)*(pels)), /* Flawfinder: ignore */
			frame+lineCounter*pels,pels);
	}
	for(lineCounter=0;lineCounter<(lines>>1);lineCounter+=2)
	{
		memcpy(tempFrame+((lineCounter>>1)*(pels>>1)) + (pels*lines) /* Flawfinder: ignore */
			,frame+(pels*lines)+lineCounter*(pels>>1),(pels>>1));
	}
	for(lineCounter=0;lineCounter<(lines>>1);lineCounter+=2)
	{
		memcpy(tempFrame+((lineCounter>>1)*(pels>>1)) +(pels*lines*5/4) /* Flawfinder: ignore */
			,frame+(pels*lines*5/4)+lineCounter*(pels>>1),(pels>>1));
	}
	for(lineCounter=1;lineCounter<lines+1;lineCounter+=2)
	{
		memcpy(tempFrame+((lines-1)-(lineCounter>>1))*pels, /* Flawfinder: ignore */
			frame+lineCounter*pels,pels);
	}
	for(lineCounter=1;lineCounter<(lines>>1)+1;lineCounter+=2)
	{
		memcpy(tempFrame + (pels*lines) + /* Flawfinder: ignore */
			(((lines>>1)-1)-(lineCounter>>1))*(pels>>1),frame+(pels*lines)+lineCounter*(pels>>1),(pels>>1));
	}
	for(lineCounter=1;lineCounter<(lines>>1)+1;lineCounter+=2)
	{
		memcpy(tempFrame + (pels*lines*5/4) + /* Flawfinder: ignore */
			(((lines>>1)-1)-(lineCounter>>1))*(pels>>1),frame+(pels*lines*5/4)+lineCounter*(pels>>1),(pels>>1));
	}
	memcpy(frame,tempFrame,(pels*lines*3)/2); /* Flawfinder: ignore */

	return;
}


#ifdef _M_IX86

////////////////////////////////////////////////////////
//
//	Deinterlace_I420_MMX
//
//	MMX optimized deinterlacing of I420 data, using both fields
//
////////////////////////////////////////////////////////

static void
Deinterlace_I420_MMX(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format)
{
	int line_shift;
	int pels_div8;

	pels_div8 = (pels >> 3);			// round down
	line_shift = ((pitch << 1) - (pels & ~7));

	__asm {

		mov			ecx, lines			// ecx -> lines		
		shr			ecx, 1				// ecx -> lines/2

		mov			edx, pitch			// pitch
		mov			eax, edx
		neg			eax					// -pitch

		mov			esi, frame			// esi -> current frame
		add			esi, pitch;			// move esi to the next line

		pxor		mm7, mm7

ALIGN 16
line_loop:
		mov			ebx, pels_div8		// ebx -> pels/8 (rounded down)
ALIGN 16
pel_loop:
										// median = (upper & A) | (mid & B) | (lower & C)
										// e.g....
		movq		mm0,[esi+eax]		//	1	2	1	3	2	3	1	3	= mm0	= (upper)
		movq		mm1,[esi]			//	2	1	3	1	3	2	1	3	= mm1	= (mid)
		movq		mm2,[esi+edx]		//	3	3	2	2	1	1	2	3	= mm2	= (lower)

		movq		mm3,mm1				//	2	1	3	1	3	2	1	3	= mm3
		psubusb		mm3,mm0				//	1	0	2	0	1	0	0	0	= mm3	
		pcmpeqb		mm3,mm7				//	0	1	0	1	0	1	1	1	= mm3	= (upper > mid)

		movq		mm4,mm2				//	3	3	2	2	1	1	2	3	= mm4	
		psubusb		mm4,mm1				//	1	2	0	1	0	0	1	0	= mm4
		pcmpeqb		mm4,mm7				//	0	0	1	0	1	1	0	1	= mm4	= (mid > lower)

		movq		mm6,mm3				//	0	1	0	1	0	1	1	1	= mm6
		pxor		mm6,mm4				//	0	1	1	1	1	0	1	0	= mm6	= ~(B)
		pandn		mm6,mm1				//	2	0	0	0	0	2	1	0	= mm6	= (mid & B)

		movq		mm5,mm2				//	3	3	2	2	1	1	2	3	= mm5	
		psubusb		mm5,mm0				//	2	1	1	0	0	0	1	0	= mm5
		pcmpeqb		mm5,mm7				//	0	0	0	1	1	1	0	1	= mm5	= (upper > lower)

		movq		mm1,mm5				//	0	0	0	1	1	1	0	1	= mm1
		pxor		mm1,mm3				//	0	1	0	0	1	0	1	0	= mm1	= (A)
		pand		mm1,mm0				//	0	2	0	0	2	0	1	0	= mm1	= (upper & A)

		por  		mm6,mm1				//	2	2	0	0	2	2	1	0	= mm6	= (upper & A) | (mid & B)

		pxor		mm4,mm5				//	0	0	1	1	0	0	0	1	= mm4	= (C)
		pand		mm4,mm2				//	0	0	2	2	0	0	0	3	= mm4	= (lower & C)

		por  		mm6,mm4				//	2	2	2	2	2	2	1	3	= mm6	= (upper & A) | (mid & B) | (lower & C)

		movq		[esi],mm6			

		lea			esi,[esi+8]		

		dec			ebx
		jnz			pel_loop

		// any pels left???
		mov			ebx, pels
		and			ebx, 0x07
		jnz			last_pels

		add			esi, line_shift;	// move esi to the next line

		dec			ecx
		jnz			line_loop
		jmp			done

last_pels:
		// take care of last four pels
		movd		mm0,[esi+eax]
		movd		mm1,[esi]
		movd		mm2,[esi+edx]

		movq		mm3,mm1				//	2	1	3	1	3	2	1	3	= mm3
		psubusb		mm3,mm0				//	1	0	2	0	1	0	0	0	= mm3	
		pcmpeqb		mm3,mm7				//	0	1	0	1	0	1	1	1	= mm3	= (upper > mid)

		movq		mm4,mm2				//	3	3	2	2	1	1	2	3	= mm4	
		psubusb		mm4,mm1				//	1	2	0	1	0	0	1	0	= mm4
		pcmpeqb		mm4,mm7				//	0	0	1	0	1	1	0	1	= mm4	= (mid > lower)

		movq		mm6,mm3				//	0	1	0	1	0	1	1	1	= mm6
		pxor		mm6,mm4				//	0	1	1	1	1	0	1	0	= mm6	= ~(B)
		pandn		mm6,mm1				//	2	0	0	0	0	2	1	0	= mm6	= (mid & B)

		movq		mm5,mm2				//	3	3	2	2	1	1	2	3	= mm5	
		psubusb		mm5,mm0				//	2	1	1	0	0	0	1	0	= mm5
		pcmpeqb		mm5,mm7				//	0	0	0	1	1	1	0	1	= mm5	= (upper > lower)

		movq		mm1,mm5				//	0	0	0	1	1	1	0	1	= mm1
		pxor		mm1,mm3				//	0	1	0	0	1	0	1	0	= mm1	= (A)
		pand		mm1,mm0				//	0	2	0	0	2	0	1	0	= mm1	= (upper & A)

		por  		mm6,mm1				//	2	2	0	0	2	2	1	0	= mm6	= (upper & A) | (mid & B)

		pxor		mm4,mm5				//	0	0	1	1	0	0	0	1	= mm4	= (C)
		pand		mm4,mm2				//	0	0	2	2	0	0	0	3	= mm4	= (lower & C)

		por  		mm6,mm4				//	2	2	2	2	2	2	1	3	= mm6	= (upper & A) | (mid & B) | (lower & C)

		movd		[esi],mm6			

		add			esi, line_shift;	// move esi to the next line

		dec			ecx
		jnz			line_loop

done:
		emms
	}
}


////////////////////////////////////////////////////////
//
//	Deinterlace_I420_Slow
//
//	Slow deinterlacing of I420 data, 
//  with Rigorous Edge Interpolation using 
//  Optical Flow
//
////////////////////////////////////////////////////////
static void
Deinterlace_I420_EdgeInterp(unsigned char *frame, unsigned char *prev_frame, int pels, int lines, int pitch, int format)
{
	int line, pel, mcountfr=1, mcountfi=1, D1=0;
	unsigned char *fpr, *fpg, *pfpg, *pfpr;
	unsigned char *fp1, *pfp1;
	unsigned char *fp2, *pfp2;
	unsigned char *fp3, *pfp3;
	# define IFrMThresh 10
	# define IFsMThresh 20
	# define FlowThresh 1200
	# define StrictFlowThresh 5	
	# define adj 10
	int frm =1;
	int diff1;
	
	int tmp;
	int *tempLine, *ptempLine;
	tempLine = (int*)malloc(pels*sizeof(int));		

	for (line = 1; line < lines - 2; line += 2)
	{
	
		int a,b,c,d,e,f;
		int D1 =0;		
		unsigned char *tfp3;
		memset(tempLine, -1, pels*sizeof(int));

		fpr = frame + line*pitch;
		
		fpg = fpr;

		
		fp2 = fpg + adj;
		
		ptempLine = &tempLine[0] + adj;


		// prev line
		fp1 = fp2 - pitch;
		
		// next line
		fp3 = fp2 + pitch;
		
		// previous frame
		if (prev_frame != 0)
		{
			pfpr = prev_frame + line * pitch;
			pfpg = pfpr;
			pfp2 = pfpg + adj;
			pfp1 = pfp2 - pitch;
			pfp3 = pfp2 + pitch;
		}
		else
		{
			Deinterlace_RGB24_Fast(frame, prev_frame, pels, lines, pitch, format);
			free(tempLine);
			return;
		}

		
		// initialize
		for (pel = adj; pel < (pels - adj); pel += 1)
		{
			
			int moving = 0;
			int fi, fD;
			int minf;
			int pix;
			int error2, error21, error22, error3, d2=0, j;

			// Load *next* pels
			a = (int)(*fp1);
			b = (int)(*fp2);
			c = (int)(*fp3);

			// intra frame
			D1 = *(fp1+1) - *(fp1-1);
			D1 = ABS(D1);
			if( D1 > 20) { 
					d = *(fp1+1);
					e = *(fp1-1);
					tfp3 = fp3;
					tmp = (a - *(tfp3));					
					minf = DIFF_FCN(tmp);
					tmp = (d - *(tfp3+1));
					minf += DIFF_FCN(tmp);
					tmp = (e - *(tfp3-1));
					minf += DIFF_FCN(tmp);

					fi = 0;
					tfp3--;					
					for (j=-1; j>=-adj; j--) {	
						tmp = (a - *(tfp3));
						fD = DIFF_FCN(tmp);
						if (fD > minf)
							goto gskip;
						tmp = (d - *(tfp3+1));
						fD= fD + DIFF_FCN(tmp);
						if (fD > minf)
							goto gskip;
						tmp = (e - *(tfp3-1));
						fD= fD + DIFF_FCN(tmp);
						if(fD<minf) {
							minf = fD;
							fi = j;
						}
gskip:
						tfp3--;						
					}

					tfp3 = fp3 + 1;
					for (j=1; j<=adj; j++) {	
						tmp = (a - *(tfp3));
						fD = DIFF_FCN(tmp);
						if (fD > minf)
							goto gskip2;
						tmp = (d - *(tfp3+1));
						fD += DIFF_FCN(tmp);
						if (fD > minf)
							goto gskip2;
						tmp = (e - *(tfp3-1));
						fD += DIFF_FCN(tmp);
						if(fD<minf) {
							minf = fD;
							fi = j;
						}
gskip2:
						tfp3++;						
					}

					#ifdef TIME_DEINTERLACE
					//num_flow++;
					#endif
					
					if (minf < FlowThresh)  {
						
						if ((fi == 1) || (fi == -1) || (fi == 0)) {							
							tempLine[pel] = (*fp1 + *fp3)/2;
						} else {
							
							f = *(fp3+ fi);
							pix = (a + f) / 2;
							d2 = (int)(fi)/2;
						
							f = *(fp1 + d2);
							tmp = (pix - f);
							error2 = ABS(tmp);
							
							f = *(fp1 + d2 + 1);
							pix = (d + *(fp3 + fi + 1))/2;
							tmp = (pix - f);
							error21 = ABS(tmp);

							f = *(fp1 + d2 -1);
							pix = (e + *(fp3 + fi - 1))/2;
							tmp = (pix - f);
							error22 = ABS(tmp);
														

							if(error2 < 15 && ( ((error21 < 15) && (error22 > 25)) || ((error21 > 25) && (error22 < 15)) ) ) {
								
								pix = (a + *(fp3+fi)) / 2;
								tempLine[pel + d2] = pix;
							
								pix = (d + *(fp3 + fi + 1))/2;
								tempLine[pel + d2 + 1] = pix;

								pix = (e + *(fp3 + fi - 1))/2;
								tempLine[pel + d2 - 1] = pix;								

								goto gadvance;
							}
							
							pix = (a + *(fp3 + fi))/2;
							f = *(fp3 + d2);
							tmp = (pix - f);
							error3 = ABS(tmp);
							
							f = *(fp3 + d2 + 1);
							pix = (d + *(fp3 + fi + 1))/2;
							tmp = (pix - f);
							error21 = ABS(tmp);
							
							f = *(fp3 + d2 -1);
							pix = (e + *(fp3 + fi - 1))/2;
							tmp = (pix - f);
							error22 = ABS(tmp);	
							
							pix = (a + *(fp3 + fi))/2;

							if( error3 < 15 && ( ((error21 < 15) && (error22 > 25)) || ((error21 > 25) && (error22 < 15))) ) {
							
								tempLine[pel + d2] = pix;
							
								pix = (d + *(fp3 + fi + 1))/2;
								tempLine[pel + d2 + 1] = pix;

								pix = (e + *(fp3 + fi - 1))/2;
								tempLine[pel + d2 - 1] = pix;
													
							
								goto gadvance;
							} else {
								/*
								*fp2 = 0;
								*fpr2 = 0;
								*fpb2 = 255;
								tempLine[pel] = 0;
								*/
								
							}
							
						}
					} else {
						/*
						*fp2 = 0;
						*fpr2 = 255;
						*fpb2 = 0;
						tempLine[pel] = 0;
						*/
						
					}

			}
gadvance:
			fp1++;
			fp2++;
			fp3++;
			pfp1++;
			pfp2++;
			pfp3++;
			ptempLine++;
		}

		// current frame
		// current line
		fp2 = fpg;
		// prev line
		fp1 = fp2 - pitch;
		// next line
		fp3 = fp2 + pitch;
		// current frame
		
		//prev frame
		// current line
		pfp2 = pfpg;
		// prev line
		pfp1 = pfp2 - pitch;
		// next line
		pfp3 = pfp2 + pitch;

		ptempLine = &tempLine[0];		

		for (pel = 0; pel < pels-1; pel += 1)		
		{			
			// change
					
				tmp = (*fp1 - *(fp3));
				diff1 = DIFF_FCN(tmp);
				tmp = (*(fp1-1) - *(fp3-1));
				diff1 += DIFF_FCN(tmp);
				tmp = (*(fp3+1) - *(pfp3+1));
				diff1 += DIFF_FCN(tmp);				

				if( ABS(diff1) > 4800 ) {
					// horiz
					tmp = (*fp1 - *(pfp1));
					diff1 = DIFF_FCN(tmp);
					tmp = (*(fp1-1) - *(pfp1-1));
					diff1 += DIFF_FCN(tmp);
					tmp = (*(fp1+1) - *(pfp1+1));
					diff1 += DIFF_FCN(tmp);
					tmp = (*fp3 - *(pfp3));
					diff1 += DIFF_FCN(tmp);
					tmp = (*(fp3-1) - *(pfp3-1));
					diff1 += DIFF_FCN(tmp);
					tmp = (*(fp3+1) - *(pfp3+1));
					diff1 += DIFF_FCN(tmp);
					
				
					if (diff1 < 2400) {
						// not moving						
					
						*fp2 = MEDIAN_3(*fp1,*fp2,*fp3);											
					
					} else {

						if (*ptempLine != -1) {
							*fp2 = (unsigned char) *ptempLine;
						} else {
							tmp = (*fp1 + *fp3)/2;
							*fp2 = 	(unsigned char) tmp;	
						}						

						#ifdef TIME_DEINTERLACE
							num_avg++;
						#endif				
			
					}
				} else {
				// not horiz
				// avg
					if (*ptempLine != -1) {
						D1 = *(fp1+1) - *(fp1-1);
						D1 = ABS(D1);
						if( D1 > 20) { 
							*fp2 = (unsigned char) *ptempLine;
						} else {
							tmp = (*fp1 + *fp3)/2;
							*fp2 = 	(unsigned char) tmp;															
								// not vertical								
						}
					} else {
						tmp = (*fp1 + *fp3)/2;
						*fp2 = 	(unsigned char) tmp;							
					}					
			
					#ifdef TIME_DEINTERLACE
						num_avg++;
					#endif	
											
				}

			
			fp1++;
			fp2++;
			fp3++;
			pfp1++;
			pfp2++;
			pfp3++;
			ptempLine++;
		}
	}
	
	free(tempLine);	

	return;

}	


#endif





















































