#include "pch.hpp"
//===========================================================================//
// File:	 Texture Convert.cpp											 //
// Project:  GameOS					                                         //
// Contents: Texture Format (pixel depth) Conversion						 //
//---------------------------------------------------------------------------//
// Copyright (C) 2000 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "Texture Manager.hpp"
#include "MemoryManager.hpp"
#include "DirectX.hpp"
#include "DXRasterizer.hpp"
#include "3DRasterizer.hpp"


extern int HasMMX;				// 1 when MMX instructions are present
volatile __int64 TimeConvertTextures;


DWORD BBRShiftRed;
DWORD BBMaskRed;
DWORD BBLShiftRed;
DWORD BBRShiftGreen;
DWORD BBMaskGreen;
DWORD BBLShiftGreen;
DWORD BBRShiftBlue;
DWORD BBMaskBlue;
DWORD BBLShiftBlue;


//
// Given a 16 bit RGB value from the backbuffer, return the DWORD version
//
DWORD GetBackBufferColor( WORD In )
{
	DWORD Out=	((((In>> BBLShiftRed)   & BBMaskRed  ) << BBRShiftRed  ) << 16 )|
				((((In>> BBLShiftGreen) & BBMaskGreen) << BBRShiftGreen) << 8  )|
				((( In>> BBLShiftBlue)  & BBMaskBlue ) << BBRShiftBlue          ) ;
	return Out;
}

//
// Given a 24 bit RGB value, return back buffer format word or dword
//
DWORD GetPixelColor( DWORD In )
{
	DWORD Out;

	DWORD   Red=(In>>16)&0xff;
	DWORD Green=(In>>8)&0xff;
	DWORD  Blue=In&0xff;
	
	Out=((((  Red>>BBRShiftRed  ) & BBMaskRed  ) << BBLShiftRed  ) |
		((( Green>>BBRShiftGreen) & BBMaskGreen) << BBLShiftGreen) | 
		(((  Blue>>BBRShiftBlue ) & BBMaskBlue ) << BBLShiftBlue ) );

	return Out;
}


//
// Routines used in writing out to different pixel formats.
//  Assumes never more than 8 bits of R,G,B or A
//
static DWORD MaskArray[]={ 0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff };

DWORD GetBits( DWORD in )
{
	DWORD result=0;
	for( int t0=0; t0<32; t0++ )
		if( in&(1<<t0) )
			result++;
	return (result > 8) ? 8 : result;
}
DWORD GetRShift( DWORD in )
{
	return 8-GetBits(in);
}
DWORD GetMask( DWORD in )
{
	return MaskArray[ GetBits(in) ];
}
DWORD GetLShift( DWORD in )
{
	DWORD result=0;
	if( in )
		while( 0==(in&(1<<result)) )
			result++;
	return result;
}




void UpdateBackBufferFormat()
{
	DDPIXELFORMAT Format;

	memset( &Format, 0, sizeof(Format) );
	Format.dwSize=sizeof(Format);
		
	wGetPixelFormat( BackBufferSurface, &Format );

	BBRShiftRed		= GetRShift( Format.dwRBitMask );
	BBMaskRed		= GetMask( Format.dwRBitMask );
	BBLShiftRed		= GetLShift( Format.dwRBitMask );

	BBRShiftGreen	= GetRShift( Format.dwGBitMask );
	BBMaskGreen		= GetMask( Format.dwGBitMask );
	BBLShiftGreen	= GetLShift( Format.dwGBitMask );

	BBRShiftBlue	= GetRShift( Format.dwBBitMask );
	BBMaskBlue		= GetMask( Format.dwBBitMask );
	BBLShiftBlue	= GetLShift( Format.dwBBitMask );
}







//
// Convert the pixel format of a sliver (1..3 pixels wide) of a single mip level from 8888 to the 16bpp destination	
//
void ConvertSliver( DWORD Bpp, IDirectDrawSurface7* DestSurface, DWORD *Source, DWORD SourcePitch, DWORD Hints, DWORD left, DWORD top, DWORD width, DWORD height )
{
	DDSURFACEDESC2 DESTddsd;
//
// Convert pitch from bytes to dwords, discount copied region
//
	SourcePitch= (SourcePitch>>2) - width;


//
// Lock the largest texture and make it the destination
//
	DESTddsd.dwSize = sizeof(DESTddsd);
	wLock( DestSurface, NULL, &DESTddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL );
	SHORT* Dest=(SHORT*)DESTddsd.lpSurface;
	DWORD DestPitch=(DESTddsd.lPitch>>1)-width;
	Dest = Dest + left + top * (DESTddsd.lPitch>>1);
//
// Setup pixel writing, shifts and masks
//
	DWORD RShiftRed		= GetRShift( DESTddsd.ddpfPixelFormat.dwRBitMask );
	DWORD MaskRed		= GetMask(   DESTddsd.ddpfPixelFormat.dwRBitMask );
	DWORD LShiftRed		= GetLShift( DESTddsd.ddpfPixelFormat.dwRBitMask );

	DWORD RShiftGreen	= GetRShift( DESTddsd.ddpfPixelFormat.dwGBitMask );
	DWORD MaskGreen		= GetMask(   DESTddsd.ddpfPixelFormat.dwGBitMask );
	DWORD LShiftGreen	= GetLShift( DESTddsd.ddpfPixelFormat.dwGBitMask );

	DWORD RShiftBlue	= GetRShift( DESTddsd.ddpfPixelFormat.dwBBitMask );
	DWORD MaskBlue		= GetMask(   DESTddsd.ddpfPixelFormat.dwBBitMask );

	DWORD RShiftAlpha	= GetRShift( DESTddsd.ddpfPixelFormat.dwRGBAlphaBitMask );
	DWORD MaskAlpha		= GetMask(   DESTddsd.ddpfPixelFormat.dwRGBAlphaBitMask );
	DWORD LShiftAlpha	= GetLShift( DESTddsd.ddpfPixelFormat.dwRGBAlphaBitMask );

//
// Convert two pixels at once in the high and low parts of a DWORD
//
	DWORD Pixel;
	DWORD Blue;
	DWORD Green;
	DWORD Red;
	DWORD Alpha;


	for( int y=0; y<height; y++ )
	{
		for( int x=0; x<width; x++ )
		{
			Pixel=*Source++;

			if( UseGammaCorrection )
				Pixel=(GammaTable[(Pixel>>0)&0xff]<<0)+(GammaTable[(Pixel>>8)&0xff]<<8)+(GammaTable[(Pixel>>16)&0xff]<<16)+(Pixel&0xff000000);

			Blue =((Pixel    )&255);
			Green=((Pixel>>8 )&255);
			Red  =((Pixel>>16)&255);
			Alpha=((Pixel>>24)&255);

			*Dest++=(SHORT)((((  Red>>RShiftRed  ) & MaskRed  ) << LShiftRed  ) |
							(((Green>>RShiftGreen) & MaskGreen) << LShiftGreen) | 
							((( Blue>>RShiftBlue ) & MaskBlue ) ) | 
							(((Alpha>>RShiftAlpha) & MaskAlpha) << LShiftAlpha) );
		}
		Dest+=DestPitch;
		Source+=SourcePitch;
	}
	wUnlock( DestSurface, NULL );
}









//
// Convert the pixel format of a subrect of a single mip level from 8888 to the current destination	
//
void ConvertRect( DWORD Bpp, IDirectDrawSurface7* DestSurface, DWORD *Source, DWORD SourcePitch, DWORD Hints, DWORD left, DWORD top, DWORD width, DWORD height )
{
#ifdef LAB_ONLY
	__int64 Temp=GetCycles();
#endif
	DDSURFACEDESC2 DESTddsd;


//
// If 32 bit 8888 surface, just use copy texture (cannot use BLT due to 888 to 8888 conversion problems)
//
	if( Bpp==32 )
	{

//
// Convert pitch from bytes to dwords, discount copied region
//
		SourcePitch= (SourcePitch>>2) - width;


//
// Lock the largest texture and make it the destination
//
		DESTddsd.dwSize = sizeof(DESTddsd);
		wLock( DestSurface, NULL, &DESTddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL );
		DWORD* Dest=(DWORD*)DESTddsd.lpSurface;
		DWORD DestPitch=(DESTddsd.lPitch>>2) - width;
		Dest = Dest + left + top * (DESTddsd.lPitch>>2);

		if( UseGammaCorrection )
		{
			for( int y=0; y<height; y++ )
			{
				for( int x=0; x<width; x++ )
				{
					DWORD Pixel=*Source++;
					Pixel=(GammaTable[(Pixel>>0)&0xff]<<0)+(GammaTable[(Pixel>>8)&0xff]<<8)+(GammaTable[(Pixel>>16)&0xff]<<16)+(Pixel&0xff000000);
					*Dest++=Pixel;
				}
				Dest+=DestPitch;
				Source+=SourcePitch;
			}
		}
		else
		{
			for( int y=0; y<height; y++ )
			{
				memcpy( Dest, Source, width*4 );
				Dest+=DestPitch+width;
				Source+=SourcePitch+width;
			}
		}
	}
	else
//
// For 16 bit surfaces, must do pixel format conversions
//
	{

		DWORD fetchSize = ( HasMMX && !UseGammaCorrection ) ? 4 : 2;

//
// Adjust left to multiple of fetchSize pixels by handling sliver from left edge
//

		if( ( left & (fetchSize-1) ) || ( width < fetchSize ) )
		{
			DWORD sliverWidth = fetchSize - ( left & (fetchSize-1) );
			if( sliverWidth > width )
				sliverWidth = width;
			ConvertSliver( Bpp, DestSurface, Source, SourcePitch, Hints, left, top, sliverWidth, height );
			Source += sliverWidth;
			left += sliverWidth;
			width -= sliverWidth;
		}

//
// Adjust right to multiple of fetchSize pixels by handling sliver from right edge
//

		if( width & (fetchSize-1) )
		{
			DWORD sliverWidth = width & (fetchSize-1);
			DWORD sliverOffset = width & ~(fetchSize-1);
			ConvertSliver( Bpp, DestSurface, Source+sliverOffset, SourcePitch, Hints, left+sliverOffset, top, sliverWidth, height );
			width -= sliverWidth;
		}


//
// Convert pitch from bytes to dwords, discount copied region
//
		SourcePitch= (SourcePitch>>2) - width;


//
// Lock the largest texture and make it the destination
//
		DESTddsd.dwSize = sizeof(DESTddsd);
		wLock( DestSurface, NULL, &DESTddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL );
		DWORD* Dest=(DWORD*)DESTddsd.lpSurface;
		DWORD DestPitch=(DESTddsd.lPitch>>2)-(width>>1);
		Dest = Dest + (left>>1) + top * (DESTddsd.lPitch>>2);
//
// Setup pixel writing, shifts and masks
//
		DWORD RShiftRed		= GetRShift( DESTddsd.ddpfPixelFormat.dwRBitMask );
		DWORD MaskRed		= GetMask(   DESTddsd.ddpfPixelFormat.dwRBitMask );
		DWORD LShiftRed		= GetLShift( DESTddsd.ddpfPixelFormat.dwRBitMask );

		DWORD RShiftGreen	= GetRShift( DESTddsd.ddpfPixelFormat.dwGBitMask );
		DWORD MaskGreen		= GetMask(   DESTddsd.ddpfPixelFormat.dwGBitMask );
		DWORD LShiftGreen	= GetLShift( DESTddsd.ddpfPixelFormat.dwGBitMask );

		DWORD RShiftBlue	= GetRShift( DESTddsd.ddpfPixelFormat.dwBBitMask );
		DWORD MaskBlue		= GetMask(   DESTddsd.ddpfPixelFormat.dwBBitMask );

		DWORD RShiftAlpha	= GetRShift( DESTddsd.ddpfPixelFormat.dwRGBAlphaBitMask );
		DWORD MaskAlpha		= GetMask(   DESTddsd.ddpfPixelFormat.dwRGBAlphaBitMask );
		DWORD LShiftAlpha	= GetLShift( DESTddsd.ddpfPixelFormat.dwRGBAlphaBitMask );

		MaskRed  +=MaskRed<<16;
		MaskGreen+=MaskGreen<<16;
		MaskBlue +=MaskBlue<<16;
		MaskAlpha+=MaskAlpha<<16;
//
// Convert two pixels at once in the high and low parts of a DWORD
//
		DWORD Pixel1;
		DWORD Pixel2;
		DWORD Blue;
		DWORD Green;
		DWORD Red;
		DWORD Alpha;

		if( UseGammaCorrection )
		{
			for( int y=0; y<height; y++ )
			{
				for( int x=0; x<width; x+=2 )
				{
					Pixel1=*Source++;
					Pixel2=*Source++;

					Pixel1=(GammaTable[(Pixel1>>0)&0xff]<<0)+(GammaTable[(Pixel1>>8)&0xff]<<8)+(GammaTable[(Pixel1>>16)&0xff]<<16)+(Pixel1&0xff000000);
					Pixel2=(GammaTable[(Pixel2>>0)&0xff]<<0)+(GammaTable[(Pixel2>>8)&0xff]<<8)+(GammaTable[(Pixel2>>16)&0xff]<<16)+(Pixel2&0xff000000);

					Blue =((Pixel1    )&255)+((Pixel2<<16)&0xff0000);
					Green=((Pixel1>>8 )&255)+((Pixel2<<8 )&0xff0000);
					Red  =((Pixel1>>16)&255)+((Pixel2    )&0xff0000);
					Alpha=((Pixel1>>24)&255)+((Pixel2>>8 )&0xff0000);

					*Dest++=(DWORD)((((  Red>>RShiftRed  ) & MaskRed  ) << LShiftRed  ) |
									(((Green>>RShiftGreen) & MaskGreen) << LShiftGreen) | 
									((( Blue>>RShiftBlue ) & MaskBlue ) ) | 
									(((Alpha>>RShiftAlpha) & MaskAlpha) << LShiftAlpha) );
				}
				Dest+=DestPitch;
				Source+=SourcePitch;
			}
		}
		else
		{
//
// Minimum 8 byte width!  (GameOS smallest textures are 16*16)
//
			gosASSERT( DESTddsd.dwWidth>4 );
//
// Fast convert routine on MMX machines
//
			if( HasMMX && width )
			{
				__int64 BMaskBlue;
				__int64 BMaskGreen;
				__int64 BMaskRed;
				__int64 BMaskAlpha;
//
// 565 format
//
				if( MaskGreen==0x3f003f )
				{
					BMaskBlue =0x0000f8000000f800;
					BMaskGreen=0x00fc000000fc0000;
					BMaskRed  =0xf8000000f8000000;

					_asm
					{
						movq mm6,BMaskRed
						movq mm5,BMaskGreen
						movq mm4,BMaskBlue
						
						mov eax,DestPitch
						mov ecx,SourcePitch

						mov esi,Source
						mov edi,Dest

						mov edx,height			// Height

lp1:					mov ebx,width			// Convert 4 pixels of ARGB into 4 16 bit words of ARGB in pixel format

lp2:
						movq mm0,[esi]
						psllq mm0,8
						movq mm1,mm0

						movq mm2,mm0
						pand mm0,mm4
						psrad mm0,11						// Blue

						pand mm1,mm5
						psrad mm1,13
						por mm0,mm1							// Green

						pand mm2,mm6
						psrad mm2,16						// Red
						por mm0,mm2

						movq mm3,[esi+8]
						psllq mm3,8
						movq mm1,mm3

						movq mm2,mm3
						pand mm1,mm4
						psrad mm1,11						// Blue

						pand mm2,mm5
						psrad mm2,13
						por mm1,mm2							// Green

						pand mm3,mm6
						psrad mm3,16						// Red
						por mm3,mm1

						packssdw mm0,mm3
						add esi,16
						sub ebx,4

						movq [edi],mm0
						lea edi,[edi+8]
						jnz lp2

						dec edx
						lea esi,[ecx*4+esi]
						lea edi,[eax*4+edi]
						
						jnz lp1
						emms
					}
				}
//
// 4444 format
//
				else if( MaskAlpha==0x0f000f )
				{
					BMaskBlue =0x000000f0000000f0;
					BMaskGreen=0x0000f0000000f000;
					BMaskRed  =0x00f0000000f00000;
					BMaskAlpha=0xf0000000f0000000;

					_asm
					{
						movq mm7,BMaskAlpha
						movq mm6,BMaskRed
						movq mm5,BMaskGreen
						movq mm4,BMaskBlue
						
						mov eax,DestPitch
						mov ecx,SourcePitch

						mov esi,Source
						mov edi,Dest

						mov edx,height			// Height

lp3:					mov ebx,width			// Convert 4 pixels of ARGB into 4 16 bit words of ARGB in pixel format

lp4:
						movq mm0,[esi]
						movq mm1,mm0
						movq mm2,mm0

						pand mm0,mm4
						psrad mm0,4							// Blue
						pand mm1,mm5

						psrad mm1,8
						por mm0,mm1							// Green
						movq mm1,mm2

						pand mm1,mm6
						psrad mm1,12						// Red
						por mm0,mm1

						pand mm2,mm7						// Alpha
						psrad mm2,16
						por mm0,mm2

						movq mm3,[esi+8]
						movq mm1,mm3
						movq mm2,mm3

						pand mm1,mm4
						psrad mm1,4							// Blue
						pand mm2,mm5

						psrad mm2,8
						por mm1,mm2							// Green
						movq mm2,mm3

						pand mm3,mm6
						psrad mm3,12						// Red
						pand mm2,mm7

						psrad mm2,16						// Alpha
						por mm3,mm1
						por mm3,mm2

						packssdw mm0,mm3
						add esi,16
						sub ebx,4

						movq [edi],mm0
						lea edi,[edi+8]
						jnz lp4

						dec edx
						lea esi,[ecx*4+esi]
						lea edi,[eax*4+edi]

						jnz lp3
						emms
					}
				}
//
// Must be 0555 or 1555 format (treated the same)
//
				else
				{
					BMaskBlue =0x000000f8000000f8;
					BMaskGreen=0x0000f8000000f800;
					BMaskRed  =0x00f8000000f80000;
					BMaskAlpha=0x8000000080000000;

					_asm
					{
						movq mm7,BMaskAlpha
						movq mm6,BMaskRed
						movq mm5,BMaskGreen
						movq mm4,BMaskBlue
						
						mov eax,DestPitch
						mov ecx,SourcePitch

						mov esi,Source
						mov edi,Dest

						mov edx,height			// Height

lp5:					mov ebx,width			// Convert 4 pixels of ARGB into 4 16 bit words of ARGB in pixel format

lp6:
						movq mm0,[esi]
						movq mm1,mm0
						movq mm2,mm0

						pand mm0,mm4
						psrad mm0,3							// Blue
						pand mm1,mm5

						psrad mm1,6
						por mm0,mm1							// Green
						movq mm1,mm2

						pand mm1,mm6
						psrad mm1,9							// Red
						por mm0,mm1

						pand mm2,mm7						// Alpha
						psrad mm2,16
						por mm0,mm2

						movq mm3,[esi+8]
						movq mm1,mm3
						movq mm2,mm3

						pand mm1,mm4
						psrad mm1,3							// Blue
						pand mm2,mm5

						psrad mm2,6
						por mm1,mm2							// Green
						movq mm2,mm3

						pand mm3,mm6
						psrad mm3,9							// Red
						pand mm2,mm7

						psrad mm2,16						// Alpha
						por mm3,mm1
						por mm3,mm2

						packssdw mm0,mm3
						add esi,16
						sub ebx,4

						movq [edi],mm0
						lea edi,[edi+8]
						jnz lp6

						dec edx
						lea esi,[ecx*4+esi]
						lea edi,[eax*4+edi]
						
						jnz lp5
						emms
					}
				}
			}
			else
//
// Non MMX generic code
//
			{
				for( int y=0; y<height; y++ )
				{
					for( int x=0; x<width; x+=2 )
					{
						Pixel1=*Source++;
						Pixel2=*Source++;

						Blue =((Pixel1    )&255)+((Pixel2<<16)&0xff0000);
						Green=((Pixel1>>8 )&255)+((Pixel2<<8 )&0xff0000);
						Red  =((Pixel1>>16)&255)+((Pixel2    )&0xff0000);
						Alpha=((Pixel1>>24)&255)+((Pixel2>>8 )&0xff0000);

						*Dest++=(DWORD)((((  Red>>RShiftRed  ) & MaskRed  ) << LShiftRed  ) |
										(((Green>>RShiftGreen) & MaskGreen) << LShiftGreen) | 
										((( Blue>>RShiftBlue ) & MaskBlue ) ) | 
										(((Alpha>>RShiftAlpha) & MaskAlpha) << LShiftAlpha) );
					}
					Dest+=DestPitch;
					Source+=SourcePitch;
				}
			}
		}
	}
//
// Unlock the surfaces
//
	wUnlock( DestSurface, NULL );
//
// Stop recording time
//
#ifdef LAB_ONLY
	TimeConvertTextures+=GetCycles()-Temp;
#endif
}


void ConvertBump( DWORD Bpp, IDirectDrawSurface7* DestSurface, DWORD *Source, DWORD SourcePitch, DWORD Hints, DWORD left, DWORD top, DWORD width, DWORD height )
{
	// This routine converts a monochrome height map to a bump map
#ifdef LAB_ONLY
	__int64 Temp=GetCycles();
#endif
	DDSURFACEDESC2 DESTddsd;

//
// Lock the destination
//
	DESTddsd.dwSize = sizeof(DESTddsd);
	wLock( DestSurface, NULL, &DESTddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL );
	WORD* Dest=(WORD*)DESTddsd.lpSurface;

//
// We only support 16bpp dUdV surfaces
//
	DWORD DestPitch=DESTddsd.lPitch >> 1;

	SourcePitch >>= 2;	// convert source pitch to count of dwords

	for( int y=0; y<height; y++ )
    {
        BYTE* pDstT = (BYTE *)Dest;
        LONG* pSrcL0 = (LONG*)Source;
        LONG* pSrcL1 = ( pSrcL0 + SourcePitch );
        LONG* pSrcL2 = ( pSrcL0 - SourcePitch );


        if( y == height-1 )   // Don't go past the last line
            pSrcL1 = pSrcL0;
        if( y == 0 )                 // Don't go before first line
            pSrcL2 = pSrcL0;

        for( DWORD x=0; x<width; x++ )
        {
            LONG v00 = pSrcL0[0]; // Get the current pixel
            LONG v01 = (x==width-1)?v00:pSrcL0[1]; // and the pixel to the right
            LONG vM1 = (x==0)?v00:pSrcL0[-1]; // and the pixel to the left
            LONG v10 = pSrcL1[0]; // and the pixel one line below.
            LONG v1M = pSrcL2[0]; // and the pixel one line above.

            LONG iDu = (vM1-v01); // The delta-u bump value
            LONG iDv = (v1M-v10); // The delta-v bump value

            if ( (v00 < vM1) && (v00 < v01) )  // If we are at valley
            {
                iDu = vM1-v00;                 // Choose greater of 1st order diffs
                if ( iDu < v00-v01 ) iDu = v00-v01;
            }

            *pDstT++ = (BYTE)iDu;
            *pDstT++ = (BYTE)iDv;

            pSrcL0++; // Move one pixel to the right (src is 32-bpp)
            pSrcL1++;
            pSrcL2++;
        }
        Source += SourcePitch; // Move to the next line
        Dest += DestPitch;
    }


	wUnlock( DestSurface, NULL );

#ifdef LAB_ONLY
	TimeConvertTextures+=GetCycles()-Temp;
#endif

}

void ConvertNormal( DWORD Bpp, IDirectDrawSurface7* DestSurface, DWORD *Source, DWORD SourcePitch, DWORD Hints, DWORD left, DWORD top, DWORD width, DWORD height )
{
	// This routine converts a monochrome height map to a normal map
#ifdef LAB_ONLY
	__int64 Temp=GetCycles();
#endif
	DDSURFACEDESC2 DESTddsd;

//
// Lock the destination
//
	DESTddsd.dwSize = sizeof(DESTddsd);
	wLock( DestSurface, NULL, &DESTddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL );
	WORD* Dest=(WORD*)DESTddsd.lpSurface;

	DWORD RShiftRed		= GetRShift( DESTddsd.ddpfPixelFormat.dwRBitMask );
	DWORD MaskRed		= GetMask(   DESTddsd.ddpfPixelFormat.dwRBitMask );
	DWORD LShiftRed		= GetLShift( DESTddsd.ddpfPixelFormat.dwRBitMask );

	DWORD RShiftGreen	= GetRShift( DESTddsd.ddpfPixelFormat.dwGBitMask );
	DWORD MaskGreen		= GetMask(   DESTddsd.ddpfPixelFormat.dwGBitMask );
	DWORD LShiftGreen	= GetLShift( DESTddsd.ddpfPixelFormat.dwGBitMask );

	DWORD RShiftBlue	= GetRShift( DESTddsd.ddpfPixelFormat.dwBBitMask );
	DWORD MaskBlue		= GetMask(   DESTddsd.ddpfPixelFormat.dwBBitMask );

	DWORD RShiftAlpha	= GetRShift( DESTddsd.ddpfPixelFormat.dwRGBAlphaBitMask );
	DWORD MaskAlpha		= GetMask(   DESTddsd.ddpfPixelFormat.dwRGBAlphaBitMask );
	DWORD LShiftAlpha	= GetLShift( DESTddsd.ddpfPixelFormat.dwRGBAlphaBitMask );

	DWORD DestPitch= DESTddsd.lPitch >> 1;	// even if dest is 32bpp

	SourcePitch >>= 2;	// convert source pitch to count of dwords

	for( int y=0; y<height; y++ )
    {
        SHORT* pDstT = (SHORT *)Dest;
        LONG* pSrcL0 = (LONG*)Source;
        LONG* pSrcL1 = ( pSrcL0 + SourcePitch );
        LONG* pSrcL2 = ( pSrcL0 - SourcePitch );


        if( y == height-1 )   // Don't go past the last line
            pSrcL1 = pSrcL0;
        if( y == 0 )                 // Don't go before first line
            pSrcL2 = pSrcL0;

        for( DWORD x=0; x<width; x++ )
        {
            LONG v00 = pSrcL0[0]; // Get the current pixel
            LONG v01 = (x==width-1)?v00:pSrcL0[1]; // and the pixel to the right
            LONG vM1 = (x==0)?v00:pSrcL0[-1]; // and the pixel to the left
            LONG v10 = pSrcL1[0]; // and the pixel one line below.
            LONG v1M = pSrcL2[0]; // and the pixel one line above.

			v00 &= 255L;
			v01 &= 255L;
			vM1 &= 255L;
			v10 &= 255L;
			v1M &= 255L;

            LONG iDu = (vM1-v01); // The delta-u bump value
            LONG iDv = (v1M-v10); // The delta-v bump value

            if ( (v00 < vM1) && (v00 < v01) )  // If we are at valley
            {
                iDu = vM1-v00;                 // Choose greater of 1st order diffs
                if ( iDu < v00-v01 ) iDu = v00-v01;
            }

			double fRed = (double)iDu / 255.0;	// -1..+1
			double fGreen = (double)iDv / 255.0;	// -1..+1
			double fBlue = sqrt((1.0 - fRed*fRed) * (1.0 - fGreen*fGreen));
			double magnitude = sqrt(fRed*fRed+fGreen*fGreen+fBlue*fBlue);
			fRed /= magnitude;
			fGreen /= magnitude;
			fBlue /= magnitude;

			DWORD Red = (DWORD)((fRed+1.0)*128.0);
			DWORD Green = (DWORD)((fGreen+1.0)*128.0);
			DWORD Blue = (DWORD)((fBlue+1.0)*128.0);

			if( Bpp == 32 )
			{
				*(unsigned long *)pDstT = (255L<<24)|(Red<<16)|(Green<<8)|(Blue);
				pDstT += 2;
			}
			else
			{
				*pDstT++=(SHORT)((((  Red>>RShiftRed  ) & MaskRed  ) << LShiftRed  ) |
					(((Green>>RShiftGreen) & MaskGreen) << LShiftGreen) | 
					((( Blue>>RShiftBlue ) & MaskBlue ) ) | 
					(((255>>RShiftAlpha) & MaskAlpha) << LShiftAlpha) );
			}

            pSrcL0++; // Move one pixel to the right (src is 32-bpp)
            pSrcL1++;
            pSrcL2++;
        }
        Source += SourcePitch; // Move to the next line
        Dest += DestPitch;
    }


	wUnlock( DestSurface, NULL );

#ifdef LAB_ONLY
	TimeConvertTextures+=GetCycles()-Temp;
#endif

}
