/*
 * yuv2yuv.c
 *
 * This library performs format-conversion between YUV-planar formats.
 * The input and output are both always planar (three pointers)
 * The functions can also reduce the input-resolution by half.
 * When the g_vHalfResFlag is set, this 1/2 reduction is performed.
 * Performing the reduction here (as opposed to VRESIZE) offers the
 * possible advantage of better input/output matching.
 *
 *   For example, converting a full-resolution CHROMA420 -> half-resolution
 *   CHROMA444 bitmap requires just the Y-plane to be downsampled.
 *
 * (The conversion to a final YUY2, RGB24, or YV12 surface is performed
 *  elsewhere.)  
 *
 * This block of functions sit between the MPEG-decoder's output, and
 * the resizer input
 *
 * | MPEG2DEC | -> |YUY2YUY| -> |VRESIZER| -> |CROP & CSC| -> |AVI WRITER|
 *                 *half-res
 *
 * The calling function supplies input bitmap, input format, valid ptr to 
 * output bitmap, and output format.  yuv2yuv will choose a conversion
 * strategy which retains the most detail.  yuv2yuv also processes the
 * flag g_vHalfRes.
 *
 * Note that these routines call functions in other files (vidfilt.c,
 * vresizer.c)
 *
 * MPEG2AVI
 * --------
 *
 * v0.15B5 initial release
 * v0.16B22 MMX optimizations to downsample_half()
 * v0.16B23 relocated the yuv(planar) -> yuv(planar) convert functions here
 *         (they used to be integrated into store_avi_gdi() as a switch() 
 * v0.16B24 fixed u_src_ptr, v_src_ptr assignment error in y422toyuy2()
 *         (used the wrong formula, for yuv420)
 * v0.16B3 changed var declaration in asm functions, to enforce data alignment
 *         declaring the variables static helps Visual C++ 6 align the vars
 *         moved static memory-buffers to file scope.
 * v0.16B32 modified yuv420toyv12(), now source_Y reads are always QWORD 
 *         aligned
 */

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

/* downsample 8bpp-packed bitmap image by half in X & Y directions
   i.e. 720x480 8bpp -> 360x240 8bpp
  
   src[] and dest[] can be the same ( overwrites original src[] ! )
*/

/* private variables */
// yuv2yuv()
static unsigned char *yhalf =NULL, *uhalf=NULL, *vhalf=NULL;

	
void
downsample_half( unsigned char dest1[], unsigned char dest2[], unsigned const 
	char src1[], unsigned const char src2[], long s_x, long s_y )
{
	static long x, y;
	const static __int64 mmmask_00FF[] = { 0x00FF00FF00FF00FF}; // AND-mask
	const static __int64 mmmask_0002[] = { 0x0002000200020002};	// add-mask
	// the add-mask implements correct rounding, for the truncation
	// [( a + b + c + d + 2 ) >> 2 ] == (a + b + c + d)/2

	x = 0;
	y = 0;

	/*  This downsampler performs a simple arithmetic average
        over the four adjacent pixels.  "fast" */

  	if ( g_fastfilter )
	{ /* fastfilter mode is on */
	  if ( src2 == NULL || dest2 == NULL )
	  { /* operate on just src1[], not src2[] */
		__asm	{
			mov edi, dword ptr [ dest1 ];	// edi = dst1[]
			 mov eax, 0x00;	// x = 0;
		
			movq mm6, qword ptr [ mmmask_0002 ]

			mov esi, dword ptr [ src1 ];	// esi = src1[]
			 mov ebx, 0x00;	// y = 0;

			movq mm7, qword ptr [ mmmask_00FF ]
		
			mov ecx, [ s_x ];	// ecx = s_x

			// for ( y= 0; y < s_y; s_y+=2 );
		lppt_1 :	// for ( x= 0; x < s_x; 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 0002_0002_0002_0002
			movq mm2, qword ptr [ esi + ecx ]; // mm2 = row(y+1) pixels
			 movq mm1, mm0;

			movq mm3, mm2;
			 psrlw mm1, 8;	// mm1 <= row (y)  pixel[ _ 8 _ 6 _ 4 _ 2 ]

			psrlw mm3, 8;	// mm3 <= row (y+1)pixel[ _ 8 _ 6 _ 4 _ 2 ]
			 pand mm0, mm7;	// mm0 <= row (y) pixel [ _ 7 _ 5 _ 3 _ 1 ]

			pand mm2, mm7;	// mm2 <= row (y+1)pixel [ _ 7 _ 5 _ 3 _ 1 ]
			 paddusw mm1, mm3; // mm1 <= sum (y/y+1) pixel[ _8 _6 _4 _2]

			paddusw mm0, mm2; // mm0 <= row (y/y+1) pixel[ _7 _5 _3 _1]
			 paddusw mm1, mm6; // mm1 <= sum + (2)

			paddusw mm1, mm0; // mm1 <= sum of four-pixel sqr [ 87 65 43 21 ]
			 add eax, 0x08; // x += 8                        [ 87 65 43 21 ]

			psrlw mm1, 2;	// mm1 <= avg of four-pixel sqr
			 add edi, 0x04;	// dst1 += 4

			packuswb mm1, mm1;	//
			 add esi, 0x08;	// src1 += 8

			// stall 

			movd dword ptr [edi-4], mm1;	// store 
			 cmp eax, ecx	// compare x <> s_x

			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 += s_x; skip one line
			 cmp ebx, [s_y] 	// compare y <> s_y

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

		} // __asm end
/*
		for ( y = 0; y < s_y; y+=2 )
		{
			for ( x = 0; x < s_x; x+=2 )
			{
				*(dest1 +( x>>1 ) + (y >> 1 )*(s_x>>1) ) = ( (unsigned int) 
					*(src1 + x + y*s_x ) + 
					*(src1 + x+1 + y*s_x ) +
					*(src1 + x + (y+1)*s_x ) + 
					*(src1 + x+1 + (y+1)*s_x ) + 2 ) >> 2;
			}
		} // endfor y
	*/
	  } /* endif */
	  else
	  { /* operate on both src1[] and src2[] */

		__asm	{
			mov edi, dword ptr [ dest1 ];	// edi = dst1[]
			 mov eax, 0x00;	// x = 0;
		
			mov esi, dword ptr [ src1 ];	// esi = src1[]
			 mov [y], eax;	// set [y] = 0

			movq mm7, qword ptr [ mmmask_00FF ]

			movq mm6, qword ptr [ mmmask_0002 ]
		
			mov edx, dword ptr [ dest2 ];	// edx = dest2 pointer
	
			mov ebx, dword ptr [ src2 ];	// ebx = src2 pointer

		lppt_2a:	// for ( y= 0; y < s_y; s_y+=2 );
			mov ecx, [s_x];	// reload ecx = s_x

		lppt_2 :	//	for ( x= 0; x < s_x; s_x+=8 );

			movq mm0, qword ptr [ esi ];	// mm0 = pixel[ 8 7 6 5 4 3 2 1]
			 add edi, 0x04;	// dst1 += 4


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

			movq mm3, mm2;	// 0) copy mm2 to mm3
			 psrlw mm1, 8;	// 1)mm1 <= row (y)  pixel[ _8 _6 _4 _2 ]

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

			pand mm2, mm7;	// 4)mm2 <= row (y+1)pixel [ _ 7 _ 5 _ 3 _ 1 ]
			 paddusw mm1, mm3; // 5)mm1 <= sum (y/y+1) pixel[ _8 _6 _4 _2]

			paddusw mm0, mm2; // 6)mm0 <= row (y/y+1) pixel[ _7 _5 _3 _1]
			 paddusw mm1, mm6; // 7)mm1 <= sum + (2)

			// begin processing src2->dest2
			movq mm4, qword ptr [ ebx ]; // 1) mm4 = row(y) _8 _6 _4 _2
			 paddusw mm0, mm1; // 8)mm0 <= sum of four-pixel dest1

			movq mm2, qword ptr [ ebx + ecx]; // mm2 = row(y) _8 _6 _4 _2
			 psrlw mm0, 2;	// 9)mm0 <= avg of four-pixel dest1

			packuswb mm0, mm0;	// 10) pack
			 movq mm1, mm4;	// 0) copy mm4 to mm1

			movq mm3, mm2;	// 0) copy mm2 to mm3
			 psrlw mm1, 8;	// 1)mm1 <= row (y)  pixel[ _8 _6 _4 _2 ]

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

			pand mm2, mm7;	// 4)mm2 <= row (y+1)pixel [ _ 7 _ 5 _ 3 _ 1 ]
			 paddusw mm1, mm3; // 5)mm1 <= sum (y/y+1) pixel[ _8 _6 _4 _2]

			paddusw mm4, mm2; // 6)mm4 <= row (y/y+1) pixel[ _7 _5 _3 _1]
			 paddusw mm1, mm6; // 7)mm1 <= sum + (2)

			paddusw mm4, mm1; // 8)mm4 <= sum of four-pixel dest2
			 add edx, 0x04;	// dst2 += 4

 			movd dword ptr [edi-4], mm0;	// 11) store dest1[]
			 psrlw mm4, 2;	// 9)mm4 <= avg of four-pixel dest2

			packuswb mm4, mm4;	// 10)
			 add esi, 0x08;	// src1 += 8

			add eax, 0x08; // x += 8                        [ 87 65 43 21 ]
			 add ebx, 0x08; // src2 += 8

 			movd dword ptr [edx-4], mm4;	// 11) store dest2[]
			 cmp eax, ecx	// compare x <> s_x

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

			mov ecx, [y];	// pop [y] off stack
			 mov eax, 0x00;	// reset x = 0

			add ecx, 0x02; // y+= 2;
			 add esi, [s_x]; // src1 += s_x; skip one line

			add ebx, [s_x]; // src2 += s_x; skip one line

			mov [y], ecx; // push ecx back to y
			 cmp ecx, [s_y]; // compare y <> s_y

		// endloop (y)
			jl lppt_2a;	// if ( y < s_y ) loop

			emms;
		} // __asm end
	/*
		for ( y = 0; y < s_y; y+=2 )
		{
			for ( x = 0; x < s_x; x+=2 )
			{
				*(dest1 +( x>>1 ) + (y >> 1 )*(s_x>>1) ) = ( (unsigned int) 
					*(src1 + x + y*s_x ) + 
					*(src1 + x+1 + y*s_x ) +
					*(src1 + x + (y+1)*s_x ) + 
					*(src1 + x+1 + (y+1)*s_x ) + 2 ) >> 2;
				*(dest2 +( x>>1 ) + (y >> 1 )*(s_x>>1) ) = ( (unsigned int) 
					*(src2 + x + y*s_x ) + 
					*(src2 + x+1 + y*s_x ) +
					*(src2 + x + (y+1)*s_x ) + 
					*(src2 + x+1 + (y+1)*s_x ) + 2 ) >> 2;
			}
		} // endfor y 
	*/
	  } /* endif ( src2 == NULL || dest2 == NULL ) */
	} /* endif ( g_fastfilter ) */
	else
	{	/* not on fast-filter mode, use FIR low-pass downssampler */
		conv444to422(src1,src2,dest1,dest2, s_x, s_y );
		conv422to420(dest1,dest2,dest1,dest2, s_x, s_y );
	}
} /* endfunction downsample_half() */


/* downsample planar YUV (3 planes of 8bpp, Y/U/V) in X & Y directions
   i.e. 720x480 YUV420 -> 360x240 YUV420
  
   src[] and dest[] can be the same ( overwrites original src[] ! )
*/
void
half420to420( unsigned char y_dest[], unsigned char u_dest[], 
	unsigned char v_dest[], unsigned const char y_src[], 
	unsigned const char u_src[],unsigned const char v_src[], LPRECT lpsrcRect)
{
	long s_x = lpsrcRect->right, s_y = lpsrcRect->bottom;

	//long x, y;
	long s_x_h = (s_x >> 1 );
	long s_y_h = (s_y >> 1 ); /* s_x half, s_y half */

	if ( g_fastfilter )
	{ /* fastfilter mode is on */
		// downsample the U, V planes first (half-resolution)
		downsample_half( u_dest, v_dest, u_src, v_src, s_x_h, s_y_h );

		// downsample the Y plane (full-resolution)
		downsample_half( y_dest, NULL, y_src, NULL, s_x, s_y );

	/*
		for ( y = 0; y < s_y; y+=2 )
		{
			for ( x = 0; x < s_x; x+=2 )
			{
				*(y_dest +( x>>1 ) + (y >> 1 )*(s_x_h) ) = ( (unsigned int) 
					*(y_src + x + y*s_x ) + 
					*(y_src + x+1 + y*s_x ) +
					*(y_src + x + (y+1)*s_x ) + 
					*(y_src + x+1 + (y+1)*s_x ) + 2 ) >> 2;
			}
		} // endfor y

		// downsample the U and V planes...
		for ( y = 0; y < s_y_h; y+=2 )
		{
			for ( x = 0; x < s_x_h; x+=2 )
			{
				*(u_dest +( x>>1 ) + (y >> 1 )*(s_x_h>>1) ) = ( (unsigned int) 
					*(u_src + x + y*s_x_h ) + 
					*(u_src + x+1 + y*s_x_h ) +
					*(u_src + x + (y+1)*s_x_h ) + 
					*(u_src + x+1 + (y+1)*s_x_h ) + 2 ) >> 2;
				*(v_dest +( x>>1 ) + (y >> 1 )*(s_x_h>>1) ) = ( (unsigned int) 
					*(v_src + x + y*s_x_h ) + 
					*(v_src + x+1 + y*s_x_h ) +
					*(v_src + x + (y+1)*s_x_h ) + 
					*(v_src + x+1 + (y+1)*s_x_h ) + 2 ) >> 2;
			}
		} // endfor y
	*/
	} 
	else
	{	/* not on fast-filter mode, use FIR low-pass downssampler */
		conv444to422(y_src,NULL,y_dest, NULL, s_x, s_y );
		conv422to420(y_dest,NULL,y_dest,NULL, s_x, s_y );
		conv444to422(u_src,v_src,u_dest,v_dest,(s_x>>1), (s_y>>1) );
		conv422to420(u_dest,v_dest,u_dest,v_dest, (s_x>>1), (s_y>>1) );
	}
} /* endfunction downsample_half() */



void
yuv2yuv( tImageOpBlock *convBlock )
{
	//static unsigned char *yhalf, *uhalf, *vhalf;
	unsigned char *yin, *uin, *vin;
	unsigned char *ydst, *udst, *vdst;
	BOOL half_flag = g_vHalfResFlag;
	DWORD src_format = convBlock->dwSrcFormat;
	DWORD dst_format = convBlock->dwDstFormat;
	static int first_iteration = 1;

	long W = convBlock->srcRect.right, H = convBlock->srcRect.bottom;
	/* initialize the convertor */
	yin = convBlock->src1;
	uin = convBlock->src2;
	vin = convBlock->src3;
	ydst = convBlock->dst1; 
	convBlock->outdst1 = ydst;
	udst =  convBlock->dst2;
	convBlock->outdst2 =udst;
	vdst = convBlock->dst3;
	convBlock->outdst3 = vdst;
	
	if ( first_iteration )
	{
		first_iteration = 0;
		yhalf = (unsigned char *)malloc( W * H );
		uhalf = (unsigned char *)malloc( W * H );
		vhalf = (unsigned char *)malloc( W * H );

		if ( !( yhalf && uhalf && vhalf) )
		{
			fprintf( stderr, "\nyuv2yuv() : malloc() failed.");
			exit( ERROR );
		}
	} /* endif (first_iteration) */
		

  if ( dst_format == CHROMA420 )
  {
	if ( half_flag )
	{
	  switch ( src_format )
	  {
		case CHROMA420 : /* 420 -> 420 half */
			half420to420( ydst, udst, vdst, yin, uin, vin, &(convBlock->srcRect) );
			//downsample_half( ydst, NULL, yin, NULL, W, H );
			//downsample_half( udst, vdst, uin, vin, (W>>1), (H>>1));
			convBlock->outdst1 = ydst;
			convBlock->outdst2 = udst;
			convBlock->outdst3 = vdst;
			return;
			break;
		case CHROMA422 : /* 422 -> 420 half */
			downsample_half( yhalf, NULL, yin, NULL, W, H );
			downsample_half( uhalf, vhalf, uin, vin, (W>>1), H );
			yin = yhalf;
			uin = uhalf;
			vin = vhalf;
			H = ( H >> 1 );
			W = ( W >> 1 );
			break;
		default : /* 444 -> 420 half */
			downsample_half( yhalf, NULL, yin, NULL, W, H );
			downsample_half( uhalf, vhalf, uin, vin, W, H );
			yin = yhalf;
			uin = uhalf;
			vin = vhalf;
			H = ( H >> 1 );
			W = ( W >> 1 );
	  } /*endswitch ( src_format ) */
	} /* endif ( half_flag ) */

	/* switch statement will convert src_format -> 420 */
	switch( src_format ) /* dest_format is YUV420 */
	{
		case CHROMA444 : /* 444 -> 420 */
			downsample_half( udst, vdst, uin, vin, W, H );
			convBlock->outdst1 = ydst;
			convBlock->outdst2 = uin;
			convBlock->outdst3 = vin;
			break;
		case CHROMA422 : /* 422 -> 420 */
			conv422to420( uin, vin, udst, vdst, W, H);
			convBlock->outdst1 = yin;
			convBlock->outdst2 = udst;
			convBlock->outdst3 = vdst;
			break;
		case CHROMA420 : /* 420 -> 420 */
			convBlock->outdst1 = yin;
			convBlock->outdst2 = uin;
			convBlock->outdst3 = vin;
			break;
		default:
			fprintf( stderr, "\nyuv2yuv() : unknown chroma-format!");
			exit( ERROR );
	} /* endswitch */
  } /* endif ( dst_format == CHROMA420 ) */

  if ( dst_format == CHROMA422 )
  {
	if ( half_flag )
	{
	  switch ( src_format )
	  {
		case CHROMA420 : /* 420 -> 422 half */
			downsample_half( ydst, NULL, yin, NULL, W, H );
			conv444to422( uin, vin, udst, vdst, (W>>1), (H>>1) );
			convBlock->outdst1 = ydst;
			convBlock->outdst2 = udst;
			convBlock->outdst3 = vdst;
			return;
			break;
		case CHROMA422 : /* 422 -> 422 half */
			downsample_half( ydst, NULL, yin, NULL, W, H );
			downsample_half( udst, vdst, uin, vin, (W>>1), H );
			convBlock->outdst1 = ydst;
			convBlock->outdst2 = udst;
			convBlock->outdst3 = vdst;
			return;
			break;
		default : /* 444 -> 422 half */
			/* chop all 3 planes in half */
			downsample_half( yhalf, NULL, yin, NULL, W, H );
			downsample_half( uhalf, vhalf, uin, vin, W, H );
			yin = yhalf;
			uin = uhalf;
			vin = vhalf;
			H = ( H >> 1 );
			W = ( W >> 1 );
	  } /*endswitch ( src_format ) */
	} /* endif ( half_flag ) */

	switch( src_format ) /* dest_format is YUV422 */
	{
		case CHROMA444 : /* 444 -> 422 */
			conv444to422( uin, vin, udst, vdst, W, H);
			convBlock->outdst1 = yin;
			convBlock->outdst2 = udst;
			convBlock->outdst3 = vdst;
			break;
		case CHROMA422 : /* 422 -> 422 */
			convBlock->outdst1 = yin;
			convBlock->outdst2 = uin;
			convBlock->outdst3 = vin;
			break;
		case CHROMA420 : /* 420 -> 422 */
			conv420to422( uin, vin, udst, vdst, W, H );
			convBlock->outdst1 = yin;
			convBlock->outdst2 = udst;
			convBlock->outdst3 = vdst;
			break;
		default:
			fprintf( stderr, "\nyuv2yuv() : unknown chroma-format!");
			exit( ERROR );
	} /* endswitch */
  } /* endif ( dst_format == CHROMA422 ) */

  if ( dst_format == CHROMA444 )
  {
	if ( half_flag )
	{
	  switch ( src_format )
	  {
		case CHROMA420 : /* 420 -> 444 half */
			downsample_half( ydst, NULL, yin, NULL, W, H );
			convBlock->outdst1 = ydst;
			convBlock->outdst2 = uin;
			convBlock->outdst3 = vin;
			return;
			break;
		case CHROMA422 : /* 422 -> 444 half */
			downsample_half( ydst, NULL, yin, NULL, W, H );
			conv422to420( uin, vin, udst, vdst, W, H);
			convBlock->outdst1 = ydst;
			convBlock->outdst2 = udst;
			convBlock->outdst3 = vdst;
			return;
			break;
		case CHROMA444 : /* 444 -> 444 half */
			/* chop all 3 planes in half */
			downsample_half( ydst, NULL, yin, NULL, W, H );
			downsample_half( udst, vdst, uin, vin, W, H );
			convBlock->outdst1 = ydst;
			convBlock->outdst2 = udst;
			convBlock->outdst3 = vdst;
			return;
			break;
		default :
			downsample_half( yhalf, NULL, yin, NULL, W, H);
			downsample_half( uhalf, vhalf, uin, vin, (W>>1), (H>>1));
			yin = yhalf;
			uin = uhalf;
			vin = vhalf;
			H = ( H >> 1 );
			W = ( W >> 1 );
	  } /*endswitch ( src_format ) */
	} /* endif ( half_flag ) */

	switch( src_format ) /* dest_format is YUV444 */
	{
		case CHROMA444 : /* 444 -> 444 */
			convBlock->outdst1 = yin;
			convBlock->outdst2 = uin;
			convBlock->outdst3 = vin;
			break;
		case CHROMA422 : /* 422 -> 444 */
			conv444to422( uin, vin, udst, vdst, W, H);
			convBlock->outdst1 = yin;
			convBlock->outdst2 = udst;
			convBlock->outdst3 = vdst;
			break;
		case CHROMA420 : /* 420 -> 444 */
			if ( g_fastfilter )
			{
				fast420to444( uin, vin, udst, vdst, W, H);
				convBlock->outdst1 = yin;
				convBlock->outdst2 = udst;
				convBlock->outdst3 = vdst;
			}
			else
			{
				conv420to422( uin, vin, uhalf, vhalf, W, H );
				conv422to444( uhalf, vhalf, udst, vdst, W, H );
				convBlock->outdst1 = yin;
				convBlock->outdst2 = udst;
				convBlock->outdst3 = vdst;
			}
			break;
		default:
			fprintf( stderr, "\nyuv2yuv() : unknown chroma-format!");
			exit( ERROR );
	} /* endswitch */
  } /* endif ( dst_format == CHROMA444 ) */

}


// convert YUV420 planar to YV12 (contiguous) frame 
// srcRect ->top & ->left point to upper-left corner of viewport window
// srcRect ->right & ->bottom indicate srcW, srcH (respectively)
// ->src1 = y_src, ->src2 = u_src, ->src3 = v_src, ->dst1 = fb_yuv
void
yuv420toyv12( tImageOpBlock *pConvBlock )
{
	/* convert & crop YUV4:2:0 planar to YV12 12bpp planar format */
	/* framebuffer[] = YV12 planar image */
	/* do not need to invert image */
	// The YV12 frame can be considered the joined output of
	// y_src[], v_src[], u_src[] (in that order)

	// converted to static to enforce DWORD alignment
	static DWORD srcW;
	static DWORD srcH;
	static DWORD dstW;
	static DWORD dstH;
	static DWORD y_stride, uv_stride, uv_stride1;
	static DWORD u_dalign_cb, v_dalign_cb, y_dalign_cb;
	static DWORD dstWdiv2;
	static DWORD ysrlAmt, ysllAmt;

	// XXX_src_ptr points to upper-left co-ordinates of rectangle, in xxx_src[]
	unsigned char *u_src_ptr;
	unsigned char *v_src_ptr;
	unsigned char *y_src_ptr;
	unsigned char *fb_yuv; // output buffer

	srcW = pConvBlock->srcRect.right;
	srcH = pConvBlock->srcRect.bottom;
	dstW = pConvBlock->dstRect.right;
	dstH = pConvBlock->dstRect.bottom;
	dstWdiv2 = (dstW >> 1);

	u_src_ptr = pConvBlock->src2 + (pConvBlock->srcRect.top>>1)*
		(srcW>>1) + (pConvBlock->srcRect.left>>1);
	v_src_ptr = pConvBlock->src3+ (pConvBlock->srcRect.top>>1)*
		(srcW>>1) + (pConvBlock->srcRect.left>>1);
	y_src_ptr = pConvBlock->src1+ pConvBlock->srcRect.top*srcW + 
		pConvBlock->srcRect.left;
	fb_yuv = pConvBlock->dst1; // output buffer

	y_stride = (srcW<<1); // #bytes per source Y, 2 rows
	uv_stride = ( y_stride >> 1);	// #bytes per source UV, 2 rows
	uv_stride1 = uv_stride >> 1;	// #bytes per source UV, 1 row
	y_dalign_cb = ((DWORD)y_src_ptr) & 0x07;	// # bytes to shift Y

	y_src_ptr = y_src_ptr - y_dalign_cb; // align y_src_ptr to QWORD

	ysrlAmt = (y_dalign_cb << 3);	// # bits to rshift Y[7..0]
	ysllAmt = 64 - ysrlAmt ; // # bits to lshift Y[15..8]
	u_dalign_cb = ((DWORD)u_src_ptr) & 0x03;	// # bytes to shift U
	v_dalign_cb = ((DWORD)v_src_ptr) & 0x03;	// # bytes to shift V

		// MMX-loop copies the Y-plane 
		// the loop reads 8 pixels * two rows per iteration (using 2 movq's,
		// to eliminated misaligned source reads.)
		__asm  
		{
		mov eax, dword ptr [ y_src_ptr ];	// y_src (row y) [qword aligned]

		mov ecx, dword ptr [ fb_yuv ];	// ecx = fb_yuv (row y)
		 mov ebx, eax;	// 

		movd mm6, [ysrlAmt];	// mm6 = right shift amount for alignment
		 mov esi, 0x00;	// x = 0;

		mov edi, [dstH]; // y = dstH
		 mov edx, ecx;

		movd mm7, [ysllAmt];	// mm7 = left shift amount for alignment

		add ebx, [srcW];	// ebx = y_src (row y+1)

		add edx, [dstW];	// edx = fb_yuv (row y+1)
		 jmp lp_yv12y;	// skip first store operation

	lp_yv12ya: // post-loop store operation
		movq qword ptr [ edx - 8], mm3; // store Y[7..0] row(y+1)

		//for( y = dstH; y > 0; y = y -2 )
	lp_yv12y:	// for( x = 0; x < m_OutputWidth; x += 8)
		// Note that the (movd & punpckldq) is equivalent to 1 movq,
		// but the (movd & punpckldq) have lower chance of misalignment!
		movq mm0, qword ptr [ eax + esi ]; // mm0 = Y[7..0] row(y)
		 add edx, 0x08;

		movq mm1, qword ptr [ eax + esi + 8]; // mm1 = Y[15..8] row(y)
		 psrlq mm0, mm6;	

		movq mm2, qword ptr [ ebx + esi ]; // mm2 = Y[7..0] row(y+1)
		 psllq mm1, mm7;

		movq mm3, dword ptr [ ebx + esi + 8 ]; // mm2 = Y[15..8] row(y+1)
		 por mm1, mm0;	// mm1 = desired output Y[7..0] 

		psrlq mm2, mm6;
		 add esi, 0x08;	// x += 8

		movq qword ptr [ ecx ], mm1; // store Y[7..0] row(y)
		 psllq mm3, mm7;

		por mm3, mm2;	// mm3 = desired output Y[7..0]
		 add ecx, 0x08;

		//movq qword ptr [ edx - 8], mm3; // store Y[7..0] row(y+1)
		// store operation moved to AFTER branch, to remove stall

		cmp esi, [dstW]; // compare x <> m_OutputWidth
		 jl lp_yv12ya	;// end for( x = 0; x < m_OutputWidth; x += 8)

		add eax, [y_stride]; // y_ptr_src += y_stride (row y), skip 1 row
		 sub edi, 0x02;	// y = y - 2 (two rows per iteration)

		add ebx, [y_stride]; // y_ptr_src += y_stride (row y+1), skip 1 row
		 add ecx, esi;	// fb_yuv += m_OutputWidth (1-row) [y]

		movq qword ptr [ edx - 8], mm3; // store Y[7..0] row(y+1)
		 add edx, esi;	// fb_yuv += m_OutputWidth (1-row) [y+1]

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

		jg lp_yv12y;// end for( y = dstH; y > 0; y = y -2 )
//	} // end __asm convert yuv420 -> yv12 (y-plane only)

	// 2V) now convert V-plane

		mov eax, [v_src_ptr];	// eax = v_src[] row(y) (input)
//		 ecx = fb_yuv ( row y) output, carried over from previous loop!

		mov edx, [ v_dalign_cb ]; // mm7 = #bytes to shift for DWORD alignment
		 mov esi, 0x00;	// x = 0

		mov edi, [dstH];	// edi = y 
		 sub eax, edx;	// eax = v_src_ptr_aligned

		mov ebx, eax;	// ebx = v_src[] row(y+1)
		 movd mm7, edx; // mm7 = #bytes to shift for DWORD alignment

		add ebx, [ uv_stride1];	// ebx = u_src[] ( row y+1) input
		 mov edx, ecx;	// edx = fb_yuv (row y+1) output

		add edx, [ dstWdiv2];	// edx, add (dstW/2) (UV = half-resolution)
		 psllw mm7, 0x03;	// mm7 (convert #bytes to shift -> #bits to shift)

		;//eax, ebx are dword-aligned (rounded down) to v_src

		// the following statement is equivalent to
		//		"for( y = (dstH>>1); y > 0; y -= 2 );"
		// for (y = dstH; y > 0; y-= 4 )
	lp_yv12v:	// for (x = 0; x < dstWdiv2; x += 4 )
		movd mm0, [ eax + esi ]; // mm0 = row y, 4-bytes of V
		  add ecx, 0x04;	// fb_yuv(row y) += 4

		punpckldq mm0, [ eax +esi+ 4];	// mm0 = 8-bytes V (row y)
		 add edx, 0x04;	// fb_yuv(row y+1) += 4

		movd mm1, [ ebx + esi ]; // mm1 = row y+1, 4-bytes of V
		 psrlq mm0, mm7;	// mm7 = shift align amount
			// mm0 [ xxxx DATA ] (lower 32-bits hold valid data )

		punpckldq mm1, [ebx +esi+ 4]; //mm1 = 8-bytes V (row_y+1)
		 add esi, 0x04;
		
		movd [ ecx - 4 ], mm0; // store 4 V-bytes, row-y
		 psrlq mm1, mm7;	// mm7 = shift align amount;

		cmp esi, [ dstWdiv2];	// compare ( x <> dstWdvi2)

		movd [ edx - 4 ], mm1; // store 4 V-bytes row (y+1)

		 jl lp_yv12v;	// end for (x = 0; x < dstWdiv2; x += 4 )

		add eax, [ uv_stride ];
		 add ecx, esi;	// fb_yuv(row y) : skip 1-row
		
		add ebx, [ uv_stride ];
		 add edx, esi;	// fb_yuv(row y+1) : skip 1-row

		sub edi, 0x04;	// y = y -4
		 mov esi, 0x00;	// x =0

		cmp edi, 0x00;
		 jg lp_yv12v;		// end for (y = dstH; y > 0; y-= 4 )

//	} // end __asm convert yuv420 -> yv12 (v-plane only)

		 	// 2U) now convert U-plane

		mov eax, [u_src_ptr];	// eax = u_src[] row(y) (input)
//		 ecx = fb_yuv ( row y) output, carried over from previous loop!

		mov edx, [ u_dalign_cb ]; // mm7 = #bytes to shift for DWORD alignment
		 mov esi, 0x00;	// x = 0

		mov edi, [dstH];	// edi = y 
		 sub eax, edx;	// eax = u_src_ptr_aligned

		mov ebx, eax;	// ebx = u_src[] row(y+1)
		 movd mm7, edx; // mm7 = #bytes to shift for DWORD alignment

		add ebx, [ uv_stride1];	// ebx = u_src[] ( row y+1) input
		 mov edx, ecx;	// edx = fb_yuv (row y+1) output

		add edx, [ dstWdiv2];	// edx, add (dstW/2) (UV = half-resolution)
		 psllw mm7, 0x03;	// mm7 (convert #bytes to shift -> #bits to shift)

		;//eax, ebx are dword-aligned (rounded down) to u_src

		// for (y = dstH; y > 0; y-= 4 )
	lp_yv12u:	// for (x = 0; x < dstWdiv2; x += 4 )
		movd mm0, [ eax + esi ]; // mm0 = row y, 4-bytes of U
		  add ecx, 0x04;	// fb_yuv(row y) += 4

		punpckldq mm0, [ eax +esi+ 4];	// mm0 = 8-bytes U (row y)
		 add edx, 0x04;	// fb_yuv(row y+1) += 4

		movd mm1, [ ebx + esi ]; // mm1 = row y+1, 4-bytes of U
		 psrlq mm0, mm7;	// mm7 = shift align amount
			// mm0 [ xxxx DATA ] (lower 32-bits hold valid data )

		punpckldq mm1, [ebx +esi+ 4]; //mm1 = 8-bytes U (row_y+1)
		 add esi, 0x04;
		
		movd [ ecx - 4 ], mm0; // store 4 U-bytes, row-y
		 psrlq mm1, mm7;	// mm7 = shift align amount;

		cmp esi, [ dstWdiv2];	// compare ( x <> dstWdvi2)

		movd [ edx - 4 ], mm1; // store 4 U-bytes row (y+1)

		jl lp_yv12u;	// end for (x = 0; x < dstWdiv2; x += 4 )

		add eax, [ uv_stride ];
		 add ecx, esi;	// fb_yuv(row y) : skip 1-row
			
		add ebx, [ uv_stride ];
		 add edx, esi;	// fb_yuv(row y+1) : skip 1-row

		mov esi, 0x00;	// x =0
		 sub edi, 0x04;	// y = y -4

		cmp edi, 0x00;
		 jg lp_yv12u;		// end for (y = dstH; y > 0; y-= 4 )

		emms;
	
	} // end __asm convert yuv420 -> yv12 (u-plane only)

	/* equivalent integer code, no longer in use
	
		for (j=0; j<m_OutputHeight ; ++j )  // convert y-plane
		{
		y_src_addr = (j+CropRect.top)*Resized_Picture_Width + CropRect.left;
		dest_addr = j * m_OutputWidth;
		memcpy( fb_yuv + dest_addr, y_ptr + y_src_addr, m_OutputWidth);
		}
	
		for ( j=0; j< (m_OutputHeight>>1); ++j ) // convert U & V planes
		{
			dest_addr = ( m_OutputHeight * m_OutputWidth) +
				(j * (m_OutputWidth>>1) );
			uv_src_addr = (j+(CropRect.top>>1))*(Resized_Picture_Width>>1) + 
				(CropRect.left>>1);
			memcpy( fb_yuv+dest_addr, v_ptr+uv_src_addr, (m_OutputWidth>>1) );
	
			dest_addr += (m_OutputHeight>>1) * (m_OutputWidth>>1);
			//src_addr = (j+CropRect.top)*(Resized_Picture_Width>>1) + 
			//	(CropRect.left>>1);
			memcpy( fb_yuv+dest_addr, u_ptr+uv_src_addr, (m_OutputWidth>>1) );
		} // endfor j
	*/
}  // end function yuv444toyv12( );


void
yuv422toyuy2( tImageOpBlock *pConvBlock )
{
	/* convert & crop YUV4:2:2 planar to YUY2 packed 16bpp format */
	/* fb_yuv[] = YUY2 planar image */
	/* do not need to invert image */
	  /*
		for (j=0; j<m_OutputHeight ; ++j )
		{
			for ( i=0; i< (m_OutputWidth>>1); ++i )
			{	//i = "group index" (one group is a set of four pixels [YUYV]
				dest_addr = (j<<1) * m_OutputWidth + (i<<2);
				y_src_addr = (j+CropRect.top)*Resized_Picture_Width + 
					CropRect.left + (i<<1);
				uv_src_addr = (j+CropRect.top)*(Resized_Picture_Width>>1) + 
					(CropRect.left>>1) + i;
				*(fb_yuv+dest_addr+ 0) = *(y_ptr + y_src_addr  );	// 0) Y 
				*(fb_yuv+dest_addr+ 1) = *(u_ptr + uv_src_addr );	// 1) U 
				*(fb_yuv+dest_addr+ 2) = *(y_ptr + y_src_addr +1 );	// 2) Y 
				*(fb_yuv+dest_addr+ 3) = *(v_ptr + uv_src_addr );	// 3) V 
			}
		} // endfor ( j=0 ... ) */
	
	// variable declarations are static, to enforce DWORD alignment
	static DWORD y_stride, uv_stride;
	static DWORD u_dalign_cb, v_dalign_cb;

	static DWORD srcW ;
	static DWORD srcH ;
	static DWORD dstW ;
	static DWORD dstH ;
	static DWORD dstWdiv2;

	// XXX_src_ptr points to upper-left co-ordinates of rectangle, in xxx_src[]
	unsigned char *u_src_ptr;
	unsigned char *v_src_ptr;
	unsigned char *y_src_ptr;
	unsigned char *fb_yuv; // output buffer

	srcW = pConvBlock->srcRect.right;
	srcH = pConvBlock->srcRect.bottom;
	dstW = pConvBlock->dstRect.right;
	dstH = pConvBlock->dstRect.bottom;
	dstWdiv2 = (dstW >> 1);

	u_src_ptr = pConvBlock->src2 + (pConvBlock->srcRect.top)*
		(srcW>>1) + (pConvBlock->srcRect.left>>1);
	v_src_ptr = pConvBlock->src3+ (pConvBlock->srcRect.top)*
		(srcW>>1) + (pConvBlock->srcRect.left>>1);
	y_src_ptr = pConvBlock->src1+ pConvBlock->srcRect.top*srcW + 
		pConvBlock->srcRect.left;
	fb_yuv = pConvBlock->dst1; // output buffer


	y_stride = srcW; // #bytes per source Y, 1 row
	uv_stride = ( y_stride >> 1);	// #bytes per source UV, 1 row
	u_dalign_cb = ((DWORD)u_src_ptr) & 0x03;	// # bytes to shift U
	v_dalign_cb = ((DWORD)v_src_ptr) & 0x03;	// # bytes to shift V

//	y_stride = Resized_Picture_Width;
//	uv_stride = (Resized_Picture_Width>>1);
//	dstWdiv2 = (m_OutputWidth>>1);
//	dstH = m_OutputHeight;
//	u_dalign_cb = ((DWORD)u_src_ptr) & 0x03;	// # bytes to shift U
//	v_dalign_cb = ((DWORD)v_src_ptr) & 0x03;	// # bytes to shift V

	__asm {
	;//eax = y_plane
	;//ebx = u_plane
	;//ecx = v_plane
	;//edx = fb_yuv (output) plane
	;//esi = x (in terms of input_width/2)
	;//edi = y (in terms of output_height)
	;//mm6 = shift amount for U-plane dword alignment
	;//mm7 = shift amount for V-plane dword alignment
	;//mm0 = 8 Y-bytes
	;//mm1 = 4 U-bytes (temporary)
	;//mm2 = 4 V-bytes (unpacked)
	;//mm3 = 4 U-bytes (unpacked)
	;//mm4 = 4 V-bytes (temporary)

		mov ebx, dword ptr [ u_src_ptr ];
		 pxor mm1, mm1;	// clear mm1
		mov eax, dword ptr [ y_src_ptr ];

		mov ecx, dword ptr [ v_src_ptr ];

		mov edx, dword ptr [ fb_yuv ];

		mov edi, [u_dalign_cb];
		 mov esi, 0x00;	// x = 0;

		sub ebx, edi;
		 movd mm6, edi;	// mm6 = #bytes to shift right for U DWORD alignment

		mov edi, [v_dalign_cb];
		 psllw mm6, 0x03; // mm6 = #bits to shift left for U DWORD alignment

		sub ecx, edi;
		 movd mm7, edi;	// mm7 = #bytes to shift right for V DWORD alignment

		mov edi, [dstH]; // y =0;
		 psllw mm7, 0x03; // mm7 = #bits to shift left for V DWORD alignment
		
		// for( y = dstH; y > 0; ++y )
	lp_yuy2conv:	// for ( x = 0; x < dstWdiv2; x+=4)

	// The 4 U & 4 V bytes are read using two DWORD-aligned 
	// movd instructions.  The overhead is a few cycles, but this eliminates
	// unaligned reads.

		movd mm1, dword ptr [ ebx + esi ]; // load 4 U-bytes
		 pxor mm3, mm3;	// clear mm3 = 0x0000...

		punpckldq mm1, dword ptr [ ebx + esi + 4]; // mm1 = 8 U-bytes
		 pxor mm2, mm2; // clear mm2 = 0x0000...
//		punpcklbw mm1, dword ptr [ ebx + esi ]; // load & unpack 4 U[3..0] bytes

		movd mm4, dword ptr [ ecx + esi ] ; // load 4 V-bytes
		 psrlq mm1, mm6;	// align mm1 = [ ____ UUUU ] desired U-bytes

		punpckldq mm4, dword ptr [ ecx + esi + 4] ; // mm2 = 8 V-bytes

		movd mm0, dword ptr [ eax + 2*esi]; // load & 4 Y[3..0] bytes
		 psrlq mm4, mm7;	// align mm2 = [ ____ VVVV ] desired V-bytes
		
	// likewise, the Y-read is performed with 2 movd (vs 1 movq) instruction,
	// diminishes the chance of misaligned read.

	//	movq mm0, dword ptr [ eax + 2*esi ]; // load & 4 Y[7..4] bytes
	//	punpcklbw mm2, dword ptr [ ecx + esi ]; // load & unpack 4 V[3..0] bytes

		punpckldq mm0, dword ptr [ eax + 2*esi + 4]; // Y[7..0] bytes

		punpcklbw mm1, mm3;	// mm1 = [ _U _U _U _U]
//		 psrlq mm1, 0x08;	// mm1 = [ _U _U _U _U ]

		punpcklbw mm2, mm4; // mm2 = [ V_ V_ V_ V_ ]

		// (high->low bytes)
		// mm0 = [ YYYY YYYY ], mm1 = [ _U _U _U _U ], mm2 = [ V_ V_ V_ V_ ]
		por mm2, mm1; // mm2 = [ VUVU VUVU ] ( 33 22 11 00 )
		 movq mm4, mm0;	// mm4 = Y[7..0]
//		 psllq mm4, 0x20;	// mm4 = [ 7654 ____ ]

		punpcklbw mm0, mm2; // mm0 = [ VYUY VYUY ] ( 3 2 1 0 )
		 add esi, 0x04;	// x += 4, move 4 pairs of pixels (16 bytes at output)

		cmp esi, [dstWdiv2]
		 punpckhbw mm4, mm2; // mm4 = [ VYUY VYUY ] ( 7 6 5 4 )

		movq qword ptr [ edx + 4*esi -16 ], mm0;	// store [3..0] to fb_yuv[]
		 pxor mm1, mm1;	// clear mm1

		movq qword ptr [ edx + 4*esi -8 ], mm4; // store [7..4] to fb_yuv[]
		 jl lp_yuy2conv;	// end for ( x=0; x < dstWdiv2; x+=4)

		add ebx, [ uv_stride ];
		 shl esi, 0x02;	// esi = (esi * 8)  [ #fb_yuv bytes written per line]

		add ecx, [ uv_stride ];
		 add edx, esi;

		add eax, [ y_stride ];
		 sub edi, 0x01;	// y = y - 1

		mov esi, 0x00;	// x = 0;
		 cmp edi, 0x00;

		jg lp_yuy2conv;	// end for (y=dstH; y > 0; ++y)

		emms;
	} // end __asm yuy2conv


}

