/* vresizer.c : various video filter routines
 *
 * MPEG2AVI
 * --------
 * v0.16B35
 *
 * v0.16B34
 *    changed order of shrink2x( src1, src2) | shrink2x( src3, NULL ),
 *    if the user entered invalid parameters, shrink2x() would error out.
 *    In the old code, shrink2...() is first called with U/V planes,
 *    which would report a resolution 1/2 the actual value.  This caused
 *    user confusion.
 *    Doubled the size of global buffers g_qwAcc[]
 * v0.16B33
 *    fixed reporting error in process_command_resize()
 *    more mmx optimization (to shrink2x()), substantial reorganization to 
 *    the internal shrink functions.
 *    the componnent x/y/xy functions of shrink420(), shrink422(), shrink444()
 *    have been replaced with generic shrink2x(), shrink2y(), shrink2xy()
 *     each function will shrink either one or two planes.
 *    The dispatcher function shrink() now calls these new functions
 *
 *                    -> shrink420()    --> shrink2x()
 *     shrink_frame() -> shrink422() --+--> shrink2y()
 *                    -> shrink444()    --> shrink2xy()
 *
 *    Several more mmx functions mmx_acc2pix(), convert the accumulator to
 *    8-bit pixel values.
 *
 * v0.16B32
 *    Added mmx_rowacc() functions to speed-up row-accumulation calculations.
 *    This greatly accelerates Y-scaling (doesn't help X-scaling though)
 *    Re-examination of the 16-bit multiplication math, I just realized
 *    that I didn't catch a possible error earlier.  The SHRINK_GRANULARITY
 *    is set to 65536, a 17-bit magnitude.  Since the scaling "numerator" 
 *    comes from SHRINK_GRANULARITY, I've imposed a minsize restriction on the 
 *    source and destination width/height, 32 pixels.   see shrink_frame()
 *
 *    The value sounds arbitrary, but it's based on the worst case assumption 
 *    that a CHROMA420 MPEG bitstream (16 x 16 U/V resolution), will produce
 *    a numerator no larger than 16384, a 15-bit magnitude.  This fits safely
 *    in a 16-bit signed integer.
 *
 *    So far, only the shrink420y() and shrink420xy() functions are MMX-accel.
 *    It would be trivial to accelerate the shrink422 shrink444 functions,
 *    but I don't have any test bitstreams so I'd rather leave them alone for
 *    now.  (I doubt very many users need more than shrink420() )
 *    I just realized DWORD = UNsigned long ... oppps ...
 *
 * v0.16B31
 *    mild optimization to shrink() functions, removed a redundant
 *    multiply (multiplication numerX or numerY).  This is now done at
 *    the end of the loop (during the store row operation), instead of
 *    on a per input-pixel basis.
 *    all shrink420() functions are fully tested, the rest are not tested
 * v0.15B51
 *    restored all shrink420(), shrink422() functions back to full
 *    granularity.  The functions were needlessly restricted to
 *    pixel-increments of 2^(SHRINK_GRANULARITY_EXP + 1)
 *    Math mistake of my part, doh
 * v0.15B5
 *    completely restructured the calling structure for the shrink
 *    functions.
 *    Now, only one public function, shrink_frame()
 *      shrink_frame() parses the format, and dispatches the request to :
 *        1) shrink420
 *        2) shrink422
 *        3) shrink444
 *
 *    added shrink420xy() (faster X&Y simul shrinking for YV12)
 *    added shrink422() functions for CHROMA422 planar
 *    renamed shrink888() to shrink444()
 *    adjusted shrink_val to 16, fixed process_resize_cmd() to avoid
 *     setting the resize-values to non-granular values
 *
 * v0.15B4
 *    added shrink420() functions, planar YUV420 downsample,
 *      shrink420 functions have HALF the granularity as shrink888
 *    added half420to420() - downsample YUV420 (planar) by 1/2
 *    moved downsample_half() from vidfilt.c to vresizer.c
 *    changed source_width/height parameters from long -> short
 *
 * v0.15 - initial release
 *    shrink888()  - public interface to shrink888 engine
 *    shrink888x() - downsize in X-direction only
 *    shrink888y() - downsize in Y-direction only  
 *    shrink888xy()- downsize in X & Y directions  
 *    shrink420()  - public interface to shrink420 engine
 *
 *       All shrink() functions require input & output vars to be
 *       already allocated.  The output-bitmap has same logical (addressing)
 *       dimensions as input.  The downsized-output is bordered by blackness.
 *
 * bitmap HORIZONTAL shrinker ... resizes bitmap in X-direction
   Resizes a source-bitmap (8bpp) planar to arbitrary (smaller) X-dimensions.
   The output bitmap retains the input's co-ordinate addressing,
   (i.e. it is centered with respect to the source bitmap's co-ordinate axis.)

   example, source 720x480 -> dest 640x480
    in this example, the dest_bmp occupies rectangle whose two corners are
	at (40,0) and (679,479)

   OUTPUT : bitmap's logical (memory addressing) resolution is not changed
            shrunk bitmap is center-aligned with source window

   dest_bmp[] and src_bmp[] must be VALID (already allocated) memory!
   cannot be the same (overwriting src does not work!)
   
	 Based on Bressenham's line-algorithm, arithmetic accumulator.  
	 The resampling operation calculates each output-pixel by taking
	 weighted-averages of input-pixels.

   (no fancy bilinear/bicubic/quadric filtering.)
 */


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

#define STEP_FRACTION_EXP 16 /* denominator exponent (2^10) for approximation*/
#define STEP_FRACTION_VAL 65536 /* denominator numerical value (2^10) = 1024 */

#define SHRINK_GRANULARITY_EXP 3 /* (2^EXP) = VAL */
#define SHRINK_GRANULARITY_VAL 8 /* co-ordinate granularity of target bmp */

#define MAXLINE 4096 /* maximum width */

// private variables & MMX functions, v0.16B32

__inline void mmx_rowacc1( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	unsigned char *pSrc1, DWORD dwAddmask );
__inline void mmx_rowacc1l( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD pSrc1, DWORD dwAddmask );


__inline void mmx_rowacc2( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1,
	LPDWORD prowAcc2, unsigned char *pSrc1, unsigned char *pSrc2 );
__inline void mmx_rowacc2l( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD prowAcc2, LPDWORD pSrc1, LPDWORD pSrc2 );

__inline void mmx_rowset2( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD prowAcc2, unsigned char *pSrc1, unsigned char *pSrc2 );
__inline void mmx_rowset2l( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD prowAcc2, LPDWORD pSrc1, LPDWORD pSrc2 );
__inline void mmx_acc2pix1( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD pDst1 );
__inline void mmx_acc2pix2( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD prowAcc2, LPDWORD pDst1, LPDWORD pDst2 );

// row accumulator memory
// row buffer memory
// the rowAcc is temporary storage space used for y-only scaling
// the rowbuf is additional storage space used by the XY scaling functions
 
static __int64 qwAcc1[MAXLINE], qwAcc2[MAXLINE], qwAcc3[MAXLINE];
static __int64 qwBuf1[MAXLINE], qwBuf2[MAXLINE], qwBuf3[MAXLINE];

static LPDWORD g_rowAcc1 = (LPDWORD)qwAcc1, g_rowAcc2 = (LPDWORD)qwAcc2,
	g_rowAcc3 = (LPDWORD)qwAcc3;
static LPDWORD g_rowBuf1 = (LPDWORD)qwBuf1, g_rowBuf2= (LPDWORD)qwBuf2,
	g_rowBuf3= (LPDWORD)qwBuf3;
#define rowAcc1 g_rowAcc1
#define rowAcc2 g_rowAcc2
#define rowAcc3 g_rowAcc3
#define rowbuf1 g_rowBuf1
#define rowbuf2 g_rowBuf2
#define rowbuf3 g_rowBuf3


/* private functions */
/*
static void shrink444x(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  unsigned char dest3bmp[], const unsigned char src1bmp[], 
		  const unsigned char src2bmp[], const unsigned char src3bmp[],
		  short srcW,short srcH, short dstW );
static void shrink444y(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  unsigned char dest3bmp[], const unsigned char src1bmp[], 
		  const unsigned char src2bmp[], const unsigned char src3bmp[],
		  short srcW,short srcH, short dstH );
static void shrink444xy(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  unsigned char dest3bmp[], const unsigned char src1bmp[], 
		  const unsigned char src2bmp[], const unsigned char src3bmp[],
		  short srcW,short srcH, short dstW, short dstH );
*/

static void shrink2x(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  const unsigned char src1bmp[], const unsigned char src2bmp[], 
		  short srcW,short srcH, short dstW );
static void shrink2y(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  const unsigned char src1bmp[], const unsigned char src2bmp[], 
		  short srcW,short srcH, short dstH );
static void shrink2xy(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  const unsigned char src1bmp[], const unsigned char src2bmp[], 
		  short srcW,short srcH, short dstW, short dstH );

/* PX3 OUT
static void shrink420(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  unsigned char dest3bmp[], const unsigned char src1bmp[], 
		  const unsigned char src2bmp[], const unsigned char src3bmp[],
		  LPRECT lpsrcRect, LPRECT lpdstRect );
static void shrink422(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  unsigned char dest3bmp[], const unsigned char src1bmp[], 
		  const unsigned char src2bmp[], const unsigned char src3bmp[],
		  LPRECT lpsrcRect, LPRECT lpdstRect );
static void shrink444(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  unsigned char dest3bmp[], const unsigned char src1bmp[], 
		  const unsigned char src2bmp[], const unsigned char src3bmp[],
		  LPRECT lpsrcRect, LPRECT lpdstRect );
*/
/* read the control-variable, g_cmd_ivtc, process it, then clear it. */
BOOL
process_resize_cmd( void )
{
	BOOL fCommand = TRUE; /* was there a recognized command? */
	int curX = (CHECKOPT( UserOptResizeX ) ) ? UserOptResizeX.data :
		Coded_Picture_Width;
	int curY = (CHECKOPT( UserOptResizeY ) ) ? UserOptResizeY.data :
		Coded_Picture_Height;
	int temp;
	int gran = (SHRINK_GRANULARITY_VAL );
	
	switch ( g_cmd_resize )
	{
		case KEYEX_RESIZE_XINC :
			temp = curX + gran;
			temp -= ( temp % gran );
			SETOPT( UserOptResizeX, temp );
			printf("\r<RESIZE>  '-3X %d'\n", temp );
			break;
		case KEYEX_RESIZE_XDEC :
			temp = curX - gran;
			temp -= ( temp % gran );
			SETOPT( UserOptResizeX, temp );
			printf("\r<RESIZE>  '-3X %d'\n", temp );
			break;
		case KEYEX_RESIZE_YINC :
			temp = curY + gran;
			temp -= ( temp % gran );
			SETOPT( UserOptResizeY, temp );
			printf("\r<RESIZE>  '-3Y %d'\n", temp );
			break;
		case KEYEX_RESIZE_YDEC :
			temp = curY - gran;
			temp -= ( temp % gran );
			SETOPT( UserOptResizeY, temp );
			printf("\r<RESIZE>  '-3Y %d'\n", temp );
			break;
		case KEY_RESIZE_RESET :
			printf("\r<RESIZE>  RESET <%d X %d>\n", Coded_Picture_Width,
				Coded_Picture_Height );
			CLEAROPT( UserOptResizeX );
			CLEAROPT( UserOptResizeY );
			break;
		case 0 : /* no command */
			break;
		default :
			printf("\r<RESIZE>  unknown command!\n");
			fCommand = FALSE;
	}  /* endswitch */

	g_cmd_resize = 0; /* reset command-variable */
	
	if ( CHECKOPT( UserOptResizeX ) )
		if ( UserOptResizeX.data == Coded_Picture_Width )
			CLEAROPT( UserOptResizeX );

	if ( CHECKOPT( UserOptResizeY ) )
		if ( UserOptResizeY.data == Coded_Picture_Height )
			CLEAROPT( UserOptResizeY );

	return fCommand;

}

void
shrink_frame( unsigned char dest1bmp[], unsigned char dest2bmp[],
		  unsigned char dest3bmp[], const unsigned char src1bmp[], 
		  const unsigned char src2bmp[], const unsigned char src3bmp[],
		  LPRECT lpsrcRect, LPRECT lpdstRect, DWORD format )
{
	// v0.16B32 check for minimum source/dest size
	// ist jetzt in EncodeMpeg2Avi in mpeg2dec.c
/*	if ( lpsrcRect->bottom < 32 || lpsrcRect->right < 32 ||
		lpdstRect->bottom < 32 || lpdstRect->right < 32 )
	{
		fprintf( stderr, "\nshrink_frame() : minimum size requirement (32x32) not met!");
		exit( ERROR );
	}*/

	if ( src1bmp != NULL )  /* did ivtc delete current frame? */
	{ /* no, resize the output! */
		switch ( format )
		{
			case CHROMA420 : 
				if ( ( lpsrcRect->right != lpdstRect->right ) && 
					 ( lpsrcRect->bottom != lpdstRect->bottom ) )
				{	/* shrinking in both X & Y co-ordinates */
					shrink2xy( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right, lpdstRect->bottom );
					shrink2xy( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, lpsrcRect->bottom>>1, 
						lpdstRect->right>>1, lpdstRect->bottom>>1 );
				}
				else if ( lpsrcRect->right != lpdstRect->right )
				{	/* shrinking in X co-ordinate only */
					shrink2x( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right );
					shrink2x( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, 
						lpsrcRect->bottom>>1, lpdstRect->right>>1 );
				}
				else if ( lpsrcRect->bottom != lpdstRect->bottom )
				{	/* shrinking in Y co-ordinate only */
					shrink2y( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->bottom );
					shrink2y( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, 
						lpsrcRect->bottom>>1, lpdstRect->bottom>>1 );
				}
				else
				{   /* no shrinking, just copy the data */
					memcpy( dest1bmp, src1bmp, (long) lpsrcRect->right*lpsrcRect->bottom );
					memcpy( dest2bmp, src2bmp, (long) (lpsrcRect->right>>1)*(lpsrcRect->bottom>>1) );
					memcpy( dest3bmp, src3bmp, (long) (lpsrcRect->right>>1)*(lpsrcRect->bottom>>1) );
				}
				break;
			case CHROMA422 :
				if ( ( lpsrcRect->right != lpdstRect->right ) && ( lpsrcRect->bottom != lpdstRect->bottom ) )
				{	/* shrinking in both X & Y co-ordinates */
					shrink2xy( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right, lpdstRect->bottom );
					shrink2xy( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, lpsrcRect->bottom, 
						lpdstRect->right>>1, lpdstRect->bottom );
				}
				else if ( lpsrcRect->right != lpdstRect->right )
				{	/* shrinking in X co-ordinate only */
					shrink2x( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right );
					shrink2x( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, lpsrcRect->bottom, 
						lpdstRect->right>>1 );
				}
				else if ( lpsrcRect->bottom != lpdstRect->bottom )
				{	/* shrinking in Y co-ordinate only */
					shrink2y( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->bottom );
					shrink2y( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, lpsrcRect->bottom, 
						lpdstRect->bottom );
				}
				else
				{   /* no shrinking, just copy the data */
					memcpy( dest1bmp, src1bmp, (long) lpsrcRect->right*lpsrcRect->bottom );
					memcpy( dest2bmp, src2bmp, (long) (lpsrcRect->right)*(lpsrcRect->bottom>>1) );
					memcpy( dest3bmp, src3bmp, (long) (lpsrcRect->right)*(lpsrcRect->bottom>>1) );
				}
				break;
			case CHROMA444 :
				if ( ( lpsrcRect->right != lpdstRect->right ) && ( lpsrcRect->bottom != lpdstRect->bottom ) )
				{	/* shrinking in both co-ordinates, X & Y */
					shrink2xy( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right, lpdstRect->bottom );
					shrink2xy( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right, lpsrcRect->bottom, 
						lpdstRect->right, lpdstRect->bottom );
				}
				else if ( lpsrcRect->right != lpdstRect->right )
				{	/* shrinking in X co-ordinate only */
					shrink2x( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right );
					shrink2x( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right );
				}
				else if ( lpsrcRect->bottom != lpdstRect->bottom )
				{	/* shrinking in Y co-ordinate only */
					shrink2y( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->bottom );
					shrink2y( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->bottom );
				}
				else
				{   /* no shrinking, just copy the data */
					memcpy( dest1bmp, src1bmp, (long) lpsrcRect->right*lpsrcRect->bottom );
					memcpy( dest2bmp, src2bmp, (long) lpsrcRect->right*lpsrcRect->bottom );
					memcpy( dest3bmp, src3bmp, (long) lpsrcRect->right*lpsrcRect->bottom );
				}
				break;
			default :
				fprintf( stderr, "\nshrink_frame() : unknown format (%ld)!",
					format );
		} /* endswitch */

/*		switch ( format )
		{
			case CHROMA420 : 
				shrink420( dest1bmp, dest2bmp, dest3bmp, src1bmp, src2bmp,
					src3bmp, lpsrcRect, lpdstRect );
				break;
			case CHROMA422 :
				shrink422( dest1bmp, dest2bmp, dest3bmp, src1bmp, src2bmp,
					src3bmp, lpsrcRect, lpdstRect );
				break;
			case CHROMA444 :
				shrink444( dest1bmp, dest2bmp, dest3bmp, src1bmp, src2bmp,
					src3bmp, lpsrcRect, lpdstRect );
				break;
			default :
				fprintf( stderr, "\nshrink_frame() : unknown format (%ld)!",
					format );
		} /* endswitch */
	} /* endif */
}
/*
void
shrink444(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  unsigned char dest3bmp[], const unsigned char src1bmp[], 
		  const unsigned char src2bmp[], const unsigned char src3bmp[],
		  LPRECT lpsrcRect, LPRECT lpdstRect )
{
	

	if ( ( lpsrcRect->right != lpdstRect->right ) && ( lpsrcRect->bottom != lpdstRect->bottom ) )
	{	
		shrink2xy( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right, lpdstRect->bottom );
		shrink2xy( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right, lpsrcRect->bottom, 
			lpdstRect->right, lpdstRect->bottom );
	}
	else if ( lpsrcRect->right != lpdstRect->right )
	{	
		shrink2x( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right );
		shrink2x( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right );
	}
	else if ( lpsrcRect->bottom != lpdstRect->bottom )
	{	
		shrink2y( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->bottom );
		shrink2y( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->bottom );
	}
	else
	{   
		memcpy( dest1bmp, src1bmp, (long) lpsrcRect->right*lpsrcRect->bottom );
		memcpy( dest2bmp, src2bmp, (long) lpsrcRect->right*lpsrcRect->bottom );
		memcpy( dest3bmp, src3bmp, (long) lpsrcRect->right*lpsrcRect->bottom );
	}
}*/


/* downsizes a YUV4:2:0 planar bitmap 
void
shrink420(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  unsigned char dest3bmp[], const unsigned char src1bmp[], 
		  const unsigned char src2bmp[], const unsigned char src3bmp[],
		  LPRECT lpsrcRect, LPRECT lpdstRect )
{


	if ( ( lpsrcRect->right != lpdstRect->right ) && 
		 ( lpsrcRect->bottom != lpdstRect->bottom ) )
	{
		shrink2xy( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right, lpdstRect->bottom );
		shrink2xy( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, lpsrcRect->bottom>>1, 
			lpdstRect->right>>1, lpdstRect->bottom>>1 );
	}
	else if ( lpsrcRect->right != lpdstRect->right )
	{
		shrink2x( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right );
		shrink2x( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, 
			lpsrcRect->bottom>>1, lpdstRect->right>>1 );
	}
	else if ( lpsrcRect->bottom != lpdstRect->bottom )
	{
		shrink2y( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->bottom );
		shrink2y( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, 
			lpsrcRect->bottom>>1, lpdstRect->bottom>>1 );
	}
	else
	{ 
		memcpy( dest1bmp, src1bmp, (long) lpsrcRect->right*lpsrcRect->bottom );
		memcpy( dest2bmp, src2bmp, (long) (lpsrcRect->right>>1)*(lpsrcRect->bottom>>1) );
		memcpy( dest3bmp, src3bmp, (long) (lpsrcRect->right>>1)*(lpsrcRect->bottom>>1) );
	}
}
*/

/* downsizes a YUV4:2:2 planar bitmap 
void
shrink422(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  unsigned char dest3bmp[], const unsigned char src1bmp[], 
		  const unsigned char src2bmp[], const unsigned char src3bmp[],
		  LPRECT lpsrcRect, LPRECT lpdstRect )
{


	if ( ( lpsrcRect->right != lpdstRect->right ) && ( lpsrcRect->bottom != lpdstRect->bottom ) )
	{
		shrink2xy( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right, lpdstRect->bottom );
		shrink2xy( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, lpsrcRect->bottom, 
			lpdstRect->right>>1, lpdstRect->bottom );
	}
	else if ( lpsrcRect->right != lpdstRect->right )
	{
		shrink2x( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->right );
		shrink2x( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, lpsrcRect->bottom, 
			lpdstRect->right>>1 );
	}
	else if ( lpsrcRect->bottom != lpdstRect->bottom )
	{
		shrink2y( dest1bmp, NULL, src1bmp, NULL, lpsrcRect->right, lpsrcRect->bottom, lpdstRect->bottom );
		shrink2y( dest2bmp, dest3bmp, src2bmp, src3bmp, lpsrcRect->right>>1, lpsrcRect->bottom, 
			lpdstRect->bottom );
	}
	else
	{  
		memcpy( dest1bmp, src1bmp, (long) lpsrcRect->right*lpsrcRect->bottom );
		memcpy( dest2bmp, src2bmp, (long) (lpsrcRect->right)*(lpsrcRect->bottom>>1) );
		memcpy( dest3bmp, src3bmp, (long) (lpsrcRect->right)*(lpsrcRect->bottom>>1) );
	}
}
*/
void
shrink2x(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  const unsigned char src1bmp[], const unsigned char src2bmp[], 
		  short srcW,short srcH, short dstW )
{
	long pixAcc[4];/* pixel-sample accumulator */
	DWORD dwPxWeight;

	short srcXStep; /* source bitmap step size <x-direction> */
	short destXStep; /* dest bitmap step size <x-drection> */
	short diffXAcc;  /* differential accumulator */
	short srcXcount;   /* pixel index <X co-ordinate> */
	short destXcount;   /* pixel index <X co-ordinate> */
	short pixWeight; /* current source-pixel's weighting factor */

	short numerX = 0; /* numerator component for approximating destXstep */
	    /* destXstep = (destX_numerator / STEP_FRACTION_VAL) */
	
	short y; /* for-loop variable, <y> index for dest_bmp[] */

	
	unsigned char *pSrc1 = NULL, *pSrc2= NULL;
	unsigned char *pDest1= NULL, *pDest2=NULL;


	/* srcW will always be a multiple of 8 */
	srcXStep = ( dstW >> (SHRINK_GRANULARITY_EXP-1) ); 
	destXStep= ( srcW >> (SHRINK_GRANULARITY_EXP-1) );  

	// this code reduces the srcXStep and destXStep as much as possible
	while ( !( (srcXStep & 0x01) || (destXStep & 0x01) ) )
	{
		srcXStep = srcXStep >> 1;
		destXStep = destXStep >> 1;
	}
	

	/* dstW might not be multiple of EXP! */
/* PX3 TEMP OUT .. needs to be reimplemented.. 
	if ( ( dstW % (SHRINK_GRANULARITY_VAL>>1) ) != 0  ) 
	{
		fprintf( stderr,"\nshrink2x() : DestX (%ld) not divisible by %d!",
			dstW, (SHRINK_GRANULARITY_VAL) );
		exit( ERROR );
	}

	if ( dstW > srcW )
	{
		fprintf( stderr,"\nshrink2x() : DestX (%ld) > InputX (%ld) !",
			dstW, srcW );
		exit( ERROR );
	}*/

	numerX = (STEP_FRACTION_VAL / destXStep); /* numerator for divisor*/
	   /* divisor = destXstep = (numer / STEP_FRACTION_VAL) */

	pixAcc[0]=pixAcc[1]=pixAcc[2]=pixAcc[3]=0;
	srcXcount=0;
	destXcount=0;

  // determine whether we should scale TWO planes src1, src2,  orjust 1 plane

  if ( dest2bmp != NULL && src2bmp != NULL )
	for ( y=0; y < srcH; ++y ) /* 1) convert two planes */
	{
		/* pSrc = leftmost pixel on current row [x] in src_bmp */
		pSrc1 = (unsigned char *)(src1bmp + (y*srcW));
		pSrc2 = (unsigned char *)(src2bmp + (y*srcW));
		/* output is centered, with respect to source bitmap's co-ordinate axis */
		/* pDest = leftmost pixel on current row [x] in dest_bmp */
		pDest1 = dest1bmp + (y*srcW + ((srcW - dstW)>>1) ); 
		pDest2 = dest2bmp + (y*srcW + ((srcW - dstW)>>1) ); 
		
		diffXAcc = 0;
		srcXcount = 0; /* reset to left of current scan line (row) */
		for( destXcount = 0; destXcount < dstW; ++destXcount )
		{
		  /* Do we have a "residual" previous src pixel? */
		  if ( diffXAcc != 0 )
		  {
			pixWeight = - diffXAcc;
			/* initialize Accumulator to residual portion of 
			   previous pixel (src_bmp[]) to pixelAcc */
			pixAcc[0] = ( pixWeight * pSrc1[ srcXcount ] );
			pixAcc[1] = ( pixWeight * pSrc2[ srcXcount ] );
			++srcXcount; /* done with residual, move to next src pixel */
		  }
		  else
		  {	/* no residual previous source pixel, reset accumulator */
			pixAcc[0] = 0; 
			pixAcc[1] = 0; 
		  }

		  diffXAcc += destXStep; /* add increment to diff accumulator */

		  /* scan and add source pixels to pixel accumulator */
		  while ( diffXAcc >= srcXStep )
		  {
			/* add current pixel (src_bmp[]) to pixel-Accumulator */
			pixAcc[0] += (srcXStep * pSrc1[ srcXcount ] );
			pixAcc[1] += (srcXStep * pSrc2[ srcXcount ] );
			diffXAcc -= srcXStep; /* update diff accumulator */
			/* move to next src_bmp pixel */
			++srcXcount; 
		  }

		
		  if ( diffXAcc != 0 )  /* check for source-pixel "residual" */
		  {	/* yes residual, add fractional part of current pixel */
			pixAcc[0] += (diffXAcc * pSrc1[ srcXcount ] );
			pixAcc[1] += (diffXAcc * pSrc2[ srcXcount ] );
		  }

		  /* done with current output pixel, write it to dest_bmp[] */
		  //pDest1[ destXcount ] = (pixAcc[0] * numerX ) >> STEP_FRACTION_EXP;
		  //pDest2[ destXcount ] = (pixAcc[1] * numerX ) >> STEP_FRACTION_EXP ;
		  rowbuf1[ destXcount ] = pixAcc[0];
		  rowbuf2[ destXcount ] = pixAcc[1];

		  /* originally, the output-pixel = pixAcc[0] / destXstep, however
		     INTEGER-DIVIDE is *slow*.  Instead, the divide operation is
		   "approximated" by a multiplication followed by bitshift (>>)
		   For example, (pixAcc / 7.000) can be emulated like this :
		      ( pixAcc * 146 ) / 1024             ( 146/1024 = ~7.013 )
		      ( pixAcc * numer ) << 10            ( (<<10)  = ~ /1024 )
			  ( pixAcc * numer ) << EXPONENT_VALUE    (numer=146, EXPONENT=10)
		   */

		  diffXAcc -= srcXStep;
		  /* diffXAcc is eithre 0 or negative. A negative value */
		  /* indicates a "residual" pixWeight for the next output-pixel*/

		} /* endfor( destYcount ) */

		dwPxWeight = (DWORD) numerX;
	//	mmx_acc2pix2( dwPxWeight, dstW, rowbuf1, rowbuf2, pDest1, pDest2 );
		mmx_acc2pix1( dwPxWeight, dstW, rowbuf1, pDest1 );
		mmx_acc2pix1( dwPxWeight, dstW, rowbuf2, pDest2 );
	} /* endfor (y=0...) */

  else

	for ( y=0; y < srcH; ++y )	/* convert ONE plane only */
	{
		/* pSrc = leftmost pixel on current row [x] in src_bmp */
		pSrc1 = (unsigned char *)(src1bmp + (y*srcW));
		//pSrc2 = src2bmp + (y*srcW);
		/* output is centered, with respect to source bitmap's co-ordinate axis */
		/* pDest = leftmost pixel on current row [x] in dest_bmp */
		pDest1 = dest1bmp + (y*srcW + ((srcW - dstW)>>1) ); 
		//pDest2 = dest2bmp + (y*srcW + ((srcW - dstW)>>1) ); 
		
		diffXAcc = 0;
		srcXcount = 0; /* reset to left of current scan line (row) */
		for( destXcount = 0; destXcount < dstW; ++destXcount )
		{
		  /* Do we have a "residual" previous src pixel? */
		  if ( diffXAcc != 0 )
		  {
			pixWeight = - diffXAcc;
			/* initialize Accumulator to residual portion of 
			   previous pixel (src_bmp[]) to pixelAcc */
			pixAcc[0] = ( pixWeight * pSrc1[ srcXcount ] );
			//pixAcc[1] = ( pixWeight * pSrc2[ srcXcount ] );
			++srcXcount; /* done with residual, move to next src pixel */
		  }
		  else
		  {	/* no residual previous source pixel, reset accumulator */
			pixAcc[0] = 0; 
			//pixAcc[1] = 0; 
		  }

		  diffXAcc += destXStep; /* add increment to diff accumulator */

		  /* scan and add source pixels to pixel accumulator */
		  while ( diffXAcc >= srcXStep )
		  {
			/* add current pixel (src_bmp[]) to pixel-Accumulator */
			pixAcc[0] += (srcXStep * pSrc1[ srcXcount ] );
			//pixAcc[1] += (srcXStep * pSrc2[ srcXcount ] );
			diffXAcc -= srcXStep; /* update diff accumulator */
			/* move to next src_bmp pixel */
			++srcXcount; 
		  }

		
		  if ( diffXAcc != 0 )  /* check for source-pixel "residual" */
		  {	/* yes residual, add fractional part of current pixel */
			pixAcc[0] += (diffXAcc * pSrc1[ srcXcount ] );
			//pixAcc[1] += (diffXAcc * pSrc2[ srcXcount ] );
		  }

		  /* done with current output pixel, write it to dest_bmp[] */
		  rowbuf1[ destXcount ] = pixAcc[0];
		  //pDest1[ destXcount ] = (pixAcc[0] * numerX ) >> STEP_FRACTION_EXP;
		  //pDest2[ destXcount ] = (pixAcc[1] * numerX ) >> STEP_FRACTION_EXP ;

		  /* originally, the output-pixel = pixAcc[0] / destXstep, however
		     INTEGER-DIVIDE is *slow*.  Instead, the divide operation is
		   "approximated" by a multiplication followed by bitshift (>>)
		   For example, (pixAcc / 7.000) can be emulated like this :
		      ( pixAcc * 146 ) / 1024             ( 146/1024 = ~7.013 )
		      ( pixAcc * numer ) << 10            ( (<<10)  = ~ /1024 )
			  ( pixAcc * numer ) << EXPONENT_VALUE    (numer=146, EXPONENT=10)
		   */

		  diffXAcc -= srcXStep;
		  /* diffXAcc is eithre 0 or negative. A negative value */
		  /* indicates a "residual" pixWeight for the next output-pixel*/

		} /* endfor( destYcount ) */

		dwPxWeight = (DWORD) numerX;
		mmx_acc2pix1( dwPxWeight, dstW, rowbuf1, pDest1 );
	} /* endfor (y=0...) */

} /* endfunction shrink2x() */



/* tweaked to scale image ROW-by-ROW, (not pixel by pixel) */
/* downsizes YUV4:2:0 planar bitmap in Y-direction, */
void
shrink2y(unsigned char dest1bmp[], unsigned char dest2bmp[],
		  const unsigned char src1bmp[], const unsigned char src2bmp[],
		  short srcW,short srcH, short dstH )
{
	short srcYStep; /* source bitmap step size <Y-direction> */
	short destYStep; /* dest bitmap step size <Y-drection> */
	short diffYAcc;  /* differential accumulator */
	short srcYcount;   /* pixel index <Y co-ordinate> */
	short destYcount;   /* pixel index <Y co-ordinate> */
	short pixWeight; /* current source-pixel's weighting factor */
	long dwPixWeight; /* DWORD pixweight mask for mmx-function */

	short numerY = 0; /* numerator component for approximating destYstep */
	    /* destYstep = (destY_numerator / STEP_FRACTION_VAL) */
	
	short x; /* for-loop variable, <x> index for dest_bmp[] */

	unsigned char *pSrc1 = NULL, *pSrc2= NULL;
	unsigned char *pDest1= NULL, *pDest2=NULL;

	/* pixel row accumulator */
	//long rowAcc1[MAXLINE],rowAcc2[MAXLINE],rowAcc3[MAXLINE];

	srcYcount=0;
	destYcount=0;

	/* srcH will always be a multiple of 16 */
	srcYStep = ( dstH >> (SHRINK_GRANULARITY_EXP-1) ); 
	destYStep= ( srcH >> (SHRINK_GRANULARITY_EXP-1) );  

	// this code reduces the srcYStep and destYStep as much as possible
	while ( !( (srcYStep & 0x01) || (destYStep & 0x01) ) )
	{
		srcYStep = srcYStep >> 1;
		destYStep = destYStep >> 1;
	}

	/* dstH might not be multiple of EXP! */
/* PX3 TEMP OUT .. needs to be reimplemented.. 
	if ( ( dstH % (SHRINK_GRANULARITY_VAL>>1) ) != 0  ) 
	{
		fprintf( stderr,"\nshrink2y() : DestY (%ld) not divisible by %d!",
			dstH, (SHRINK_GRANULARITY_VAL>>1) );
		exit( ERROR );
	}

	if ( dstH > srcH )
	{
		fprintf( stderr,"\nshrink2y() : DestY (%ld) > InputY (%ld) !",
			dstH, srcH );
		exit( ERROR );
	}*/

	numerY = (STEP_FRACTION_VAL / destYStep); /* numerator for divisor*/
	   /* divisor = destYstep = (numer / STEP_FRACTION_VAL) */


	diffYAcc = 0;
	srcYcount = 0; /* reset to top of current column */

  if ( src1bmp != NULL && src2bmp != NULL )
	for( destYcount = 0; destYcount < dstH; ++destYcount )
	{
		/* Do we have a "residual" previous src pixel? */
		if ( diffYAcc != 0 )
		{
			pixWeight = -diffYAcc;
			dwPixWeight = (DWORD)  pixWeight;
			/* initialize Accumulator to residual portion of 
			   previous row (src_bmp[]) to rowAcc */
			pSrc1 = (unsigned char *)(src1bmp + srcYcount*srcW);
			pSrc2 = (unsigned char *)(src2bmp + srcYcount*srcW);

			mmx_rowset2( dwPixWeight, srcW, rowAcc1, rowAcc2, pSrc1, pSrc2 );
		//	for ( x=0; x < srcW; ++x )
		//	{
		//	  rowAcc1[x] = ( pixWeight * pSrc1[ x]);
		//	  rowAcc2[x] = ( pixWeight * pSrc2[ x]);
		//	}

			++srcYcount; /* done with residual, move to next src pixel */
		}
		else
		{	/* no residual previous source pixel, reset row accumulator */
			for ( x=0; x < srcW; ++x )
			{
			  rowAcc1[x] = 0;
			  rowAcc2[x] = 0;
			}
		}
		
		diffYAcc += destYStep; /* add increment to diff accumulator */

		while ( diffYAcc >= srcYStep )
		{
		  pSrc1 = (unsigned char *)(src1bmp + srcYcount*srcW); /* calculate address */
		  pSrc2 = (unsigned char *)(src2bmp + srcYcount*srcW);

		  /* add weighted current row (src_bmp[]) to row-Accumulator */

		  dwPixWeight = (DWORD)  srcYStep;
	//	  mmx_rowacc1( dwPixWeight, srcW, rowAcc1, pSrc1, 0xFFFFFFFF );
		  mmx_rowacc2( dwPixWeight, srcW, rowAcc1, rowAcc2, pSrc1, pSrc2 );

		//  for ( x=0; x < srcW; ++x )
		//  {
		//	rowAcc1[x] += (srcYStep * pSrc1[ x ]);
		//	rowAcc2[x] += (srcYStep * pSrc2[ x ]);
		//  }
		  diffYAcc -= srcYStep; /* update diff accumulator */
		  ++srcYcount; /* move to next src_bmp row */
		}

		pSrc1 += srcW; /* update pSrc pointers */
		pSrc2 += srcW;

		dwPixWeight = (DWORD)  diffYAcc; /* add residual of last row */
//		mmx_rowacc1( dwPixWeight, srcW, rowAcc1, pSrc1, 0xFFFFFFFF );
		mmx_rowacc2( dwPixWeight, srcW, rowAcc1, rowAcc2, pSrc1, pSrc2 );

//		for ( x=0; x < srcW; ++x ) 
//		{
//			rowAcc1[x] += (diffYAcc * pSrc1[ x ]);
//			rowAcc2[x] += (diffYAcc * pSrc2[ x ]);
//		}


		/* output is centered, with respect to source bitmap's co-ordinate axis */
		/* pDest = topmost pixel on current column [x] in dest_bmp */
		pDest1 = dest1bmp + (destYcount + ((srcH - dstH)>>1) )*srcW; 
		pDest2 = dest2bmp + (destYcount + ((srcH - dstH)>>1) )*srcW; 

		/* done with current row, write it to dest_bmp[] */
		dwPixWeight = (DWORD) numerY;
	//	mmx_acc2pix2( dwPixWeight, srcW, rowAcc1, rowAcc2, pDest1, pDest2 );
		mmx_acc2pix1( dwPixWeight, srcW, rowAcc1, pDest1 );
		mmx_acc2pix1( dwPixWeight, srcW, rowAcc2, pDest2 );
	//	for ( x=0; x < srcW; ++x ) /* add residual of last row */
	//	{
	//	  pDest1[ x ] = ( rowAcc1[x] * numerY ) >> STEP_FRACTION_EXP;
	//	  pDest2[ x ] = ( rowAcc2[x] * numerY ) >> STEP_FRACTION_EXP;
	//	}

		diffYAcc -= srcYStep;

		/* diffYAcc is now either 0 or a negative #. A negative # */
		/* indicates a "residual" pixWeight for the next output-pixel*/


		/* originally, the output-pixel = pixAcc[0] / destXstep, however
		   INTEGER-DIVIDE is *slow*.  Instead, the divide operation is
		   "approximated" by a multiplication followed by bitshift (>>)
		   For example, (pixAcc / 7.000) can be emulated like this :
		      ( pixAcc * 146 ) / 1024             ( 146/1024 = ~7.013 )
		      ( pixAcc * numer ) << 10            ( (<<10)  = ~ /1024 )
			  ( pixAcc * numer ) << EXPONENT_VALUE    (numer=146, EXPONENT=10)
		 */
	} /* endfor( destYcount ) */

  else

	for( destYcount = 0; destYcount < dstH; ++destYcount )
	{
		/* Do we have a "residual" previous src pixel? */
		if ( diffYAcc != 0 )
		{
			pixWeight = -diffYAcc;
			dwPixWeight = (DWORD)  pixWeight;
			/* initialize Accumulator to residual portion of 
			   previous row (src_bmp[]) to rowAcc */
			pSrc1 = (unsigned char *)(src1bmp + srcYcount*srcW);
			//pSrc2 = src2bmp + srcYcount*srcW;

		//	mmx_rowset2( dwPixWeight, srcW, rowAcc1, rowAcc2, pSrc1, pSrc2 );
			mmx_rowacc1( dwPixWeight, srcW, rowAcc1, pSrc1, 0x00000000 );
		//	for ( x=0; x < srcW; ++x )
		//	{
		//	  rowAcc1[x] = ( pixWeight * pSrc1[ x]);
		//	  rowAcc2[x] = ( pixWeight * pSrc2[ x]);
		//	}

			++srcYcount; /* done with residual, move to next src pixel */
		}
		else
		{	/* no residual previous source pixel, reset row accumulator */
			for ( x=0; x < srcW; ++x )
			{
			  rowAcc1[x] = 0;
		//	  rowAcc2[x] = 0;
			}
		}
		
		diffYAcc += destYStep; /* add increment to diff accumulator */

		while ( diffYAcc >= srcYStep )
		{
		  pSrc1 = (unsigned char *)(src1bmp + srcYcount*srcW); /* calculate address */
		  //pSrc2 = src2bmp + srcYcount*srcW;

		  /* add weighted current row (src_bmp[]) to row-Accumulator */

		  dwPixWeight = (DWORD)  srcYStep;
		  mmx_rowacc1( dwPixWeight, srcW, rowAcc1, pSrc1, 0xFFFFFFFF );
		//  mmx_rowacc2( dwPixWeight, srcW, rowAcc1, rowAcc2, pSrc1, pSrc2 );

		//  for ( x=0; x < srcW; ++x )
		//  {
		//	rowAcc1[x] += (srcYStep * pSrc1[ x ]);
		//	rowAcc2[x] += (srcYStep * pSrc2[ x ]);
		//  }
		  diffYAcc -= srcYStep; /* update diff accumulator */
		  ++srcYcount; /* move to next src_bmp row */
		}

		pSrc1 += srcW; /* update pSrc pointers */
		//pSrc2 += srcW;

		dwPixWeight = (DWORD)  diffYAcc; /* add residual of last row */
		mmx_rowacc1( dwPixWeight, srcW, rowAcc1, pSrc1, 0xFFFFFFFF );
		//mmx_rowacc2( dwPixWeight, srcW, rowAcc1, rowAcc2, pSrc1, pSrc2 );
//		for ( x=0; x < srcW; ++x ) 
//		{
//			rowAcc1[x] += (diffYAcc * pSrc1[ x ]);
//			rowAcc2[x] += (diffYAcc * pSrc2[ x ]);
//		}


		/* output is centered, with respect to source bitmap's co-ordinate axis */
		/* pDest = topmost pixel on current column [x] in dest_bmp */
		pDest1 = dest1bmp + (destYcount + ((srcH - dstH)>>1) )*srcW; 
		//pDest2 = dest2bmp + (destYcount + ((srcH - dstH)>>1) )*srcW; 

		/* done with current row, write it to dest_bmp[] */
		dwPixWeight = (DWORD) numerY;
		mmx_acc2pix1( dwPixWeight, srcW, rowAcc1, pDest1 );
	//	for ( x=0; x < srcW; ++x ) /* add residual of last row */
	//	{
	//	  pDest1[ x ] = ( rowAcc1[x] * numerY ) >> STEP_FRACTION_EXP;
	//	}

		diffYAcc -= srcYStep;

		/* diffYAcc is now either 0 or a negative #. A negative # */
		/* indicates a "residual" pixWeight for the next output-pixel*/


		/* originally, the output-pixel = pixAcc[0] / destXstep, however
		   INTEGER-DIVIDE is *slow*.  Instead, the divide operation is
		   "approximated" by a multiplication followed by bitshift (>>)
		   For example, (pixAcc / 7.000) can be emulated like this :
		      ( pixAcc * 146 ) / 1024             ( 146/1024 = ~7.013 )
		      ( pixAcc * numer ) << 10            ( (<<10)  = ~ /1024 )
			  ( pixAcc * numer ) << EXPONENT_VALUE    (numer=146, EXPONENT=10)
		 */
	} /* endfor( destYcount ) */

} /* endfunction shrink2y() */


void
shrink2xy(unsigned char dest1bmp[], unsigned char dest2bmp[],
	const unsigned char src1bmp[], const unsigned char src2bmp[], 
	short srcW,short srcH, short dstW, short dstH )
{
	long pixAcc[4];/* pixel-sample accumulator */
	DWORD dwPixWeight;
	short srcXStep; /* source bitmap step size <x-direction> */
	short destXStep; /* dest bitmap step size <x-drection> */
	short diffXAcc;  /* differential accumulator */
	short srcXcount;   /* pixel index <X co-ordinate> */
	short destXcount;   /* pixel index <X co-ordinate>, loop variable */

	static short srcYStep; /* source bitmap step size <Y-direction> */
	short destYStep; /* dest bitmap step size <Y-drection> */
	short diffYAcc;  /* differential accumulator */
	short srcYcount;   /* pixel index <Y co-ordinate> */
	short destYcount;   /* pixel index <Y co-ordinate>, loop variable */

	short numerY = 0; /* numerator component for approximating destYstep */
	    /* destYstep = (destY_numerator / STEP_FRACTION_VAL) */

	short numerX = 0; /* numerator component for approximating destXstep */
	    /* destXstep = (destX_numerator / STEP_FRACTION_VAL) */

	long dest_offset = 0; /* destination address offset */

	short i; /* loop variable */
	short yweight; /* pixWeight in Y-direction */

	
	unsigned char *pSrc1 = NULL, *pSrc2= NULL;
	unsigned char *pDest1= NULL, *pDest2=NULL;
	//unsigned char *rowAcc1=NULL, *rowAcc2=NULL;
	//unsigned char *rowbuf1=NULL, *rowbuf2=NULL;
	//long rowAcc1[MAXLINE], rowAcc2[MAXLINE];
	//long rowbuf1[MAXLINE], rowbuf2[MAXLINE];

	/* srcW & srcH will always be a multiple of 8 */
	srcXStep = ( dstW >> ( SHRINK_GRANULARITY_EXP -1 ) ); 
	destXStep= ( srcW >> ( SHRINK_GRANULARITY_EXP -1 ) );  
	srcYStep = ( dstH >> ( SHRINK_GRANULARITY_EXP -1 ) ); 
	destYStep= ( srcH >> ( SHRINK_GRANULARITY_EXP -1 ) );  

	// this code reduces the srcXStep and destXStep as much as possible
	while ( !( (srcXStep & 0x01) || (destXStep & 0x01) ) )
	{
		srcXStep = srcXStep >> 1;
		destXStep = destXStep >> 1;
	}

	while ( !( (srcYStep & 0x01) || (destYStep & 0x01) ) )
	{
		srcYStep = srcYStep >> 1;
		destYStep = destYStep >> 1;
	}


	/* dstW might not be multiple of EXP! */
/* PX3 TEMP OUT .. needs to be reimplemented.. 
	if ( ( ( dstW % (SHRINK_GRANULARITY_VAL>>1 ) ) != 0 ) || 
		( ( dstH % (SHRINK_GRANULARITY_VAL>>1 ) ) != 0 ) ) 
	{
		fprintf( stderr,"\nshrink2xy() : DestX or DestY not divisible by %d!",
			(SHRINK_GRANULARITY_VAL ) );
		exit( ERROR );
	}

	if ( (dstW > srcW) || (dstH > srcH) )
	{
		fprintf( stderr,"\nshrink2xy() : Dest size > Input size !" );
		exit( ERROR );
	}*/

	numerX = (STEP_FRACTION_VAL / destXStep); /* numerator for divisor*/
	   /* divisor = destXstep = (numer / STEP_FRACTION_VAL) */
	numerY = (STEP_FRACTION_VAL / destYStep); /* numerator for divisor*/
	   /* divisor = destYstep = (numer / STEP_FRACTION_VAL) */

	pixAcc[0]=pixAcc[1]=pixAcc[2]=pixAcc[3]=0;
	srcXcount=0;
	destXcount=0;
	srcYcount=0;
	destYcount=0;
	diffYAcc = 0;

  if ( src2bmp != NULL && dest2bmp != NULL )
  for ( destYcount=0; destYcount < dstH; ++destYcount )  /* scale 2 planes */
  {
	/* 1) add residual fraction of previous row (src_bmp[]) to rowAcc[] */
	if ( diffYAcc != 0 ) /* is there a residual in the Y direction? */
	{
	  --srcYcount; /* backtrack one source row */
	  yweight = abs( diffYAcc );

	  dwPixWeight = (DWORD) yweight;
	  mmx_rowset2l( dwPixWeight, dstW, rowAcc1, rowAcc2, rowbuf1, rowbuf2 );
	//  for ( i = 0; i < dstW; ++i )
	//  {
	//	rowAcc1[ i ] = ( yweight * rowbuf1[ i ] );
	//	rowAcc2[ i ] = ( yweight * rowbuf2[ i ]);
	//  } 
	  ++srcYcount; /* advance to next source row */
	}
	else
	{	/* no residual, clear the rowAccumulator */
	  for ( i = 0; i < dstW; ++i )
	  {
		rowAcc1[ i ] = 0;
		rowAcc2[ i ] = 0;
	  } 
	}

	diffYAcc += destYStep; /* add increment to vertical diff acc */

/*----- inner while loop ... calculate by ROWS, process one row at a time */
	while ( diffYAcc > 0 )
	{
	  diffXAcc = 0;
	  srcXcount = 0; /* reset to left of current scan line (row) */
  
	  pSrc1 = (unsigned char *)(src1bmp + (srcYcount*srcW));
	  pSrc2 = (unsigned char *)(src2bmp + (srcYcount*srcW));
	
	  for( destXcount = 0; destXcount < dstW; ++destXcount )
	  {
		/* check for a "residual" from previous row */
		if ( diffXAcc != 0 ) 
		{
		  /* add residual portion of previous pixel (src_bmp[]) to pixelAcc */
		  pixAcc[0] = ( abs( diffXAcc ) * pSrc1[ srcXcount ] );
		  pixAcc[1] = ( abs( diffXAcc ) * pSrc2[ srcXcount ]);
		  ++srcXcount;  /* advance to next row */
		}
		else
		{ /* no residual, clear the rowAcc[] */
		  pixAcc[0] = 0;
		  pixAcc[1] = 0;
		}
		
		diffXAcc += destXStep; /* add increment to diff accumulator */

		while ( diffXAcc >= srcXStep )
		{
			diffXAcc -= srcXStep;
			/* add whole current pixel (src_bmp[]) to pixel-Accumulator */
			pixAcc[0] += (srcXStep * pSrc1[ srcXcount ] );
			pixAcc[1] += (srcXStep * pSrc2[ srcXcount ]);
			++srcXcount; /* move to next src_bmp pixel */
		}

		/* add fractional of current pixel (src_bmp[]) to pixel-Acc */
		pixAcc[0] += (diffXAcc * pSrc1[ srcXcount ] );
		pixAcc[1] += (diffXAcc * pSrc2[ srcXcount ]);

		diffXAcc -= srcXStep; /* diffXAcc is now a negative #, a "residual" */
		/* done generating current scanline, now write it to rowbuf[] */
		// ">> STEP_FRACTION_EXP " is part of mmx)rowacc2l()
		rowbuf1[ destXcount ] = (pixAcc[0]*numerX) ;
		rowbuf2[ destXcount ] = (pixAcc[1]*numerX) ;

	  } /* endfor( destXcount ) */

	  /* Current row (rowbuf[] has been filled. merge it with 
	     the row accumulator rowAcc[] */
	  yweight = ( diffYAcc > srcYStep ) ? srcYStep : diffYAcc;
 	  dwPixWeight = (DWORD) yweight;
	  mmx_rowacc2l( dwPixWeight, dstW, rowAcc1, rowAcc2, rowbuf1, rowbuf2 );
	//  for( i =0; i < dstW; ++i )
	//  {
	//	  rowAcc1[ i ] += ( yweight * rowbuf1[ i ]);
	//	  rowAcc2[ i ] += ( yweight * rowbuf2[ i ]);
	//  }

	  diffYAcc -= srcYStep;
	  ++srcYcount; /* move to next source row */

	} /*endwhile ( diffYAcc > 0 ); */
/*----- end of inner while loop ... ROWS */

	/* Now write the rowAcc to the destination bitmap */
	/* diffYAcc is now either 0 or negative...negative implies "residual" */

	/* calculate the upper-left of the output rectangle...this complicated
	   expression is actually pDest = destbmp + (Y_offset) + (X_offset) ; */
	/* output is centered, with respect to source bitmap's co-ordinate axis */

	/* pDest = leftmost pixel on current row [y] in dest_bmp */

	dest_offset = ( (srcW - dstW)>>1 ) + 
		( destYcount + ( (srcH - dstH)>>1 ) ) * srcW;
	pDest1 = dest1bmp + dest_offset ; 
	pDest2 = dest2bmp + dest_offset ;

	dwPixWeight = (DWORD) numerY;
//	mmx_acc2pix2( dwPixWeight, dstW, rowAcc1, rowAcc2, pDest1, pDest2 );
	mmx_acc2pix1( dwPixWeight, dstW, rowAcc1, pDest1 );
	mmx_acc2pix1( dwPixWeight, dstW, rowAcc2, pDest2 );
//	for ( i = 0; i < dstW; ++i )
//	{	/* write entire rowAccumulator to dest_bmp[] */
//		pDest1[ i ] = (rowAcc1[ i ]*numerY) >> STEP_FRACTION_EXP;
//		//pDest2[ i ] = (rowAcc2[ i ]*numerY) >> STEP_FRACTION_EXP;
//	}

  } /* endfor (y=0...) */
else
  for ( destYcount=0; destYcount < dstH; ++destYcount ) // convert 1  plane
  {
	/* 1) add residual fraction of previous row (src_bmp[]) to rowAcc[] */
	if ( diffYAcc != 0 ) /* is there a residual in the Y direction? */
	{
	  --srcYcount; /* backtrack one source row */
	  yweight = abs( diffYAcc );

	  dwPixWeight = (DWORD) yweight;
	  mmx_rowacc1l( dwPixWeight, dstW, rowAcc1, rowbuf1, 0x00000000 );
	//  for ( i = 0; i < dstW; ++i )
	//  {
	//	rowAcc1[ i ] = ( yweight * rowbuf1[ i ] );
	//	//rowAcc2[ i ] = ( yweight * rowbuf2[ i ]);
	//  } 
	  ++srcYcount; /* advance to next source row */
	}
	else
	{	/* no residual, clear the rowAccumulator */
	  for ( i = 0; i < dstW; ++i )
	  {
		rowAcc1[ i ] = 0;
		//rowAcc2[ i ] = 0;
	  } 
	}

	diffYAcc += destYStep; /* add increment to vertical diff acc */

/*----- inner while loop ... calculate by ROWS, process one row at a time */
	while ( diffYAcc > 0 )
	{
	  diffXAcc = 0;
	  srcXcount = 0; /* reset to left of current scan line (row) */
  
	  pSrc1 = (unsigned char *)(src1bmp + (srcYcount*srcW));
	  //pSrc2 = src2bmp + (srcYcount*srcW);
	
	  for( destXcount = 0; destXcount < dstW; ++destXcount )
	  {
		/* check for a "residual" from previous row */
		if ( diffXAcc != 0 ) 
		{
		  /* add residual portion of previous pixel (src_bmp[]) to pixelAcc */
		  pixAcc[0] = ( abs( diffXAcc ) * pSrc1[ srcXcount ] );
		  //pixAcc[1] = ( abs( diffXAcc ) * pSrc2[ srcXcount ]);
		  ++srcXcount;  /* advance to next row */
		}
		else
		{ /* no residual, clear the rowAcc[] */
		  pixAcc[0] = 0;
		}
		
		diffXAcc += destXStep; /* add increment to diff accumulator */

		while ( diffXAcc >= srcXStep )
		{
			diffXAcc -= srcXStep;
			/* add whole current pixel (src_bmp[]) to pixel-Accumulator */
			pixAcc[0] += (srcXStep * pSrc1[ srcXcount ] );
			//pixAcc[1] += (srcXStep * pSrc2[ srcXcount ]);
			++srcXcount; /* move to next src_bmp pixel */
		}

		/* add fractional of current pixel (src_bmp[]) to pixel-Acc */
		pixAcc[0] += (diffXAcc * pSrc1[ srcXcount ] );
		//pixAcc[1] += (diffXAcc * pSrc2[ srcXcount ]);

		diffXAcc -= srcXStep; /* diffXAcc is now a negative #, a "residual" */
		/* done generating current scanline, now write it to rowbuf[] */
		// ">> STEP_FRACTION_EXP" is part of mxx_rowacc1l();
		rowbuf1[ destXcount ] = (pixAcc[0]*numerX) ;
		//rowbuf2[ destXcount ] = (pixAcc[1]*numerX) >> STEP_FRACTION_EXP;

	  } /* endfor( destXcount ) */

	  /* Current row (rowbuf[] has been filled. merge it with 
	     the row accumulator rowAcc[] */
	  yweight = ( diffYAcc > srcYStep ) ? srcYStep : diffYAcc;
 	  dwPixWeight = (DWORD) yweight;
	  mmx_rowacc1l( dwPixWeight, dstW, rowAcc1, rowbuf1, 0xFFFFFFFF );
	//  for( i =0; i < dstW; ++i )
	//  {
	//	  rowAcc1[ i ] += ( yweight * rowbuf1[ i ]);
	//	  //rowAcc2[ i ] += ( yweight * rowbuf2[ i ]);
	// }

	  diffYAcc -= srcYStep;
	  ++srcYcount; /* move to next source row */

	} /*endwhile ( diffYAcc > 0 ); */
/*----- end of inner while loop ... ROWS */

	/* Now write the rowAcc to the destination bitmap */
	/* diffYAcc is now either 0 or negative...negative implies "residual" */

	/* calculate the upper-left of the output rectangle...this complicated
	   expression is actually pDest = destbmp + (Y_offset) + (X_offset) ; */
	/* output is centered, with respect to source bitmap's co-ordinate axis */

	/* pDest = leftmost pixel on current row [y] in dest_bmp */

	dest_offset = ( (srcW - dstW)>>1 ) + 
		( destYcount + ( (srcH - dstH)>>1 ) ) * srcW;
	pDest1 = dest1bmp + dest_offset ; 
	//pDest2 = dest2bmp + dest_offset ;

	dwPixWeight = (DWORD) numerY;
	mmx_acc2pix1( dwPixWeight, dstW, rowAcc1, pDest1 );
//	for ( i = 0; i < dstW; ++i )
//	{	/* write entire rowAccumulator to dest_bmp[] */
//		pDest1[ i ] = (rowAcc1[ i ]*numerY) >> STEP_FRACTION_EXP;
//		//pDest2[ i ] = (rowAcc2[ i ]*numerY) >> STEP_FRACTION_EXP;
//	}

  } /* endfor (y=0...) */

} /* endfunction shrink2xy() */




// this function performs a row-accumulation operation on two
// source planes.
// dwPxWeight = [ 00 | Mask ], the upper-16 bits MUST BE 00!
// row_width : (# pixels, must be divisible by 4)
void
mmx_rowacc2( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD prowAcc2, unsigned char *pSrc1, unsigned char *pSrc2 )
{
	//DWORD dwPxWeight;	// pixel weight mask, 32-bit version
	//unsigned char *pSrc1, *pSrc2;	// source bitmap pointers

	LPDWORD pSrc1_aligned = (LPDWORD)(((DWORD) pSrc1 ) & 0xFFFFFFFC);
	LPDWORD pSrc2_aligned = (LPDWORD)(((DWORD) pSrc2 ) & 0xFFFFFFFC);
	DWORD pSrc1srlAmt = ((DWORD) pSrc1) - ((DWORD) pSrc1_aligned);
	DWORD pSrc2srlAmt = ((DWORD) pSrc2) - ((DWORD) pSrc2_aligned);

// this function performs the following operation :
//
//		for ( x=0; x < row_width; ++x ) 
//		{
//			prowAcc1[x] += (PixWeight * pSrc1[ x ]);
//			prowAcc2[x] += (PixWeight * pSrc2[ x ]);
//		}

	__asm {
		// the MMX implementation processes 4 source-pixels per plane,
		// per iteration.  All reads are DWORD aligned

		mov eax, [pSrc1_aligned];	// eax = source1
		 mov esi, 0x00;		// esi = x

		mov ebx, [pSrc2_aligned];	// ebx = source2

		mov ecx, [prowAcc1];	// ecx = rowAcc1

		movd mm5, [pSrc1srlAmt]; // byte rshift logical amount for psrc1

		movd mm6, [pSrc2srlAmt]; // byte rshift logical amount for psrc2
		 psllq mm5, 0x03;	// mm5 = bitshift amount for pSrc1 alignment

		mov edx, [prowAcc2];	// edx = rowAcc2
		 psllq mm6, 0x03;	// mm5 = bitshift amount for pSrc1 alignment

		mov edi, [row_width]; // edi = row_width
		 // loop begin, 4 pSrc1 + 4 pSrc2 pixels per iteration

	lp_mmx_rowAcc2: // for ( x = 0; x < row_width; x += 4 )
		movd mm7, [dwPxWeight]; // load pixelmask

		movd mm0, dword ptr [eax + esi]; // read from pSrc1[x]
		 punpckldq mm7, mm7;	// double to 64-bit, mm7 = pixel weight mask

		punpckldq mm0, dword ptr [eax + esi +4]; // mm0 = pSrc1[7..0]
		 pxor mm1, mm1;		// clear mm1 

		movd mm2, dword ptr [ebx + esi]; // read from pSrc2[x]
		 psrlq mm0, mm5;	// mm0 = desired input 4-pixels of pSrc1

		punpckldq mm2, dword ptr [ebx + esi +4]; // mm1 = pSrc2[7..0]

		// convert the four 8-bit pixels in mm0, mm2 -> 16-bit values

		punpcklbw mm0, mm1; // mm0 = 16-bit values pSrc1
		 psrlq mm2, mm6;	// mm2 = desired input 4-pixels of pSrc2

		punpcklbw mm2, mm1; // mm2 = 16-bit values pSrc2
		 movq mm1, mm0;		// mm1 = copy of mm0

		// the multiplies are arranged in funny order, output looks like this:
		/// "wPix1[x] = weighted pixel#x Src1 (pixel * weight)" (32-bit value)
		// mm0 = { wPix1[2] | wPix1[0] }, mm1 = { wPix1[3] | wPix1[1] }
		// mm2 = { wPix2[2] | wPix2[0] }, mm3 = { wPix2[3] | wPix2[1] }

		pmaddwd mm0, mm7;	// mm0 = partial result Acc1
		 psrlq mm1, 16;		// mm1 = pSrc1[ __ _3 _2 _1 ]

		pmaddwd mm1, mm7;	// mm1 = partial result Acc1
		 movq mm3, mm2;		// mm3 = copy of mm2

		pmaddwd mm2, mm7;	// mm2 = partial result Acc2
		 psrlq mm3, 16;		// mm3 = pSrc2[ __ _3 _2 _1 ]

		pmaddwd mm3, mm7;	// mm3 = partial result Acc2
		 movq mm7, mm0;		// mm7 = copy of partial result Acc1

		punpckldq mm0, mm1; // mm0 = { wPix1[1] | wPix1[0] }
		 add esi, 0x04;		// x= x+4

		paddd mm0, qword ptr [ecx];	// mm0 = rowAcc1[1..0] + wPix1[1..0]
		 punpckhdq mm7, mm1;	// mm7 = { wPix1[3] | wPix1[2] }

		paddd mm7, qword ptr [ecx+8];	// mm7 = rowAcc1[3..2] + wPix1[3..2]
		 movq mm1, mm2;	// mm1 = copy of partial result Acc2

		movq qword ptr [ecx], mm0;	// store rowAcc1[1..0]
		 punpckldq mm1, mm3;	// mm1 = {wPix2[1] | wPix2[0] }

		paddd mm1, qword ptr [edx];	// mm1 = rowAcc2[1..0] + wPix2[1..0]
		 punpckhdq mm2, mm3;	// mm2 = {wPix2[3] + wPix2[2] }

		movq qword ptr [ecx+8], mm7;// store rowAcc1[3..2]
		 add ecx, 16;	// rowAcc1 += 16

		paddd mm2, qword ptr [edx+8];// mm2 = rowAcc2[3..2] + wPix2[3..2]

		movq qword ptr [edx], mm1;	// store rowAcc2[1..0]
		
		movq qword ptr [edx+8], mm2;	// store rowAcc2[3..2]
		 add edx, 16;	// rowAcc2 += 16

		cmp esi, edi;	// compare (x<> row_width)
		 jl lp_mmx_rowAcc2;	// end for ( x = 0; x < row_width; x += 4 )

		emms;
	} // end __asm
//	__asm {
//		emms;
//	}
} // end function mmx_rowacc2()


// this function performs a row-set operation on two source planes.
// it is nearly identical to mmx_rowacc2(), except the mult result is
// not summed with the prowAcc[]
// dwPxWeight = [ 00 | Mask ], the upper-16 bits MUST BE 00!
// row_width : (# pixels, must be divisible by 4)
void
mmx_rowset2( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD prowAcc2, unsigned char *pSrc1, unsigned char *pSrc2 )
{
	//DWORD dwPxWeight;	// pixel weight mask, 32-bit version
	//unsigned char *pSrc1, *pSrc2;	// source bitmap pointers

	LPDWORD pSrc1_aligned = (LPDWORD)(((DWORD) pSrc1 ) & 0xFFFFFFFC);
	LPDWORD pSrc2_aligned = (LPDWORD)(((DWORD) pSrc2 ) & 0xFFFFFFFC);
	DWORD pSrc1srlAmt = ((DWORD) pSrc1) - ((DWORD) pSrc1_aligned);
	DWORD pSrc2srlAmt = ((DWORD) pSrc2) - ((DWORD) pSrc2_aligned);

// this function performs the following operation :
//
//		for ( x=0; x < row_width; ++x ) 
//		{
//			prowAcc1[x] = (PixWeight * pSrc1[ x ]);
//			prowAcc2[x] = (PixWeight * pSrc2[ x ]);
//		}

	__asm {
		// the MMX implementation processes 4 source-pixels per plane,
		// per iteration.  All reads are DWORD aligned

		mov eax, [pSrc1_aligned];	// eax = source1
		 mov esi, 0x00;		// esi = x

		mov ebx, [pSrc2_aligned];	// ebx = source2

		mov ecx, [prowAcc1];	// ecx = rowAcc1

		movd mm5, [pSrc1srlAmt]; // byte rshift logical amount for psrc1

		movd mm6, [pSrc2srlAmt]; // byte rshift logical amount for psrc2
		 psllq mm5, 0x03;	// mm5 = bitshift amount for pSrc1 alignment

		mov edx, [prowAcc2];	// edx = rowAcc2
		 psllq mm6, 0x03;	// mm5 = bitshift amount for pSrc1 alignment

		mov edi, [row_width]; // edi = row_width
		 // loop begin, 4 pSrc1 + 4 pSrc2 pixels per iteration

	lp_mmx_rowSet2: // for ( x = 0; x < row_width; x += 4 )
		movd mm7, [dwPxWeight]; // load pixelmask

		movd mm0, dword ptr [eax + esi]; // read from pSrc1[x]
		 punpckldq mm7, mm7;	// double to 64-bit, mm7 = pixel weight mask

		punpckldq mm0, dword ptr [eax + esi +4]; // mm0 = pSrc1[7..0]
		 pxor mm1, mm1;		// clear mm1 

		movd mm2, dword ptr [ebx + esi]; // read from pSrc2[x]
		 psrlq mm0, mm5;	// mm0 = desired input 4-pixels of pSrc1

		punpckldq mm2, dword ptr [ebx + esi +4]; // mm1 = pSrc2[7..0]

		// convert the four 8-bit pixels in mm0, mm2 -> 16-bit values

		punpcklbw mm0, mm1; // mm0 = 16-bit values pSrc1
		 psrlq mm2, mm6;	// mm2 = desired input 4-pixels of pSrc2

		punpcklbw mm2, mm1; // mm2 = 16-bit values pSrc2
		 movq mm1, mm0;		// mm1 = copy of mm0

		// the multiplies are arranged in funny order, output looks like this:
		/// "wPix1[x] = weighted pixel#x Src1 (pixel * weight)" (32-bit value)
		// mm0 = { wPix1[2] | wPix1[0] }, mm1 = { wPix1[3] | wPix1[1] }
		// mm2 = { wPix2[2] | wPix2[0] }, mm3 = { wPix2[3] | wPix2[1] }

		pmaddwd mm0, mm7;	// mm0 = partial result Acc1
		 psrlq mm1, 16;		// mm1 = pSrc1[ __ _3 _2 _1 ]

		pmaddwd mm1, mm7;	// mm1 = partial result Acc1
		 movq mm3, mm2;		// mm3 = copy of mm2

		pmaddwd mm2, mm7;	// mm2 = partial result Acc2
		 psrlq mm3, 16;		// mm3 = pSrc2[ __ _3 _2 _1 ]

		pmaddwd mm3, mm7;	// mm3 = partial result Acc2
		 movq mm7, mm0;		// mm7 = copy of partial result Acc1

		punpckldq mm0, mm1; // mm0 = { wPix1[1] | wPix1[0] }
		 add esi, 0x04;		// x= x+4

		punpckhdq mm7, mm1;	// mm7 = { wPix1[3] | wPix1[2] }
		 movq mm1, mm2;	// mm1 = copy of partial result Acc2

		movq qword ptr [ecx], mm0;	// store rowAcc1[1..0]
		 punpckldq mm1, mm3;	// mm1 = {wPix2[1] | wPix2[0] }

		movq qword ptr [ecx+8], mm7;// store rowAcc1[3..2]
		 punpckhdq mm2, mm3;	// mm2 = {wPix2[3] + wPix2[2] }

		movq qword ptr [edx], mm1;	// store rowAcc2[1..0]
		 add ecx, 16;	// rowAcc1 += 16
		
		movq qword ptr [edx+8], mm2;	// store rowAcc2[3..2]
		 add edx, 16;	// rowAcc2 += 16

		cmp esi, edi;	// compare (x<> row_width)
		 jl lp_mmx_rowSet2;	// end for ( x = 0; x < row_width; x += 4 )

		emms;
	} // end __asm
//	__asm {
//		emms;
//	}
} // end function mmx_rowset2()


// this function performs a row-set operation on two source planes.
// it is nearly identical to mmx_rowset2(), except that pSrc1 & pSrc2
// are DWORD acumulators (not byte accumulators)
// dwPxWeight = [ 00 | Mask ], the upper-16 bits MUST BE 00!
// row_width : (# pixels, must be divisible by 4)
void
mmx_rowset2l( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD prowAcc2, LPDWORD pSrc1, LPDWORD pSrc2 )
{
	//DWORD dwPxWeight;	// pixel weight mask, 32-bit version
	//unsigned char *pSrc1, *pSrc2;	// source bitmap pointers

// this function performs the following operation :
//
//		for ( x=0; x < row_width; ++x ) 
//		{
//			prowAcc1[x] = PixWeight * ( pSrc1[ x ] >> EXP );
//			prowAcc2[x] = PixWeight * ( pSrc2[ x ] >> EXP );
//		}

	__asm {
		// the MMX implementation processes 4 source-pixels per plane,
		// per iteration.  All reads are DWORD aligned

		mov eax, [pSrc1];	// eax = source1

		mov ebx, [pSrc2];	// ebx = source2

		movd mm7, [dwPxWeight]; // load pixelmask (for multiplication)

		mov ecx, [prowAcc1];	// ecx = rowAcc1
		 punpckldq mm7, mm7;	// double to 64-bit, mm7 = pixel mult weight mask

		mov edx, [prowAcc2];	// edx = rowAcc2

		mov edi, [row_width]; // edi = x

		 // loop begin, 4 pSrc1 + 4 pSrc2 pixels per iteration

	lp_mmx_rowSet2L: // for ( x = row_width; x>0 ; x = x -4 )

		movq mm0, qword ptr [eax ]; // read mm0 = pSrc1[1..0]
		 sub edi, 0x04;		// x= x-4

		movq mm1, qword ptr [eax + 8]; // read mm0 = pSrc1[3..2]
		 psrld mm0, STEP_FRACTION_EXP;

		pmaddwd mm0, mm7;	// mm0 = partial result Acc1
		 psrld mm1, STEP_FRACTION_EXP;

		movq mm2, qword ptr [ebx ]; // read mm2 = pSrc2[1..0]
		 pmaddwd mm1, mm7;	// mm1 = partial result Acc1

		movq mm3, qword ptr [ebx +8]; // read mm2 = pSrc2[3..2]
		 psrld mm2, STEP_FRACTION_EXP;

		pmaddwd mm2, mm7;	// mm2 = partial result Acc2
		 psrld mm3, STEP_FRACTION_EXP;

		// the multiplies are arranged in normal order, output looks like this:
		/// "wPix1[x] = weighted pixel#x Src1 (pixel * weight)" (32-bit value)
		// mm0 = { wPix1[1] | wPix1[0] }, mm1 = { wPix1[3] | wPix1[2] }
		// mm2 = { wPix2[1] | wPix2[0] }, mm3 = { wPix2[3] | wPix2[2] }

		movq qword ptr [ecx], mm0;	// store rowAcc1[1..0]
		 pmaddwd mm3, mm7;	// mm3 = partial result Acc2

		movq qword ptr [ecx+8], mm1;// store rowAcc1[3..2]
		 add ecx, 16;	// rowAcc1 += 16

		movq qword ptr [edx], mm2;	// store rowAcc2[1..0]
		 add eax, 16;	// pSrc1 += 4 pixels

		movq qword ptr [edx+8], mm3;	// store rowAcc2[3..2]
		 add edx, 16;	// rowAcc2 += 16

		add ebx, 16;	// pSrc2 += 4 pixels
		 cmp edi, 0x00;	// compare (x<> row_width)

		jg lp_mmx_rowSet2L;	// end for ( x = row_width; x > 0; x -= 4 )

		emms;

	} // end __asm
//	__asm {
//		emms;
//	}
} // end function mmx_rowset2l()


// this function performs a row-set operation on two source planes.
// it is nearly identical to mmx_rowset2(), except that pSrc1 & pSrc2
// are DWORD acumulators (not byte accumulators)
// dwPxWeight = [ 00 | Mask ], the upper-16 bits MUST BE 00!
// row_width : (# pixels, must be divisible by 4)
void
mmx_rowacc2l( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD prowAcc2, LPDWORD pSrc1, LPDWORD pSrc2 )
{
	//DWORD dwPxWeight;	// pixel weight mask, 32-bit version
	//unsigned char *pSrc1, *pSrc2;	// source bitmap pointers

// this function performs the following operation :
//
//		for ( x=0; x < row_width; ++x ) 
//		{
//			prowAcc1[x] += (PixWeight >> EXP ) * pSrc1[ x ]);
//			prowAcc2[x] += (PixWeight >> EXP ) * pSrc2[ x ]);
//		}

	__asm {
		// the MMX implementation processes 4 source-pixels per plane,
		// per iteration.  All reads are DWORD aligned

		mov eax, [pSrc1];	// eax = source1

		mov ebx, [pSrc2];	// ebx = source2

		movd mm7, [dwPxWeight]; // load pixelmask (for multiplication)

		mov ecx, [prowAcc1];	// ecx = rowAcc1
		 punpckldq mm7, mm7;	// double to 64-bit, mm7 = pixel mult weight mask

		mov edx, [prowAcc2];	// edx = rowAcc2

		mov edi, [row_width]; // edi = x = row_width

		 // loop begin, 4 pSrc1 + 4 pSrc2 pixels per iteration

	lp_mmx_rowAcc2L: // for ( x = row_width; x > 0; x = x - 4 )

		movq mm0, qword ptr [eax ]; // read mm0 = pSrc1[1..0]
		 sub edi, 0x04;		// x= x-4

		movq mm1, qword ptr [eax + 8]; // read mm0 = pSrc1[3..2]
		 psrld mm0, STEP_FRACTION_EXP;

		psrld mm1, STEP_FRACTION_EXP;
		 pmaddwd mm0, mm7;	// mm0 = partial result Acc1

		movq mm2, qword ptr [ebx ]; // read mm2 = pSrc2[1..0]
		 pmaddwd mm1, mm7;	// mm1 = partial result Acc1

		movq mm3, qword ptr [ebx +8]; // read mm2 = pSrc2[3..2]
		 psrld mm2, STEP_FRACTION_EXP;

		pmaddwd mm2, mm7;	// mm2 = partial result Acc2
		 psrld mm3, STEP_FRACTION_EXP;


		// the multiplies are arranged in normal order, output looks like this:
		/// "wPix1[x] = weighted pixel#x Src1 (pixel * weight)" (32-bit value)
		// mm0 = { wPix1[1] | wPix1[0] }, mm1 = { wPix1[3] | wPix1[2] }
		// mm2 = { wPix2[1] | wPix2[0] }, mm3 = { wPix2[3] | wPix2[2] }

		pmaddwd mm3, mm7;	// mm3 = partial result Acc2
		 add edx, 16;	// rowAcc2 += 16

		paddd mm0, qword ptr [ecx];	// mm0 += rowAcc1[1..0]
		 add ebx, 16;	// pSrc2 += 4 pixels 

		paddd mm1, qword ptr [ecx+8];;	// mm1 += rowAcc1[3..2]
		 add eax, 16;	// pSrc1 += 4 pixels 

		paddd mm2, qword ptr [edx-16];	// mm2 += rowAcc2[1..0]

		movq qword ptr [ecx], mm0;	// store rowAcc1[1..0]

		paddd mm3, qword ptr [edx-8];	// mm3 += rowAcc2[3..2]

		movq qword ptr [ecx+8], mm1;// store rowAcc1[3..2]
		 add ecx, 16;	// rowAcc1 += 16

		movq qword ptr [edx-16], mm2;	// store rowAcc2[1..0]

		movq qword ptr [edx-8], mm3;	// store rowAcc2[3..2]
		 cmp edi, 0x00;	// compare (x<> row_width)

		jg lp_mmx_rowAcc2L;	// end for ( x = row_width; x > 0; x += 4 )

		emms;

	} // end __asm
//	__asm {
//		emms;
//	}
} // end function mmx_rowacc2l()


// this function performs a row-accumulation operation on one
// source plane.
// dwPxWeight = [ 00 | Mask ], the upper-16 bits MUST BE 00!
// dwAddmask :  0x0000_0000 do not accumulate with prowAcc1[] 
// dwAddmask :  0xFFFF_FFFF add to prowAcc1[] 
// row_width : (# pixels, must be divisible by 4)
void
mmx_rowacc1( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	unsigned char *pSrc1, DWORD dwAddmask )
{
	//DWORD dwPxWeight;	// pixel weight mask, 32-bit version
	//unsigned char *pSrc1, *pSrc2;	// source bitmap pointers

	LPDWORD pSrc1_aligned = (LPDWORD)(((DWORD) pSrc1 ) & 0xFFFFFFFC);
	DWORD pSrc1srlAmt = ((DWORD) pSrc1) - ((DWORD) pSrc1_aligned);

// this function performs the following operation :
//
//   case 1 : dwAddmask = 0x00000000
//
//		for ( x=0; x < row_width; ++x ) 
//			prowAcc1[x] = (dwPxWeight * pSrc1[ x ]);
//
//   case 2 : dwAddmask = 0xFFFFFFFF
//
//		for ( x=0; x < row_width; ++x ) 
//			prowAcc1[x] += (dwPxWeight * pSrc1[ x ]);

	__asm {
		// the MMX implementation processes 4 source-pixels 
		// per iteration.  All reads are DWORD aligned.

		movd mm7, [dwPxWeight]; // mm7 = pixelmask

		mov eax, [pSrc1_aligned];	// eax = source1
		 punpckldq mm7, mm7;	// double to 64-bit, mm7 = pixel weight mask

		movd mm5, [pSrc1srlAmt]; // byte rshift logical amount for psrc1

		movd mm6, [dwAddmask]; 
		 psllq mm5, 0x03;	// mm5 = bitshift amount for pSrc1 alignment

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

		mov ecx, [prowAcc1];	// ecx = rowAcc1
		 punpckldq mm6, mm6;	// mm6 = [ dwAddmask | dwAddmask ]

		 // loop begin, 4 pSrc1 + 4 pSrc2 pixels per iteration
	lp_mmx_rowAcc1: // for ( x = 0; x < row_width; x += 4 )
		movd mm0, dword ptr [eax + esi]; // read from pSrc1[x]
		 pxor mm2, mm2;		// clear mm2 

		punpckldq mm0, dword ptr [eax + esi +4]; // mm0 = pSrc1[7..0]
		 add esi, 0x04;		// x= x+4

		movq mm3, qword ptr [ecx];	// mm3 = rowAcc1[1..0] 
		 psrlq mm0, mm5;	// mm0 = desired input 4-pixels of pSrc1

		// convert the four 8-bit pixels in mm0, mm2 -> 16-bit values
		movq mm4, qword ptr [ecx+8];	// mm4 = rowAcc1[3..2] 
		 punpcklbw mm0, mm2; // mm0 = 16-bit values pSrc1

		movq mm1, mm0;		// mm1 = copy of mm0
		 punpcklwd mm0, mm2; // mm0 = 32-bit values pSrc1[ ___1 | ___0 ]

		// the multiplies are arranged in correct order, like this:
		/// "wPix1[x] = weighted pixel#x Src1 (pixel * weight)" (32-bit value)
		// mm0 = { wPix1[1] | wPix1[0] }, mm1 = { wPix1[3] | wPix1[2] }

		pmaddwd mm0, mm7;	// mm0 = partial result Acc1
		 punpckhwd mm1, mm2;	// mm1 = 32-bit values pSrc1[ ___3 | ___2 ]

		pmaddwd mm1, mm7;	// mm1 = partial result Acc1
		 pand mm3, mm6;	// mm3 = mm3 & qwAddmask

		pand mm4, mm6;	// mm4 = mm4 & qwAddmask
		 paddd mm3, mm0; //mm3 = rowAcc1[1..0] + wPix1[1..0]

		paddd mm4, mm1;	//mm4 = rowAcc1[3..2] + wPix1[3..2]

		movq qword ptr [ecx], mm3;	// store rowAcc1[1..0]

		movq qword ptr [ecx+8], mm4;// store rowAcc1[3..2]
		 add ecx, 16;	// rowAcc1 += 16

		cmp esi, edi;	// compare (x<> row_width)
		 jl lp_mmx_rowAcc1;	// end for ( x = 0; x < row_width; x += 4 )

		 emms;
	} // end __asm
	//__asm {
	//	emms;
	//}
} // end function mmx_rowacc1()




// this function performs a row-accumulation operation on one
// source plane.
// dwPxWeight = [ 00 | Mask ], the upper-16 bits MUST BE 00!
// row_width : (# pixels, must be divisible by 4)
// dwAddmask :  0x0000_0000 do not accumulate with prowAcc1[] 
// dwAddmask :  0xFFFF_FFFF add to prowAcc1[] 
void
mmx_rowacc1l( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD pSrc1, DWORD dwAddmask )
{
	//DWORD dwPxWeight;	// pixel weight mask, 32-bit version
	//unsigned char *pSrc1, *pSrc2;	// source bitmap pointers

// this function performs the following operation :
//
//   case 1 : dwAddmask = 0xFFFFFFFF
//		for ( x=0; x < row_width; ++x ) 
//			prowAcc1[x] += dwPxWeight* (pSrc1[ x ]  >> EXP );
//   case 2 : dwAddmask = 0x00000000
//
//		for ( x=0; x < row_width; ++x ) 
//			prowAcc1[x] = dwPxWeight * ( pSrc1[ x ] >> EXP );

	__asm {
		// the MMX implementation processes 4 source-pixels 
		// per iteration.  All reads are DWORD aligned.

		movd mm7, [dwPxWeight]; // mm7 = pixelmask

		mov eax, [pSrc1];	// eax = source1
		 punpckldq mm7, mm7;	// double to 64-bit, mm7 = pixel weight mask

		movd mm6, [dwAddmask]; 

		mov edi, [row_width]; // edi = row_width
		 punpckldq mm6, mm6;	// double to 64-bit, mm6 = addMask

		mov ecx, [prowAcc1];	// ecx = rowAcc1
		 mov esi, 0x00;		// esi = x

		 // loop begin, 4 pSrc1 + 4 pSrc2 pixels per iteration
	lp_mmx_rowAcc1l: // for ( x = 0; x < row_width; x += 4 )
		movq mm0, dword ptr [eax ]; // read pSrc1[x], mm0 = pSrc1[1..0]
		 add esi, 0x04;		// x= x+4

		movq mm1, dword ptr [eax +8]; // read pSrc1[x], mm0 = pSrc1[3..2]
		 psrld mm0, STEP_FRACTION_EXP;	// mm0 >> STEP_FRACTION_EXP

		pmaddwd mm0, mm7;	// mm0 = partial result Acc1
		 psrld mm1, STEP_FRACTION_EXP;	// mm1 >> STEP_FRACTION_EXP

		movq mm2, qword ptr [ecx];	// mm3 = rowAcc1[1..0] 
		 pmaddwd mm1, mm7;	// mm1 = partial result Acc1

		// convert the four 8-bit pixels in mm0, mm2 -> 16-bit values
		movq mm3, qword ptr [ecx+8];	// mm4 = rowAcc1[3..2]
		 pand mm2, mm6;
		// the multiplies are arranged in correct order, like this:
		/// "wPix1[x] = weighted pixel#x Src1 (pixel * weight)" (32-bit value)
		// mm0 = { wPix1[1] | wPix1[0] }, mm1 = { wPix1[3] | wPix1[2] }

		// the and operations control the store operation, either
		// accumulation ( rowAcc1 += wPix1), or store (rowAcc1 = wPix1)

		paddd mm2, mm0; //mm2 = rowAcc1[1..0] + wPix1[1..0]
		 pand mm3, mm6;

		paddd mm3, mm1;	//mm4 = rowAcc1[3..2] + wPix1[3..2]
		 add eax, 16;	// pSrc1 += 16

		movq qword ptr [ecx], mm2;	// store rowAcc1[1..0]

		movq qword ptr [ecx+8], mm3;// store rowAcc1[3..2]
		 add ecx, 16;	// rowAcc1 += 16

		cmp esi, edi;	// compare (x<> row_width)
		 jl lp_mmx_rowAcc1l;	// end for ( x = 0; x < row_width; x += 4 )

		emms;
	} // end __asm
	//__asm {
	//	emms;
	//}
} // end function mmx_rowacc1l()


// convert accumulator row to pixel,
// dwPxWeight, multiplication factor
// row_width : (#output pixels to generate, must be divisble by 4)
// prowAcc1[] : source accumulator (32-bit vectors)
// pDst1[] : destination scanline (8-bit pixels)
void
mmx_acc2pix1( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD pDst1 )
{

	const static __int64 mmAndMask = 0x7FFF7FFF7FFF7FFF;
	static __int64 qwWghtLow, qwWghtHigh;

//	static DWORD dwPxWeight; // pixel weight

// this function performs the following operation :
//
//   dwPxWeight = [ Weight | weight ] (weight =15-bit unsigned int)
//
//   a) Process row 8-pixels-per-iteration, #iteratations = (row_width>>3)
//		for ( x= row_width; x > 4; x= x+ 8 ) 
//			pDst1[x] = (dwPxWeight * prowAcc1[ x ]) >> STEP_FRACTION_EXP;
//   b) If (x != 0), process the "left over 4 pixels)
//			pDst1[x] = (dwPxWeight * prowAcc1[ x ]) >> STEP_FRACTION_EXP;

	__asm {
		// This loop proceses 8 destination pixels per iteration

		// qwWghtLow = [ ____ Wght | ____ Wght ]  mult mask, lower-16bits
		// QwWghtHigh= [ Wght ____ | Wght ____ ]  mult mask, upper-16bits
		// mm7 = [ 7fff 7fff | 7fff 7fff ]  prowAcc[] ANDmask
		movd mm6, [dwPxWeight];  // mm6 = pixel weight(multiply) mask
		 ;//slot
		mov eax, [prowAcc1];	//eax = pointer rowAcc1[]
		 punpckldq mm6,mm6;		// mm6 = fully extended QWORD mask

		mov ecx, [pDst1];	//eax = pointer rowAcc1[]
		 movq mm5, mm6;			// mm5 = copy of mm6

		mov esi, dword ptr [row_width];	// esi = x = row_width (# pixels)
		 pslld mm5, 16;		// mm5 = left-shifted pixelmask

		movq [qwWghtLow], mm6;	// store the extended QWORD mask for later use
		 ;//slot
		movq mm7, [mmAndMask];
		 ;//slot
		movq [qwWghtHigh], mm5;	// store the extended QWORD mask for later use
		 jmp lp_acc1pix;	// skip first store operation

	lp_acc1pix_a:	// previous store operation
		movq qword ptr [ecx - 8], mm2;	// store final result
		// start loop, for ( x=row_width; x > 0 ; x = x- 8 )
	lp_acc1pix:
		movq mm0, qword ptr [eax];	// 1) mm0 = prowAcc1[1..0]
		 ;//slot

		movq mm1, qword ptr [eax+8];	// mm1 = prowAcc1[3..2]
		 movq mm2, mm0;	// 2) mm2 = copy of mm0

		// first, multiply the lower 15-bits of each 32-bit rowAcc pixel[1..0]
		movq mm3, mm1;	// mm3 = copy of mm1
		 pand mm0, mm7;	// 3) mm0 = 15-bit int converted rowAcc[1..0]
						// mm0 = mm0 & [ 7FFF 7FFF 7FFF 7FFF ]
		pand mm1, mm7;	// mm1 = 15-bit int converted rowAcc[2..0]
		 pslld mm2, 0x01;	// rowAcc1 [ 30..15  ____ | 30..15 ____ ],

		pmaddwd mm0, [qwWghtLow]; // 4) mm0 =  [1..0] pixel[14..0] * pxWeight
		 pand mm2, mm7;	// rowAcc1 mm2 = [ _29..15  ____ | _29..15 ____ ]

		// now multply the the next 15-bits (bits 29..15)
		pmaddwd mm1, [qwWghtLow]; // mm1 =  [3..2] pixel[14..0]* pxWeight
		 pslld mm3, 0x01; // rowAcc2 [ 30..15  ____ | 30..15 ____ ],
			// we will delete bit #30 of each dword

		pmullw mm2, [qwWghtHigh];	// mm2 = pixelAcc1[29..15] * pxWeight
		 pand mm3, mm7;	// rowAcc1 mm3 = [ _29..15  ____ | _29..15 ____ ]

		pmullw mm3, [qwWghtHigh];  // mm3 = pixelAcc1[29..15] * pxWeight
		 ;//slot

	// start converting pixels prowAcc1[7..4]
 		movq mm4, qword ptr [eax+16];	// 1) mm4 = prowAcc1[5..4]
		 ;//slot

		movq mm5, qword ptr [eax+24];	// mm5 = prowAcc1[7..6]
		 movq mm6, mm4;

		paddd mm2, mm0;
		 movq mm0, mm5;	// mm0 =copy of prowAcc1[7..6]

		paddd mm3, mm1;
		 pand mm4, mm7;	// 3) mm4 = 15-bit int converted rowAcc[5..4]

		pmaddwd mm4, [qwWghtLow]; // 4) mm4 = [5..4] pixel[14..0] * pxWeight
		 pand mm5, mm7;

		pmaddwd mm5, [qwWghtLow]; // 4) mm5 = [7..6] pixel[14..0] * pxWeight
		 pslld mm6, 0x01;	// rowAcc1[5..4] [ 30..15  ____ | 30..15 ____ ],

		pslld mm0, 0x01; // rowAcc2[7..6] [ 30..15  ____ | 30..15 ____ ],
		 pand mm6, mm7; // rowAcc1[5..4] mm3 = [ _29..15  ____ | _29..15 ____ ]

	// we will delete bit #30 of each dword
		pmullw mm6, [qwWghtHigh];	// mm6 = [5..4] pixelAcc1[29..15] * pxWeight
		 pand mm0, mm7;	// rowAcc1[7..6] mm0 = [ _29..15  ____ | _29..15 ____ ]

		pmullw mm0, [qwWghtHigh];  // mm6 =[7..6] pixelAcc1[29..15] * pxWeight
		 psrld mm2, STEP_FRACTION_EXP; // mm2 = final pDst1[1..0] value (32-bit)

		add ecx, 8; // pDst1 += 8
		 psrld mm3, STEP_FRACTION_EXP; // mm3 = final pDst2[3..2] value (32-bit)

		packssdw mm2, mm3;	// mm2 = 16-bit of pDst[3..0] (partial result) 
		 paddd mm6, mm4;

		paddd mm0, mm5;
		 psrld mm6, STEP_FRACTION_EXP; // mm6 = final pDst1[5..4] value (32-bit)

		psrld mm0, STEP_FRACTION_EXP; // mm0 = final pDst1[7..6] value (32-bit)
		 sub esi, 8; // x = x - 8

		packssdw mm6, mm0;	// mm6 = 16-bit of pDst [7..4] (partial result) 
		 add eax, 32; // prowAcc1 += 16

		packuswb mm2, mm6;	// mm6 = 8-bit pixels pDst[7..4] (final result) 
		 cmp esi, 0x04;	// compare x <> 4

		// software pipelining, store is now AFTER the loop
		//movq qword ptr [ecx - 8], mm2;	// store final result

		jg lp_acc1pix_a; 
	// end for ( x=row_width; x > 0 ; x = x- 8 )

		movq qword ptr [ecx - 8], mm2;	// store final result
		 cmp esi, 0x00; // compare x <> 0

	// if x==0, no "left over 4 pixels", done
		je mmx_acc2_pix1_done; 

	// start processing "residual 4 pixels"
		movq mm0, qword ptr [eax];	// 1) mm0 = prowAcc1[1..0]
		 ;//slot

		movq mm1, qword ptr [eax+8];	// mm1 = prowAcc1[3..2]
		 movq mm2, mm0;	// 2) mm2 = copy of mm0

		// first, multiply the lower 15-bits of each 32-bit rowAcc pixel[1..0]
		movq mm3, mm1;	// mm3 = copy of mm1
		 pand mm0, mm7;	// 3) mm0 = 15-bit int converted rowAcc[1..0]
						// mm0 = mm0 & [ 7FFF 7FFF 7FFF 7FFF ]
		pand mm1, mm7;	// mm1 = 15-bit int converted rowAcc[2..0]
		 pslld mm2, 0x01;	// rowAcc1 [ 30..15  ____ | 30..15 ____ ],

		pmaddwd mm0, [qwWghtLow]; // 4) mm0 =  [1..0] pixel[14..0] * pxWeight
		 pand mm2, mm7;	// rowAcc1 mm2 = [ _29..15  ____ | _29..15 ____ ]

		// now multply the the next 15-bits (bits 29..15) of pixels [1..0]
		pmullw mm2, [qwWghtHigh];	// mm2 = pixelAcc1[29..15] * pxWeight
		 pslld mm3, 0x01; // rowAcc2 [ 30..15  ____ | 30..15 ____ ],
			// we will delete bit #30 of each dword

		// repeat the operation for pixels [3..2]
		pmaddwd mm1, [qwWghtLow]; // mm1 =  [3..2] pixel[14..0]* pxWeight
		 pand mm3, mm7;	// rowAcc1 mm3 = [ _29..15  ____ | _29..15 ____ ]

		pmullw mm3, [qwWghtHigh];  // mm3 = pixelAcc1[29..15] * pxWeight
		 ;//slot

	// start converting pixels prowAcc1[7..4]

		paddd mm2, mm0;

		psrld mm2, STEP_FRACTION_EXP; // mm2 = final pDst1[1..0] value (32-bit)

		paddd mm3, mm1;

		psrld mm3, STEP_FRACTION_EXP; // mm3 = final pDst2[3..2] value (32-bit)


		packssdw mm2, mm3;	// mm2 = 16-bit of pDst[3..0] (partial result) 

		packuswb mm2, mm2;	// mm2 = 8-bit pixels pDst[7..4] (final result) 

		movq qword ptr [ecx], mm2;	// store final result (last 4 pixels)

	mmx_acc2_pix1_done :

		emms;
	}
//	__asm {
//		emms;
//	}
} // end function mmx_acc2pix1()

/*
// convert accumulator row to pixel,
// dwPxWeight, multiplication factor
// row_width : (#output pixels to generate, must be divisble by 8)
// prowAcc1[] : source accumulator (32-bit vectors)
// pDst1[] : destination scanline (8-bit pixels)
void
mmx_acc2pix2( DWORD dwPxWeight, DWORD row_width, LPDWORD prowAcc1, 
	LPDWORD prowAcc2, LPDWORD pDst1, LPDWORD pDst2 )
{

	const static __int64 mmAndMask = 0x7FFF7FFF7FFF7FFF;
	static __int64 qwWghtLow, qwWghtHigh;

//	static DWORD dwPxWeight; // pixel weight

	// this function performs the following operation :
//
//   dwPxWeight = [ Weight | weight ] (weight =15-bit unsigned int)
//
//		for ( x=0; x < row_width; ++x ) 
//			pDst1[x] = (dwPxWeight * prowAcc1[ x ]) >> STEP_FRACTION_EXP;

	__asm {
		// This loop proceses 8 destination pixels per iteration

		// qwWghtLow = [ ____ Wght | ____ Wght ]  mult mask, lower-16bits
		// QwWghtHigh= [ Wght ____ | Wght ____ ]  mult mask, upper-16bits
		// mm7 = [ 7fff 7fff | 7fff 7fff ]  prowAcc[] ANDmask
		movd mm6, [dwPxWeight];  // mm6 = pixel weight(multiply) mask
		 ;//slot
		mov eax, [prowAcc1];	//eax = pointer rowAcc1[]
		 punpckldq mm6,mm6;		// mm6 = fully extended QWORD mask

		mov ecx, [pDst1];	//eax = pointer rowAcc1[]
		 movq mm5, mm6;			// mm5 = copy of mm6

		mov esi, dword ptr [row_width];	// esi = x = row_width (# pixels)
		 pslld mm5, 16;		// mm5 = left-shifted pixelmask

		movq [qwWghtLow], mm6;	// store the extended QWORD mask for later use
		 ;//slot
		movq mm7, [mmAndMask];
		 ;//slot
		movq [qwWghtHigh], mm5;	// store the extended QWORD mask for later use
		 jmp lp_acc1pix;	// skip first store operation

	lp_acc1pix_a:	// previous store operation
		movq qword ptr [ecx - 8], mm2;	// store final result
		// start loop, for ( x=row_width; x > 0 ; x = x- 8 )
	lp_acc1pix:
		movq mm0, qword ptr [eax];	// 1) mm0 = prowAcc1[1..0]
		 ;//slot

		movq mm1, qword ptr [eax+8];	// mm1 = prowAcc1[3..2]
		 movq mm2, mm0;	// 2) mm2 = copy of mm0

		// first, multiply the lower 15-bits of each 32-bit rowAcc pixel[1..0]
		movq mm3, mm1;	// mm3 = copy of mm1
		 pand mm0, mm7;	// 3) mm0 = 15-bit int converted rowAcc[1..0]
						// mm0 = mm0 & [ 7FFF 7FFF 7FFF 7FFF ]
		pand mm1, mm7;	// mm1 = 15-bit int converted rowAcc[2..0]
		 pslld mm2, 0x01;	// rowAcc1 [ 30..15  ____ | 30..15 ____ ],

		pmaddwd mm0, [qwWghtLow]; // 4) mm0 =  [1..0] pixel[14..0] * pxWeight
		 pand mm2, mm7;	// rowAcc1 mm2 = [ _29..15  ____ | _29..15 ____ ]

		// now multply the the next 15-bits (bits 29..15)
		pmaddwd mm1, [qwWghtLow]; // mm1 =  [3..2] pixel[14..0]* pxWeight
		 pslld mm3, 0x01; // rowAcc2 [ 30..15  ____ | 30..15 ____ ],
			// we will delete bit #30 of each dword

		pmullw mm2, [qwWghtHigh];	// mm2 = pixelAcc1[29..15] * pxWeight
		 pand mm3, mm7;	// rowAcc1 mm3 = [ _29..15  ____ | _29..15 ____ ]

		pmullw mm3, [qwWghtHigh];  // mm3 = pixelAcc1[29..15] * pxWeight
		 ;//slot

	// start converting pixels prowAcc1[7..4]
 		movq mm4, qword ptr [eax+16];	// 1) mm4 = prowAcc1[5..4]
		 ;//slot

		movq mm5, qword ptr [eax+24];	// mm5 = prowAcc1[7..6]
		 movq mm6, mm4;

		paddd mm2, mm0;
		 movq mm0, mm5;	// mm0 =copy of prowAcc1[7..6]

		paddd mm3, mm1;
		 pand mm4, mm7;	// 3) mm4 = 15-bit int converted rowAcc[5..4]

		pmaddwd mm4, [qwWghtLow]; // 4) mm4 = [5..4] pixel[14..0] * pxWeight
		 pand mm5, mm7;

		pmaddwd mm5, [qwWghtLow]; // 4) mm5 = [7..6] pixel[14..0] * pxWeight
		 pslld mm6, 0x01;	// rowAcc1[5..4] [ 30..15  ____ | 30..15 ____ ],

		pslld mm0, 0x01; // rowAcc2[7..6] [ 30..15  ____ | 30..15 ____ ],
		 pand mm6, mm7; // rowAcc1[5..4] mm3 = [ _29..15  ____ | _29..15 ____ ]

	// we will delete bit #30 of each dword
		pmullw mm6, [qwWghtHigh];	// mm6 = [5..4] pixelAcc1[29..15] * pxWeight
		 pand mm0, mm7;	// rowAcc1[7..6] mm0 = [ _29..15  ____ | _29..15 ____ ]

		pmullw mm0, [qwWghtHigh];  // mm6 =[7..6] pixelAcc1[29..15] * pxWeight
		 psrld mm2, STEP_FRACTION_EXP; // mm2 = final pDst1[1..0] value (32-bit)

		add ecx, 8; // pDst1 += 8
		 psrld mm3, STEP_FRACTION_EXP; // mm3 = final pDst2[3..2] value (32-bit)

		packssdw mm2, mm3;	// mm2 = 16-bit of pDst[3..0] (partial result) 
		 paddd mm6, mm4;

		paddd mm0, mm5;
		 psrld mm6, STEP_FRACTION_EXP; // mm6 = final pDst1[5..4] value (32-bit)

		psrld mm0, STEP_FRACTION_EXP; // mm0 = final pDst1[7..6] value (32-bit)
		 sub esi, 8; // x = x - 8

		packssdw mm6, mm0;	// mm6 = 16-bit of pDst [7..4] (partial result) 
		 add eax, 32; // prowAcc1 += 16

		packuswb mm2, mm6;	// mm6 = 8-bit pixels pDst[7..4] (final result) 
		 cmp esi, 0x00;	// compare x <> 0

		// software pipelining, store is now AFTER the loop
		//movq qword ptr [ecx - 8], mm2;	// store final result

		jg lp_acc1pix_a; 
	// end for ( x=row_width; x > 0 ; x = x- 8 )

		movq qword ptr [ecx - 8], mm2;	// store final result

	//
	// start converting the 2nd plane
	//

		movd mm6, [dwPxWeight];  // mm6 = pixel weight(multiply) mask
		 ;//slot
		mov eax, [prowAcc2];	//eax = pointer rowAcc1[]
		 punpckldq mm6,mm6;		// mm6 = fully extended QWORD mask

		mov ecx, [pDst2];	//eax = pointer rowAcc1[]
		 movq mm5, mm6;			// mm5 = copy of mm6

		mov esi, dword ptr [row_width];	// esi = x = row_width (# pixels)
		 pslld mm5, 16;		// mm5 = left-shifted pixelmask

		movq [qwWghtLow], mm6;	// store the extended QWORD mask for later use
		 ;//slot
		movq mm7, [mmAndMask];
		 ;//slot
		movq [qwWghtHigh], mm5;	// store the extended QWORD mask for later use
		 jmp lp_acc2pix;	// skip first store operation

	lp_acc2pix_a:	// previous store operation
		movq qword ptr [ecx - 8], mm2;	// store final result
		// start loop, for ( x=row_width; x > 0 ; x = x- 8 )
	lp_acc2pix:
		movq mm0, qword ptr [eax];	// 1) mm0 = prowAcc1[1..0]
		 ;//slot

		movq mm1, qword ptr [eax+8];	// mm1 = prowAcc1[3..2]
		 movq mm2, mm0;	// 2) mm2 = copy of mm0

		// first, multiply the lower 15-bits of each 32-bit rowAcc pixel[1..0]
		movq mm3, mm1;	// mm3 = copy of mm1
		 pand mm0, mm7;	// 3) mm0 = 15-bit int converted rowAcc[1..0]
						// mm0 = mm0 & [ 7FFF 7FFF 7FFF 7FFF ]
		pand mm1, mm7;	// mm1 = 15-bit int converted rowAcc[2..0]
		 pslld mm2, 0x01;	// rowAcc1 [ 30..15  ____ | 30..15 ____ ],

		pmaddwd mm0, [qwWghtLow]; // 4) mm0 =  [1..0] pixel[14..0] * pxWeight
		 pand mm2, mm7;	// rowAcc1 mm2 = [ _29..15  ____ | _29..15 ____ ]

		// now multply the the next 15-bits (bits 29..15)
		pmaddwd mm1, [qwWghtLow]; // mm1 =  [3..2] pixel[14..0]* pxWeight
		 pslld mm3, 0x01; // rowAcc2 [ 30..15  ____ | 30..15 ____ ],
			// we will delete bit #30 of each dword

		pmullw mm2, [qwWghtHigh];	// mm2 = pixelAcc1[29..15] * pxWeight
		 pand mm3, mm7;	// rowAcc1 mm3 = [ _29..15  ____ | _29..15 ____ ]

		pmullw mm3, [qwWghtHigh];  // mm3 = pixelAcc1[29..15] * pxWeight
		 ;//slot

	// start converting pixels prowAcc1[7..4]
 		movq mm4, qword ptr [eax+16];	// 1) mm4 = prowAcc1[5..4]
		 ;//slot

		movq mm5, qword ptr [eax+24];	// mm5 = prowAcc1[7..6]
		 movq mm6, mm4;

		paddd mm2, mm0;
		 movq mm0, mm5;	// mm0 =copy of prowAcc1[7..6]

		paddd mm3, mm1;
		 pand mm4, mm7;	// 3) mm4 = 15-bit int converted rowAcc[5..4]

		pmaddwd mm4, [qwWghtLow]; // 4) mm4 = [5..4] pixel[14..0] * pxWeight
		 pand mm5, mm7;

		pmaddwd mm5, [qwWghtLow]; // 4) mm5 = [7..6] pixel[14..0] * pxWeight
		 pslld mm6, 0x01;	// rowAcc1[5..4] [ 30..15  ____ | 30..15 ____ ],

		pslld mm0, 0x01; // rowAcc2[7..6] [ 30..15  ____ | 30..15 ____ ],
		 pand mm6, mm7; // rowAcc1[5..4] mm3 = [ _29..15  ____ | _29..15 ____ ]

	// we will delete bit #30 of each dword
		pmullw mm6, [qwWghtHigh];	// mm6 = [5..4] pixelAcc1[29..15] * pxWeight
		 pand mm0, mm7;	// rowAcc1[7..6] mm0 = [ _29..15  ____ | _29..15 ____ ]

		pmullw mm0, [qwWghtHigh];  // mm6 =[7..6] pixelAcc1[29..15] * pxWeight
		 psrld mm2, STEP_FRACTION_EXP; // mm2 = final pDst1[1..0] value (32-bit)

		add ecx, 8; // pDst1 += 8
		 psrld mm3, STEP_FRACTION_EXP; // mm3 = final pDst2[3..2] value (32-bit)

		packssdw mm2, mm3;	// mm2 = 16-bit of pDst[3..0] (partial result) 
		 paddd mm6, mm4;

		paddd mm0, mm5;
		 psrld mm6, STEP_FRACTION_EXP; // mm6 = final pDst1[5..4] value (32-bit)

		psrld mm0, STEP_FRACTION_EXP; // mm0 = final pDst1[7..6] value (32-bit)
		 sub esi, 8; // x = x - 8

		packssdw mm6, mm0;	// mm6 = 16-bit of pDst [7..4] (partial result) 
		 add eax, 32; // prowAcc1 += 16

		packuswb mm2, mm6;	// mm6 = 8-bit pixels pDst[7..4] (final result) 
		 cmp esi, 0x00;	// compare x <> 0

		// software pipelining, store is now AFTER the loop
		//movq qword ptr [ecx - 8], mm2;	// store final result

		jg lp_acc2pix_a; 
	// end for ( x=row_width; x > 0 ; x = x- 8 )

		movq qword ptr [ecx - 8], mm2;	// store final result
	}
} // end function mmx_acc2pix2()
*/