BMP文件学习笔记(一): C/C++语言实现.bmp文件读写

1 认识BMP文件

bmp文件结构:

第一部分:位图文件头BITMAPFILEHEADER,是一个结构。这个结构的长度是固定的,为14个字节(WORD为无符号16位整数,DWORD为无符号32位整数)。

其定义如下:
typedef struct tagBITMAPFILEHEADER {
WORD           bfType;//位图文件类型,必须是0x4D42,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM”
DWORD        bfSize;//位图文件大小,包括这14个字节
WORD           bfReserved1;//保留字,设为0
WORD           bfReserved2;//保留字,设为0
DWORD        bfOffBits;//从文件头到实际的位图数据的偏移字节数,单位:字节
} BITMAPFILEHEADER;


第二部分:位图信息头BITMAPINFOHEADER,是一个结构。这个结构的长度也是固定的,为40个字节(LONG为32位整数)。

其定义如下:

typedef struct tagBITMAPINFOHEADER{
DWORD        biSize;//本结构所占用字节数,大小为40字节
LONG            biWidth;//位图宽度,单位:字节
LONG            biHeight;//位图高度,单位:字节
WORD           biPlanes;//目标设备级别,必须为1
WORD           biBitCount;//表示颜色时每个像素要用到的位数,常用的值为1(黑白二色图), 4(16色图), 8(256色), 24(真彩色图)
DWORD        biCompression;// 位图是否压缩,其类型是 0(BI_RGB不压缩),  1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)
DWORD        biSizeImage;//实际的位图数据占用的字节数
LONG            biXPelsPerMeter;//位图水平分辨率,每米像素数
LONG            biYPelsPerMeter;//位图垂直分辨率,每米像素数
DWORD        biClrUsed;//指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2的biBitCount次幂个
DWORD        biClrImportant;//指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的
} BITMAPINFOHEADER;

第三部分:调色板Palette。这里是对那些需要调色板的位图文件而言的,有些位图,如真彩色图,是不需要调色板的,BITMAPINFOHEADER后直接是位图数据。
调色板实际上是一个数组,共有biClrUsed个元素(如果该值为零,则有2的biBitCount次幂个元素)。数组中每个元素的类型是一个RGBQUAD结构,占4个字节,其定义如下:
typedef struct tagRGBQUAD {
BYTE    rgbBlue; //该颜色的蓝色分量(值范围为0-255)  
BYTE    rgbGreen; //该颜色的绿色分量(值范围为0-255) 
BYTE    rgbRed; //该颜色的红色分量(值范围为0-255) 
BYTE    rgbReserved; //保留值,设为0
} RGBQUAD;

第四部分:实际的图象数据。对于用到调色板的位图,图象数据就是该象素颜在调色板中的索引值。对于真彩色图,图象数据就是实际的R、G、B值。
对于2色位图,用1位就可以表示该象素的颜色(一般0表示黑,1表示白),所以一个字节可以表示8个象素。
对于16色位图,用4位可以表示一个象素的颜色,所以一个字节可以表示2个象素。
对于256色位图,一个字节刚好可以表示1个象素。
对于真彩色图,三个字节表示1个象素。

注意:
(1)    每一行的字节数必须是4的整倍数,如果不是,则需要补齐。这在前面介绍biSizeImage时已经提到了。
(2)    一般来说,.bMP文件的数据从下到上,从左到右的。也就是说,从文件中最先读到的是图象最下面一行的左边第一个象素,然后是左边第二个象素……接下来是倒数第二行左边第一个象素,左边第二个象素……依次类推 ,最后得到的是最上面一行的最右一个象素。


2 编程

代码如下:
#include 
#include 
using std::cout;
using std::endl;

unsigned char * pData;
int width;
int height;
int bitCount;
RGBQUAD * pRGBQUAD;

bool readBmpFile(char * filename) 
{
	FILE * pf;
	pf = fopen(filename, "rb");
	if (NULL == pf)
	{
		cout << "文件打开失败!" << endl;
		fclose(pf);
		return false;
	}
	BITMAPFILEHEADER bitMapFileHeader;
	BITMAPINFOHEADER bitMapInfoHeader;
	fread(&bitMapFileHeader, sizeof(BITMAPFILEHEADER), 1, pf);
	if (0x4D42 != bitMapFileHeader.bfType)
	{
		cout << "此文件不是BMP文件!" << endl;
		return false;
	}
	fread(&bitMapInfoHeader, sizeof(BITMAPINFOHEADER), 1, pf);
	cout << "位图文件头:" << endl;
	cout << "位图文件类型: " << bitMapFileHeader.bfType << endl;
	cout << "位图文件大小: " << bitMapFileHeader.bfSize << endl;
	cout << "偏移的字节数: " << bitMapFileHeader.bfOffBits << endl;

	width = bitMapInfoHeader.biWidth;
	height = bitMapInfoHeader.biHeight;
	bitCount = bitMapInfoHeader.biBitCount;
	cout << "\n位图信息头:" << endl;
	cout << "信息头占用字节数:" << bitMapInfoHeader.biSize << endl;
	cout << "位图宽度: " << bitMapInfoHeader.biWidth << endl;
	cout << "位图高度: " << bitMapInfoHeader.biHeight << endl;
	cout << "位图压缩类型: " << bitMapInfoHeader.biCompression << endl;
	cout << "位图每像素占用位数: " << bitMapInfoHeader.biBitCount << endl;
	cout << "位图数据占用字节数: " << bitMapInfoHeader.biSizeImage << endl;

	
	if (8 == bitMapInfoHeader.biBitCount)
	{
		pRGBQUAD = new RGBQUAD[256];
		fread(pRGBQUAD, sizeof(RGBQUAD), 256, pf);
	}
	//数据每行字节数为4的倍数
	int lineByte = (bitMapInfoHeader.biWidth * bitMapInfoHeader.biBitCount / 8 + 3) / 4 * 4;
	pData = new unsigned char[bitMapInfoHeader.biHeight * lineByte];
	fread(pData, sizeof(unsigned char), bitMapInfoHeader.biHeight * lineByte, pf);
	fclose(pf);
	return true;
}

bool writeBmpFile(char * filename, unsigned char * pData, int biWidth, int biHeight, int biBitCount)
{
	FILE * pf;
	pf = fopen(filename, "wb");
	if (NULL == pf)
	{
		cout << "文件打开失败!" << endl;
		fclose(pf);
		return false;
	}

	int colorTablesize = 0;
	if (biBitCount == 8)
		colorTablesize = 1024;

	//待存储图像数据每行字节数为4的倍数
	int lineByte = (biWidth * biBitCount / 8 + 3) / 4 * 4;

	//申请位图文件头结构变量,填写文件头信息
	BITMAPFILEHEADER bitMapFileHeader;
	bitMapFileHeader.bfType = 0x4D42;//bmp类型
	bitMapFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTablesize + lineByte*biHeight;
	bitMapFileHeader.bfReserved1 = 0;
	bitMapFileHeader.bfReserved2 = 0;
	bitMapFileHeader.bfOffBits = 54 + colorTablesize;

	//申请位图信息头结构变量,填写信息头信息
	BITMAPINFOHEADER bitMapInfoHeader;
	bitMapInfoHeader.biBitCount = biBitCount;
	bitMapInfoHeader.biClrImportant = 0;
	bitMapInfoHeader.biClrUsed = 0;
	bitMapInfoHeader.biCompression = 0;
	bitMapInfoHeader.biHeight = biHeight;
	bitMapInfoHeader.biPlanes = 1;
	bitMapInfoHeader.biSize = 40;
	bitMapInfoHeader.biSizeImage = lineByte * biHeight;
	bitMapInfoHeader.biWidth = biWidth;
	bitMapInfoHeader.biXPelsPerMeter = 0;
	bitMapInfoHeader.biYPelsPerMeter = 0;

	//写文件头进文件
	fwrite(&bitMapFileHeader, sizeof(BITMAPFILEHEADER), 1, pf);
	//写位图信息头进内存
	fwrite(&bitMapInfoHeader, sizeof(BITMAPINFOHEADER), 1, pf);

	//如果灰度图像,有颜色表,写入文件 
	if (biBitCount == 8)
	{
		fwrite(pRGBQUAD, sizeof(RGBQUAD), 256, pf);
	}

	fwrite(pData, sizeof(unsigned char), biHeight * lineByte, pf);
	fclose(pf);
	return true;
}

int main()
{
	char * toReadfilename = "read.bmp";
	bool ret = readBmpFile(toReadfilename);
	if (!ret)
	{
		cout << "读BMP文件失败!" << endl;
		return -1;
	}
	else
	{
		cout << "读BMP文件成功!" << endl;
	}
	cout << endl;

	char * toWritefilename = "write.bmp";
	ret = writeBmpFile(toWritefilename, pData, width, height, bitCount);
	if (!ret)
	{
		cout << "写BMP文件失败!" << endl;
		return -1;
	}
	else
	{
		cout << "写BMP文件成功!" << endl;
	}

	delete[] pRGBQUAD;
	pRGBQUAD = NULL;

	delete[] pData;
	pData = NULL;

	return 0;
}

参考文献:
1.  http://www.cnblogs.com/lyy289065406/archive/2011/08/25/2153030.html


你可能感兴趣的:(C/C++学习笔记,c语言,C++,bmp,RGB,灰度)