#include "stdafx.h"
#define IDENTIFIER		0x4d42
#define RESERVED		0
#define MAP_PLANES		1

#include <stdio.h>

typedef struct  {
        //WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} _BITMAPFILEHEADER;

typedef struct {
        DWORD      biSize;
        LONG       biWidth;
        LONG       biHeight;
        WORD       biPlanes;
        WORD       biBitCount;
        DWORD      biCompression;
        DWORD      biSizeImage;
        LONG       biXPelsPerMeter;
        LONG       biYPelsPerMeter;
        DWORD      biClrUsed;
        DWORD      biClrImportant;
} _BITMAPINFOHEADER;

_BITMAPFILEHEADER fileHD;
_BITMAPINFOHEADER infoHD;

#define BI_RGB 0
	
int SetFileHeader(int	iWidth, int iHeight, int BPP)
{

	//fileHD.bfType = IDENTIFIER; // =0x4d42
	fileHD.bfSize = iWidth * iHeight * BPP / 8 + sizeof(_BITMAPFILEHEADER) + sizeof(_BITMAPINFOHEADER);//BMP_HEADER;
	fileHD.bfReserved1 = RESERVED;
	fileHD.bfReserved2 = RESERVED;
	fileHD.bfOffBits = sizeof(_BITMAPFILEHEADER) + sizeof(_BITMAPINFOHEADER)  + 2;//BMP_HEADER;
	if(BPP == 1){
		fileHD.bfSize += 8;
		fileHD.bfOffBits += 8;
	}
	if(BPP == 8){
		fileHD.bfSize += 256 * sizeof(ULONG);
		fileHD.bfOffBits += 256 * sizeof(ULONG);
	}
	int nAddOffs = (4 - fileHD.bfOffBits % 4) % 4;
	fileHD.bfOffBits += nAddOffs;
	fileHD.bfSize += nAddOffs;
	infoHD.biSize = sizeof(_BITMAPINFOHEADER);
	infoHD.biWidth = iWidth;
	//If biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. 
	//If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper-left corner. 
	//infoHD.biHeight = (iHeight * -1);		
	infoHD.biHeight = iHeight;		
	infoHD.biPlanes  = 1;
	infoHD.biBitCount  = BPP;
	infoHD.biCompression = BI_RGB;
	infoHD.biSizeImage = 0;
	infoHD.biXPelsPerMeter  = 0;
	infoHD.biYPelsPerMeter  = 0;
	infoHD.biClrUsed = 0;
	infoHD.biClrImportant  = 0;
	return nAddOffs;
}

bool SaveaBMP(const unsigned char *rgb, const char* FileName,
			  int xWidth, int yHeight, int BPP, bool bFlip, int nWidthStep)
{
	int nAddOffs = SetFileHeader(xWidth, yHeight, BPP);

	DWORD	nNumberOfBytesWritten;

	FILE *pFile = NULL;
	pFile = fopen(FileName, "wb");

	if ( !pFile )
		return false;



	fprintf(pFile, "BM");
	nNumberOfBytesWritten = fwrite(&fileHD, 1, sizeof(_BITMAPFILEHEADER), pFile);
	nNumberOfBytesWritten = fwrite(&infoHD, 1, sizeof(_BITMAPINFOHEADER), pFile);
	if(BPP == 1){
		ULONG colorTable[] = {0x0, 0xffffffff};
		nNumberOfBytesWritten = fwrite(colorTable, 1, sizeof(colorTable), pFile);
	}
	if(BPP == 8){
		ULONG colorTable[256];
		for(int i = 0; i < 256; ++i){
			colorTable[i] = i;
			colorTable[i] = (colorTable[i] << 8) + i;
			colorTable[i] = (colorTable[i] << 8) + i;
			colorTable[i] = (colorTable[i] << 8) + i;
		}
		nNumberOfBytesWritten = fwrite(colorTable, 1, sizeof(colorTable), pFile);
	}
	char padding[] = "\0\0\0";
	nNumberOfBytesWritten = fwrite(padding, 1, nAddOffs, pFile);
	int nLineLength = xWidth * BPP / 8;
	if(nWidthStep > 0) nLineLength = nWidthStep;
	int nAddLength = (4 - nLineLength % 4) % 4;
	if(bFlip){
		for(int i=0; i<yHeight; ++i){
			nNumberOfBytesWritten = fwrite(rgb + nLineLength * (yHeight - i - 1), 1, nLineLength, pFile);
			nNumberOfBytesWritten = fwrite(padding, 1, nAddLength, pFile);
		}
	}
	else if(nAddLength > 0){
		for(int i=0; i<yHeight; ++i){
			nNumberOfBytesWritten = fwrite(rgb + nLineLength * i, 1, nLineLength, pFile);
			nNumberOfBytesWritten = fwrite(padding, 1, nAddLength, pFile);
		}
	}
	else{
		nNumberOfBytesWritten = fwrite(rgb, 1, xWidth * yHeight*BPP/8, pFile);
	}
	fclose(pFile);
	return true;
}

PUCHAR Read1BppBMP(const char* sBmpFileName, int &nWidth, int &nHeight){
	FILE *inFile;
	if(!(inFile = fopen(sBmpFileName, "r"))){
		return NULL;
	}
	_BITMAPFILEHEADER fHD;
	_BITMAPINFOHEADER iHD;
	WORD bfType = 0;
	fread(&bfType, 1, sizeof(WORD), inFile);
	fread(&fHD, 1, sizeof(_BITMAPFILEHEADER), inFile);
	fread(&iHD, 1, sizeof(_BITMAPINFOHEADER), inFile);
	if(iHD.biBitCount != 1){
		fclose(inFile);
		return NULL;
	}
	nWidth = iHD.biWidth;
	nHeight = iHD.biHeight;

	fseek(inFile, fHD.bfOffBits, SEEK_SET);
	int nByteWidth = nWidth / 8;
	int nLineLength = nByteWidth;
	int nAddLength = (4 - nLineLength % 4) % 4;
	PUCHAR pBuf = new UCHAR[nByteWidth * nHeight];
	if(nAddLength > 0){
		for(int i=0; i<nHeight; ++i){
			fread(pBuf + nByteWidth * i, 1, nByteWidth, inFile);
			fseek(inFile, nAddLength, SEEK_CUR);
		}
	}
	else{
		fread(pBuf, 1, nLineLength * nHeight, inFile);
	}
	fclose(inFile);
	return pBuf;
}

void FlipImage(UCHAR *pImg, int nWidthStep, int nHeight) {
	UCHAR *pTmp = new UCHAR[nWidthStep];
	for(UCHAR *pEnd = pImg + nWidthStep * (nHeight - 1); pImg < pEnd; pImg += nWidthStep, pEnd -= nWidthStep) {
		memcpy(pTmp, pImg, nWidthStep);
		memcpy(pImg, pEnd, nWidthStep);
		memcpy(pEnd, pTmp, nWidthStep);
	}
	delete [] pTmp;
}
	
