您的位置:首页 > 编程语言 > C语言/C++

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

2017-06-26 19:27 741 查看

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 <iostream>
#include <Windows.h>
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;
}

9fa9

参考文献:

1. http://www.cnblogs.com/lyy289065406/archive/2011/08/25/2153030.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐