/* vidfilt.c : various video filter routines
 *
 * MPEG2AVI
 * --------
 *
 * v0.16B23- relocated the color-space-conversion (YUV->RGB) functions to
 *          yuv2rgb.c
 *
 * v0.16B22- MMX optimization to fast420to444(), fast444to422(),
 *			MMX optimization to fast420to422()
 *
 * v0.15B5 - added fast422to420(), fast420to422(), fast444to422()
 *           called by their respective convXXXtoXXX() functions.
 *
 * v0.15B4 - fixed stupid bug in conv422to444 (MPEG-1 video color error),
 *    restored separate MPEG-1/MPEG-2 cases for 

 *    conv444to422, conv422to420 can convert either (1) or (2) planes simul
 *
 * v0.15B3 -
 *    converted conv420to422,422to444,fast420to444 -> convert two
 *      bitmaps (src1,src2) at the same time...faster
 *
 * v0.15 - initial release
 *    All upsample/downsample filters have been collected from other source
 *    files and stored here, in vidfilt.c
 *
 *    conv422to444 - upsample (from store.c)
 *    conv420to422 - upsample (from store.c)
 *    conv444to422 - downsample (from MPEG2V12 encode readpic.c)
 *    conv422to420 - downsample (from MPEG2V12 encode readpic.c)
 *    fast420to444 - upsample (no interpolation) from 1/2 -> full

 *    downsample_half() - performs downsample operation on input 8bpp image.
 *      Currently uses a simple arithmetic average over 4-pixels (square)
 *      No low-pass pre-filtering (necessary to avoid aliasing.)
 *   
 */

#include "config.h"
#include "global.h"
#include<stdio.h>
#include<windows.h>  /* used only to get type declaration BOOL */
#include<memory.h>

/* private functions */
void fast420to422( unsigned char src1[], unsigned char src2[], 
	unsigned char dst1[], unsigned char dst2[], short dst_x,short dst_y );
void fast422to420(unsigned char *src1,unsigned char *src2,unsigned char *dst1,
				  unsigned char *dst2,long srcW, long srcH );
void fast444to422(unsigned char *src1,unsigned char *src2,unsigned char *dst1, 
				  unsigned char *dst2, long srcW, long srcH );


/* read the control-variable, g_cmd_ivtc, process it, then clear it. */
BOOL
process_filter_cmd( void )
{
	BOOL fCommand = TRUE; /* was there a recognized command? */
	
	switch ( g_cmd_filter )
	{
		case KEY_FILTER_TOGGLE :  /* toggle filter between 0 & 1 */
			g_fastfilter = ( g_fastfilter == 0 ) ? 1 : 0;
			printf("\r<FILTER> %s\n",g_fastfilter ? "fast":"Hi-Q");
			break;
		case 0 : /* no command */
			break;
		default :
			printf("\r<FILTER> unknown command!\n");
			fCommand = FALSE;
	}  /* endswitch */

	g_cmd_filter = 0; /* reset command-variable */
	
	return fCommand;
}



/* converts a YUV420 3-plane -> YV12 surface (1 contiguous bitmap) */
/* In this instance, the input is a 3 planes, Y[]/U[]/V[], the
   Y[]-plane is at full resolution ( g_AVIcropX * g_AVIcropY )
   The U[]/V[] planes are at half-resolution.*/
/* output = YV12 bitmap of ( g_AVIcropX * g_AVIcropY )*/
void
y420yv12( unsigned char fb[], LPRECT RCrop )
{
  long i, j; /* loop index vars */
  long dest_h = RCrop->bottom - RCrop->top; 
  long  dest_w = RCrop->right - RCrop->left;
  unsigned char *pydst, *pudst, *pvdst;

  pudst = &fb[ dest_h*dest_w ]; /* address of u-plane ... */
  pvdst = &fb[ ((dest_h*dest_w)<<1) ];/* address of v-plane ... */
  /*unsigned char *py=NULL, *pu=NULL, *pv=NULL; */
 

  /* because we are *overwriting* the original image, we must avoid
     deleting source data before we write dest data.  We can do this by
	 copying the planes one a time : Y-plane, then U-plane, then V-plane. */

  /* 1) copy Y-plane */

  for ( j = 0; j < dest_h; ++j )
  {
	//pysrc = &fb[  ];	/* address of current y-scanline */
    memcpy( (LPVOID) &pydst[ j * dest_w ], (LPVOID) &fb[ j ], dest_w );
    for ( i = 0; i < dest_w; ++i )
	{
		/* store in temporary display buffer */
		/* calculate address of pixel(x,y) in *framebuffer */
		
	} /* endfor i */
  } /* endfor j */
}


/* horizontal 1:2 interpolation UPsample filter, originally from store.c */
void conv422to444(src1,src2,dst1,dst2,dstW,dstH)
unsigned char *src1,*src2,*dst1,*dst2;
long dstW, dstH;
{
	//long srcW = lpsrcRect->right, srcH = lpsrcRect->bottom;

	long i, i2, w, j, im3, im2, im1, ip1, ip2, ip3;

//  unsigned char *psrc, *pdst;
  w = dstW>>1;

  if (base.MPEG2_Flag)
  {
    for (j=0; j<dstH; j++)
    {
      for (i=0; i<w; i++)
      {
        i2 = i<<1;
        im2 = (i<2) ? 0 : i-2;
        im1 = (i<1) ? 0 : i-1;
        ip1 = (i<w-1) ? i+1 : w-1;
        ip2 = (i<w-2) ? i+2 : w-1;
        ip3 = (i<w-3) ? i+3 : w-1;

        /* FIR filter coefficients (*256): 21 0 -52 0 159 256 159 0 -52 0 21 */
        /* even samples (0 0 256 0 0) */
        dst1[i2] = src1[i];
		dst2[i2] = src2[i];
        /* odd samples (21 -52 159 159 -52 21) */
        dst1[i2+1] = Clip[(int)(21*(src1[im2]+src1[ip3])
                        -52*(src1[im1]+src1[ip2]) 
                       +159*(src1[i]+src1[ip1])+128)>>8];
		dst2[i2+1] = Clip[(int)(21*(src2[im2]+src2[ip3])
                        -52*(src2[im1]+src2[ip2]) 
                       +159*(src2[i]+src2[ip1])+128)>>8];

      }
      src1+= w;
	  src2+= w;
      dst1+= dstW;
	  dst2+= dstW;
    }
  }
  else
  {
    for (j=0; j<dstH; j++)
    {
      for (i=0; i<w; i++)
      {

        i2 = i<<1;
        im3 = (i<3) ? 0 : i-3;
        im2 = (i<2) ? 0 : i-2;
        im1 = (i<1) ? 0 : i-1;
        ip1 = (i<w-1) ? i+1 : w-1;
        ip2 = (i<w-2) ? i+2 : w-1;
        ip3 = (i<w-3) ? i+3 : w-1;

        /* FIR filter coefficients (*256): 5 -21 70 228 -37 11 */
        dst1[i2] =   Clip[(int)(  5*src1[im3]
                         -21*src1[im2]
                         +70*src1[im1]
                        +228*src1[i]
                         -37*src1[ip1]
                         +11*src1[ip2]+128)>>8];


       dst1[i2+1] = Clip[(int)(  5*src1[ip3]
                         -21*src1[ip2]
                         +70*src1[ip1]
                        +228*src1[i]
                         -37*src1[im1]
                         +11*src1[im2]+128)>>8];

	   dst2[i2] =   Clip[(int)(  5*src2[im3]
                         -21*src2[im2]
                         +70*src2[im1]
                        +228*src2[i]
                         -37*src2[ip1]
                         +11*src2[ip2]+128)>>8];

	   /*dst1[i2+1] = Clip[(int)(  5*src2[ip3] DOH...*/
	   dst2[i2+1] = Clip[(int)(  5*src2[ip3]
                         -21*src2[ip2]
                         +70*src2[ip1]
                        +228*src2[i]
                         -37*src2[im1]
                         +11*src2[im2]+128)>>8];
      }
      src1+= w;
	  src2+= w;
      dst1+= dstW;
	  dst2+= dstW;
    }
  }
}


/* vertical 1:2 interpolation UPsampling filter, originally from store.c */
void conv420to422(src1,src2,dst1,dst2,dstW,dstH)
/* optimzed performance by inverting loop order */
unsigned char *src1,*src2,*dst1,*dst2;
long dstW, dstH;
{
	//long srcW = lpsrcRect->right, srcH = lpsrcRect->bottom;
	long w, h, i, j, j2;
	long jm6, jm5, jm4, jm3, jm2, jm1, jp1, jp2, jp3, jp4, jp5, jp6, jp7;
	unsigned char *psrc1, *psrc2, *pdst1, *pdst2;

	if ( g_fastfilter )
	{
		fast420to422(src1,src2,dst1,dst2,dstW,dstH);
		//fast420to422(src1,NULL,dst1,NULL,dstW,dstH);
		//fast420to422(src2,NULL,dst2,NULL,dstW,dstH);
		return;
	}

	w = dstW>>1;
	h = dstH>>1;

  if (progressive_frame)
  {
    /* intra frame */
    for (j=0; j<h; j++)
    {
      j2 = j<<1;
      jm3 = (j<3) ? 0 : j-3;
      jm2 = (j<2) ? 0 : j-2;
      jm1 = (j<1) ? 0 : j-1;
      jp1 = (j<h-1) ? j+1 : h-1;
      jp2 = (j<h-2) ? j+2 : h-1;
      jp3 = (j<h-3) ? j+3 : h-1;

	  pdst1 = dst1;
	  pdst2 = dst2;
	  psrc1 = src1;
	  psrc2 = src2;
      for (i=0; i<w; i++)
      {

        /* FIR filter coefficients (*256): 5 -21 70 228 -37 11 */
        /* New FIR filter coefficients (*256): 3 -16 67 227 -32 7 */
        pdst1[w*j2] =Clip[(int)(  3*psrc1[w*jm3]
                             -16*psrc1[w*jm2]
                             +67*psrc1[w*jm1]
                            +227*psrc1[w*j]
                             -32*psrc1[w*jp1]
                             +7*psrc1[w*jp2]+128)>>8];

        pdst1[w*(j2+1)] =Clip[(int)(3*psrc1[w*jp3]
                             -16*psrc1[w*jp2]
                             +67*psrc1[w*jp1]
                            +227*psrc1[w*j]
                             -32*psrc1[w*jm1]
                             +7*psrc1[w*jm2]+128)>>8];

		pdst2[w*j2] =   Clip[(int)(3*psrc2[w*jm3]
                             -16*psrc2[w*jm2]
                             +67*psrc2[w*jm1]
                            +227*psrc2[w*j]
                             -32*psrc2[w*jp1]
                             +7*psrc2[w*jp2]+128)>>8];

        pdst2[w*(j2+1)] =Clip[(int)(3*psrc2[w*jp3]
                             -16*psrc2[w*jp2]
                             +67*psrc2[w*jp1]
                            +227*psrc2[w*j]
                             -32*psrc2[w*jm1]
                             +7*psrc2[w*jm2]+128)>>8];
	    ++psrc1;
		++psrc2;
		++pdst1;
		++pdst2;
      } /* endfor i */
    } /* endfor j */
  } 
  else
  {
    /* intra field */
    for (j=0; j<h; j+=2)
    {
      j2 = j<<1;

      /* top field */
      jm6 = (j<6) ? 0 : j-6;
      jm4 = (j<4) ? 0 : j-4;
      jm2 = (j<2) ? 0 : j-2;
      jp2 = (j<h-2) ? j+2 : h-2;
      jp4 = (j<h-4) ? j+4 : h-2;
      jp6 = (j<h-6) ? j+6 : h-2;

	  pdst1 = dst1;
	  pdst2 = dst2;
	  psrc1 = src1;
	  psrc2 = src2;
      for (i=0; i<w; i++)
	  {

        /* Polyphase FIR filter coefficients (*256): 2 -10 35 242 -18 5 */
        /* New polyphase FIR filter coefficients (*256): 1 -7 30 248 -21 5 */
        pdst1[w*j2] = Clip[(int)(1*psrc1[w*jm6]
                         -7*psrc1[w*jm4]
                         +30*psrc1[w*jm2]
                        +248*psrc1[w*j]
                         -21*psrc1[w*jp2]
                          +5*psrc1[w*jp4]+128)>>8];
		pdst2[w*j2] = Clip[(int)(1*psrc2[w*jm6]
                         -7*psrc2[w*jm4]
                         +30*psrc2[w*jm2]
                        +248*psrc2[w*j]
                         -21*psrc2[w*jp2]
                          +5*psrc2[w*jp4]+128)>>8];

        /* Polyphase FIR filter coefficients (*256): 11 -38 192 113 -30 8 */
        /* New polyphase FIR filter coefficients (*256):7 -35 194 110 -24 4 */
        pdst1[w*(j2+2)] = Clip[(int)( 7*psrc1[w*jm4]
                             -35*psrc1[w*jm2]
                            +194*psrc1[w*j]
                            +110*psrc1[w*jp2]
                             -24*psrc1[w*jp4]
                              +4*psrc1[w*jp6]+128)>>8];
		pdst2[w*(j2+2)] = Clip[(int)( 7*psrc2[w*jm4]
                             -35*psrc2[w*jm2]
                            +194*psrc2[w*j]
                            +110*psrc2[w*jp2]
                             -24*psrc2[w*jp4]
                              +4*psrc2[w*jp6]+128)>>8];

        /* bottom field */
        jm5 = (j<5) ? 1 : j-5;
        jm3 = (j<3) ? 1 : j-3;
        jm1 = (j<1) ? 1 : j-1;
        jp1 = (j<h-1) ? j+1 : h-1;
        jp3 = (j<h-3) ? j+3 : h-1;
        jp5 = (j<h-5) ? j+5 : h-1;
        jp7 = (j<h-7) ? j+7 : h-1;

        /* Polyphase FIR filter coefficients (*256): 11 -38 192 113 -30 8 */
        /* New polyphase FIR filter coefficients (*256):7 -35 194 110 -24 4 */
        pdst1[w*(j2+1)] = Clip[(int)( 7*psrc1[w*jp5]
                             -35*psrc1[w*jp3]
                            +194*src1[w*jp1]
                            +110*psrc1[w*jm1]
                             -24*psrc1[w*jm3]
                              +4*psrc1[w*jm5]+128)>>8];

        pdst1[w*(j2+3)] = Clip[(int)(  1*psrc1[w*jp7]
                             -7*psrc1[w*jp5]
                             +30*psrc1[w*jp3]
                            +248*psrc1[w*jp1]
                             -21*psrc1[w*jm1]
                              +5*psrc1[w*jm3]+128)>>8];

		pdst2[w*(j2+1)] = Clip[(int)( 7*psrc2[w*jp5]
                             -35*psrc2[w*jp3]
                            +194*psrc2[w*jp1]
                            +110*psrc2[w*jm1]
                             -24*psrc2[w*jm3]
                              +4*psrc2[w*jm5]+128)>>8];

        pdst2[w*(j2+3)] = Clip[(int)(  1*psrc2[w*jp7]
                             -7*psrc2[w*jp5]
                             +30*psrc2[w*jp3]
                            +248*psrc2[w*jp1]
                             -21*psrc2[w*jm1]
                              +5*psrc2[w*jm3]+128)>>8];
	    ++psrc1;
		++psrc2;
		++pdst1;
		++pdst2;
      } /* endfor i */
    } /* endfor j */
  } /* endelse */
} /* endfunction conv420to422() */


void
fast420to422( unsigned char src1[], unsigned char src2[], 
	unsigned char dst1[], unsigned char dst2[], short dst_x,short dst_y )
{
	long y=0;
//	long dst_offset;
	long srcW = dst_x >> 1;
	long srcH = dst_y >> 1;

	if ( src2 == NULL || dst2 == NULL )
	{	/* one plane to convert */
		/*for ( y=0; y < srcH; ++y )
		{
			dst_offset = srcW*(y<<1);
			memcpy( &dst1[ dst_offset ], &src1[ srcH*y ], srcW );
			dst_offset = srcW*( (y<<1) + 1 );
			memcpy( &dst1[ dst_offset ], &src1[ srcH*y ], srcW );
		}*/
		__asm {
			// this MMX implementation reads 8 source bytes, and
			// writes 16-output bytes (2 rows, 8-bytes each) per iteration

			mov ecx, dword ptr [ dst1 ];	// ecx = dst1[] row(y) 
			 mov esi, 0x00;	// esi = x

			mov eax, dword ptr [ src1 ];	// eax = src1[] row(y)
			 mov edx, ecx; //  ebx = dst1[] row(y+1)
			
			mov ebx, [srcW];

			mov edi, [srcH];  // edi = y;
			 add edx, ebx;
			
			jmp lp_f420422a1a;

		lp_f420422a1:	// for ( y = srcH; y > 0; --y )

			movq qword ptr[ ecx + esi - 8], mm0; // store dst[7..0] row(y)

			movq qword ptr[ edx + esi - 8], mm0; // store dst[7..0] row(y)

		lp_f420422a1a:	// for ( x =0; x < srcW; x+=8 )

			movq mm0, qword ptr[ eax + esi ]; // mm0=src[7..0]
			 add esi, 0x08;

			cmp esi, [srcW]; //
			 jl lp_f420422a1;	
		   // end for ( x = 0; x < srcW; x += 8)

			movq qword ptr[ ecx + esi - 8], mm0; // store dst[7..0] row(y)
			 add eax, ebx;  // src[] += W (increment by 1 row)

			movq qword ptr[ edx + esi - 8], mm0; // store dst[7..0] row(y)
			 add ecx, ebx;	// dst[] += W (increment by 1 row) (row y)

			add ecx, ebx;	// dst[] += W (increment by 1 row) (row y)
			 add edx, ebx;	// dst[] += W (increment by 1 row) (row y+1)

			add edx, ebx;	// dst[] += W (increment by 1 row) (row y+1)
			 sub edi, 0x01;	// --y

			mov esi, 0x00;	// reset x = 0
			 cmp edi, 0x00; // compare y <> 0

			jg lp_f420422a1a // end for ( y = srcH; y > 0; --y)
			emms;
		} // end __asm

	}
	else
	{	/* two planes to convert */

		/*for ( y=0; y < H; ++y )
		{
			dst_offset = W*(y<<1);
			memcpy( &dst1[ dst_offset ], &src1[ W*y ], W );
			memcpy( &dst2[ dst_offset ], &src2[ W*y ], W );
			dst_offset = W*( (y<<1) + 1 );
			memcpy( &dst1[ dst_offset ], &src1[ W*y ], W );
			memcpy( &dst2[ dst_offset ], &src2[ W*y ], W );
		}*/
			y = srcH;

				__asm {
			// this MMX implementation reads 8 src1[] + 8 src2[] bytes, and
			// writes 32-output bytes (2 rows * 8 bytes per dst1, dst2)
			// per loop iteration
			// (a total of 32-output bytes per iteration )
			mov edi, [srcW];  // edi = x = srcW;

			mov ecx, dword ptr [ dst1 ];	// ecx = dst1[] row(y) 

			mov eax, dword ptr [ src1 ];	// eax = src1[] row(y)
			
			mov edx, dword ptr [ dst2 ];	// edx = dst2[] row(y) 

			mov ebx, dword ptr [ src2 ];	// ebx = src2[] row(y)
			 mov esi, edi;

			// for ( y = srcH; y > 0; --y )
		lp_f420422a2:	// for ( x = srcW; x > 0; x -= 8 )

			movq mm0, qword ptr[ eax ]; // mm0=src1[7..0]
			 add eax, 0x08;	// src1 += 8

			movq mm1, qword ptr[ ebx ]; // mm1=src2[7..0]
			 add ebx, 0x08;	// src2 += 8

			movq qword ptr[ ecx ], mm0;	// store dst1[7..0] row(y)
			 add edx, 0x08; // dst2 += 8

			movq qword ptr[ ecx +esi], mm0;	// store dst1[7..0] row(y+1)
			 add ecx, 0x08;	// dst1 += 8

			movq qword ptr[ edx -8], mm1;	// store dst2[7..0] row(y)
			 sub edi, 0x08;	// x = x - 8 decrement horizontal counter

			movq qword ptr[ edx +esi -8], mm1;	// store dst2[7..0] row(y+1)
			 cmp edi, 0x00; // compare (x <> 0)

			jg lp_f420422a2;	// end for ( x=srcW; x > 0; x -= 8 )

			sub [y], 0x01;	// --y
			 add ecx, esi;	// dst1[] += srcW (skip one-row)

			mov edi, esi;	// x = srcW
			 add edx, esi;	// dst2[] += srcW (skip one-row)
			
			cmp [y], 0x00;
			 jg lp_f420422a2;	// end for ( y= srcH; y>0; --y)

			emms;

		} // end __asm
	} /* endif ( src2 == NULL || dst2 == NULL ) */

}		


void
fast420to444(unsigned char *src1,unsigned char *src2,
			 unsigned char *dst1,unsigned char *dst2, long dstW, long dstH )
{
	//long srcW = lpsrcRect->right, srcH = lpsrcRect->bottom;

	long x=0, y=0;
	long height = dstH; /* must be multiple of 2 */
	long width = dstW; /* must be multiple of 16, for MMX */

	// The MMX version is faster than the integer version.
	// Each loop interation, 8x1 pixel block (input) is converted to
	// a 16x2 pixel block (output.)
	
	// Thus the dstW must be divisible by 16!  Fortunately, MPEG-video
	// is similarly restricted, so there should be no problem.

	__asm
	{
		mov eax, dword ptr [src1]	;// eax = src1[]
		mov ebx, dword ptr [src2]	;// ebx = src2[]
		mov edi, dword ptr [dst1]	;// edi = dst1[]
		mov edx, dword ptr [dst2]	;// edx = dst2[]

	lp_fast420to444a:	// for ( y = ... )
		mov esi, [dstW]			;// esi = dstW
		 mov ecx, 0x00	;// ecx = x = 0

	lp_fast420to444:	// for ( x = ... )
		movq mm0, qword ptr [eax];	// mm0 = src1[ 0..7 ]
		 add edi, 0x08;		// pre-condition dst1[]

		movq mm2, qword ptr [ebx];	// mm2 = src2[ 0..7 ]
		 movq mm1, mm0;	// copy mm0 => mm1

		movq mm3, qword ptr [ebx]; // copy mm2 => mm3
		 punpckhbw mm0, mm1;	// mm0 <= src1[ 44 55 66 77 ]

		movq qword ptr [ edi ], mm0 ; //dst1[even] = src1[ 44 55 66 77]
		 punpckhbw mm2, mm3;	// mm2 <= src2[ 44 55 66 77]

		movq qword ptr[ edi + esi], mm0; // dst1[odd] = src1[ 44 55 66 77]
		 movq mm4, mm1;

		punpcklbw mm3, dword ptr [ebx]; //mm3 <= src2[ 00 11 22 33]
		 add edx, 0x08;		// pre-condition dst2[]

		punpcklbw mm1, mm4; //mm1 <= src1[ 00 11 22 33]
		 sub edi, 0x08;	// dst1 -= 8, pre-condition dst1[]

		movq qword ptr [ edx ], mm2; //dst2[even] = src2[ 44 55 66 77]
		 add ebx, 0x08;				// src2 += 8

		movq qword ptr [ edx + esi], mm2; //dst2[odd] = src2[ 44 55 66 77]
		 add eax, 0x08;				// src1 += 8

		movq qword ptr [ edi ], mm1; //dst1[even] = src1[ 00 11 22 33]
		 sub edx, 0x08;				// dst -= 8, precondition dst2[]

		movq qword ptr [ edi +esi ], mm1; //dst1[odd] = src1[ 00 11 22 33]
		 add ecx, 0x10;		// x += 16 

		movq qword ptr [ edx ], mm3; //dst2[even] = src2[ 00 11 22 33]
		 add edi, 0x10;		// dst1 += 16

		movq qword ptr [ edx +esi], mm3; //dst2[odd] = src2[ 00 11 22 33]
		 add edx, 0x10;		// dst2 += 16

		cmp ecx, esi;	// compare x <> dstW
		 jl lp_fast420to444;

	// end for( x = 0 ...

		add [y], 0x02; // y+=2
		 add edi, ecx;	// dst1[] += dstW

		mov esi, [y];	// ecx <= y
		 add edx, ecx;	// dst2[] += dstW

		cmp esi, [dstH] // compare y <> dstH 

	// end for ( y = 0 ...
		jl lp_fast420to444a;

		emms;

	} // end __asm 

/*
	for( y = 0; y < ( height >> 1 ); ++y )
	{
		for ( x = 0; x < ( width>> 1 ); ++x )
		{   // render every *even* scanline 
			dst1[ (x<<1) + (y<<1)*width   ] = src1[ x + y*(width>>1) ];
			dst1[(x<<1)+1 + (y<<1)*width  ] = src1[ x + y*(width>>1) ];
			dst2[ (x<<1) + (y<<1)*width   ] = src2[ x + y*(width>>1) ];
			dst2[(x<<1)+1 + (y<<1)*width  ] = src2[ x + y*(width>>1) ];
		}
		// now copy even-scanlines -> odd-scanlines 
		memcpy( dst1 + ((y<<1)+1)*width, dst1 + (y<<1)*width, width );
		memcpy( dst2 + ((y<<1)+1)*width, dst2 + (y<<1)*width, width );
	} // endfor ( y=0, ... 
	*/
}

/* from MPEG2V12 readpic.c - horizontal downsample and 2:1 subsampling */
/* optimzed performance by inverting loop order */
/* if src2 or dst2 is NULL, only src1->dst1 are converted, otherwise
   both planes are processed */
void conv444to422(unsigned char *src1,unsigned char *src2,unsigned char *dst1, 
				  unsigned char *dst2, long srcW, long srcH )
{
	//long srcW = lpsrcRect->right, srcH = lpsrcRect->bottom;

	long i, j, im5, im4, im3, im2, im1, ip1, ip2, ip3, ip4, ip5, ip6;
	long width = srcW, height = srcH;

	unsigned char *pdst1, *pdst2, *psrc1, *psrc2;

	if ( g_fastfilter )
	{
		fast444to422(src1,src2,dst1,dst2,srcW,srcH);
		return;
	}
  /* the "mpeg1" filter is the one we want */
  /*  Why?
      MPEG-1 420         MPEG-2 420

   Y Y Y Y Y Y Y Y     Y Y Y Y Y Y Y Y Y 
    C C C C C C C      C C C C C C C C C
   Y Y Y Y Y Y Y Y     Y Y Y Y Y Y Y Y Y
    C C C C C C C      C C C C C C C C C 
   Y Y Y Y Y Y Y Y     Y Y Y Y Y Y Y Y Y

  In MPEG-2, the chroma-samples are sited 1/2 pixel to the left.
  If we have just *ONE* plane to convert, we are downsampling a Y
  plane, and thus we assume the "MPEG-1 420" arrangement

  If we have *TWO* planes to convert, we must check base.MPEG2_flag
  */

 if ( src2 == NULL || dst2 == NULL )
 { /* just one plane to convert */
    for (i=0; i<width; i+=2)
    {
      im5 = (i<5) ? 0 : i-5;
      im4 = (i<4) ? 0 : i-4;
      im3 = (i<3) ? 0 : i-3;
      im2 = (i<2) ? 0 : i-2;
      im1 = (i<1) ? 0 : i-1;
      ip1 = (i<width-1) ? i+1 : width-1;
      ip2 = (i<width-2) ? i+2 : width-1;
      ip3 = (i<width-3) ? i+3 : width-1;
      ip4 = (i<width-4) ? i+4 : width-1;
      ip5 = (i<width-5) ? i+5 : width-1;
      ip6 = (i<width-5) ? i+6 : width-1;

	  psrc1 = src1; /* reset pointers */
	  pdst1 = dst1 + (i>>1); /* reset pointers */

      for (j=0; j<height; j++ )
	  {
        /* FIR filter with 0.5 sample interval phase shift */
        /*dst[i>>1] = clp[(int)(228*(src[i]+src[ip1])*/
		(*pdst1) = Clip[(int)(228*(psrc1[i]+psrc1[ip1])
                         +70*(psrc1[im1]+psrc1[ip2])
                         -37*(psrc1[im2]+psrc1[ip3])
                         -21*(psrc1[im3]+psrc1[ip4])
                         +11*(psrc1[im4]+psrc1[ip5])
                         + 5*(psrc1[im5]+psrc1[ip6])+256)>>9];
		psrc1+= width;
	    pdst1+= (width>>1);
      } /* endfor j */
    } /* endfor i */
 } /* endif ( psrc2 == NULL || pdst2 == NULL ) */
  else
 { /* two planes to convert */
  if ( !base.MPEG2_Flag )
  {
    for (i=0; i<width; i+=2)
    {
      im5 = (i<5) ? 0 : i-5;
      im4 = (i<4) ? 0 : i-4;
      im3 = (i<3) ? 0 : i-3;
      im2 = (i<2) ? 0 : i-2;
      im1 = (i<1) ? 0 : i-1;
      ip1 = (i<width-1) ? i+1 : width-1;
      ip2 = (i<width-2) ? i+2 : width-1;
      ip3 = (i<width-3) ? i+3 : width-1;
      ip4 = (i<width-4) ? i+4 : width-1;
      ip5 = (i<width-5) ? i+5 : width-1;
      ip6 = (i<width-5) ? i+6 : width-1;

	  psrc1 = src1; /* reset pointers */
	  psrc2 = src2; /* reset pointers */
	  pdst1 = dst1 + (i>>1); /* reset pointers */
	  pdst2 = dst2 + (i>>1); /* reset pointers */

      for (j=0; j<height; j++ )
	  {
        /* FIR filter with 0.5 sample interval phase shift */
        /*dst[i>>1] = clp[(int)(228*(src[i]+src[ip1])*/
		(*pdst1) = Clip[(int)(228*(psrc1[i]+psrc1[ip1])
                         +70*(psrc1[im1]+psrc1[ip2])
                         -37*(psrc1[im2]+psrc1[ip3])
                         -21*(psrc1[im3]+psrc1[ip4])
                         +11*(psrc1[im4]+psrc1[ip5])
                         + 5*(psrc1[im5]+psrc1[ip6])+256)>>9];
		(*pdst2) = Clip[(int)(228*(psrc2[i]+psrc2[ip1])
                         +70*(psrc2[im1]+psrc2[ip2])
                         -37*(psrc2[im2]+psrc2[ip3])
                         -21*(psrc2[im3]+psrc2[ip4])
                         +11*(psrc2[im4]+psrc2[ip5])
                         + 5*(psrc2[im5]+psrc2[ip6])+256)>>9];
		psrc1+= width;
	    pdst1+= (width>>1);
		psrc2+= width;
	    pdst2+= (width>>1);
      } /* endfor j */
    } /* endfor i */
  }
  else
  {	/* MPEG-2 */
    for (j=0; j<height; j++)
    {
      psrc1 = src1 + width*j;
	  pdst1 = dst1 + (width>>1)*j;
      psrc2 = src2 + width*j;
	  pdst2 = dst2 + (width>>1)*j;
      for (i=0; i<width; i+=2)
      {
		im5 = (i<5) ? 0 : i-5;
		im3 = (i<3) ? 0 : i-3;
		im1 = (i<1) ? 0 : i-1;
		ip1 = (i<width-1) ? i+1 : width-1;
		ip3 = (i<width-3) ? i+3 : width-1;
		ip5 = (i<width-5) ? i+5 : width-1;

		/* FIR filter coefficients (*512): 22 0 -52 0 159 256 159 0 -52 0 22 */
		pdst1[i>>1] = Clip[(int)(  
			22*(psrc1[im5]
			+psrc1[ip5])
			-52*(psrc1[im3]+psrc1[ip3])
			+159*(psrc1[im1]+psrc1[ip1])+256*psrc1[i]+256)>>9];
		pdst2[i>>1] = Clip[(int)(  
			22*(psrc2[im5]
			+psrc2[ip5])
			-52*(psrc2[im3]+psrc2[ip3])
			+159*(psrc2[im1]+psrc2[ip1])+256*psrc2[i]+256)>>9];
      }
    } /* endfor j */
  } /* end else */
 } /* endif ( src2 == NULL || dst2 == NULL ) */
} /* endfunction conv444to422() */


void fast444to422(unsigned char *src1,unsigned char *src2,unsigned char *dst1, 
				  unsigned char *dst2, long srcW, long srcH )
{
	long x=0, y=0;
	long dstW = (srcW >> 1);
	long H = srcH;
	DWORD mmmask_00FF[] = {0x00FF00FF, 0x00FF00FF}; // even-pixel mask
	DWORD mmmask_0001[] = {0x00010001, 0x00010001};	// add-mask

	// the add-mask implements correct rounding, for the truncation
	// [( a + b + 1 ) >> 1 ] == (a + b )/2

	if ( src2 == NULL || dst2 == NULL )
	{ /* convert one plane only */

		// this MMX routine processes a 8x2 block per iteration.
		// each loop-iteration writes a 4x2 block to the output.
		__asm	{
			mov edi, dword ptr [ dst1 ];	// edi = dst1[]
			 mov eax, 0x00;	// x = 0;
		
			mov esi, dword ptr [ src1 ];	// esi = src1[]
			 mov ebx, 0x00;	// y = 0;

			mov ecx, [ srcW ];	// ecx = srcW
			 sub edi, 0x04;	// adjust edi for loop-start

			movq mm7, qword ptr [ mmmask_00FF ]

			mov edx, [dstW];		// edx = dstW

			movq mm6, qword ptr [ mmmask_0001 ]


			// for ( y= 0; y < srcH; ++y );
		lppt_1 :	// for ( x= 0; x < srcW; s_x+=8 );

			movq mm0, qword ptr [ esi ];	// mm0 = pixel[ 8 7 6 5 4 3 2 1]

			// mm7 = pixel mask 00FF_00FF_00FF_00FF
			// mm6 = add mask 0001_0001_0001_0001
			movq mm2, qword ptr [ esi + ecx ]; // mm2 = row(y+1) pixels
			 movq mm1, mm0;

			movq mm3, mm2;
			 psrlw mm1, 8;	// mm1 <= src1 (y)  pixel[ _8 _6 _4 _2 ]

			psrlw mm3, 8;	// mm3 <= src1 (y+1)pixel[ _8 _6 _4 _2 ]
			 pand mm0, mm7;	// mm0 <= src1 (y) pixel [ _7 _5 _3 _1 ]

			pand mm2, mm7;	// mm2 <= src1 (y+1)pixel [ _7 _5 _3 _1 ]
			 paddusw mm1, mm0; // mm1 <= sum src1(y) [ 87 65 43 21]

			paddusw mm3, mm2; // mm3 <= sum src1(y+1) pixel[ 87 65 43 21]
			 paddusw mm1, mm6; // mm1 <= src1 "+1" to each pixel-val

			paddusw mm3, mm6; // mm3 <= src1 "+1" to each pixel-val
			 psrlw mm1, 1;	// mm1 <= dst1 (y) (two-pixel avg)

			add edi, 0x04;
			 psrlw mm3, 1;	// mm3 <= dst1 (y+1) (two-pixel avg)
		
			add esi, 0x08;	// src1 += 8
			 packuswb mm1, mm1;	// prepare dst1(y) for output

			add eax, 0x08; // x += 8
			 packuswb mm3, mm3;	// prepare dst1(y+1) for output

			movd dword ptr [edi], mm1;	// store dst1(y) 
			 ; //freeslot

			movd dword ptr [edi+edx], mm3; // store dst1(y+1) 
			 cmp eax, ecx	// compare x <> srcW

			jl lppt_1;	// if ( x < s_x ) loop
		// endloop for (x= ...

			mov eax, 0x00;	// reset x = 0
			 add ebx, 0x02; // y+= 2;

			add esi, ecx; // src1 += srcW; skip one line
			 add edi, edx; // dst1 += dstW; skip one line

			cmp ebx, [srcH] 	// compare y <> srcH

		// endloop for (y= ...
			jl lppt_1;	// if ( y < s_y ) loop

			emms;

		} // __asm end

		/*
		for ( y = 0; y < H; ++y )
			for ( x = 0; x < dstW; ++x )
			{
				dst1[ x + y*dstW ] = ( src1[ (x<<1) + y*srcW ] + 
					src1[ (x<<1) + y*srcW + 1 ] + 1 ) >> 1;
			}
		*/
	}
	else
	{ /* convert two planes */

		// this MMX routine processes a 8x1 block per iteration.
		// each loop-iteration writes a 4x1 block to the output.

		__asm	{
			mov ecx, dword ptr [ dst1 ];	// ecx = dst1[]
			 mov esi, 0x00;	// x = 0;
		
			mov eax, dword ptr [ src1 ];	// eax = src1[]
			 mov ebx, 0x00;	// y = 0;

			mov edx, dword ptr [ dst2 ];	// edx = dst2[]
			 sub ecx, 0x04;	// adjust ecx for loop-start

			movq mm7, qword ptr [ mmmask_00FF ]
			 sub edx, 0x04;	// adjust edx for loop-start

			mov ebx, dword ptr [ src2];		// ebx = src2[]

			movq mm6, qword ptr [ mmmask_0001 ]

		lppt_2a :	// for ( y= 0; y < srcH; ++y );
			mov esi, 0x00;	// x = 0;
		lppt_2 :	//  for ( x= 0; x < srcW; s_x+=8 );

			movq mm0, qword ptr [ eax ];	// mm0 = src1 pixel[ 8 ... 1]
			 add ecx, 0x04;  // dst1 += 4


			// mm7 = pixel mask 00FF_00FF_00FF_00FF
			// mm6 = add mask 0001_0001_0001_0001
			movq mm2, qword ptr [ ebx];		// mm2 = src2 pixel[ 8 ... 1]
			 movq mm1, mm0;

			movq mm3, mm2;
			 psrlw mm1, 8;	// mm1 <= src1 (y)  pixel[ _8 _6 _4 _2 ]

			psrlw mm3, 8;	// mm3 <= src2 (y) pixel[ _8 _6 _4 _2 ]
			 pand mm0, mm7;	// mm0 <= src1 (y) pixel [ _7 _5 _3 _1 ]

			pand mm2, mm7;	// mm2 <= src2 (y) pixel [ _7 _5 _3 _1 ]
			 paddusw mm1, mm0; // mm1 <= sum src1(y) [ 87 65 43 21]

			paddusw mm3, mm2; // mm3 <= sum src2(y) pixel[ 87 65 43 21]
			 paddusw mm1, mm6; // mm1 <= src1 "+1" to each pixel-val

			paddusw mm3, mm6; // mm3 <= src2 "+1" to each pixel-val
			 psrlw mm1, 1;	// mm1 <= dst1 (y) (two-pixel avg)

			add edx, 0x04;  // dst2 += 4
			 psrlw mm3, 1;	// mm3 <= dst2 (y) (two-pixel avg)
		
			add eax, 0x08;	// src1 += 8
			 packuswb mm1, mm1;	// prepare dst1(y) for output

			add esi, 0x08; // x += 8
			 packuswb mm3, mm3;	// prepare dst2(y) for output

			movd dword ptr [ecx], mm1;	// store dst1(y) 
			 add ebx, 0x08;	// src2 += 8

			movd dword ptr [edx], mm3; // store dst1(y+1) 
			 cmp esi, [srcW]	// compare x <> srcW

			jl lppt_2;	// if ( x < s_x ) loop
		// endloop for (x= ...

			add [y], 0x01; // y+= 2;
			
			mov esi, [y];	// esi = y
			 cmp esi, [srcH] 	// compare y <> srcH
		// endloop for (y= ...
			jl lppt_2a;	// if ( y < s_y ) loop

			emms;
		} // __asm end
		/*
		for ( y = 0; y < H; ++y )
			for ( x = 0; x < dstW; ++x )
			{
				dst1[ x + y*dstW ] = ( src1[ (x<<1) + y*srcW ] + 
					src1[ (x<<1) + y*srcW + 1 ] + 1 ) >> 1;
				dst2[ x + y*dstW ] = ( src2[ (x<<1) + y*srcW ] + 
					src2[ (x<<1) + y*srcW + 1 ] + 1 ) >> 1;
			}
		*/
	}
}


/* from MPEG2V12 readpic.c - vertical downsample and 2:1 subsampling */
/* optimzed performance by inverting loop order */
/* if src2 or dst2 is NULL, only src1->dst1 are converted, otherwise
   both planes are processed */
void conv422to420(unsigned char *src1,unsigned char *src2,unsigned char *dst1,
				  unsigned char *dst2,long srcW, long srcH )
{
	//long srcW = lpsrcRect->right, srcH = lpsrcRect->bottom;
	/*int w, i, j, jm6, jm5, jm4, jm3, jm2, jm1;*/
	long w, i, j, jm5, jm4, jm3, jm2, jm1;
	long jp1, jp2, jp3, jp4, jp5, jp6;
	unsigned char *psrc1, *psrc2, *pdst1, *pdst2;
	long height = srcH;

	if ( g_fastfilter )
	{
		fast422to420(src1,src2,dst1,dst2,srcW,srcH);
		return;
	}

/*if (prog_frame)
  {*/
    /* intra frame */
	w = srcW>>1;

  if ( src2 == NULL || dst2 == NULL )
  {  /* convert one plane only */
      for (j=0; j<height; j+=2)
      {
        jm5 = (j<5) ? 0 : j-5;
        jm4 = (j<4) ? 0 : j-4;
        jm3 = (j<3) ? 0 : j-3;
        jm2 = (j<2) ? 0 : j-2;
        jm1 = (j<1) ? 0 : j-1;
        jp1 = (j<height-1) ? j+1 : height-1;
        jp2 = (j<height-2) ? j+2 : height-1;
        jp3 = (j<height-3) ? j+3 : height-1;
        jp4 = (j<height-4) ? j+4 : height-1;
        jp5 = (j<height-5) ? j+5 : height-1;
        jp6 = (j<height-5) ? j+6 : height-1;

		psrc1 = src1; /* reset pointer */
		pdst1 = dst1 + ( w*(j>>1) ); /* reset pointer */
	    for (i=0; i<w; i++)
		{
			/* FIR filter with 0.5 sample interval phase shift */
			/*dst[w*(j>>1)] = clp[(int)(228*(src[w*j]+src[w*jp1])*/
			pdst1[i] = Clip[(int)(228*(psrc1[w*j]+psrc1[w*jp1])
                             +70*(psrc1[w*jm1]+psrc1[w*jp2])
                             -37*(psrc1[w*jm2]+psrc1[w*jp3])
                             -21*(psrc1[w*jm3]+psrc1[w*jp4])
                             +11*(psrc1[w*jm4]+psrc1[w*jp5])
                             + 5*(psrc1[w*jm5]+psrc1[w*jp6])+256)>>9];
			++psrc1; /* move to next sample */
		} /* endfor i - inner loop */
      } /* endfor j - outer loop */
  }
  else
  {  /* convert both planes */
      for (j=0; j<height; j+=2)
      {
        jm5 = (j<5) ? 0 : j-5;
        jm4 = (j<4) ? 0 : j-4;
        jm3 = (j<3) ? 0 : j-3;
        jm2 = (j<2) ? 0 : j-2;
        jm1 = (j<1) ? 0 : j-1;
        jp1 = (j<height-1) ? j+1 : height-1;
        jp2 = (j<height-2) ? j+2 : height-1;
        jp3 = (j<height-3) ? j+3 : height-1;
        jp4 = (j<height-4) ? j+4 : height-1;
        jp5 = (j<height-5) ? j+5 : height-1;
        jp6 = (j<height-5) ? j+6 : height-1;

		psrc1 = src1; /* reset pointer */
		pdst1 = dst1 + ( w*(j>>1) ); /* reset pointer */
		psrc2 = src2; /* reset pointer */
		pdst2 = dst2 + ( w*(j>>1) ); /* reset pointer */
	    for (i=0; i<w; i++)
		{
			/* FIR filter with 0.5 sample interval phase shift */
			/*dst[w*(j>>1)] = clp[(int)(228*(src[w*j]+src[w*jp1])*/
			pdst1[i] = Clip[(int)(228*(psrc1[w*j]+psrc1[w*jp1])
                             +70*(psrc1[w*jm1]+psrc1[w*jp2])
                             -37*(psrc1[w*jm2]+psrc1[w*jp3])
                             -21*(psrc1[w*jm3]+psrc1[w*jp4])
                             +11*(psrc1[w*jm4]+psrc1[w*jp5])
                             + 5*(psrc1[w*jm5]+psrc1[w*jp6])+256)>>9];
			pdst2[i] = Clip[(int)(228*(psrc2[w*j]+psrc2[w*jp1])
                             +70*(psrc2[w*jm1]+psrc2[w*jp2])
                             -37*(psrc2[w*jm2]+psrc2[w*jp3])
                             -21*(psrc2[w*jm3]+psrc2[w*jp4])
                             +11*(psrc2[w*jm4]+psrc2[w*jp5])
                             + 5*(psrc2[w*jm5]+psrc2[w*jp6])+256)>>9];
			++psrc1; /* move to next sample */
			++psrc2; /* move to next sample */
		} /* endfor i - inner loop */
      } /* endfor j - outer loop */
  } /* endif ( src2 == NULL || dst2 == NULL ) */
} /* endfunction conv422to420() */


void fast422to420(unsigned char *src1,unsigned char *src2,unsigned char *dst1,
				  unsigned char *dst2,long srcW, long srcH )
{
	long y, x;
	long /*dst_offset, */W, H;

	W = (srcW >> 1);
	H = srcH;

	if ( src2 == NULL || dst2 == NULL )
	{	/* convert one plane only */
		for ( y = 0; y < (H>>1); ++y )
		{
			for ( x = 0; x < W; ++x)
			{
				dst1[ x + W*(y) ] = ( (long)src1[ x + W*((y<<1)) ] + 
					src1[ x + W*((y<<1)+1) ] + 1 ) >> 1;
			}
		}
	}
	else
	{	/* convert both planes */
		for ( y = 0; y < (H>>1); ++y )
		{
			for ( x = 0; x < W; ++x)
			{
				dst1[ x + W*y ] = ( (long)src1[ x + W*((y<<1)) ] + 
					src1[ x + W*((y<<1)+1) ] + 1 ) >> 1;
				dst2[ x + W*y ] = ( (long)src2[ x + W*((y<<1)) ] + 
					src2[ x + W*((y<<1)+1) ] + 1 ) >> 1;

			}
		}
	}	/* endif ( src2 == NULL || dst2 == NULL ) */
}

