#include <stdafx.h>

#include "bmpLoader.hpp"

 
/*---------------------------------------------------------------------
	format an indexed image 
		- flip the image vertically

  BMP is bottom left to top right order
  I use top left to bottom right order

  if this is used twice on the same image it goes back to original state
---------------------------------------------------------------------*/
WORD BMP::FormatImage( BYTE *img ) {
	int i, j, tx, ix ;
	BYTE *tp, *ip ;
	int line	= bmpDesc.sl ;			// scan line size
	int block	= line * bmpDesc.h ;	// memory block for bitmap
	BYTE *tmp	= new BYTE[ block ] ;	// local storage

	memset( tmp, 0, block );			// init the memory

	ix = ( block - line ) ;				// img index - start one line from end
	tx = 0 ;							// tmp index - start from beginning

	for( j = 0; j < bmpDesc.h; j++ ) {
		tp = &tmp[tx];					// move ptr to position
		ip = &img[ix];
		for ( i = 0; i < line; i++ ) {
			*tp = *ip ;					// copy value
			ip++;						// move over on item
			tp++;						//		"
		}
		ix-=line;						// go back on scan line
		tx+=line;						// go forward on scan line
	}
	
	memcpy( img, tmp, block );			// copy flipped data to img
	return 0;
}

/*---------------------------------------------------------------------

  format RGB image
	- flip vetically 
	- convert BRG to RGB format

  if this is used twice on the same image it goes back to original state

---------------------------------------------------------------------*/
WORD BMP::FormatImage( BMPRGB_t *img ) {
	int			i, j, tx, ix ;
	BMPRGB_t	*tp, *ip ;
	int			line = bmpDesc.sl ;		
	int			block = line * bmpDesc.h ;
	BMPRGB_t	*tmp = new BMPRGB_t[ block ] ;

	memset( tmp, 255, block*3 );

	tx = 0 ;				// tmp image index
	ix = block - line ;		// image index

	for( j = 0; j < bmpDesc.h; j++ ) {
		tp = &tmp[tx];
		ip = &img[ix];
		for ( i = 0; i < line; i++ ) {
			// BMP uses BGR... flip to RGB
			tp->ch[0] = ip->ch[2];	// R -> R		
			tp->ch[1] = ip->ch[1];	// G -> G
			tp->ch[2] = ip->ch[0];	// B -> B
			ip++;
			tp++;
		}
		ix-=line;
		tx+=line;
	}
	
	memcpy( img, tmp, block*3 );
	return 0;
}
/*---------------------------------------------------------------------
	Constructor
---------------------------------------------------------------------*/
BMP::BMP() {
	colTable = NULL;
	bmp8bpp = NULL;
	bmp24bpp = NULL;
	bmpPtr = NULL;
	bmpLoaded = FALSE;			// is BMP loaded
	bmpInit = FALSE;			// is BMP initialised
	memset( &fileHdr, 0, sizeof( BITMAPFILEHEADER ) );
	memset( &infoHdr, 0, sizeof( BITMAPINFOHEADER ) );
};

/*---------------------------------------------------------------------
	Destructor
---------------------------------------------------------------------*/
BMP::~BMP() {
	if ( colTable != NULL ) { delete [] colTable ; }
	if ( bmp8bpp != NULL ) { delete [] bmp8bpp ; }
	if ( bmp24bpp != NULL ) { delete [] bmp24bpp ; }
	memset( &fileHdr, 0, sizeof( BITMAPFILEHEADER ) );
	memset( &infoHdr, 0, sizeof( BITMAPINFOHEADER ) );
};

/*---------------------------------------------------------------------

  Load a BMP file.

  bmFormat - bitmap format ( TRUE=reorder bytes )

---------------------------------------------------------------------*/
WORD BMP::LoadBMP( char *filename ) {

	if ( (bmpPtr = fopen( filename, "rb" ) ) == NULL ) {
		return 1;
	}

	fseek( bmpPtr, 0, SEEK_SET );

	// read the file header
	fread( &fileHdr, sizeof( BITMAPFILEHEADER ), 1, bmpPtr );
	if( fileHdr.bfType != 0x4d42 ) {	// "BM"
		return 1;
	}

	// read bitmap info header
	fread( &infoHdr, sizeof( BITMAPINFOHEADER ), 1, bmpPtr );

	if( infoHdr.biCompression != 0 ) {	// make sure not compressed
		return 1 ;
	}

	bmpDesc.w		= infoHdr.biWidth ;
	// if the width is not a multiple of 4(long) ... make it one
	if ( !(bmpDesc.w%4) ) {
		bmpDesc.sl		= bmpDesc.w + ( 4 - ( bmpDesc.w%4 ) ) ;
	} 
	else {
		bmpDesc.sl		= bmpDesc.w ;
	}
	bmpDesc.h		= infoHdr.biHeight ;
	bmpDesc.bpp		= infoHdr.biBitCount ;	// image depth
	if ( bmpDesc.bpp == 8 ) {
		if ( !infoHdr.biClrUsed ) {			// how many colours used
			bmpDesc.cc		= 256 ;			// make 256 if = 0
		}
		else {
			bmpDesc.cc		= infoHdr.biClrUsed ; // else use value
		}
	}

	int bmpSize = bmpDesc.sl * bmpDesc.h ;	// bitmap size
	switch( bmpDesc.bpp ) {
		case 8 :
			// read colour table
			colTable = new RGBQUAD[ bmpDesc.cc ] ;
			fread( colTable, sizeof( RGBQUAD ), bmpDesc.cc, bmpPtr );
			// read bitmap image with 8bpp
			bmp8bpp = new BYTE[ bmpSize ];
			fread( bmp8bpp, sizeof( BYTE ), bmpSize, bmpPtr );
			break;
		case 24 :
			// read bitmap image with 24bpp
			bmp24bpp = new BMPRGB_t[ bmpSize ];
			fread( bmp24bpp, sizeof( BMPRGB_t ), bmpSize, bmpPtr );
			break;
		default :
			return 1;
	}

	fclose( bmpPtr ) ;
	bmpLoaded = TRUE ;
	return 0;
};

/*---------------------------------------------------------------------

  get the BMP bitmap description 

---------------------------------------------------------------------*/
WORD BMP::GetBMPDesc( bitmapDesc_t *desc ) {
	memcpy( desc, &bmpDesc, sizeof( bitmapDesc_t ) );
	return 0;
};

/*---------------------------------------------------------------------

  get the bitmap data for an indexed image

---------------------------------------------------------------------*/
WORD BMP::GetBMP( BYTE *image, RGBQUAD *cList ) {
	if( !bmpLoaded || ( bmpDesc.bpp != 8 ) ) { return 1; }
	memcpy( cList, colTable, bmpDesc.cc * sizeof( RGBQUAD) );
	memcpy( image, bmp8bpp, ( bmpDesc.sl * bmpDesc.h ) );
	return 0;
};

/*---------------------------------------------------------------------

  get the bitmap data for an RGB image

---------------------------------------------------------------------*/
WORD BMP::GetBMP( BMPRGB_t *image ) {
	if( !bmpLoaded || ( bmpDesc.bpp != 24 ) ) { return 1; }
	memcpy( image, bmp24bpp, ( bmpDesc.sl * bmpDesc.h * sizeof( BMPRGB_t ) ) );
	return 0;
};

/*---------------------------------------------------------------------

  initialise the object to accept a bitmap

---------------------------------------------------------------------*/
WORD BMP::IniBMP( bitmapDesc_t *desc ) { 
	if( ( bmp8bpp != NULL) && ( bmp24bpp != NULL ) ) { return 1; }
	memcpy( &bmpDesc, desc, sizeof( bitmapDesc_t ) );
	// check if 8bpp or 24 bpp
	if ( bmpDesc.bpp != 8 ) { 
		if ( bmpDesc.bpp != 24 ) { return 1; }
	}
	
	// define BITMAPFILEHEADER
	fileHdr.bfType			= 0x4d42 ; // "BM"
	fileHdr.bfSize			= 0x0000 ;
	fileHdr.bfReserved1		= 0x0000 ;
	fileHdr.bfReserved2		= 0x0000 ;
	switch( bmpDesc.bpp ) {
		case 8 :
			fileHdr.bfOffBits = 54 + ( bmpDesc.cc * 4 ) ;	// 54 + 4 * #colours to image data
			break;
		case 24 :
			fileHdr.bfOffBits = 54 ;	// 54 bytes to RGB image data
			break;
	}

	// define BITMAPINFOHEADER
	infoHdr.biSize				= 40 ;
	infoHdr.biWidth				= bmpDesc.w	;
	infoHdr.biHeight			= bmpDesc.h ;
	infoHdr.biPlanes			= 1 ;
	infoHdr.biBitCount			= bmpDesc.bpp ;
	infoHdr.biCompression		= 0 ;
	infoHdr.biSizeImage			= 0 ;
	infoHdr.biXPelsPerMeter		= 2834 ;
	infoHdr.biYPelsPerMeter		= 2834 ; 
	infoHdr.biClrUsed			= 0 ;
	infoHdr.biClrImportant		= 0 ;
	
	if ( !(bmpDesc.w%4) ) {
		bmpDesc.sl		= bmpDesc.w + ( 4 - ( bmpDesc.w%4 ) ) ;
	} 
	else {
		bmpDesc.sl		= bmpDesc.w ;
	}

	int bmpSize = bmpDesc.sl * bmpDesc.h ;

	switch( bmpDesc.bpp ) {
		case 8 :
			bmp8bpp = new BYTE[ bmpSize ] ;
			colTable = new RGBQUAD[ bmpDesc.cc ] ;
			break;
		case 24 :
			bmp24bpp = new BMPRGB_t[ bmpSize ] ;
			break;
	}
	bmpInit = TRUE ;
	return 0;
};

/*---------------------------------------------------------------------

  define an indexed image

---------------------------------------------------------------------*/
WORD BMP::DefBMP( BYTE *image, RGBQUAD *cList ) {
	if( !bmpInit || ( bmpDesc.bpp != 8 ) ) { return 1; }
	memcpy( colTable, cList, bmpDesc.cc * sizeof( RGBQUAD) );
	memcpy( bmp8bpp, image, ( bmpDesc.sl * bmpDesc.h ) );
	return 0;
};

/*---------------------------------------------------------------------

  define and RGB image

---------------------------------------------------------------------*/
WORD BMP::DefBMP( BMPRGB_t *image ) {
	if( !bmpInit || ( bmpDesc.bpp != 24 ) ) { return 1; }
	memcpy( bmp24bpp, image, ( bmpDesc.sl * bmpDesc.h * 3 ) );
	return 0;
};

/*---------------------------------------------------------------------

	write the BMP file

    bmFormat - bitmap format ( TRUE=reorder bytes )

---------------------------------------------------------------------*/
WORD BMP::SaveBMP( char *filename ) {
	if ( (bmpPtr = fopen( filename, "wb" ) ) == NULL ) {
		return 1 ;
	}

	// write the file header
	fwrite( &fileHdr, sizeof( BITMAPFILEHEADER ), 1, bmpPtr );
	// read bitmap info header
	fwrite( &infoHdr, sizeof( BITMAPINFOHEADER ), 1, bmpPtr );
	int bmpSize = bmpDesc.sl * bmpDesc.h ;
	switch( bmpDesc.bpp ) {
		case 8 :
			fwrite( colTable, sizeof( RGBQUAD ), bmpDesc.cc, bmpPtr );
			fwrite( bmp8bpp, sizeof( BYTE ), bmpSize, bmpPtr );
			break;
		case 24 :
			fwrite( bmp24bpp, sizeof( BMPRGB_t ), bmpSize, bmpPtr );
			break;
	}

	WORD tmp = 0x0000;
	fwrite( &tmp, sizeof( WORD ), 1, bmpPtr );

	// get file size
	fseek( bmpPtr, 0, SEEK_END );
	DWORD fileSize = (DWORD)ftell( bmpPtr );
	// write file size
	fseek( bmpPtr, 2, SEEK_SET );
	fwrite( &fileSize, sizeof( DWORD ), 1, bmpPtr );

	fclose( bmpPtr ) ;
	return 0;
};

/*---------------------------------------------------------------------

  free memory and reset object locals

---------------------------------------------------------------------*/
WORD BMP::FreeBMP() {
	bmpLoaded = FALSE;	
	bmpInit = FALSE;	
	if ( colTable != NULL ) { delete [] colTable ; }
	if ( bmp8bpp != NULL ) { delete [] bmp8bpp ; }
	if ( bmp24bpp != NULL ) { delete [] bmp24bpp ; }
	colTable = NULL;
	bmp8bpp = NULL;
	bmp24bpp = NULL;
	bmpPtr = NULL;
	memset( &fileHdr, 0, sizeof( BITMAPFILEHEADER ) );
	memset( &infoHdr, 0, sizeof( BITMAPINFOHEADER ) );
	return 0 ;
};


/*
//#define __DEBUG__
#ifdef __DEBUG__

main () {
	BMP img ;
	bitmapDesc_t	imgDesc ;
	BYTE			*img8bpp = NULL ;	// 8bpp image data
	RGBQUAD			*cols = NULL ;		// colour table
	BMPRGB_t		*img24bpp = NULL ;	// 24bpp image data

	img.LoadBMP("e:\\tmp\\test1-24bpp.bmp");
	img.GetBMPDesc( &imgDesc ) ;
	img24bpp = new BMPRGB_t[ (imgDesc.sl * imgDesc.h) ] ;
	img.GetBMP( img24bpp ) ;
	//img.FormatImage( img24bpp );
	img.FreeBMP() ;
	img.IniBMP( &imgDesc );
	img.DefBMP( img24bpp );
	img.SaveBMP("e:\\tmp\\out1-24bpp.bmp");

	return 1;
}

#endif //__DEBUG__
*/
