//
//	bmp.cpp - source file / freeware
//
//	David Henry - tfc_duke@hotmail.com
//


#include	"bmp.h"
#include    <stdio.h>

#include    <libc/stubs.h>

extern "C"{
long filelength(int fhandle);
}

// --------------------------------------------------
// LoadFileBMP() - load a Windows/OS2 BITMAP image
//				   [.bmp].
//
// parameters :
//    - filename [in]  : image source file
//    - pixels	 [out] : 32 bits rgb image data
//    - width    [out] : image width in pixels
//    - height   [out] : image height in pixels
//    - flipvert [in]  : flip vertically
//
// return value :
//    - -1 : no image data
//    -  0 : failure
//    -  1 : success
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// accepted image formats :
//     # RGB 1-4-8-24-32 bits	WINDOWS - OS/2
//     # RLE 4-8 bits			WINDOWS
// --------------------------------------------------

int LoadFileBMP( const char *filename, unsigned char **pixels, int *width, int *height, bool flipvert )
{
	FILE		*file;			// file stream
	BITMAPFILEHEADER	*bmfh;			// bitmap file header
	BITMAPINFOHEADER	*bmih;			// bitmap info header (windows)
	BITMAPCOREHEADER	*bmch;			// bitmap core header (os/2)
	RGBTRIPLE			*os2_palette;	// pointer to the color palette os/2
	RGBQUAD				*win_palette;	// pointer to the color palette windows
	char				*buffer;		// buffer storing the entire file
	unsigned char		*ptr;			// pointer to pixels data
	int					bitCount;		// number of bits per pixel
	int					compression;	// compression type (rgb/rle)
	int					row, col, i;	// temporary variables
	int					w, h;			// width, height



	/////////////////////////////////////////////////////
	// read the entire file in the buffer

	file = fopen(filename,"rb");

	if( !file)
		return 0;

	long flen = filelength(fileno(file));

	buffer = new char[ flen + 1 ];
	int rd = fread(buffer, flen, 1, file);
	char *pBuff = buffer;

	fclose(file);

	/////////////////////////////////////////////////////
	

	// read the header
	bmfh = (BITMAPFILEHEADER *)pBuff;
	pBuff += sizeof( BITMAPFILEHEADER );

	// verify that it's a BITMAP file
	if( bmfh->bfType != BITMAP_ID )
	{
		delete [] buffer;
		return 0;
	}


	bmch = (BITMAPCOREHEADER *)pBuff;
	bmih = (BITMAPINFOHEADER *)pBuff;


	if( (bmih->biCompression < 0) || (bmih->biCompression > 3) )
	{
		// OS/2 style
		pBuff += sizeof( BITMAPCOREHEADER );

		bitCount	= bmch->bcBitCount;
		compression	= BI_OS2;

		w = bmch->bcWidth;
		h = bmch->bcHeight;
	}
	else
	{
		// WINDOWS style
		pBuff += sizeof( BITMAPINFOHEADER );

		bitCount	= bmih->biBitCount;
		compression	= bmih->biCompression;

		w = bmih->biWidth;
		h = bmih->biHeight;
	}


	if( width )
		*width	= w;

	if( height )
		*height	= h;


	if( !pixels )
	{
		delete [] buffer;
		return (-1);
	}


	/////////////////////////////////////////////////////
	// read the palette

	if( bitCount <= 8 )
	{
		// 24 and 32 bits images are not paletted

		// ajust the palette pointer to the memory in the buffer
		os2_palette = (RGBTRIPLE *)pBuff;
		win_palette = (RGBQUAD *)pBuff;

		//	[number of colors in the palette] * [size of one pixel]
		pBuff += (1 << bitCount) * (bitCount >> 3) * sizeof( unsigned char );
	}

	/////////////////////////////////////////////////////


	// allocate memory to store pixel data
	*pixels = new unsigned char[ w * h * 3 ];
	ptr		= &(*pixels)[0];


	// move the pixel data pointer to the begening of bitmap data
	pBuff = buffer + (bmfh->bfOffBits * sizeof( char ));


	/////////////////////////////////////////////////////
	// read pixel data following the image compression
	// type and the number of bits per pixels
	/////////////////////////////////////////////////////

	switch( compression )
	{
		case BI_OS2:
		case BI_RGB:
		{
			for( row = h - 1; row >= 0; row-- )
			{
				if( flipvert )
					ptr = &(*pixels)[ row * w * 3 ];

				switch( bitCount )
				{
					case 1:
					{
// RGB 1 BITS
						for( col = 0; col < (int)(w / 8); col++ )
						{
							// read the current pixel
							unsigned char color = *((unsigned char *)(pBuff++));

							for( i = 7; i >= 0; i--, ptr += 3 )
							{
								// convert indexed pixel (1 bit) into rgb (32 bits) pixel
								int clrIdx = ((color & (1<<i)) > 0);

								if( compression == BI_OS2 )
								{
									ptr[2] = os2_palette[ clrIdx ].rgbtRed;
									ptr[1] = os2_palette[ clrIdx ].rgbtGreen;
									ptr[0] = os2_palette[ clrIdx ].rgbtBlue;									
								}
								else
								{
									ptr[2] = win_palette[ clrIdx ].rgbRed;
									ptr[1] = win_palette[ clrIdx ].rgbGreen;
									ptr[0] = win_palette[ clrIdx ].rgbBlue;
								}
							}
						}

						break;
					}

					case 4:
					{
// RGB 4 BITS
						for( col = 0; col < (int)(w / 2); col++, ptr += 6 )
						{
							// read the current pixel
							unsigned char color = *((unsigned char *)(pBuff++));

							// convert indexed pixel (4 bits) into rgb (32 bits) pixel
							int clrIdx;

							if( compression == BI_OS2 )
							{
								clrIdx = (color >> 4);
								ptr[2] = os2_palette[ clrIdx ].rgbtRed;
								ptr[1] = os2_palette[ clrIdx ].rgbtGreen;
								ptr[0] = os2_palette[ clrIdx ].rgbtBlue;

								clrIdx = (color & 0x0F);
								ptr[6] = os2_palette[ clrIdx ].rgbtRed;
								ptr[5] = os2_palette[ clrIdx ].rgbtGreen;
								ptr[4] = os2_palette[ clrIdx ].rgbtBlue;

							}
							else
							{
								clrIdx = (color >> 4);
								ptr[2] = win_palette[ clrIdx ].rgbRed;
								ptr[1] = win_palette[ clrIdx ].rgbGreen;
								ptr[0] = win_palette[ clrIdx ].rgbBlue;

								clrIdx = (color & 0x0F);
								ptr[6] = win_palette[ clrIdx ].rgbRed;
								ptr[5] = win_palette[ clrIdx ].rgbGreen;
								ptr[4] = win_palette[ clrIdx ].rgbBlue;
							}
						}

						break;
					}

					case 8:
					{
// RGB 8 BITS
						for( col = 0; col < w; col++, ptr += 3 )
						{
							// read the current pixel
							unsigned char color = *((unsigned char *)(pBuff++));

							// convert indexed pixel (8 bits) into rgb (32 bits) pixel
							if( compression == BI_OS2 )
							{
								ptr[2] = os2_palette[ color ].rgbtRed;
								ptr[1] = os2_palette[ color ].rgbtGreen;
								ptr[0] = os2_palette[ color ].rgbtBlue;
							}
							else
							{
								ptr[2] = win_palette[ color ].rgbRed;
								ptr[1] = win_palette[ color ].rgbGreen;
								ptr[0] = win_palette[ color ].rgbBlue;
							}
						}

						break;
					}

					case 24:
					{
// RGB 24 BITS
						for( col = 0; col < w; col++, ptr += 3 )
						{
							// convert bgr pixel (24 bits) into rgb (32 bits) pixel
							RGBTRIPLE *pix = (RGBTRIPLE *)pBuff;
							pBuff += sizeof( RGBTRIPLE );

							ptr[2] = pix->rgbtRed;
							ptr[1] = pix->rgbtGreen;
							ptr[0] = pix->rgbtBlue;
						}

  						break;
					}

					case 32:
					{
// RGB 32 BITS
						for( col = 0; col < w; col++, ptr += 3 )
						{
							// // convert bgr pixel (32 bits) into rgb (32 bits) pixel
							RGBQUAD *pix = (RGBQUAD *)pBuff;
							pBuff += sizeof( RGBQUAD );

							ptr[2] = pix->rgbRed;
							ptr[1] = pix->rgbGreen;
							ptr[0] = pix->rgbBlue;

						}

						break;
					}
				}
			}

			break;
		}

		case BI_RLE8:
		{
// RLE 8 BITS
			for( row = h - 1; row >= 0; row-- )
			{
				if( flipvert )
					ptr = &(*pixels)[ row * w * 3 ];

				for( col = 0; col < w; /* nothing */ )
				{
					// get one packet (2 bytes)
					unsigned char byte1 = *((unsigned char *)(pBuff++));
					unsigned char byte2 = *((unsigned char *)(pBuff++));


					if( byte1 == RLE_COMMAND )
					{
						// absolute encoding
						for( i = 0; i < byte2; i++, ptr += 3, col++ )
						{
							// read the current pixel
							unsigned char color = *((unsigned char *)(pBuff++));

							// convert indexed pixel (8 bits) into rgb (32 bits) pixel
							ptr[2] = win_palette[ color ].rgbRed;
							ptr[1] = win_palette[ color ].rgbGreen;
							ptr[0] = win_palette[ color ].rgbBlue;
						}

						if( (byte2 % 2) == 1 )
							pBuff++;
					}
					else
					{
						// read next pixels
						for( i = 0; i < byte1; i++, ptr += 3, col++ )
						{
							// convert indexed pixel (8 bits) into rgb (32 bits) pixel
							ptr[2] = win_palette[ byte2 ].rgbRed;
							ptr[1] = win_palette[ byte2 ].rgbGreen;
							ptr[0] = win_palette[ byte2 ].rgbBlue;
						}        
					}
				}
			}

			break;
		}

		case BI_RLE4:
		{
// RLE 4 BITS
			unsigned char color;
			int	bytesRead = 0;		// number of bytes read

			for( row = h - 1; row >= 0; row-- )
			{
				if( flipvert )
					ptr = &(*pixels)[ row * w * 3 ];

				for( col = 0; col < w; /* nothing */ )
				{
					// get one packet (2 bytes)
					unsigned char byte1 = *((unsigned char *)(pBuff++));
					unsigned char byte2 = *((unsigned char *)(pBuff++));

					bytesRead += 2;


					if( byte1 == RLE_COMMAND )
					{
						// absolute encoding
						unsigned char databyte;

						for( i = 0; i < byte2; i++, ptr += 3, col++ )
						{
							if( (i % 2) == 0 )
							{
								// read the current pixel
								databyte = *((unsigned char *)(pBuff++));
								bytesRead++;

								color = (databyte >> 4);	// 4 first bits
							}
							else
							{
								color = (databyte & 0x0F);	// 4 last bits
							}

							// convert indexed pixel (4 bits) into rgb (32 bits) pixel
							ptr[2] = win_palette[ color ].rgbRed;
							ptr[1] = win_palette[ color ].rgbGreen;
							ptr[0] = win_palette[ color ].rgbBlue;
						}
          
						while( (bytesRead % 2) != 0 )
						{
							pBuff++;
							bytesRead++;
						}
					}
					else
					{
						// read next pixels
						for( i = 0; i < byte1; i++, ptr += 3, col++ )
						{
							if( (i % 2) == 0 )
								color = (byte2 >> 4);	// 4 first bits
							else
								color = (byte2 & 0x0F);	// 4 last bits

							// convert indexed pixel (4 bits) into rgb (32 bits) pixel
							ptr[2] = win_palette[ color ].rgbRed;
							ptr[1] = win_palette[ color ].rgbGreen;
							ptr[0] = win_palette[ color ].rgbBlue;
						}        
					}
				}
			}

			break;
		}
	}


	// free buffer memory
	delete [] buffer;


	// return success
	return 1;
}