/*
 * @DEC_COPYRIGHT@
 */
/*
 * HISTORY
 * $Log: sc_convert.c,v $
 * Revision 1.1.8.6  1996/10/28  17:32:17  Hans_Graves
 * 	Add use IsYUV422Sep() macro in ScConvertSepYUVToOther().
 * 	[1996/10/28  16:51:30  Hans_Graves]
 *
 * Revision 1.1.8.5  1996/10/02  18:42:50  Hans_Graves
 * 	Fix RGB 24-bit conversion in ScYuv411ToRgb().
 * 	[1996/10/02  18:32:51  Hans_Graves]
 *
 * Revision 1.1.8.4  1996/09/29  22:19:32  Hans_Graves
 * 	Added stride support to ScYuv411ToRgb()
 * 	[1996/09/29  21:27:17  Hans_Graves]
 *
 * Revision 1.1.8.3  1996/09/18  23:45:34  Hans_Graves
 * 	Added ScConvert1611PlanarTo411_C, ScConvert411sTo422s_C, and ScRgbToYuv411
 * 	[1996/09/18  21:52:27  Hans_Graves]
 *
 * Revision 1.1.8.2  1996/05/07  19:55:42  Hans_Graves
 * 	Added YUV 4:1:1 to RGB 32-bit conversion - C code
 * 	[1996/05/07  19:44:38  Hans_Graves]
 *
 * Revision 1.1.6.4  1996/04/11  20:21:57  Hans_Graves
 * 	Moved ScIsUpsideDown() from sc_convert_yuv.c
 * 	[1996/04/11  20:03:44  Hans_Graves]
 *
 * Revision 1.1.6.3  1996/04/09  16:04:24  Hans_Graves
 * 	Added ValidateBI_BITFIELDS(). Fixed BI_RGB 16 bit conversion in ScYuv411ToRgb()
 * 	[1996/04/09  14:46:27  Hans_Graves]
 *
 * Revision 1.1.6.2  1996/04/03  21:41:07  Hans_Graves
 * 	Change include path for <mmsystems.h>
 * 	[1996/04/03  21:37:55  Hans_Graves]
 *
 * Revision 1.1.4.4  1996/02/22  17:35:17  Bjorn_Engberg
 * 	Added support for BI_BITFIELDS 16 and BI_RGB 32 rendering.
 * 	[1996/02/22  17:31:35  Bjorn_Engberg]
 *
 * Revision 1.1.4.3  1996/01/02  18:30:33  Bjorn_Engberg
 * 	Got rid of compiler warnings: Added Casts
 * 	[1996/01/02  15:21:27  Bjorn_Engberg]
 *
 * Revision 1.1.4.2  1995/12/07  19:31:15  Hans_Graves
 * 	Added ScConvert422PlanarTo411_C()
 * 	[1995/12/07  17:44:00  Hans_Graves]
 *
 * Revision 1.1.2.21  1995/11/30  20:17:02  Hans_Graves
 * 	Cleaned up ScYuv422toRgb() routine
 * 	[1995/11/30  20:13:26  Hans_Graves]
 *
 * Revision 1.1.2.20  1995/11/28  22:47:28  Hans_Graves
 * 	Make XIMAGE 24 identical to BI_BITFIELDS pBGR
 * 	[1995/11/28  22:26:11  Hans_Graves]
 *
 * 	Added ScYuv1611ToRgb() routine for use by Indeo
 * 	[1995/11/28  21:34:28  Hans_Graves]
 *
 * Revision 1.1.2.19  1995/11/17  21:31:21  Hans_Graves
 * 	Add ScYuv411ToRgb() conversion routine.
 * 	[1995/11/17  20:50:51  Hans_Graves]
 *
 * Revision 1.1.2.18  1995/10/25  18:19:18  Bjorn_Engberg
 * 	Support Upside Down in ScRgbInterlToYuvInterl().
 * 	[1995/10/25  18:05:18  Bjorn_Engberg]
 *
 * Revision 1.1.2.17  1995/10/10  21:43:02  Bjorn_Engberg
 * 	Speeded up RgbToYuv code and made it table driven.
 * 	[1995/10/10  21:21:48  Bjorn_Engberg]
 *
 * Revision 1.1.2.16  1995/10/09  19:44:31  Bjorn_Engberg
 * 	Removed ValidateBI_BITFIELDS(), it's now in sc_convert_yuv.c
 * 	[1995/10/09  19:44:14  Bjorn_Engberg]
 *
 * Revision 1.1.2.15  1995/10/06  20:43:23  Farokh_Morshed
 * 	Enhance ScRgbInterlToYuvInterl to handle BI_BITFIELDS
 * 	[1995/10/06  20:42:43  Farokh_Morshed]
 *
 * Revision 1.1.2.14  1995/10/02  19:30:26  Bjorn_Engberg
 * 	Added support for Assebler YUV to RGB routines.
 * 	[1995/10/02  18:39:05  Bjorn_Engberg]
 *
 * Revision 1.1.2.13  1995/09/28  20:37:53  Farokh_Morshed
 * 	Handle negative Height
 * 	[1995/09/28  20:37:39  Farokh_Morshed]
 *
 * Revision 1.1.2.12  1995/09/26  15:58:47  Paul_Gauthier
 * 	Fix mono JPEG to interlaced 422 YUV conversion
 * 	[1995/09/26  15:58:09  Paul_Gauthier]
 *
 * Revision 1.1.2.11  1995/09/22  18:56:35  Paul_Gauthier
 * 	{** Merge Information **}
 * 	      {** Command used:       bsubmit **}
 * 	      {** Ancestor revision:  1.1.2.7 **}
 * 	      {** Merge revision:     1.1.2.10 **}
 * 	{** End **}
 * 	Use faster method for 16bit YUV output for TGA2
 * 	[1995/09/22  18:39:55  Paul_Gauthier]
 *
 * Revision 1.1.2.10  1995/09/21  18:26:45  Farokh_Morshed
 * 	When BI_RGB or BI_BITFIELDS, invert the image while translating from
 * 	YUV to RGB.  Also fix the coefficient for the YUV colors.
 * 	This work was actually done by Bjorn
 * 	[1995/09/21  18:26:19  Farokh_Morshed]
 *
 * Revision 1.1.2.9  1995/09/20  17:39:18  Karen_Dintino
 * 	Add RGB support to JPEG
 * 	[1995/09/20  17:37:22  Karen_Dintino]
 *
 * Revision 1.1.2.8  1995/09/20  14:59:31  Bjorn_Engberg
 * 	Port to NT
 * 	[1995/09/20  14:41:10  Bjorn_Engberg]
 *
 * Revision 1.1.2.7  1995/09/18  19:47:47  Paul_Gauthier
 * 	Added conversion of MPEG planar 4:1:1 to interleaved 4:2:2
 * 	[1995/09/18  19:46:13  Paul_Gauthier]
 *
 * Revision 1.1.2.6  1995/09/14  14:40:34  Karen_Dintino
 * 	Move RgbToYuv out of rendition
 * 	[1995/09/14  14:26:13  Karen_Dintino]
 *
 * Revision 1.1.2.5  1995/09/11  18:47:25  Farokh_Morshed
 * 	{** Merge Information **}
 * 		{** Command used:	bsubmit **}
 * 		{** Ancestor revision:	1.1.2.3 **}
 * 		{** Merge revision:	1.1.2.4 **}
 * 	{** End **}
 * 	Support BI_BITFIELDS format
 * 	[1995/09/11  18:43:39  Farokh_Morshed]
 *
 * Revision 1.1.2.4  1995/09/05  17:17:34  Paul_Gauthier
 * 	Fix for softjpeg decompression JPEG -> BICOMP_DECYUVDIB
 * 	[1995/09/05  17:17:09  Paul_Gauthier]
 *
 * Revision 1.1.2.3  1995/08/03  15:01:06  Hans_Graves
 * 	Moved ScConvert422ToYUV_char_C() from h261.
 * 	[1995/08/03  14:46:23  Hans_Graves]
 *
 * Revision 1.1.2.2  1995/05/31  18:07:27  Hans_Graves
 * 	Inclusion in new SLIB location.
 * 	[1995/05/31  16:06:30  Hans_Graves]
 *
 * $EndLog$
 */

/*****************************************************************************
**  Copyright (c) Digital Equipment Corporation, 1993                       **
**                                                                          **
**  All Rights Reserved.  Unpublished rights reserved under the  copyright  **
**  laws of the United States.                                              **
**                                                                          **
**  The software contained on this media is proprietary  to  and  embodies  **
**  the   confidential   technology   of  Digital  Equipment  Corporation.  **
**  Possession, use, duplication or  dissemination  of  the  software  and  **
**  media  is  authorized  only  pursuant  to a valid written license from  **
**  Digital Equipment Corporation.                                          **
**                                                                          **
**  RESTRICTED RIGHTS LEGEND Use, duplication, or disclosure by  the  U.S.  **
**  Government  is  subject  to  restrictions as set forth in Subparagraph  **
**  (c)(1)(ii) of DFARS 252.227-7013, or in FAR 52.227-19, as applicable.   **
******************************************************************************/

/*
**
**  Miscellaneous conversion utility subroutines
**
**  Author(s): Victor Bahl
**  Date:      May 27, 1993
**
*/

#include <stdio.h>  /* NULL */
#include <sys/types.h>
#ifdef WIN32
#include <windows.h>
#include <mmsystem.h>
#else /* !WIN32 */
#include <mmsystem.h>
#endif /* !WIN32 */

#include "SC_conv.h"
#include "SC_err.h"

#ifndef BI_DECSEPRGBDIB
#define BI_DECSEPRGBDIB         mmioFOURCC('D','S','R','G')
#endif

#define NEW_YCBCR		/* Use new YUV to RGB coefficients */

/*
** Name:     ScCreateBMHeader
** Purpose:  Allocate memory for a (Microsoft specified) bitmap header and
**           fill in the appropriate fields
**
*/
BITMAPINFOHEADER *ScCreateBMHeader(int width, int height, int bpp,
                                 int format, int ncolors)
{
    BITMAPINFOHEADER *pHead;
    int struct_size = sizeof(BITMAPINFOHEADER) + ncolors*sizeof(RGBQUAD);

    if ((pHead = (BITMAPINFOHEADER *)ScAlloc(struct_size)) == NULL) {
       puts("Can't Allocate memory for image headers");
       return NULL;
    }

    pHead->biSize          = sizeof(BITMAPINFOHEADER);
    pHead->biWidth         = width;
    pHead->biHeight        = height;
    pHead->biPlanes        = 1;
    pHead->biBitCount      = (WORD)bpp;
    pHead->biCompression   = format;
    pHead->biSizeImage     = 0;
    pHead->biXPelsPerMeter = 0;
    pHead->biYPelsPerMeter = 0;
    pHead->biClrUsed       = ncolors;
    pHead->biClrImportant  = 0;

    return pHead;
}

/*
** Function: ScIsUpsideDown
** Descript: Return TRUE if the current combination
** of input and output formats and image
** heights means that the image should be
** flipped during the render stage.
*/
int ScIsUpsideDown( BITMAPINFOHEADER *lpbiIn,
                    BITMAPINFOHEADER *lpbiOut )
{
    int ups = 0 ;
    if( lpbiIn )
      ups = (((lpbiIn->biCompression == BI_RGB) ||
              (lpbiIn->biCompression == BI_BITFIELDS)) ^
             ((int) lpbiIn->biHeight < 0)) ;
    if( lpbiOut )
      ups ^= (((lpbiOut->biCompression == BI_RGB) ||
               (lpbiOut->biCompression == BI_BITFIELDS)) ^
              ((int) lpbiOut->biHeight < 0)) ;
    return ups ;
}

/*
**++
**  FUNCTIONAL_DESCRIPTION:
**	Return an enum value that validates a BI_BITFIELDS bitmapinfoheader
**      with BI_BITFIELDS format.
**
**  FORMAL PARAMETERS:
**	Pointer to bitmapinfoheader
**  RETURN VALUE:
**	A value from enum type ValidBI_BITFIELDSKinds.
**/
enum ValidBI_BITFIELDSKinds ValidateBI_BITFIELDS(
    LPBITMAPINFOHEADER 	lpbi)
{
    DWORD *MaskPtr;

    if (lpbi == NULL || lpbi->biCompression != BI_BITFIELDS)
        return InvalidBI_BITFIELDS;

    MaskPtr = (DWORD *)&lpbi[1];

    /*
     * For 32-bit BI_BITFIELDS, we support
     * only the special cases 00RRGGBB and 00BBGGRR.
     */

    if( lpbi->biBitCount == 32 ) {
	if (MaskPtr[1] != 0x0000FF00U)
	    return InvalidBI_BITFIELDS;
	else if (MaskPtr[0] == 0x00FF0000U && MaskPtr[2] == 0x000000FFU)
	    return pRGB;
	else if (MaskPtr[0] == 0x000000FFU && MaskPtr[2] == 0x00FF0000U)
            return pBGR;
	else
	    return InvalidBI_BITFIELDS;
    }

#ifdef WIN32
    /*
     * For 16-bit BI_BITFIELDS, we support any legal
     * color arrangement, but RGB565 and RGB555 are
     * recognized as special since we have extra
     * fast assembler code for those cases.
     */

    else if( lpbi->biBitCount == 16 ) {
	int i ;
	if( MaskPtr[2] == 0x001f ) {
	    if( MaskPtr[0] == 0xf800 && MaskPtr[1] == 0x07e0 )
	    	return pRGB565 ;
	    else if( MaskPtr[0] == 0x7c00 && MaskPtr[1] == 0x03e00 )
	    	return pRGB555 ;
	}
	/*
	 * Generic case: First make sure that each mask is
	 * a 16-bit mask.
	 */

	if( (MaskPtr[0] | MaskPtr[1] | MaskPtr[2]) & ~0x0000ffff )
	    return InvalidBI_BITFIELDS ;

	/*
	 * Masks must not overlap.
	 */

	if( (MaskPtr[0] & MaskPtr[1]) ||
	    (MaskPtr[0] & MaskPtr[2]) ||
	    (MaskPtr[1] & MaskPtr[2]) )
	    return InvalidBI_BITFIELDS ;

	/*
	 * Make sure each mask contains a contiguous
	 * sequence of 1's (or is 0).
	 */

	for( i=0 ; i<3 ; i++ ) {
	   DWORD v = MaskPtr[i] ;
	   if( (((v-1)|v)+1)&v )
	       return InvalidBI_BITFIELDS ;	
	}

	/*
	 * If we pass all these tests, we have
	 * a valid generic 16-bit BI_BITFIELDS case.
	 */

	return pRGBnnn ;
    }

#endif /* WIN32 */

    /*
     * No other biBitCounts are supported.
     */

    return InvalidBI_BITFIELDS ;
}

/*********************** Conversion Routines ***************************/

static void sc_ExtractBlockNonInt(u_char *InData, float **OutData,
	   		          int ByteWidth, int x, int y)
{
  register int i,j;
  u_char *Inp, *IStart = InData + 8*y*ByteWidth + 8*x;

  for (i = 0 , Inp = IStart ; i < 8 ; i++ , Inp += ByteWidth)
    for (j = 0 ; j < 8 ; j++)
      *((*OutData)++) = (float)((int)(*Inp++) - 128);
}


/*
** Name:     ScConvertSepYUVToOther
** Purpose:  Convert Seperated YUV 422 to another format
**
*/
ScStatus_t ScConvertSepYUVToOther(BITMAPINFOHEADER *InBmh,
				  BITMAPINFOHEADER *OutBmh,
				  u_char *OutImage,
				  u_char *YData, u_char *CbData, u_char *CrData)
{
    /*
    ** no need to do extensive checking, DecompressBegin and CompressBegin
    ** should take care of static checking (eg. valid output colorspace
    */


    /*
    ** Are we converting from SepYUV to RGB ?
    */
    if ((OutBmh->biCompression == BI_RGB)              ||
        (OutBmh->biCompression == BI_BITFIELDS)        ||
        (OutBmh->biCompression == BI_DECXIMAGEDIB) ||
	(OutBmh->biCompression == BI_DECSEPRGBDIB)) {
        /*
	** It is assumed that YUV is subsampled 4:2:2, we will need to
	** generalize the code below to handle other cases as well -- VB
	*/
        if (InBmh->biBitCount == 8)
	  ScYuv422ToRgb (OutBmh, YData, NULL, NULL, OutImage);
        else
	  ScYuv422ToRgb (OutBmh, YData, CbData, CrData, OutImage);
    }
    /*
    ** Are we converting from SepYUV to Interlaced YUV ?
    */
    else if (IsYUV422Sep(OutBmh->biCompression))
    {
       /*
       ** It is assumed that YUV is subsampled 4:2:2, we will need to
       ** generalize the code below to handle other cases as well
       **   XXX - Bad idea to do this here, should be done as part of
       **         decompression (VB)
       **   XXX - While we can move the Sep YUV to 422 interleaved
       **         to decompression, we also need a copy here so we
       **         can use this as a general purpose dither'ing
       **         device. (JPL)
       */
       int i, j;

       /*
       ** If the input format is Mono JPEG and the output format is
       ** packed YUV422, we must reset the U and V values to neutral (Gray).
       */
       if (InBmh->biBitCount == 8) {
#ifdef __alpha
         /*
	  * If we're an Alpha, and the buffer is quadword
	  * aligned, we can use 64-bit transfers.
	  */
         if( ((INT_PTR) OutImage & 0x7) == 0 )
	   {
	     _int64 val   = 0x7f007f007f007f00L ;
	     _int64 *iptr = (_int64 *) OutImage ;
	     for( i=(OutBmh->biWidth*abs(OutBmh->biHeight)>>2) ; i>0 ; i-- )
	       *iptr++ = val ;
	   }
         else
#endif /* __alpha */
	   {
	     int val   = 0x7f007f00 ;
	     int *iptr = (int *) OutImage ;
	     for( i=(OutBmh->biWidth*abs(OutBmh->biHeight)>>1) ; i>0 ; i-- )
	       *iptr++ = val ;
	   }
         /*
	 ** Plug in the luminance samples
	 */
         for( i=(OutBmh->biWidth * abs(OutBmh->biHeight)) ; i>0 ; i-- ) {
           *OutImage = *YData++;
           OutImage +=2;
         }
       }
       /*
       ** Color input
       */
       else {
	 /* If this is quad padded in both X and Y and quad aligned,
	 ** we can call the assembly routine
	 */
	 if ( (abs(OutBmh->biHeight) & 0x7) == 0 &&
	     (OutBmh->biWidth & 0x7) == 0 &&
	     ((ULONG_PTR)OutImage & 0xf) == 0 )
	   {
	     ScConvert422PlanarTo422i(YData, CbData, CrData, OutImage,
				      OutBmh->biWidth, abs(OutBmh->biHeight));
	   }
	 else {
	   for (i=0; i<abs(OutBmh->biHeight); i++)
	     /* Remember, pixels are 16 bit in interleaved YUV. That
	     ** means the 4 bytes below represent two pixels so our
	     ** loop should be for half the width.
	     */
	     for (j=0; j<OutBmh->biWidth>>1; j++) {     /* Note: was j+=2 */
	       *OutImage++ = *YData++;
	       *OutImage++ = *CbData++;
	       *OutImage++ = *YData++;
	       *OutImage++ = *CrData++;
	     }
	 }
       }
    }
    else return(ScErrorUnrecognizedFormat);

    return(ScErrorNone);
}

/*
** Name:    ScYuv411ToRgb
** Purpose: convert 16-bit YCrCb 4:1:1 to an 16/24/32-bit RGB
**
** Note:    The code below is pixel based and is inefficient, we
**	    plan to replace faster assembly code
*/
ScStatus_t ScYuv411ToRgb (BITMAPINFOHEADER *Bmh, u_char *Y, u_char *Cb,
			u_char *Cr, u_char *ImageOut, int Width, int Height, long stride)
{
   const int pixels = Width;
   int lines  = Height;
   register int row, col;
   register int Luma, U, V;
   /* Variables to hold R, G and B values for a 2x2 matrix of pixels */
   int R1,R2,R3,R4;
   int G1,G2,G3,G4;
   int B1,B2,B3,B4;
   int Ups = 0, tmp;			/* Flip image upside down */
   u_char  *Y1=Y, *Y2=Y+pixels;
#define _LoadRGBfrom411() \
         R1 = R2 = R3 = R4 = (int) (              + (1.596 * V)); \
         G1 = G2 = G3 = G4 = (int) (- (0.391 * U) - (0.813 * V)); \
         B1 = B2 = B3 = B4 = (int) (+ (2.018 * U)              ); \
	 Luma = (int) (((int) *(Y1++) - 16) * 1.164); \
         R1 += Luma; G1 +=Luma; B1 += Luma; \
	 Luma = (int) (((int) *(Y1++) - 16) * 1.164); \
         R2 += Luma; G2 +=Luma; B2 += Luma; \
         if ((R1 | G1 | B1 | R2 | G2 | B2) & 0xffffff00) { \
           if (R1<0) R1=0; else if (R1>255) R1=255; \
           if (G1<0) G1=0; else if (G1>255) G1=255; \
           if (B1<0) B1=0; else if (B1>255) B1=255; \
           if (R2<0) R2=0; else if (R2>255) R2=255; \
           if (G2<0) G2=0; else if (G2>255) G2=255; \
           if (B2<0) B2=0; else if (B2>255) B2=255; \
         } \
         Luma = (int) (((int) *(Y2++) - 16) * 1.164); \
         R3 += Luma; G3 +=Luma; B3 += Luma; \
         Luma = (int) (((int) *(Y2++) - 16) * 1.164); \
         R4 += Luma; G4 +=Luma; B4 += Luma; \
         if ((R3 | G3 | B3 | R4 | G4 | B4) & 0xffffff00) { \
           if (R3<0) R3=0; else if (R3>255) R3=255; \
           if (G3<0) G3=0; else if (G3>255) G3=255; \
           if (B3<0) B3=0; else if (B3>255) B3=255; \
           if (R4<0) R4=0; else if (R4>255) R4=255; \
           if (G4<0) G4=0; else if (G4>255) G4=255; \
           if (B4<0) B4=0; else if (B4>255) B4=255; \
         }

   /*
    * Normally, images are stored right side up, that is with the
    * first pixel in the buffer corresponding to the top left pixel
    * in the image.
    *
    * The Microsoft standard Device Independent bitmap formats BI_RGB and
    * BI_BITFIELD are stored with the lower left pixel first.  We view that
    * as upside down.
    *
    * Each format can also have a negative height, which also signifes
    * upside down.
    *
    * Since two negatives makes a positive, that means that BI_ formats with
    * negative height are right side up.
    */
   if( Bmh->biCompression == BI_RGB ||
       Bmh->biCompression == BI_BITFIELDS )
       Ups = 1 ;

   if( lines < 0 ) {
      Ups = 1-Ups ;
      lines = -lines ;
   }

   /*
   ** The assumption is that YCrCb are subsampled 4:1:1
   **	YSize           = lines * pixels
   **   CbSize = CrSize = (lines*pixels)/4
   */
   switch (Bmh->biCompression)
   {
      case BI_RGB:
            switch (Bmh->biBitCount)
            {
              case 15:
                {
                  u_short *Sout1 = (u_short *)ImageOut, *Sout2=Sout1+pixels;
                  for (row = 0; row < lines; row+=2)
                  {
                    if (Ups)
                    {
                      tmp = stride * (lines-row-1) ;
                      Sout1 = (u_short *)ImageOut+tmp; /* For 16-bit */
                      Sout2=Sout1-stride;
                    }
                    else
                    {
                      tmp = stride * row;
                      Sout1 = (u_short *)ImageOut+tmp; /* For 16-bit */
                      Sout2=Sout1+stride;
                    }
                    Y2=Y1+pixels;
                    for (col = 0; col < pixels; col += 2)
                    {
	              U = *Cb++ - 128;
	              V = *Cr++ - 128;
                      _LoadRGBfrom411();
		      *(Sout1++) = ((R1&0xf8)<<7)|((G1&0xf8)<<2)|((B1&0xf8)>>3);
		      *(Sout1++) = ((R2&0xf8)<<7)|((G2&0xf8)<<2)|((B2&0xf8)>>3);
		      *(Sout2++) = ((R3&0xf8)<<7)|((G3&0xf8)<<2)|((B3&0xf8)>>3);
		      *(Sout2++) = ((R4&0xf8)<<7)|((G4&0xf8)<<2)|((B4&0xf8)>>3);
                    }
                    Y1=Y2;
                  }
                }
                break;
              case 16:
                {
                  u_short *Sout1 = (u_short *)ImageOut, *Sout2=Sout1+pixels;
                  for (row = 0; row < lines; row+=2)
                  {
                    if (Ups)
                    {
                      tmp = stride * (lines-row-1) ;
                      Sout1 = (u_short *)ImageOut+tmp; /* For 16-bit */
                      Sout2=Sout1-stride;
                    }
                    else
                    {
                      tmp = stride * row;
                      Sout1 = (u_short *)ImageOut+tmp; /* For 16-bit */
                      Sout2=Sout1+stride;
                    }
                    Y2=Y1+pixels;
                    for (col = 0; col < pixels; col += 2)
                    {
	              U = *Cb++ - 128;
	              V = *Cr++ - 128;
                      _LoadRGBfrom411();
#ifdef WIN95  /* RGB 565 - 16 bit */
		      *(Sout1++) = ((R1&0xf8)<<8)|((G1&0xfC)<<3)|((B1&0xf8)>>3);
		      *(Sout1++) = ((R2&0xf8)<<8)|((G2&0xfC)<<3)|((B2&0xf8)>>3);
		      *(Sout2++) = ((R3&0xf8)<<8)|((G3&0xfC)<<3)|((B3&0xf8)>>3);
		      *(Sout2++) = ((R4&0xf8)<<8)|((G4&0xfC)<<3)|((B4&0xf8)>>3);
#else /* RGB 555 - 15 bit */
		      *(Sout1++) = ((R1&0xf8)<<7)|((G1&0xf8)<<2)|((B1&0xf8)>>3);
		      *(Sout1++) = ((R2&0xf8)<<7)|((G2&0xf8)<<2)|((B2&0xf8)>>3);
		      *(Sout2++) = ((R3&0xf8)<<7)|((G3&0xf8)<<2)|((B3&0xf8)>>3);
		      *(Sout2++) = ((R4&0xf8)<<7)|((G4&0xf8)<<2)|((B4&0xf8)>>3);
#endif
                    }
                    Y1=Y2;
                  }
                }
                break;
              case 24:
                {
                  u_char *Cout1, *Cout2;
                  stride*=3;
                  for (row = 0; row < lines; row+=2)
                  {
                    if (Ups)
                    {
                      tmp = stride * (lines-row-1) ;
                      Cout1 = (u_char *)(ImageOut + tmp); /* For 24-bit */
                      Cout2=Cout1-stride;
                    }
                    else
                    {
                      tmp = stride * row;
                      Cout1 = (u_char *)(ImageOut + tmp); /* For 24-bit */
                      Cout2=Cout1+stride;
                    }
                    Y2=Y1+pixels;
                    for (col = 0; col < pixels; col += 2)
                    {
	              U = *Cb++ - 128;
	              V = *Cr++ - 128;
                      _LoadRGBfrom411();
	              *(Cout1++) = (u_char)B1; *(Cout1++) = (u_char)G1; *(Cout1++) = (u_char)R1;
	              *(Cout1++) = (u_char)B2; *(Cout1++) = (u_char)G2; *(Cout1++) = (u_char)R2;
	              *(Cout2++) = (u_char)B3; *(Cout2++) = (u_char)G3; *(Cout2++) = (u_char)R3;
	              *(Cout2++) = (u_char)B4; *(Cout2++) = (u_char)G4; *(Cout2++) = (u_char)R4;
                    }
                    Y1=Y2;
                  }
                }
                break;
              case 32:
                {
                  unsigned dword *Wout1 = (unsigned dword *)ImageOut,
                                 *Wout2=Wout1+pixels;
                  for (row = 0; row < lines; row+=2)
                  {
                    if (Ups)
                    {
                      tmp = stride * (lines-row-1);
                      Wout1 = (unsigned dword *)ImageOut + tmp;
                      Wout2=Wout1-stride;
                    }
                    else
                    {
                      tmp = stride * row;
                      Wout1 = (unsigned dword *)ImageOut + tmp;
                      Wout2=Wout1+stride;
                    }
                    Y2=Y1+pixels;
                    for (col = 0; col < pixels; col += 2)
                    {
	              U = *Cb++ - 128;
	              V = *Cr++ - 128;
                      _LoadRGBfrom411();
		      *(Wout1++) = (R1<<16) | (G1<<8) | B1;
		      *(Wout1++) = (R2<<16) | (G2<<8) | B2;
		      *(Wout2++) = (R3<<16) | (G3<<8) | B3;
		      *(Wout2++) = (R4<<16) | (G4<<8) | B4;
                    }
                    Y1=Y2;
                  }
                }
                break;
            }
            break;
      case BI_DECSEPRGBDIB: /* 24-bit separate RGB */
            {
              u_char *RData1, *GData1, *BData1;
              u_char *RData2, *GData2, *BData2;
              RData1 = ImageOut;
              GData1 = RData1 + (pixels * lines);
              BData1 = GData1 + (pixels * lines);
              RData2 = RData1 + pixels;
              GData2 = GData1 + pixels;
              BData2 = BData1 + pixels;
              for (row = 0; row < lines; row+=2)
              {
                Y2=Y1+pixels;
                for (col = 0; col < pixels; col += 2)
                {
	          U = *Cb++ - 128;
	          V = *Cr++ - 128;
                  _LoadRGBfrom411();
		  *(RData1++) = (u_char)R1; *(RData1++) = (u_char)R2;
          *(RData2++) = (u_char)R3; *(RData2++) = (u_char)R4;
		  *(GData1++) = (u_char)G1; *(GData1++) = (u_char)G2;
		  *(GData2++) = (u_char)G3; *(GData2++) = (u_char)G4;
		  *(BData1++) = (u_char)B1; *(BData1++) = (u_char)B2;
		  *(BData2++) = (u_char)B3; *(BData2++) = (u_char)B4;
	        }
                RData1=RData2;
                RData2=RData1+pixels;
                Y1=Y2;
              }
            }
            break;
      case BI_DECXIMAGEDIB:  /* XIMAGE 24 == pBGR */
      case BI_BITFIELDS: /* 32-bit RGB */
            {
              unsigned dword *Iout1 = (unsigned dword *)ImageOut,
                             *Iout2=Iout1+pixels;
              if (ValidateBI_BITFIELDS(Bmh) == pRGB)
                for (row = 0; row < lines; row+=2)
                {
                  if (Ups)
                  {
                    tmp = stride * (lines-row-1);
                    Iout1 = (unsigned dword *)ImageOut+tmp;  /* For 32-bit */
                    Iout2=Iout1-stride;
                  }
                  else
                  {
                    tmp = stride * row;
                    Iout1 = (unsigned dword *)ImageOut+tmp;  /* For 32-bit */
                    Iout2=Iout1+stride;
                  }
                  Y2=Y1+pixels;
                  for (col = 0; col < pixels; col += 2)
                  {
	            U = *Cb++ - 128;
	            V = *Cr++ - 128;
                    _LoadRGBfrom411();
                    *(Iout1++) = (R1<<16) | (G1<<8) | B1;
                    *(Iout1++) = (R2<<16) | (G2<<8) | B2;
                    *(Iout2++) = (R3<<16) | (G3<<8) | B3;
                    *(Iout2++) = (R4<<16) | (G4<<8) | B4;
                  }
                  Y1=Y2;
                }
              else /* pBGR and XIMAGE 24-bit */
                for (row = 0; row < lines; row+=2)
                {
                  if (Ups)
                  {
                    tmp = stride * (lines-row-1);
                    Iout1 = (unsigned dword *)ImageOut+tmp;  /* For 32-bit */
                    Iout2=Iout1-stride;
                  }
                  else
                  {
                    tmp = stride * row;
                    Iout1 = (unsigned dword *)ImageOut+tmp;  /* For 32-bit */
                    Iout2=Iout1+stride;
                  }
                  Y2=Y1+pixels;
                  for (col = 0; col < pixels; col += 2)
                  {
	            U = *Cb++ - 128;
	            V = *Cr++ - 128;
                    _LoadRGBfrom411();
                    *(Iout1++) = (B1<<16) | (G1<<8) | R1;
                    *(Iout1++) = (B2<<16) | (G2<<8) | R2;
                    *(Iout2++) = (B3<<16) | (G3<<8) | R3;
                    *(Iout2++) = (B4<<16) | (G4<<8) | R4;
                  }
                  Y1=Y2;
                }
            }
            break;
        default:
            return(ScErrorUnrecognizedFormat);
   }
   return (NoErrors);
}


/*
** Name:    ScYuv1611ToRgb
** Purpose: convert 16-bit YCrCb 16:1:1 (YUV9/YVU9) to 16/24/32-bit RGB
**
** Note:    The code below is pixel based and is inefficient, we
**	    plan to replace faster assembly code
** This routine is used by Indeo, which actually only has 7-bits for
** Y, U and V components.  The 8th bits are ignored.
*/
ScStatus_t ScYuv1611ToRgb (BITMAPINFOHEADER *Bmh, u_char *Y, u_char *Cb,
			u_char *Cr, u_char *ImageOut)
{
   const int pixels = Bmh->biWidth;
   int lines  = Bmh->biHeight;
   register int row, col, i;
   register int Luma, U, V;
   /* Variables to hold R, G and B values for a 4x4 matrix of pixels */
   int R[16], G[16], B[16], tmpR, tmpG, tmpB, cR, cG, cB;
   int Ups = 0, tmp;			/* Flip image upside down */
   u_char  *Y0=Y, *Y1, *Y2, *Y3;
#define _LoadRGBfrom1611() \
         cR=(int) (             + (1.596 * V));\
	 cG=(int) (-(0.391 * U) - (0.813 * V));\
	 cB=(int) (+(2.018 * U)              );\
         for (i=0; i<4; i++) { \
	   Luma = (int) ((((int)(*Y0++)<<1) - 16) * 1.164); \
           tmpR=cR + Luma; tmpG=cG + Luma; tmpB=cB + Luma; \
           if ((tmpR | tmpG | tmpB) & 0xffffff00) { \
             if (tmpR<0) R[i]=0; else if (tmpR>255) R[i]=255; else R[i]=tmpR; \
             if (tmpG<0) G[i]=0; else if (tmpG>255) G[i]=255; else G[i]=tmpG; \
             if (tmpB<0) B[i]=0; else if (tmpB>255) B[i]=255; else B[i]=tmpB; \
           } else { R[i]=tmpR; G[i]=tmpG; B[i]=tmpB; } \
         } \
         for (; i<8; i++) { \
	   Luma = (int) ((((int)(*Y1++)<<1) - 16) * 1.164); \
           tmpR=cR + Luma; tmpG=cG + Luma; tmpB=cB + Luma; \
           if ((tmpR | tmpG | tmpB) & 0xffffff00) { \
             if (tmpR<0) R[i]=0; else if (tmpR>255) R[i]=255; else R[i]=tmpR; \
             if (tmpG<0) G[i]=0; else if (tmpG>255) G[i]=255; else G[i]=tmpG; \
             if (tmpB<0) B[i]=0; else if (tmpB>255) B[i]=255; else B[i]=tmpB; \
           } else { R[i]=tmpR; G[i]=tmpG; B[i]=tmpB; } \
         } \
         for (; i<12; i++) { \
	   Luma = (int) ((((int)(*Y2++)<<1) - 16) * 1.164); \
           tmpR=cR + Luma; tmpG=cG + Luma; tmpB=cB + Luma; \
           if ((tmpR | tmpG | tmpB) & 0xffffff00) { \
             if (tmpR<0) R[i]=0; else if (tmpR>255) R[i]=255; else R[i]=tmpR; \
             if (tmpG<0) G[i]=0; else if (tmpG>255) G[i]=255; else G[i]=tmpG; \
             if (tmpB<0) B[i]=0; else if (tmpB>255) B[i]=255; else B[i]=tmpB; \
           } else { R[i]=tmpR; G[i]=tmpG; B[i]=tmpB; } \
         } \
         for (; i<16; i++) { \
	   Luma = (int) ((((int)(*Y3++)<<1) - 16) * 1.164); \
           tmpR=cR + Luma; tmpG=cG + Luma; tmpB=cB + Luma; \
           if ((tmpR | tmpG | tmpB) & 0xffffff00) { \
             if (tmpR<0) R[i]=0; else if (tmpR>255) R[i]=255; else R[i]=tmpR; \
             if (tmpG<0) G[i]=0; else if (tmpG>255) G[i]=255; else G[i]=tmpG; \
             if (tmpB<0) B[i]=0; else if (tmpB>255) B[i]=255; else B[i]=tmpB; \
           } else { R[i]=tmpR; G[i]=tmpG; B[i]=tmpB; } \
         }

   /*
    * Normally, images are stored right side up, that is with the
    * first pixel in the buffer corresponding to the top left pixel
    * in the image.
    *
    * The Microsoft standard Device Independent bitmap formats BI_RGB and
    * BI_BITFIELD are stored with the lower left pixel first.  We view that
    * as upside down.
    *
    * Each format can also have a negative height, which also signifes
    * upside down.
    *
    * Since two negatives makes a positive, that means that BI_ formats with
    * negative height are right side up.
    */
   if( Bmh->biCompression == BI_RGB ||
       Bmh->biCompression == BI_BITFIELDS )
       Ups = 1 ;

   if( lines < 0 ) {
      Ups = 1-Ups ;
      lines = -lines ;
   }

   /*
   ** The assumption is that YCrCb are subsampled 4:1:1
   **	YSize           = lines * pixels
   **   CbSize = CrSize = (lines*pixels)/4
   */
   switch (Bmh->biCompression)
   {
      case BI_DECXIMAGEDIB:  /* XIMAGE 24 == pBGR */
      case BI_BITFIELDS: /* 32-bit RGB */
            {
              unsigned dword *Iout0 = (unsigned dword *)ImageOut,
                             *Iout1, *Iout2, *Iout3;
              if (ValidateBI_BITFIELDS(Bmh) == pRGB)
                for (row = 0; row < lines; row+=4)
                {
                  if (Ups) {
                    tmp = pixels * (lines-row-1) ;
                    Iout0 = (unsigned dword *)ImageOut+tmp;
                    Iout1=Iout0-pixels; Iout2=Iout1-pixels; Iout3=Iout2-pixels;
                  }
                  else {
                    Iout1=Iout0+pixels; Iout2=Iout1+pixels; Iout3=Iout2+pixels;
                  }
                  Y1=Y0+pixels; Y2=Y1+pixels; Y3=Y2+pixels;
                  for (col = 0; col < pixels; col += 4)
                  {
                    if (*Cb & 0x80) /* if 8th bit is set, ignore */
                    {
                      for (i=0; i<4; i++) {
                        *(Iout0++) = 0;
                        *(Iout1++) = 0;
                        *(Iout2++) = 0;
                        *(Iout3++) = 0;
                      }
                      Cb++; Cr++;
                    }
                    else
                    {
	              U = ((*Cb++)<<1) - 128;
	              V = ((*Cr++)<<1) - 128;
                      _LoadRGBfrom1611();
                      for (i=0; i<4; i++)
                        *(Iout0++) = (R[i]<<16) | (G[i]<<8) | B[i];
                      for (; i<8; i++)
                        *(Iout1++) = (R[i]<<16) | (G[i]<<8) | B[i];
                      for (; i<12; i++)
                        *(Iout2++) = (R[i]<<16) | (G[i]<<8) | B[i];
                      for (; i<16; i++)
                        *(Iout3++) = (R[i]<<16) | (G[i]<<8) | B[i];
                    }
                  }
                  Iout0=Iout3;
                  Y0=Y3;
                }
              else /* pBGR and XIMAGE 24-bit */
                for (row = 0; row < lines; row+=4)
                {
                  if (Ups) {
                    tmp = pixels * (lines-row-1) ;
                    Iout0 = (unsigned dword *)ImageOut+tmp;
                    Iout1=Iout0-pixels; Iout2=Iout1-pixels; Iout3=Iout2-pixels;
                  }
                  else {
                    Iout1=Iout0+pixels; Iout2=Iout1+pixels; Iout3=Iout2+pixels;
                  }
                  Y1=Y0+pixels; Y2=Y1+pixels; Y3=Y2+pixels;
                  for (col = 0; col < pixels; col += 4)
                  {
                    if (*Cb & 0x80) /* if 8th bit is set, ignore */
                    {
                      for (i=0; i<4; i++) {
                        *(Iout0++) = 0;
                        *(Iout1++) = 0;
                        *(Iout2++) = 0;
                        *(Iout3++) = 0;
                      }
                      Cb++; Cr++;
                    }
                    else
                    {
	              U = ((*Cb++)<<1) - 128;
	              V = ((*Cr++)<<1) - 128;
                      _LoadRGBfrom1611();
                      for (i=0; i<4; i++)
                        *(Iout0++) = (B[i]<<16) | (G[i]<<8) | R[i];
                      for (; i<8; i++)
                        *(Iout1++) = (B[i]<<16) | (G[i]<<8) | R[i];
                      for (; i<12; i++)
                        *(Iout2++) = (B[i]<<16) | (G[i]<<8) | R[i];
                      for (; i<16; i++)
                        *(Iout3++) = (B[i]<<16) | (G[i]<<8) | R[i];
                    }
                  }
                  Iout0=Iout3;
                  Y0=Y3;
                }
            }
            break;
      case BI_RGB:
            switch (Bmh->biBitCount)
            {
              case 15:
              case 16:
                {
                  u_short *Sout0 = (u_short *)ImageOut, *Sout1, *Sout2, *Sout3;
                  for (row = 0; row < lines; row+=4)
                  {
                    if (Ups) {
                     tmp = pixels * (lines-row-1) ;
                     Sout0 = &((u_short *)ImageOut)[tmp];  /* For 32-bit */
                     Sout1=Sout0-pixels; Sout2=Sout1-pixels; Sout3=Sout2-pixels;
                    }
                    else {
                     Sout1=Sout0+pixels; Sout2=Sout1+pixels; Sout3=Sout2+pixels;
                    }
                    Y1=Y0+pixels; Y2=Y1+pixels; Y3=Y2+pixels;
                    for (col = 0; col < pixels; col += 4)
                    {
                      if (*Cb & 0x80) /* if 8th bit is set, ignore */
                      {
                        for (i=0; i<4; i++) {
                          *(Sout0++) = 0;
                          *(Sout1++) = 0;
                          *(Sout2++) = 0;
                          *(Sout3++) = 0;
                        }
                        Cb++; Cr++;
                      }
                      else
                      {
	                U = ((*Cb++)<<1) - 128;
	                V = ((*Cr++)<<1) - 128;
                        _LoadRGBfrom1611();
                        for (i=0; i<4; i++)
                          *(Sout0++)=
                             ((R[i]&0xf8)<<7)|((G[i]&0xf8)<<2)|((B[i]&0xf8)>>3);
                        for (; i<8; i++)
                          *(Sout1++)=
                             ((R[i]&0xf8)<<7)|((G[i]&0xf8)<<2)|((B[i]&0xf8)>>3);
                        for (; i<12; i++)
                          *(Sout2++)=
                             ((R[i]&0xf8)<<7)|((G[i]&0xf8)<<2)|((B[i]&0xf8)>>3);
                        for (; i<16; i++)
                          *(Sout3++)=
                             ((R[i]&0xf8)<<7)|((G[i]&0xf8)<<2)|((B[i]&0xf8)>>3);
                      }
                    }
                    Sout0=Sout3;
                    Y0=Y3;
                  }
                }
                break;
              case 24:
                {
                  u_char *Cout0 = (u_char *)ImageOut, *Cout1, *Cout2, *Cout3;
                  for (row = 0; row < lines; row+=4)
                  {
                    if (Ups) {
                     tmp = pixels * (lines-row-1) ;
                     Cout0 = &((u_char *)ImageOut)[tmp*3];  /* For 32-bit */
                     Cout1=Cout0-pixels; Cout2=Cout1-pixels; Cout3=Cout2-pixels;
                    }
                    else {
                     Cout1=Cout0+pixels; Cout2=Cout1+pixels; Cout3=Cout2+pixels;
                    }
                    Y1=Y0+pixels; Y2=Y1+pixels; Y3=Y2+pixels;
                    for (col = 0; col < pixels; col += 4)
                    {
                      if (*Cb & 0x80) /* if 8th bit is set, ignore */
                      {
                        for (i=0; i<4*3; i++) {
                          *(Cout0++) = 0;
                          *(Cout1++) = 0;
                          *(Cout2++) = 0;
                          *(Cout3++) = 0;
                        }
                        Cb++; Cr++;
                      }
                      else
                      {
	                U = ((*Cb++)<<1) - 128;
	                V = ((*Cr++)<<1) - 128;
                        _LoadRGBfrom1611();
                        for (i=0; i<4; i++)
                        { *(Cout0++)=(u_char)B[i]; *(Cout0++)=(u_char)G[i]; *(Cout0++)=(u_char)R[i]; }
                        for (; i<8; i++)
                        { *(Cout1++)=(u_char)B[i]; *(Cout1++)=(u_char)G[i]; *(Cout1++)=(u_char)R[i]; }
                        for (; i<12; i++)
                        { *(Cout2++)=(u_char)B[i]; *(Cout2++)=(u_char)G[i]; *(Cout2++)=(u_char)R[i]; }
                        for (; i<16; i++)
                        { *(Cout3++)=(u_char)B[i]; *(Cout3++)=(u_char)G[i]; *(Cout3++)=(u_char)R[i]; }
                      }
                    }
                    Cout0=Cout3;
                    Y0=Y3;
                  }
                }
                break;
            }
            break;
        default:
            return(ScErrorUnrecognizedFormat);
   }
   return (NoErrors);
}


/*
** Name:    ScYuv422ToRgb
** Purpose: convert 16-bit YCrCb 4:2:2 to an 24-bit/16-bit/32-bit RGB
**
** Note:    The code below is pixel based and is *extremely* inefficient, we
**	    plan to replace the dumb code below with some fast code
** If Cb==NULL and Cr==NULL then assume BI_DECGRAYDIB (use only Y component).
*/
ScStatus_t ScYuv422ToRgb (BITMAPINFOHEADER *Bmh, u_char *Y, u_char *Cb,
			u_char *Cr, u_char *ImageOut)
{
   register int row, col;
   register int Luma,U=0,V=0;
   int R1,R2, G1,G2, B1,B2;
   int Ups = 0, tmp;			/* Flip image upside down */
   u_char *RData, *GData, *BData;	/* pointers for non-interlaced mode */
   u_char  *Cout = (u_char *)ImageOut;
   u_short *Sout = (u_short *)ImageOut;
   u_int   *Iout = (u_int *)ImageOut;
   int pixels = Bmh->biWidth;
   int lines  = Bmh->biHeight;
#ifdef NEW_YCBCR
#define _LoadRGBfrom422() \
         if (U || V) { \
           R1 = R2 = (int) (              + (1.596 * V)); \
           G1 = G2 = (int) (- (0.391 * U) - (0.813 * V)); \
           B1 = B2 = (int) (+ (2.018 * U)              );  \
         } else { R1=R2=G1=G2=B1=B2=0; } \
	 Luma = (int) (((int) *(Y++) - 16) * 1.164); \
         R1 += Luma; G1 += Luma; B1 += Luma; \
	 Luma = (int) (((int) *(Y++) - 16) * 1.164); \
         R2 += Luma; G2 += Luma; B2 += Luma; \
         if ((R1 | G1 | B1 | R2 | G2 | B2) & 0xffffff00) { \
           if (R1<0) R1=0; else if (R1>255) R1=255; \
           if (G1<0) G1=0; else if (G1>255) G1=255; \
           if (B1<0) B1=0; else if (B1>255) B1=255; \
           if (R2<0) R2=0; else if (R2>255) R2=255; \
           if (G2<0) G2=0; else if (G2>255) G2=255; \
           if (B2<0) B2=0; else if (B2>255) B2=255; \
         }
#else
#define _LoadRGBfrom422() \
	 Luma = *(Y++); \
         R1 = Luma                + (1.4075 * V); \
         G1 = Luma - (0.3455 * U) - (0.7169 * V); \
         B1 = Luma + (1.7790 * U); \
	 Luma = *(Y++); \
         R2 = Luma                + (1.4075 * V); \
         G2 = Luma - (0.3455 * U) - (0.7169 * V); \
         B2 = Luma + (1.7790 * U); \
         if ((R1 | G1 | B1 | R2 | G2 | B2) & 0xffffff00) { \
           if (R1<0) R1=0; else if (R1>255) R1=255; \
           if (G1<0) G1=0; else if (G1>255) G1=255; \
           if (B1<0) B1=0; else if (B1>255) B1=255; \
           if (R2<0) R2=0; else if (R2>255) R2=255; \
           if (G2<0) G2=0; else if (G2>255) G2=255; \
           if (B2<0) B2=0; else if (B2>255) B2=255; \
         }
#endif /* NEW_YCBCR */

   /*
    * Normally, images are stored right side up,
    * that is with the first pixel in the buffer
    * corresponding to the top left pixel in the image.
    *
    * The Microsoft standard Device Independent bitmap
    * formats BI_RGB and BI_BITFIELD are stored with
    * the lower left pixel first.
    * We view that as upside down.
    *
    * Each format can also have a negative height,
    * which also signifes upside down.
    *
    * Since two negatives makes a positive, that means
    * that BI_ formats with a negative height are right side up.
    */

   if( Bmh->biCompression == BI_RGB ||
       Bmh->biCompression == BI_BITFIELDS)
       Ups = 1 ;

   if( lines < 0 ) {
      Ups = 1-Ups ;
      lines = -lines ;
   }

   /*
   ** needed if the three components are to be provided in a
   ** non-interlaced mode:
   */
   if (Bmh->biCompression == BI_DECSEPRGBDIB) {
      RData = ImageOut;
      GData = RData + (pixels * lines);
      BData = GData + (pixels * lines);
   }


   /*
   ** The assumption is that YCrCb are subsampled 4:2:2
   **	YSize           = lines * pixels
   **   CbSize = CrSize = (lines*pixels)/2
   */
   switch (Bmh->biCompression)
   {
      case BI_RGB:
            switch (Bmh->biBitCount)
            {
              case 15:
              case 16:
                {
                  u_short *Sout = (u_short *)ImageOut;
                  for (row = 0; row < lines; row++)
                  {
                    if (Ups)
                    {
                      tmp = pixels * (lines-row-1) ;
                      Sout = &((u_short *)ImageOut)[tmp]; /* For 16-bit */
                    }
                    for (col = 0; col < pixels; col += 2)
                    {
                      if (Cb) {
	                U = *Cb++ - 128;
	                V = *Cr++ - 128;
                      }
                      _LoadRGBfrom422();
		      *(Sout++) = ((R1&0xf8)<<7)|((G1&0xf8)<<2)|((B1&0xf8)>>3);
		      *(Sout++) = ((R2&0xf8)<<7)|((G2&0xf8)<<2)|((B2&0xf8)>>3);
                    }
                  }
                }
                break;
              case 24:
                {
                  u_char *Cout = (u_char *)ImageOut;
                  for (row = 0; row < lines; row++)
                  {
                    if (Ups)
                    {
                      tmp = pixels * (lines-row-1) ;
                      Cout = &((u_char *)ImageOut)[3*tmp]; /* For 24-bit */
                    }
                    for (col = 0; col < pixels; col += 2)
                    {
                      if (Cb) {
	                U = *Cb++ - 128;
	                V = *Cr++ - 128;
                      }
                      _LoadRGBfrom422();
	              *(Cout++) = (u_char)B1; *(Cout++) = (u_char)G1; *(Cout++) = (u_char)R1;
	              *(Cout++) = (u_char)B2; *(Cout++) = (u_char)G2; *(Cout++) = (u_char)R2;
                    }
                  }
                }
                break;
              case 32:
                {
                  u_int *Iout = (u_int *)ImageOut;
                  for (row = 0; row < lines; row++)
                  {
                    if (Ups)
                    {
                      tmp = pixels * (lines-row-1) ;
                      Iout = &((u_int *)ImageOut)[tmp]; /* For 32-bit */
                    }
                    for (col = 0; col < pixels; col += 2)
                    {
                      if (Cb) {
	                U = *Cb++ - 128;
	                V = *Cr++ - 128;
                      }
                      _LoadRGBfrom422();
		      *(Iout++) = (R1<<16) | (G1<<8) | B1 ;
		      *(Iout++) = (R2<<16) | (G2<<8) | B2 ;
                    }
                  }
                }
                break;
            }
            break;
      case BI_DECSEPRGBDIB: /* 24-bit separate RGB */
            {
              u_char *RData, *GData, *BData;
              RData = ImageOut;
              GData = RData + (pixels * lines);
              BData = GData + (pixels * lines);
              for (row = 0; row < lines; row++)
              {
                for (col = 0; col < pixels; col += 2)
                {
                  if (Cb) {
	            U = *Cb++ - 128;
	            V = *Cr++ - 128;
                  }
                  _LoadRGBfrom422();
		  *(RData++) = (u_char)R1; *(RData++) = (u_char)R2;
		  *(GData++) = (u_char)G1; *(GData++) = (u_char)G2;
		  *(BData++) = (u_char)B1; *(BData++) = (u_char)B2;
	        }
              }
            }
            break;
      case BI_DECXIMAGEDIB:  /* XIMAGE 24 == pBGR */
      case BI_BITFIELDS: /* 16 or 32-bit RGB */
            switch (Bmh->biBitCount)
            {
	    case 16:
	      {	/* 16-bit BI_BITFIELDS, hardcoded to RGB565 */
		u_short *Sout = (u_short *)ImageOut;
                for (row = 0; row < lines; row++)
                {
                  if (Ups)
                  {
                    tmp = pixels * (lines-row-1) ;
                    Sout = &((u_short *)ImageOut)[tmp];  /* For 16-bit */
                  }
                  for (col = 0; col < pixels; col += 2)
                  {
                    if (Cb) {
	              U = *Cb++ - 128;
	              V = *Cr++ - 128;
                    }
                    _LoadRGBfrom422();
                    *(Sout++) = ((R1<<8) & 0xf800) | ((G1<<3) & 0x07e0) | ((B1>>3) & 0x01f);
                    *(Sout++) = ((R2<<8) & 0xf800) | ((G2<<3) & 0x07e0) | ((B2>>3) & 0x01f);
                  }
                }
	      }
	      break ;
	    case 24:
	    case 32:
	      { /* 32-bit RGB */
                u_int *Iout = (u_int *)ImageOut;
                if (ValidateBI_BITFIELDS(Bmh) == pRGB)
		{
                  for (row = 0; row < lines; row++)
                  {
                    if (Ups)
                    {
                      tmp = pixels * (lines-row-1) ;
                      Iout = &((u_int *)ImageOut)[tmp];  /* For 32-bit */
                    }
                    for (col = 0; col < pixels; col += 2)
                    {
                      if (Cb) {
	                U = *Cb++ - 128;
	                V = *Cr++ - 128;
                      }
                      _LoadRGBfrom422();
                      *(Iout++) = (R1<<16) | (G1<<8) | B1;
                      *(Iout++) = (R2<<16) | (G2<<8) | B2;
                    }
                  }
		}
                else /* pBGR and XIMAGE 24-bit */
                {
		  for (row = 0; row < lines; row++)
                  {
                    if (Ups)
                    {
                      tmp = pixels * (lines-row-1) ;
                      Iout = &((u_int *)ImageOut)[tmp];  /* For 32-bit */
                    }
                    for (col = 0; col < pixels; col += 2)
                    {
                      if (Cb) {
	                U = *Cb++ - 128;
	                V = *Cr++ - 128;
                      }
                      _LoadRGBfrom422();
                      *(Iout++) = (B1<<16) | (G1<<8) | R1;
                      *(Iout++) = (B2<<16) | (G2<<8) | R2;
                    }
                  }
		}	
	      }
	      break;
	    }
            break;
       default:
	    return(ScErrorUnrecognizedFormat);
   }
   return (NoErrors);
}

/*
** Name: ScInitRgbToYuv
** Purpose: Initializes tables for RGB to YUV conversion.
**
** Notes:
**
**	The tables are allocated and filled in once the first
**	time they are needed. They will remin for the lifetime
**	of the server.
**
**	The following formula is used:
**
**	y =  0.257 * r + 0.504 * g + 0.098 * b +  16 ; /+  16 - 235 +/
**	u = -0.148 * r - 0.291 * g + 0.439 * b + 128 ; /+  16 - 239 +/
**	v =  0.439 * r - 0.368 * g - 0.071 * b + 128 ; /+  16 - 239 +/
**
**	But we rewrite it thus:
**
**	y = 16.000 + 0.257 * r       + 0.504 * g       + 0.098 * b       ;
**	u = 16.055 + 0.148 * (255-r) + 0.291 * (255-g) + 0.439 * b       ;
**	v = 16.055 + 0.439 * r       + 0.368 * (255-g) + 0.071 * (255-b) ;
**
**    ( By the way, the old constants are:				     )
**    ( y = r *  0.299 + g *  0.587 + b *  0.114        ;		     )
**    ( u = r * -0.169 + g * -0.332 + b *  0.500  + 128 ;		     )
**    ( v = r *  0.500 + g * -0.419 + b * -0.0813 + 128 ;		     )
**    ( or								     )
**    ( y =  0.0    + 0.299 * r       + 0.587 * g       + 0.1140 * b       ; )
**    ( u =  0.245  + 0.169 * (255-r) + 0.332 * (255-g) + 0.5000 * b       ; )
**    ( v =  0.4235 + 0.500 * r       + 0.419 * (255-g) + 0.0813 * (255-b) ; )
**
**	This particular arrangement of the formula allows Y, U and V values
**	to be calculated in paralell by simple table lookup.
**	The paralellism comes from the fact that Y,U and V values
**	are stored in the same word, but in different bytes.
**	The tables are such that the contribution from red, green
**	and blue can simply be added together, without any carry
**	between bytes. Since the YUV space is larger than the RGB
**	cube, and the RGB cube fits entirely within YUV space,
**	there is no overflow and no range checking is needed.
**
*/

#ifdef NEW_YCBCR
/*
**	y = 16.000 + 0.257 * r       + 0.504 * g       + 0.098 * b       ;
**	u = 16.055 + 0.148 * (255-r) + 0.291 * (255-g) + 0.439 * b       ;
**	v = 16.055 + 0.439 * r       + 0.368 * (255-g) + 0.071 * (255-b) ;
*/
#define YC 16.000
#define UC 16.055
#define VC 16.055
#define YR  0.257
#define UR  0.148
#define VR  0.439
#define YG  0.504
#define UG  0.291
#define VG  0.368
#define YB  0.098
#define UB  0.439
#define VB  0.071

#else /* !NEW_YCBCR */
/*
**    ( y =  0.0      0.299 * r       + 0.587 * g       + 0.1140 * b       ; )
**    ( u =  0.245  + 0.169 * (255-r) + 0.332 * (255-g) + 0.5000 * b       ; )
**    ( v =  0.4235 + 0.500 * r       + 0.419 * (255-g) + 0.0813 * (255-b) ; )
*/
#define YC 0.0
#define UC 0.245
#define VC 0.4235
#define YR 0.299
#define UR 0.169
#define VR 0.500
#define YG 0.587
#define UG 0.332
#define VG 0.419
#define YB 0.1140
#define UB 0.5000
#define VB 0.0813

#endif /* !NEW_YCBCR */

/*
 * We only need an int (32 bits) per table entry but
 * 64-bit aligned access is faster on alpha.
 */

#ifdef __alpha
_int64 *RedToYuyv, *GreenToYuyv, *BlueToYuyv ;
#else /* !__alpha */
unsigned int *RedToYuyv, *GreenToYuyv, *BlueToYuyv ;
#endif /* !__alpha */

int ScInitRgbToYuv()
{
  int i, y, u, v ;

  if( RedToYuyv == NULL ) {
#ifdef __alpha
    RedToYuyv   = (_int64 *) ScAlloc( 256 * sizeof( _int64 ) ) ;
    GreenToYuyv = (_int64 *) ScAlloc( 256 * sizeof( _int64 ) ) ;
    BlueToYuyv  = (_int64 *) ScAlloc( 256 * sizeof( _int64 ) ) ;
#else /* !__alpha */
    RedToYuyv   = (unsigned int *) ScAlloc( 256 * sizeof( unsigned int ) ) ;
    GreenToYuyv = (unsigned int *) ScAlloc( 256 * sizeof( unsigned int ) ) ;
    BlueToYuyv  = (unsigned int *) ScAlloc( 256 * sizeof( unsigned int ) ) ;
#endif /* !__alpha */

    if( !RedToYuyv || !GreenToYuyv || !BlueToYuyv )
      return 0 ;

    for( i=0 ; i<256 ; i++ ) {

      /*
       * Calculate contribution from red.
       * We will also add in the constant here.
       * Pack it into the tables thus: lsb->YUYV<-msb
       */

      y = (int) (YC + YR * i) ;
      u = (int) (UC + UR * (255-i)) ;
      v = (int) (VC + VR * i) ;
      RedToYuyv[i] = (y | (u<<8) | (y<<16) | (v<<24)) ;

      /*
       * Calculate contribution from green.
       */

      y = (int) (YG * i) ;
      u = (int) (UG * (255-i)) ;
      v = (int) (VG * (255-i)) ;
      GreenToYuyv[i] = (y | (u<<8) | (y<<16) | (v<<24)) ;

      /*
       * Calculate contribution from blue.
       */

      y = (int) (YB * i) ;
      u = (int) (UB * i) ;
      v = (int) (VB * (255-i)) ;
      BlueToYuyv[i] = (y | (u<<8) | (y<<16) | (v<<24)) ;

    }
  }
  return 1 ;
}

/*
** Name:    ScConvertRGB24sTo422i_C
** Purpose: convert 24-bit RGB (8:8:8 format) to 16-bit YCrCb (4:2:2 format)
*/
ScStatus_t ScConvertRGB24sTo422i_C(BITMAPINFOHEADER *Bmh, u_char *R, u_char *G,
                                   u_char *B, u_short *ImageOut)
{
   register int row, col;
   int yuyv,r,g,b;
   int pixels = Bmh->biWidth;
   int lines  = abs(Bmh->biHeight);

   if( !RedToYuyv && !ScInitRgbToYuv() )
     return ScErrorMemory ;

   for (row = 0; row < lines; row++) {
      for (col = 0; col < pixels; col++) {
        r = *(R++); g = *(G++); b = *(B++);

	/*
	 * Quick convert to YUV.
	 */

	yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]) ;

        /*
         * Pack 4:2:2 = YUYV YUYV ...
	 * We'll pack YU or YV depending on whether col is odd or not.
	 * Shift yuyv 0 for even, 16 for odd columns.
         */

	*(ImageOut++) = yuyv >> ((col & 1) << 4) ;

      }
    }
    return (NoErrors);
}

#define M_RND(f) ((int) ((f) + .5))

/*
** Name:    ScConvertRGB24To411s_C
** Purpose: convert 24-bit RGB (8:8:8 format) to 16-bit YCrCb (4:1:1 format)
*/
ScStatus_t ScConvertRGB24To411s_C(u_char *inimage,
                                  u_char *Y, u_char *U, u_char *V,
                                  int width, int height)
{
  register int row, col;
  int yuyv, r, g, b;
  u_char *tmp, *evl, *odl;

  if( !RedToYuyv && !ScInitRgbToYuv() )
    return(ScErrorMemory);
  if (height<0) /* flip image */
  {
    height = -height;
    for (row = height-1; row; row--)
    {
      tmp = inimage+(width*row*3);
      if (row & 1)
      {
        odl = tmp;
        evl = tmp-(width*3);
      }
      else
      {
        evl = tmp;
        odl = tmp-(width*3);
      }
      for (col = 0; col < width; col++)
      {
        r = *tmp++;
        g = *tmp++;
        b = *tmp++;
        yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
        *Y++ = (yuyv&0xff);

        /* We only store every fourth value of u and v components */
        if ((col & 1) && (row & 1))
        {
          /* Compute average r, g and b values */
          r = *evl++ + *odl++;
          g = *evl++ + *odl++;
          b = *evl++ + *odl++;
          r += (*evl++ + *odl++);
          g += (*evl++ + *odl++);
          b += (*evl++ + *odl++);
          r = r >> 2;
          g = g >> 2;
          b = b >> 2;

          yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
          *U++ = ((yuyv&0xff000000) >> 24);       // V
          *V++ = ((yuyv&0xff00) >> 8);            // U
        }
      }
    }
  }
  else
  {
    tmp = inimage;
    for (row = 0; row < height; row++)
    {
      if (row & 1)
        odl = tmp;
      else
        evl = tmp;
      for (col = 0; col < width; col++)
      {
        r = *tmp++;
        g = *tmp++;
        b = *tmp++;
        yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
        *Y++ = (yuyv&0xff);

        /* We only store every fourth value of u and v components */
        if ((col & 1) && (row & 1))
        {
          /* Compute average r, g and b values */
          r = *evl++ + *odl++;
          g = *evl++ + *odl++;
          b = *evl++ + *odl++;
          r += (*evl++ + *odl++);
          g += (*evl++ + *odl++);
          b += (*evl++ + *odl++);
          r = r >> 2;
          g = g >> 2;
          b = b >> 2;

          yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
          *U++ = ((yuyv&0xff000000) >> 24);       // V
          *V++ = ((yuyv&0xff00) >> 8);            // U
        }
      }
    }
  }
  return (NoErrors);
}

/*
** Name:    ScConvertRGB555To411s_C
** Purpose: convert 16-bit RGB (5:5:5 format) to 16-bit YCrCb (4:1:1 format)
*/
ScStatus_t ScConvertRGB555To411s_C(u_char *inimage, u_char *outimage,
                                  int width, int height)
{
  u_char *Y=outimage, *U, *V;
  register int row, col, inpixel;
  int yuyv, r, g, b;
  unsigned word *tmp, *evl, *odl;

#define GetRGB555(in16, r, g, b) b = (inpixel>>7)&0xF8; \
                                 g = (inpixel>>2)&0xF8; \
                                 r = (inpixel<<3)&0xF8

#define AddRGB555(in16, r, g, b) b += (inpixel>>7)&0xF8; \
                                 g += (inpixel>>2)&0xF8; \
                                 r += (inpixel<<3)&0xF8

  if( !RedToYuyv && !ScInitRgbToYuv() )
    return(ScErrorMemory);
  if (height<0) /* flip image */
  {
    height = -height;
    U=Y+(width*height);
    V=U+(width*height*1)/4;
    for (row = height-1; row; row--)
    {
      tmp = ((unsigned word *)inimage)+(width*row);
      if (row & 1)
      {
        odl = tmp;
        evl = tmp-width;
      }
      else
      {
        evl = tmp;
        odl = tmp-width;
      }
      for (col = 0; col < width; col++)
      {
        inpixel=*tmp++;
        GetRGB555(inpixel, r, g, b);
        yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
        *Y++ = (yuyv&0xff);

        /* We only store every fourth value of u and v components */
        if ((col & 1) && (row & 1))
        {
          /* Compute average r, g and b values */
          inpixel=*evl++;
          GetRGB555(inpixel, r, g, b);
          inpixel=*evl++;
          AddRGB555(inpixel, r, g, b);
          inpixel=*odl++;
          AddRGB555(inpixel, r, g, b);
          inpixel=*odl++;
          AddRGB555(inpixel, r, g, b);
          r = r >> 2;
          g = g >> 2;
          b = b >> 2;

          yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
          *U++ = ((yuyv&0xff000000) >> 24);       // V
          *V++ = ((yuyv&0xff00) >> 8);            // U
        }
      }
    }
  }
  else
  {
    U=Y+(width*height);
    V=U+(width*height*1)/4;
    tmp = (unsigned word *)inimage;
    for (row = 0; row < height; row++)
    {
      if (row & 1)
        odl = tmp;
      else
        evl = tmp;
      for (col = 0; col < width; col++)
      {
        inpixel=*tmp++;
        GetRGB555(inpixel, r, g, b);
        yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
        *Y++ = (yuyv&0xff);

        /* We only store every fourth value of u and v components */
        if ((col & 1) && (row & 1))
        {
          /* Compute average r, g and b values */
          inpixel=*evl++;
          GetRGB555(inpixel, r, g, b);
          inpixel=*evl++;
          AddRGB555(inpixel, r, g, b);
          inpixel=*odl++;
          AddRGB555(inpixel, r, g, b);
          inpixel=*odl++;
          AddRGB555(inpixel, r, g, b);
          r = r >> 2;
          g = g >> 2;
          b = b >> 2;

          yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
          *U++ = ((yuyv&0xff000000) >> 24);       // V
          *V++ = ((yuyv&0xff00) >> 8);            // U
        }
      }
    }
  }
  return (NoErrors);
}

/*
** Name:    ScConvertRGB565To411s_C
** Purpose: convert 16-bit RGB (5:6:5 format) to 16-bit YCrCb (4:1:1 format)
*/
ScStatus_t ScConvertRGB565To411s_C(u_char *inimage, u_char *outimage,
                                  int width, int height)
{
  u_char *Y=outimage, *U, *V;
  register int row, col, inpixel;
  int yuyv, r, g, b;
  unsigned word *tmp, *evl, *odl;

#define GetRGB565(in16, r, g, b) b = (inpixel>>8)&0xF8; \
                                 g = (inpixel>>3)&0xFC; \
                                 r = (inpixel<<3)&0xF8

#define AddRGB565(in16, r, g, b) b += (inpixel>>8)&0xF8; \
                                 g += (inpixel>>3)&0xFC; \
                                 r += (inpixel<<3)&0xF8

  if( !RedToYuyv && !ScInitRgbToYuv() )
    return(ScErrorMemory);
  if (height<0) /* flip image */
  {
    height = -height;
    U=Y+(width*height);
    V=U+(width*height*1)/4;
    for (row = height-1; row; row--)
    {
      tmp = ((unsigned word *)inimage)+(width*row);
      if (row & 1)
      {
        odl = tmp;
        evl = tmp-width;
      }
      else
      {
        evl = tmp;
        odl = tmp-width;
      }
      for (col = 0; col < width; col++)
      {
        inpixel=*tmp++;
        GetRGB565(inpixel, r, g, b);
        yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
        *Y++ = (yuyv&0xff);

        /* We only store every fourth value of u and v components */
        if ((col & 1) && (row & 1))
        {
          /* Compute average r, g and b values */
          inpixel=*evl++;
          GetRGB565(inpixel, r, g, b);
          inpixel=*evl++;
          AddRGB565(inpixel, r, g, b);
          inpixel=*odl++;
          AddRGB565(inpixel, r, g, b);
          inpixel=*odl++;
          AddRGB565(inpixel, r, g, b);
          r = r >> 2;
          g = g >> 2;
          b = b >> 2;

          yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
          *U++ = ((yuyv&0xff000000) >> 24);       // V
          *V++ = ((yuyv&0xff00) >> 8);            // U
        }
      }
    }
  }
  else
  {
    U=Y+(width*height);
    V=U+(width*height*1)/4;
    tmp = (unsigned word *)inimage;
    for (row = 0; row < height; row++)
    {
      if (row & 1)
        odl = tmp;
      else
        evl = tmp;
      for (col = 0; col < width; col++)
      {
        inpixel=*tmp++;
        GetRGB565(inpixel, r, g, b);
        yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
        *Y++ = (yuyv&0xff);

        /* We only store every fourth value of u and v components */
        if ((col & 1) && (row & 1))
        {
          /* Compute average r, g and b values */
          inpixel=*evl++;
          GetRGB565(inpixel, r, g, b);
          inpixel=*evl++;
          AddRGB565(inpixel, r, g, b);
          inpixel=*odl++;
          AddRGB565(inpixel, r, g, b);
          inpixel=*odl++;
          AddRGB565(inpixel, r, g, b);
          r = r >> 2;
          g = g >> 2;
          b = b >> 2;

          yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]);
          *U++ = ((yuyv&0xff000000) >> 24);       // V
          *V++ = ((yuyv&0xff00) >> 8);            // U
        }
      }
    }
  }
  return (NoErrors);
}

/*
** Name:    ScRgbInterlToYuvInterl
** Purpose: convert many RGB formats to 16-bit YCrCb (4:2:2 format)
*/
ScStatus_t ScRgbInterlToYuvInterl (
    LPBITMAPINFOHEADER Bmh,
    int Width,
    int Height,
    u_char *ImageIn,
    u_short *ImageOut)
{
    register int row, col;
    int yuyv,r,g,b,mask=0x00ff;
    int pixels = Width;
    int lines  = abs(Height);
    int IspBGR = (ValidateBI_BITFIELDS(Bmh) == pBGR) ||
         (Bmh->biCompression==BI_DECXIMAGEDIB && Bmh->biBitCount==24);
    int IspRGB_BI_RGB_24 = (Bmh->biCompression==BI_RGB && Bmh->biBitCount==24);
    int linestep = 0 ;

    if( !RedToYuyv && !ScInitRgbToYuv() )
      return ScErrorMemory ;

    /*
     * Check the input format and decide
     * whether the image is to be turned
     * upside down or not.
     */

    if( (Bmh->biCompression == BI_RGB ||
    	 Bmh->biCompression == BI_BITFIELDS) ^
	((int) Bmh->biHeight < 0) ) {
	ImageOut = &ImageOut[ pixels * (lines - 1) ] ;
	linestep = -(pixels << 1) ;
    }

    /*
     * To avoid if-then-else statements inside
     * the inner loop, we have 3 loops.
     */

    /*
     * 24 bits per pixel RGB.
     */

    if (IspRGB_BI_RGB_24) {

      for (row = 0; row < lines; row++) {
	for (col = 0; col < pixels; col++) {
	  b = *(ImageIn++);
	  g = *(ImageIn++);
	  r = *(ImageIn++);

	  /*
	   * Quick convert from RGB to YUV. Just add together
	   * the contributions from each of Red, Green and Blue.
	   */

	  yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]) ;
	
	  /*
	   * Pack 4:2:2 = YUYV YUYV ...
	   * We'll pack YU or YV depending on whether col is odd or not.
	   * Shift yuyv 0 for even, 16 for odd columns.
	   */
	
	  *(ImageOut++) = yuyv >> ((col & 1) << 4) ;

	}
	/*
	 * In case we're turning the image upside down.
	 * This will do nothing if it's right side up.
	 */
	ImageOut = &ImageOut[linestep] ;
      }
    }
    /*
     * 32 bits per pixels 0BGR.
     */
    else if (IspBGR) {
      for (row = 0; row < lines; row++) {
	for (col = 0; col < pixels; col++) {
	  r = *((int *) ImageIn)++ ;
	  b = (r>>16) & mask ;
	  g = (r>> 8) & mask ;
	  r &= mask ;
	  yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]) ;
	  *(ImageOut++) = yuyv >> ((col & 1) << 4) ;
	}
	ImageOut = &ImageOut[linestep] ;
      }
    }
    /*
     * 32 bits per pixels 0RGB.
     */
    else {
      for (row = 0; row < lines; row++) {
	for (col = 0; col < pixels; col++) {
	  b = *((int *) ImageIn)++ ;
	  r = (b>>16) & mask ;
	  g = (b>> 8) & mask ;
	  b &= mask ;
	  yuyv = (int) (RedToYuyv[r] + GreenToYuyv[g] + BlueToYuyv[b]) ;
	  *(ImageOut++) = yuyv >> ((col & 1) << 4) ;
	}
	ImageOut = &ImageOut[linestep] ;
      }
    }

    return (NoErrors);
}


/*
** Function: ScConvert422ToYUV_char_C
** Purpose:  Extract the Y, U and V components into separate planes.
**           The interleaved format is YUYV, 4:2:2, we want 4:1:1 so,
**           only copy every other line of the chrominance
*/
ScStatus_t ScConvert422ToYUV_char_C (u_char *RawImage,
                         u_char *Y, u_char *U, u_char *V,
                         int Width, int Height)
{
  register int x, y;

  Width/=2;
  Height=abs(Height)/2;
  for (y = 0; y < Height; y++)
  {
    for (x = 0 ; x < Width; x++)
    {
      *Y++ = *RawImage++;
      *U++ = *RawImage++;
      *Y++ = *RawImage++;
      *V++ = *RawImage++;
    }
    for (x = 0; x < Width; x++)
    {
      *Y++ = *RawImage;
      RawImage+=2;
      *Y++ = *RawImage;
      RawImage+=2;
    }
 }
 return (NoErrors);
}

/*
** Function: ScConvert422PlanarTo411_C
** Purpose:  Extract the Y, U and V components from (4:2:2)
**           planes and convert to 4:1:1 so,
**           only copy every other line of the chrominance
*/
ScStatus_t ScConvert422PlanarTo411_C (u_char *RawImage,
                         u_char *Y, u_char *U, u_char *V,
                         int Width, int Height)
{
  register int y;
  const int HalfWidth=Width/2;
  unsigned char *RY, *RU, *RV;
  RY=RawImage;
  RU=RY+(Width*Height);
  RV=RU+(Width*Height/2);

  Height=abs(Height);
  memcpy(Y, RawImage, Width*Height);
  for (y = Height/2; y > 0; y--)
  {
    memcpy(U, RU, HalfWidth);
    memcpy(V, RV, HalfWidth);
    U+=HalfWidth;
    V+=HalfWidth;
    RU+=Width; /* skip odd U and V lines */
    RV+=Width;
 }
 return (NoErrors);
}

/*
** C versions of block-extraction routines. To be replaced by ASM
*/
void ScConvertSep422ToBlockYUV (u_char *RawImage, int bpp,
			    float *Comp1, float *Comp2, float *Comp3,
			    int Width, int Height)
{
  int x,y;
  int VertBlocks = abs(Height)/8;
  int YBlocks    = Width/8;
  int UBlocks    = Width/16;
  int VBlocks    = Width/16;
  int ByteWidth  = Width*2;
  u_char *I1 = RawImage;
  u_char *I2 = I1 + Width*abs(Height);
  u_char *I3 = I2 + Width*abs(Height)/2;
  float *C1 = Comp1, *C2 = Comp2, *C3 = Comp3;

  for (y = 0 ; y < VertBlocks ; y++) {
    for (x = 0 ; x < YBlocks ; x++)
      sc_ExtractBlockNonInt(I1, &C1, ByteWidth, x, y);
    for (x = 0 ; x < UBlocks ; x++)
      sc_ExtractBlockNonInt(I2, &C2, ByteWidth, x, y);
    for (x = 0 ; x < VBlocks ; x++)
      sc_ExtractBlockNonInt(I3, &C3, ByteWidth, x, y);
  }
}

void ScConvertGrayToBlock (u_char *RawImage, int bpp,
		       float *Comp1, int Width, int Height)
{
  int x,y;
  int VertBlocks = abs(Height)/8;
  int YBlocks    = Width/8;
  int ByteWidth  = Width;
  u_char *I1 = RawImage;
  float *C1 = Comp1;

  for (y = 0 ; y < VertBlocks ; y++)
    for (x = 0 ; x < YBlocks ; x++)
      sc_ExtractBlockNonInt(I1, &C1, ByteWidth, x, y);
}


/*
** Function: ScSepYUVto422i_C
** Purpose:  Convert a 4:1:1 YUV image in separate-component form to its
**           equivalent interleaved 4:2:2 form.
*/
extern int ScSepYUVto422i_C(u_char *Y, u_char *U,
                            u_char *V, u_char *ImageOut,
                            u_int width, u_int height)
{
  /* need C code for this */
  return(0);
}

/*
** Function: ScConvert422PlanarTo422i_C
** Purpose:  Convert a 4:2:2 YUV image in separate-component form to its
**           equivalent interleaved 4:2:2 form.
*/
extern void ScConvert422PlanarTo422i_C(u_char *Y, u_char *Cb,
				     u_char *Cr, u_char *OutImage,
				     long width, long height)
{
  int i, j;
  height=abs(height);
  width=width>>1;
  for (i=0; i<height; i++)
  {
    /* Remember, pixels are 16 bit in interleaved YUV. That
     ** means the 4 bytes below represent two pixels so our
     ** loop should be for half the width.
     */
    for (j=0; j<width; j++)
    {
       *OutImage++ = *Y++;
       *OutImage++ = *Cb++;
       *OutImage++ = *Y++;
       *OutImage++ = *Cr++;
    }
  }
}

/*
** Function: ScConvert422iTo422s_C
** Purpose:  Convert a 4:2:2 YUV interleaved to 4:2:2 planar.
*/
extern void ScConvert422iTo422s_C(u_char *InImage,
                                  u_char *Y, u_char *U, u_char *V,
                                  long width, long height)
{
  int i, j;
  height=abs(height);
  width=width>>1;
  for (i=0; i<height; i++)
  {
    /* Remember, pixels are 16 bit in interleaved YUV. That
     ** means the 4 bytes below represent two pixels so our
     ** loop should be for half the width.
     */
    for (j=0; j<width; j++)
    {
       *Y++ = *InImage++;
       *U++ = *InImage++;
       *Y++ = *InImage++;
       *V++ = *InImage++;
    }
  }
}

/*
** Function: ScConvert422iTo422sf_C
** Purpose:  Convert a 4:2:2 YUV interleaved to 4:2:2 planar.
*/
extern void ScConvert422iTo422sf_C(u_char *InImage, int bpp,
                                  float *Y, float *U, float *V,
                                  long width, long height)
{
  int i, j;
  height=abs(height);
  width=width>>1;
  for (i=0; i<height; i++)
  {
    /* Remember, pixels are 16 bit in interleaved YUV. That
     ** means the 4 bytes below represent two pixels so our
     ** loop should be for half the width.
     */
    for (j=0; j<width; j++)
    {
       *Y++ = (float)*InImage++;
       *U++ = (float)*InImage++;
       *Y++ = (float)*InImage++;
       *V++ = (float)*InImage++;
    }
  }
}

/*
** Function: ScConvert411sTo422i_C
** Purpose:  Convert a 4:1:1 YUV image in separate-component form to its
**           equivalent interleaved 4:2:2 form.
*/
extern void ScConvert411sTo422i_C(u_char *Y, u_char *Cb,
                                  u_char *Cr, u_char *OutImage,
                                  long width, long height)
{
  u_char *p422e, *p422o, *Yo=Y+width;
  int i, j;
  height=abs(height)/2;
  p422e=OutImage;
  p422o=OutImage+width*2;

  for (i=0; i<height; i++)
  {
    for (j=0; j<width; j+=2)
    {
      *p422e++ = *Y++;
      *p422e++ = *Cb;
      *p422e++ = *Y++;
      *p422e++ = *Cr;
      *p422o++ = *Yo++;
      *p422o++ = *Cb++;
      *p422o++ = *Yo++;
      *p422o++ = *Cr++;
    }
    p422e=p422o;
    p422o=p422e+width*2;
    Y=Yo;
    Yo=Y+width;
  }
}

/*
** Function: ScConvert411sTo422s_C
** Purpose:  Convert a 4:1:1 YUV image in separate-component form to its
**           equivalent interleaved 4:2:2 form.
*/
extern void ScConvert411sTo422s_C(u_char *Y, u_char *Cb,
                                  u_char *Cr, u_char *OutImage,
                                  long width, long height)
{
  u_char *p411, *p422e, *p422o;
  int i, j;
  height=abs(height);

  if (OutImage!=Y)
    memcpy(OutImage, Y, width*height); /* copy Y components */
  p411=Cb+((height/2)-1)*(width/2);
  p422e=OutImage+((height*width*3)/2)-width; /* U component */
  p422o=p422e+(width/2);
  for (i=0; i<height; i+=2)
  {
    for (j=0; j<width; j+=2, p411++, p422e++, p422o++)
      *p422e=*p422o=*p411;
    p411-=width;
    p422o=p422e-width;
    p422e=p422o-(width/2);
  }
  p411=Cr+((height/2)-1)*(width/2);
  p422e=OutImage+(height*width*2)-width; /* V component */
  p422o=p422e+(width/2);
  for (i=0; i<height; i+=2)
  {
    for (j=0; j<width; j+=2, p411++, p422e++, p422o++)
      *p422e=*p422o=*p411;
    p411-=width;
    p422o=p422e-width;
    p422e=p422o-(width/2);
  }
}

/*
** Name:    ScConvert1611sTo411s_C
** Purpose: convert a YCrCb 16:1:1 (YUV9/YVU9) to YCrCb 4:1:1
*/
ScStatus_t ScConvert1611sTo411s_C (u_char *inimage,
                         u_char *Y, u_char *U, u_char *V,
                         int Width, int Height)
{
  register int y;
  const int HalfWidth=Width/2;
  unsigned char *RU, *RV;
  int pixels = Width * abs(Height), tmp;

  tmp = pixels / 16;
  RU=inimage+pixels;
  RV=RU+tmp;

  memcpy(Y, inimage, pixels);
  for (y = 0; y < tmp; y++)
  {
      *U++ = *RU;
      *U++ = *RU;
      *U++ = *RU;
      *U++ = *RU++;
      *V++ = *RV;
      *V++ = *RV;
      *V++ = *RV;
      *V++ = *RV++;
 }
 return (NoErrors);
}

/*
** Name:    ScConvert1611sTo422s_C
** Purpose: convert a YCrCb 16:1:1 (YUV9/YVU9) to YCrCb 4:2:2
*/
ScStatus_t ScConvert1611sTo422s_C(u_char *inimage,
                         u_char *Y, u_char *U, u_char *V,
                         int Width, int Height)
{
  register int x, y;
  const int HalfWidth=Width/2;
  unsigned char *RU, *RV;
  unsigned char *Uo, *Vo;
  int pixels = Width * abs(Height), tmp;

  tmp = pixels / 16;
  RU=inimage+pixels;
  RV=RU+tmp;

  memcpy(Y, inimage, pixels);
  for (y = Height/32; y>0; y--)
  {
    Vo=V+Width;
    Uo=U+Width;
    for (x = Width/4; x > 0; x--)
    {
      *U++ = *Uo++ = *RU;
      *U++ = *Uo++ = *RU;
      *U++ = *Uo++ = *RU;
      *U++ = *Uo++ = *RU++;
      *V++ = *Vo++ = *RV;
      *V++ = *Vo++ = *RV;
      *V++ = *Vo++ = *RV;
      *V++ = *Vo++ = *RV++;
    }
    V=Vo; U=Uo;
  }
 return (NoErrors);
}

/*
** Name:    ScConvert1611sTo422i_C
** Purpose: convert a YCrCb 16:1:1 (YUV9/YVU9) to 4:2:2 interleaved
*/
ScStatus_t ScConvert1611sTo422i_C(u_char *inimage, u_char *outimage,
                                  int Width, int Height)
{
  register int x, y;
  const int HalfWidth=Width/2;
  unsigned char *Ye, *Yo, *Ye2, *Yo2, *RU, *RV;
  unsigned char *o1, *e1, *o2, *e2;
  unsigned char U, V;

  RU=inimage+Width*abs(Height);
  RV=RU+(Width*abs(Height))/16;

  e1=outimage;
  Ye=inimage;
  for (y = abs(Height)/4; y>0; y--)
  {
    Yo=Ye+Width;
    Ye2=Yo+Width;
    Yo2=Ye2+Width;
    o1=e1+Width*2;
    e2=o1+Width*2;
    o2=e2+Width*2;
    for (x = Width/4; x > 0; x--)
    {
      U = *RU++;
      V = *RV++;
      /* even line */
      *e1++ = *Ye++;
      *e1++ = U;
      *e1++ = *Ye++;
      *e1++ = V;
      *e1++ = *Ye++;
      *e1++ = U;
      *e1++ = *Ye++;
      *e1++ = V;
      /* odd line */
      *o1++ = *Yo++;
      *o1++ = U;
      *o1++ = *Yo++;
      *o1++ = V;
      *o1++ = *Yo++;
      *o1++ = U;
      *o1++ = *Yo++;
      *o1++ = V;
      /* even line */
      *e2++ = *Ye2++;
      *e2++ = U;
      *e2++ = *Ye2++;
      *e2++ = V;
      *e2++ = *Ye2++;
      *e2++ = U;
      *e2++ = *Ye2++;
      *e2++ = V;
      /* odd line */
      *o2++ = *Yo2++;
      *o2++ = U;
      *o2++ = *Yo2++;
      *o2++ = V;
      *o2++ = *Yo2++;
      *o2++ = U;
      *o2++ = *Yo2++;
      *o2++ = V;
    }
    e1=o2;
    Ye=Yo2;
  }
 return (NoErrors);
}

/*
** Name:    ScConvert411sTo1611s_C
** Purpose: convert a YCrCb 4:1:1 to YCrCb 16:1:1 (YUV9/YVU9)
*/
ScStatus_t ScConvert411sTo1611s_C (u_char *inimage,
                         u_char *Y, u_char *U, u_char *V,
                         int Width, int Height)
{
  register int x, y, c0, c1, c2, c3;
  unsigned char *Ue, *Uo, *Ve, *Vo;
  int pixels = Width * abs(Height), tmp;
  Width/=2;
  tmp = pixels / 4;
  Ue=inimage+pixels;
  Uo=Ue+Width;
  Ve=Ue+tmp;
  Vo=Ve+Width;

  memcpy(Y, inimage, pixels);
  for (y = 0; y < tmp; y+=2)
  {
    for (x=0; x<Width; x+=2)
    {
      c0=*Ue++;
      c1=*Ue++;
      c2=*Uo++;
      c3=*Uo++;
      *U++ = (c0+c1+c2+c3)/4;
    }
    Ue=Uo;
    Uo+=Width;
  }
  for (y = 0; y < tmp; y+=2)
  {
    for (x=0; x<Width; x+=2)
    {
      c0=*Ve++;
      c1=*Ve++;
      c2=*Vo++;
      c3=*Vo++;
      *V++ = (c0+c1+c2+c3)/4;
    }
    Ve=Vo;
    Vo+=Width;
  }
  return (NoErrors);
}

/*
** Function: ScConvertNTSCtoCIF422()
** Purpose:  Convert a Q/CIF frame from a 4:2:2 NTSC input.  We dup every 10th
**           pixel horizontally and every 4th line vertically.  We also
**           discard the chroma on every other line, since CIF wants 4:1:1.
*/
ScStatus_t ScConvertNTSC422toCIF411_C(u_char *framein,
                         u_char *yp, u_char *up, u_char *vp,
                         int stride)
{
  int h, w;

  int vdup = 5;
  for (h = 0; h < 240; ++h)
  {
    int hdup = 10/2;
    for (w = 320; w > 0; w -= 2)
    {
      yp[0] = framein[0];
      yp[1] = framein[2];
      yp += 2;
      if ((h & 1) == 0)
      {
        *up++ = framein[1];
        *vp++ = framein[3];
      }
      framein += 4;
      if (--hdup <= 0)
      {
        hdup = 10/2;
        yp[0] = yp[-1];
        yp += 1;
        if ((h & 1) == 0)
        {
          if ((w & 2) == 0)
          {
            up[0] = up[-1];
            ++up;
            vp[0] = vp[-1];
            ++vp;
          }
        }
      }
    }
    if (--vdup <= 0)
    {
      vdup = 5;
      /* copy previous line */
      memcpy((char*)yp, (char*)yp - stride, stride);
      yp += stride;
      if ((h & 1) == 0)
      {
        int s = stride >> 1;
        memcpy((char*)up, (char*)up - s, s);
        memcpy((char*)vp, (char*)vp - s, s);
        up += s;
        vp += s;
      }
    }
  }
  return (NoErrors);
}