//---GEM--------------------------------------------------------------------------
//	textures.cpp - GEM texture engine
//
//	version:                                                                       
//		0.1		19/08/99		acid	initial version                                               
//		0.2		13/09/99		acid	added tga & jpeg readers
//		0.5b	08/11/99		acid	changed to fit directgem structures
//		0.6		05/01/00		acid	rewrite almost all rutines to make them
//										easier to use
//
//	desc:                                                                        
//		the engine supportd bmp, tga and jpeg files (jpeg decompresion comes from		
//		IJG jpeg lib), at first the file is loade a dimension and bpp are save then
//		enumerations for appropiate texture format comes, and resizing or bpp
//		changes comes (if necesary), engine support loading of alpha chanel
//		from 8bit bmp, seting a trasparncy value and more
//
//	(c)	ZONE51 1999
//--------------------------------------------------------------------------------

#define		STRICT	

#include	<stdio.h>
#include	<tchar.h>
extern "C"{
#include	"jpeglib.h"
}
#include	"texture.h"
#include	"directgem.h"


enum TGAImageType
{
	TGAColorMapped = 1,
	TGAFullColor,
	TGAGrayScale,
	TGACompressedColorMapped = 9,
	TGACompressedFullColor,
	TGACompressedGrayScale
};


#pragma pack(1)
struct TGAHeader 
{
    BYTE	IDLength;		/* length of Identifier String */
    BYTE	ColorMapType;	/* 0 = no map */
    BYTE	ImgType;		/* image type (see below for values) */
    WORD    ColorMapIndex;	/* index of first color map entry */
    WORD	ColorMapLenght;	/* number of entries in color map */
    BYTE	ColorSize;		/* size of color map entry (15,16,24,32) */
    WORD	xorg;			/* x origin of image */
    WORD	yorg;			/* y origin of image */
    WORD    width;			/* width of image */
    WORD	height;			/* height of image */
    BYTE	bpp;			/* pixel size (8,16,24,32) */
	BYTE	attribs;    
};
#pragma pack()

#pragma pack(1)						
typedef struct header
{

	WORD			ID;			/* identyfikator BM */
	DWORD			filesize;	/* rozmiar pliku */
	DWORD			reserved;
	DWORD			pixeloffset;/* poczatek danych bitmapy */

	DWORD			bmisize;	/* rozmiar infa musi byc 40 */
	DWORD			width;		/* szerokosc */
	DWORD			height;		/* wysokosc */
	WORD			planes;		/* liczba plaszczyzn bitowych */
	WORD			bpp;		/* liczba bitow na pixel */
	DWORD			compression;/* typ kompresji */
	DWORD			cmpsize;	/* rozmiar skompresowanego pliku */
	DWORD			xscale;		/* pixle na metr, nieuzywane */
	DWORD			yscale;		/* jak wyzej */
	DWORD			colors;		/* uzyte kolory, nieuzywane */
	DWORD			impcolors;	/* wazne kolor, nieuzywane */

} BMPHeader;
#pragma pack()


#define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }

#define ARGB32( r, g, b ) (((r)<<16) + ((g)<<8) +(b))
#define BMP_ID			0x4D42


DWORD BitCount( DWORD value )
{
	DWORD						count = 0;

	for( ; value ; value = value>>1 )	
		if( value&0x1 )
			count++;

	return count;
}

WORD _32TO16BITS( DWORD col )
{
	WORD			r, g, b;

	r = (col&0x00FF0000)>>16;
	g = (col&0x0000FF00)>>8;
	b = (col&0x000000FF);

	return ((r>>3)<<11) + ((g>>2)<<5) + (b>>3);
}

WORD _32TO15BITS( DWORD col )
{
	WORD			r, g, b;

	r = (col&0x00FF0000)>>16;
	g = (col&0x0000FF00)>>8;
	b = (col&0x000000FF);

	return ((r>>3)<<10) + ((g>>3)<<5) + (b>>3);
}
		
struct	TEXTUREENUMINFO
{
	LPDDPIXELFORMAT			pddpfFoundedFormat;
	DWORD					dwDesireFormat;
	
	BOOL					bFound;
};

struct	IMAGE
{
	LPDWORD					buffer;
	
	DWORD					height;
	DWORD					width;
};


//--------------------------------------------------------------------------------
//name: GetDDFromDevice()
//desc: retrives a DD interface from D3DDevice interface
//--------------------------------------------------------------------------------
LPDIRECTDRAW7 GetDDFromDevice( LPDIRECT3DDEVICE7 pd3dDevice )
{
	LPDIRECTDRAW7        pDD = NULL;
	LPDIRECTDRAWSURFACE7 pddsRender;

    if( pd3dDevice )
	{
	    // Get the current render target
		if( SUCCEEDED( pd3dDevice->GetRenderTarget( &pddsRender ) ) )
		{
		    // Get the DDraw4 interface from the render target
			pddsRender->GetDDInterface( (VOID**)&pDD );
			pddsRender->Release();
		}
	}

	return pDD;
}


//--------------------------------------------------------------------------------
//name: TextureSearch()
//desc: using in pixel format enumeration to find desire format of texture
//		the mode we look for is specified by dwDesireMode field of TEXTUREENUMINFO
//		structure (the value of this field could be one of GEMTEXTUREFORAMT enums,
//		for more info look in enum.h)
//--------------------------------------------------------------------------------
static HRESULT CALLBACK TextureSearch( DDPIXELFORMAT* pddpf, LPVOID pparam )
{
	WORD				rgbBitCount;

    if( NULL==pddpf || NULL==pparam )
        return DDENUMRET_OK;

	TEXTUREENUMINFO* info = (TEXTUREENUMINFO*)pparam;

    // skip any funky mode
    if( pddpf->dwFlags & (DDPF_LUMINANCE|DDPF_BUMPLUMINANCE|DDPF_BUMPDUDV) )
        return DDENUMRET_OK;

	// Else, skip any paletized formats (all modes under 16bpp)
	if( pddpf->dwRGBBitCount < 16 )
		return DDENUMRET_OK;

	// skip any FourCC formats
	if( pddpf->dwFourCC != 0 )
		return DDENUMRET_OK;

	//check alpha format
	if( info->dwDesireFormat == ARGB8888 || info->dwDesireFormat == ARGB1555 )
	{
		if( !(pddpf->dwFlags&DDPF_ALPHAPIXELS) )
			return DDENUMRET_OK;
	}
	else
	{
		if( pddpf->dwFlags&DDPF_ALPHAPIXELS )
			return DDENUMRET_OK;
	}

	//found 32bit format now we must check if we looking for it
	if( pddpf->dwRGBBitCount == 32 )
	{
		if( info->dwDesireFormat == ARGB8888 || info->dwDesireFormat == RGB888 )
			info->bFound = TRUE;
		else
			return DDENUMRET_OK;
	}

	//found 16bit format, at start we must determine if format is 15 or 16 bits (DX doesnt differ
	//them) and then we check if we found what we want
	if( pddpf->dwRGBBitCount == 16 )
	{
		rgbBitCount = BitCount( pddpf->dwRBitMask ) + BitCount( pddpf->dwGBitMask ) + BitCount( pddpf->dwBBitMask );

		if( rgbBitCount == 16 )
		{
			if( info->dwDesireFormat == RGB565 )
				info->bFound = TRUE;
			else
				return DDENUMRET_OK;
		}

		if( rgbBitCount == 15 )
		{
			if( info->dwDesireFormat == ARGB1555 || info->dwDesireFormat == RGB555 )
				info->bFound = TRUE;
			else
				return DDENUMRET_OK;
		}
	}

	//format found, cancel enumeration
	if( info->bFound )
	{
		memcpy( info->pddpfFoundedFormat, pddpf, sizeof(DDPIXELFORMAT) );
		return DDENUMRET_CANCEL;
	}

    return DDENUMRET_OK;;
}

//----------------------------------------------------------------------------
//	main texture engine
//----------------------------------------------------------------------------

//NamedItemList<gem_Texture>		TextureList;


//----------------------------------------------------------------------------
//	loaders function
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
//	name: LoadBMPFile()
//	desc: loads noncompressed, one planar .BMP image file
//----------------------------------------------------------------------------
HRESULT LoadBMPFile( TCHAR* name, IMAGE* image )
{

	FILE*				inFile;
	BMPHeader			header;
	DWORD				linelength;
	LPBYTE				line;

	DWORD				palette[256];
	DWORD				colornbr;
	LONG				i, j, k;
	LPDWORD				buffer;
	BYTE				tmp, r, g, b;
	DWORD				pos;


	// file open
	inFile = fopen( name, "rb" );
	if( inFile == NULL )
		return E_FAIL;

	// read header
	fread( &header, sizeof(header), 1, inFile );

	// id check
	if( header.ID != BMP_ID )
	{
		fclose( inFile );
		return E_FAIL;
	}

	// skip compressed bitmaps
	if( header.compression || header.planes>1 )
	{
		fclose( inFile );
		return E_FAIL;
	}
	
	image->width	= header.width;
	image->height	= header.height;

	linelength = ( (header.width*header.bpp >> 3) + 3 ) & 0xFFFFFFFC;

	image->buffer = new DWORD[header.width*header.height];
		
	// tmp line holder
	line = (LPBYTE)malloc( linelength );

	// palete read
	if( header.bpp != 24 )
	{
		// calculate palette entry number
		colornbr = (1LU<<header.bpp);
		// palete read
		for( i = 0 ; i<colornbr ; i++ )
		{
			fread( &palette[i], sizeof(DWORD), 1, inFile );
		}
	}

	fseek( inFile, header.pixeloffset, SEEK_SET );

	
	for( i = header.height-1 ; i>=0 ; i-- )		
		if( fread( line, 1, linelength, inFile ) != linelength )
		{	
			fclose( inFile );
			return 4;
		}
		else
		{			
			buffer = image->buffer + header.width*i;
			for( j = 0, pos = 0 ; j<header.width ; )
			{
				switch( header.bpp )
				{
					case 1:
					{						
						tmp = line[pos++];						
						for( k = 7 ; k>=0 ; k--, j++)
						{
							r = (tmp>>k)&0x01;								
							buffer[j] = palette[r];
						}
					}
					break;
					
					case 4:
					{						
						tmp = line[pos++];						
						buffer[j++] = palette[tmp>>4];						
						buffer[j++] = palette[tmp&0xF];
					}
					break;

					case 8:
					{						
						tmp = line[pos++];
						buffer[j++] = palette[tmp];
					}
					break;

					case 24:
					{							
						b = line[pos++];
						g = line[pos++];
						r = line[pos++];						
						buffer[j++] = ARGB32( r, g, b );
					}
					break;
					
				}
			}
		}

	delete line;
	fclose( inFile );

	return S_OK;
}

HRESULT Load8bitBMPFile( TCHAR* name, IMAGE* image )
{
	FILE*				inFile;
	BMPHeader			header;
	DWORD				linelength;
	LPBYTE				line;
	
	LONG				i;
	LPBYTE				buffer, current;	

	// file open
	inFile = fopen( name, "rb" );
	if( inFile == NULL )
		return E_FAIL;

	// read header
	fread( &header, sizeof(header), 1, inFile );

	// id check
	if( header.ID != BMP_ID )
	{
		fclose( inFile );
		return E_FAIL;
	}

	// skip compressed bitmaps
	if( header.compression || header.planes>1 )
	{
		fclose( inFile );
		return E_FAIL;
	}

	if( header.bpp != 8 )
	{
		fclose( inFile );
		return E_FAIL;
	}
	
	image->width	= header.width;
	image->height	= header.height;

	linelength = ( (header.width*header.bpp >> 3) + 3 ) & 0xFFFFFFFC;

	buffer = new BYTE[header.width*header.height];
	image->buffer = (LPDWORD)buffer;
		
	// tmp line holder
	line = new BYTE[linelength];
	
	fseek( inFile, header.pixeloffset, SEEK_SET );
	
	for( i = header.height-1 ; i>=0 ; i-- )		
		if( fread( line, 1, linelength, inFile ) != linelength )
		{	
			fclose( inFile );
			return E_FAIL;
		}
		else
		{			
			current = buffer + i*image->width;
			memcpy( current, line, image->width );
		}
			
	delete line;
	fclose( inFile );

	return S_OK;
}

//----------------------------------------------------------------------------
//name: LoadJPGfile()
//desc: loades a .jpg file, decompresion rutine comes from IJP (IndependedJPEG
//		Group)
//----------------------------------------------------------------------------
HRESULT LoadJPGFile(TCHAR* name, IMAGE* image)
{	
	DWORD						line;
	DWORD						width;

	//seting up a decompresion stuctures
	struct jpeg_decompress_struct	cinfo;
	struct jpeg_error_mgr			jerr;	

	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);

	//seting input source
	FILE*						infile;
	
	if( ( infile = fopen( name, "rb" ) ) == NULL ) 
		return E_FAIL;

	jpeg_stdio_src( &cinfo, infile );
	jpeg_read_header( &cinfo, TRUE );
	jpeg_start_decompress( &cinfo );

	image->width	= cinfo.output_width;
	image->height	= cinfo.output_height;	
	image->buffer	= new DWORD[image->width*image->height];	

	line = 0;	

	width = cinfo.output_width;

	if( cinfo.out_color_components != 3 )
	{
		jpeg_finish_decompress(&cinfo);
		jpeg_destroy_decompress(&cinfo);
		fclose( infile );
		return E_FAIL;
	}

	JSAMPROW	sl = new JSAMPLE[cinfo.output_width*cinfo.out_color_components];

	while (cinfo.output_scanline < cinfo.output_height)
	{
		jpeg_read_scanlines( &cinfo, &sl, 1);		
		for( DWORD i = 0 ; i<cinfo.output_width ; i++ )				
			image->buffer[line*width+i] = ARGB32( sl[i*3], sl[3*i+1], sl[3*i+2] );				
		
		line++;
	}

	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);
	fclose( infile );

	delete sl;

	return S_OK;
}

//----------------------------------------------------------------------------
//name: LoadTGAfile()
//desc: only fullcolor file are supported
//----------------------------------------------------------------------------
HRESULT LoadTGAFile(TCHAR* name, IMAGE* image)
{
	TGAHeader			header;	
	FILE*				imgFile = fopen( name, "rb" );
	LONG				i, j;			
	WORD				p1;
	DWORD				p2;	

	if(	!imgFile )		
		return E_FAIL;

	fread( &header, sizeof(TGAHeader), 1, imgFile );
	
	if( header.ImgType!=TGAFullColor )
	{
		fclose( imgFile );
		return E_FAIL;
	}

	image->width	= header.width;
	image->height	= header.height;
	image->buffer	= new DWORD[header.width*header.height];
	
	switch( header.bpp )
	{
		case 16:								
			for( i = (LONG)header.height-1 ; i>=0 ; i--)
				for( j = 0 ; j<(LONG)header.width ; j++ )
				{
					fread( &p1, sizeof(WORD), 1, imgFile );										
					image->buffer[i*header.width+j] = ARGB32( ((p1>>10)&0x1F)<<3, ((p1>>5)&0x1F)<<3, (p1&0x1F)<<3 );						
				}			
		break;

		case 32:
			for( i = (LONG)header.height-1 ; i>=0 ; i--)
				for( j = 0 ; j<(LONG)header.width ; j++ )
				{
					fread( &p2, sizeof(DWORD), 1, imgFile );					
					image->buffer[i*header.width+j] = p2;
				}			
		break;

		default:
			return E_FAIL;
		break;
	}

	return S_OK;
}

VOID InitSurfaceDesc( DDSURFACEDESC2* ddsd )
{
	ZeroMemory( ddsd, sizeof(DDSURFACEDESC2) );

	ddsd->dwSize				 = sizeof(DDSURFACEDESC2);
	ddsd->ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
}

VOID FreeImage( IMAGE* image )
{
	if( image->buffer )
	{
		delete image->buffer;
	}

	image->height = 0;
	image->width  = 0;
}

HRESULT ConvertImage( IMAGE* image, DDPIXELFORMAT* pf )
{
	int			bitsCount;
	LPWORD		buffer;
	WORD		col;

	if( pf->dwRGBBitCount == 32 )
		return S_OK;

	if( pf->dwRGBBitCount == 16 )
	{
		bitsCount = BitCount( pf->dwRBitMask ) + BitCount( pf->dwGBitMask ) + BitCount( pf->dwBBitMask );
		if( bitsCount!=16 && bitsCount!=15 )
			return E_FAIL;

		buffer = new WORD[image->width*image->height];

		for( int i  = 0 ; i<image->width ; i++ )
			for( int j = 0 ; j<image->height ; j++ )
			{
				if( bitsCount == 16 )
					col = _32TO16BITS( image->buffer[i + j*image->width] );
				else
					col = _32TO15BITS( image->buffer[i + j*image->width] );

				buffer[i + j*image->width] = col;
			}

		delete image->buffer;
		image->buffer = (DWORD*)buffer;

		return S_OK;
	}

	return E_FAIL;
}

// well i dont know why but i dont like style of this function..., anyway it works just fine
// so for now it will do				
LPDIRECTDRAWSURFACE7 CopyToSurface( IMAGE* image, LPDIRECTDRAW7 pDD, DWORD flags )
{		
	LPDIRECTDRAWSURFACE7	pdds;
	DDSURFACEDESC2			ddsd;
	InitSurfaceDesc( &ddsd );

	ddsd.dwFlags		= DDSD_WIDTH | DDSD_HEIGHT;
	ddsd.dwHeight		= image->height;
	ddsd.dwWidth		= image->width;

	if( flags&GEMIMAGE_SYSTEMMEMORY )
	{
		ddsd.dwFlags |= DDSD_CAPS;
		ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
	}
	
	if( flags&GEMIMAGE_OFFSCREEN || flags&GEMIMAGE_COLORFORMAT )
	{	
		ddsd.dwFlags |= DDSD_CAPS;
		ddsd.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN;
	}
		
	if( flags&GEMIMAGE_COLORFORMAT )
	{
		ddsd.dwFlags |= DDSD_PIXELFORMAT;
		ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;

		// kinda "unelegant" but it works just fine
		// maybe some makros will make it looks better
		if( flags&GEMIMAGE_32BIT )
		{
			ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
			ddsd.ddpfPixelFormat.dwRBitMask = 0x00FF0000;
			ddsd.ddpfPixelFormat.dwGBitMask = 0x0000FF00;
			ddsd.ddpfPixelFormat.dwBBitMask = 0x000000FF;
		}
		else
		{
			ddsd.ddpfPixelFormat.dwRGBBitCount = 16;
			if( flags&GEMIMAGE_16BIT )
			{
				ddsd.ddpfPixelFormat.dwRBitMask = 0x0000F800;
				ddsd.ddpfPixelFormat.dwGBitMask = 0x000007E0;
				ddsd.ddpfPixelFormat.dwBBitMask = 0x0000001F;	
			}
			else
			{
				ddsd.ddpfPixelFormat.dwRBitMask = 0x00007C00;
				ddsd.ddpfPixelFormat.dwGBitMask = 0x000003E0;
				ddsd.ddpfPixelFormat.dwBBitMask = 0x0000001F;
			}
		}

		if( FAILED( ConvertImage( image, &ddsd.ddpfPixelFormat ) ) )
			return NULL;
	}
	else
	{
		DDSURFACEDESC2		_ddsd;
		InitSurfaceDesc( &_ddsd );
	
		pDD->GetDisplayMode( &_ddsd );

		if( FAILED(ConvertImage( image, &_ddsd.ddpfPixelFormat ) ) )
			return NULL;
	}		
	
	if( FAILED( pDD->CreateSurface( &ddsd, &pdds, NULL ) ) )
		return NULL;		

	if( SUCCEEDED( pdds->Lock( NULL, &ddsd, DDLOCK_WRITEONLY, NULL ) ) )
	{	
		int	size = ddsd.ddpfPixelFormat.dwRGBBitCount == 32 ? 4 : 2;
		for( DWORD j = 0 ; j<image->height ; j++ )		
		{					
			memcpy( (BYTE*)ddsd.lpSurface + j*ddsd.lPitch, (BYTE*)image->buffer + j*image->width*size, image->width*size );								
		}
	}
	else
		return NULL;

	pdds->Unlock( NULL );

	return pdds;
}

HRESULT LoadImage( TCHAR* name, LPDIRECTDRAWSURFACE7* lplpSurface, LPDIRECT3DDEVICE7 pd3dDevice, DWORD flags )
{
	TCHAR*				strExt;
	TCHAR*				strName = name;
	HRESULT				hr = E_FAIL;
	IMAGE				image;
	LPDIRECTDRAW7		pDD = GetDDFromDevice( pd3dDevice );

	if( !pDD )
		return E_FAIL;

	strExt = _tcsrchr( strName, TEXT('.') );

	if( strExt == NULL )
		return E_FAIL;

	if( !lstrcmpi( strExt, ".bmp" ) )
	{
		hr = LoadBMPFile( strName, &image );
	}
	
	if( !lstrcmpi( strExt, ".jpg") )
	{
		hr = LoadJPGFile( strName, &image );
	}

	if( !lstrcmpi( strExt, ".tga" ) )
	{
		hr = LoadTGAFile( strName, &image );
	}
							
	if( FAILED( hr ) )
		return E_FAIL;

	*lplpSurface = CopyToSurface( &image, pDD, flags );

	FreeImage( &image );

	if( *lplpSurface == NULL )
		return E_FAIL;

	return S_OK;
}


HRESULT LoadImage( TCHAR* name, LPDIRECTDRAWSURFACE7* lplpSurface, LPDIRECTDRAW7 pDD, DWORD flags )
{
	TCHAR*				strExt;
	TCHAR*				strName = name;
	HRESULT				hr = E_FAIL;
	IMAGE				image;		

	strExt = _tcsrchr( strName, TEXT('.') );

	if( strExt == NULL )
		return E_FAIL;

	if( !lstrcmpi( strExt, ".bmp" ) )
	{
		hr = LoadBMPFile( strName, &image );
	}
	
	if( !lstrcmpi( strExt, ".jpg") )
	{
		hr = LoadJPGFile( strName, &image );
	}

	if( !lstrcmpi( strExt, ".tga" ) )
	{
		hr = LoadTGAFile( strName, &image );
	}
							
	if( FAILED( hr ) )
		return E_FAIL;

	*lplpSurface = CopyToSurface( &image, pDD, flags );

	FreeImage( &image );

	if( *lplpSurface == NULL )
		return E_FAIL;

	return S_OK;
}


HRESULT CopyToTexture( IMAGE* image, LPDIRECTDRAWSURFACE7 pddsTexture )
{
	LPDIRECTDRAWSURFACE7	pddsTmp;
	LPDIRECTDRAW7			pDD;
	DDSURFACEDESC2			ddsd;
	InitSurfaceDesc( &ddsd );

	if( FAILED( pddsTexture->GetDDInterface( (LPVOID*)&pDD ) ) )
		return E_FAIL;

	if( FAILED( pddsTexture->GetPixelFormat( &ddsd.ddpfPixelFormat ) ) )
		return E_FAIL;

	if( FAILED( ConvertImage( image, &ddsd.ddpfPixelFormat ) ) )
		return E_FAIL;

	ddsd.dwFlags		= DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS;
	ddsd.dwHeight		= image->height;
	ddsd.dwWidth		= image->width;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

	if( FAILED( pDD->CreateSurface( &ddsd, &pddsTmp, NULL ) ) )
		return E_FAIL;

	if( SUCCEEDED( pddsTmp->Lock( NULL, &ddsd, DDLOCK_WRITEONLY, NULL ) ) )
	{	
		int	size = ddsd.ddpfPixelFormat.dwRGBBitCount == 32 ? 4 : 2;
		for( DWORD j = 0 ; j<image->height ; j++ )		
		{					
			memcpy( (BYTE*)ddsd.lpSurface + j*ddsd.lPitch, (BYTE*)image->buffer + j*image->width*size, image->width*size );								
		}
	}
	else
		return E_FAIL;

	pddsTmp->Unlock( NULL );

	if( FAILED( pddsTexture->Blt( NULL, pddsTmp, NULL, 0, NULL ) ) )
	{
		pddsTmp->Release();
		return E_FAIL;
	}

	pddsTmp->Release();

	return S_OK;
}

HRESULT LoadTexture( TCHAR* name, LPDIRECTDRAWSURFACE7* lplpTexture, LPDIRECT3DDEVICE7 pd3dDevice, DWORD flags, DWORD format )
{
	TCHAR*					strExt;
	TCHAR*					strName = name;
	HRESULT					hr = E_FAIL;
	IMAGE					image;
	TEXTUREENUMINFO			info;
	LPDIRECTDRAW7			pDD = GetDDFromDevice( pd3dDevice );	
		
	if( !pDD )
		return E_FAIL;

	strExt = _tcsrchr( strName, TEXT('.') );

	if( strExt == NULL )
		return E_FAIL;

	if( !lstrcmpi( strExt, ".bmp" ) )
	{
		hr = LoadBMPFile( strName, &image );
	}
	
	if( !lstrcmpi( strExt, ".jpg") )
	{
		hr = LoadJPGFile( strName, &image );
	}
		
	if( !lstrcmpi( strExt, ".tga" ) )
	{
		hr = LoadTGAFile( strName, &image );
	}	

	info.bFound = FALSE;
	info.dwDesireFormat = ARGB8888;

	if( flags&GEMTEXTURE_OVERRIDEFORMAT )
	{
		info.dwDesireFormat = format;
	}		

	DDSURFACEDESC2	ddsd;
	InitSurfaceDesc( &ddsd );

	info.pddpfFoundedFormat = &ddsd.ddpfPixelFormat;

	pd3dDevice->EnumTextureFormats( &TextureSearch, &info );

	if( info.bFound )
	{
		DWORD			width = image.width;
		DWORD			height = image.height;

		if( flags&GEMTEXTURE_POW2 )
		{
			for( width = 1 ; width<image.width ; width <<= 1 );
			for( height = 1 ; height<image.height ; height <<= 1 );
		}

		if( flags&GEMTEXTURE_SQUARE )
		{
			if( width > height )
				height = width;
			else
				width = height;
		}

		ddsd.dwWidth		= width;
		ddsd.dwHeight		= height;
		ddsd.dwFlags		= DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_CKSRCBLT;
		ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE;

		if( flags&GEMTEXTURE_SYSTEMMEMORY )
			ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;

		if( flags&GEMTEXTURE_MANAGING )
			ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE;

		if( FAILED( pDD->CreateSurface( &ddsd, lplpTexture, NULL ) ) )
			return E_FAIL;

		if( FAILED( CopyToTexture( &image, *lplpTexture ) ) )
			return E_FAIL;
	}
	else
		return E_FAIL;

	FreeImage( &image );

	return S_OK;
}

HRESULT ConvertAlphaToImage( IMAGE* image )
{
	LPDWORD		buffer = new DWORD[image->width*image->height];
	LPBYTE		tmp    = (LPBYTE)image->buffer;

	if( !tmp || !buffer )
	{
		if( buffer )
			delete buffer;

		return E_FAIL;
	}

	for( LONG i = 0 ; i<image->height ; i++ )
		for( LONG j = 0 ; j<image->width ; j++ )
			buffer[i*image->width + j] = ( ((DWORD)(tmp[i*image->width + j]))<<24 );

	delete tmp;
	image->buffer = buffer;

	return S_OK;
}

HRESULT ResizeAlpha( IMAGE* image, LPDDSURFACEDESC2 pddsd )
{
	if( pddsd->dwWidth != image->width || pddsd->dwHeight != image->height )
	{
		LPBYTE	newBuffer = new BYTE[pddsd->dwWidth*pddsd->dwHeight];
		LPBYTE	oldBuffer = (LPBYTE)image->buffer;

		FLOAT	dx = (FLOAT)image->width/(FLOAT)pddsd->dwWidth;
		FLOAT	dy = (FLOAT)image->height/(FLOAT)pddsd->dwHeight;

		FLOAT	x, y;
		LONG	i, j;

		for( i = 0, y = 0 ; i<pddsd->dwHeight ; i++, y+=dy )
			for( j = 0, x = 0 ; j<pddsd->dwWidth ; j++, x+=dx )
				newBuffer[i*pddsd->dwWidth + j] = oldBuffer[(LONG)y*image->width + (LONG)x];

		delete oldBuffer;

		image->buffer = (LPDWORD)newBuffer;
		image->width  = pddsd->dwWidth;
		image->height = pddsd->dwHeight;
	}

	return S_OK;
}


HRESULT LoadAlphaChannel( TCHAR* name, LPDIRECTDRAWSURFACE7* lplpTexture, LPDIRECT3DDEVICE7 pd3dDevice, DWORD flags )
{

	IMAGE		image;

	if( *lplpTexture == NULL && !(flags&GEMALPHA_CREATE) )
		return E_FAIL;
	else
		if( *lplpTexture!=NULL && flags&GEMALPHA_CREATE )
			return E_FAIL;

	if( FAILED( Load8bitBMPFile( name, &image ) ) )
		return E_FAIL;
			
	TEXTUREENUMINFO			info;
	LPDIRECTDRAW7			pDD = GetDDFromDevice( pd3dDevice );	
		
	if( !pDD )
		return E_FAIL;

	if( flags&GEMALPHA_CREATE )
	{
		info.bFound = FALSE;
		info.dwDesireFormat = ARGB8888;	

		DDSURFACEDESC2	ddsd;
		InitSurfaceDesc( &ddsd );

		info.pddpfFoundedFormat = &ddsd.ddpfPixelFormat;

		pd3dDevice->EnumTextureFormats( &TextureSearch, &info );

		if( !info.bFound )
		{
			FreeImage( &image );
			return E_FAIL;
		}

		ConvertAlphaToImage( &image );

		DWORD			width = image.width;
		DWORD			height = image.height;

		if( flags&GEMTEXTURE_POW2 )
		{
			for( width = 1 ; width<image.width ; width <<= 1 );
			for( height = 1 ; height<image.height ; height <<= 1 );
		}

		if( flags&GEMTEXTURE_SQUARE )
		{
			if( width > height )
				height = width;
			else
				width = height;
		}


		ddsd.dwWidth		= width;
		ddsd.dwHeight		= height;
		ddsd.dwFlags		= DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_CAPS;
		ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE;

		if( flags&GEMTEXTURE_SYSTEMMEMORY )
			ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;

		if( flags&GEMTEXTURE_MANAGING )
			ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE;

		if( FAILED( pDD->CreateSurface( &ddsd, lplpTexture, NULL ) ) )
			return E_FAIL;

		if( FAILED( CopyToTexture( &image, *lplpTexture ) ) )
			return E_FAIL;
	}
	else
	{
		DDSURFACEDESC2	ddsd;
		InitSurfaceDesc( &ddsd );

		(*lplpTexture)->GetSurfaceDesc( &ddsd );		

		if( ddsd.ddpfPixelFormat.dwRGBBitCount != 32 )
			return E_FAIL;

		ResizeAlpha( &image, &ddsd );
		InitSurfaceDesc( &ddsd );

		if( SUCCEEDED( (*lplpTexture)->Lock( NULL, &ddsd, DDLOCK_NOSYSLOCK, NULL ) ) )
		{
			LPBYTE		pMem = (LPBYTE)ddsd.lpSurface;
			LPDWORD		pSurface;
			LONG		lPitch = ddsd.lPitch;
			LPBYTE		pAlpha = (LPBYTE)image.buffer;

			for( LONG i = 0 ; i<ddsd.dwHeight ; i++ , pMem += lPitch )
			{
				pSurface = (LPDWORD)pMem;

				for( LONG j = 0 ; j<ddsd.dwWidth ; j++ )
				{				
					pSurface[j] &= 0x00FFFFFF;
					pSurface[j] |= (((DWORD)pAlpha[i*image.width + j])<<24);
				}
			}

			(*lplpTexture)->Unlock( NULL );
		}
	}	


	FreeImage( &image );

	return S_OK;
}
	
